page.go (3686B)
1 package main 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 "notabug.org/gearsix/suti" 12 ) 13 14 const timefmt = "2006-01-02" 15 16 func titleFromPath(path string) (title string) { 17 if title = filepath.Base(path); title == "/" { 18 title = "Home" 19 } 20 title = strings.TrimSuffix(title, filepath.Ext(title)) 21 title = strings.ReplaceAll(title, "-", " ") 22 title = strings.Title(title) 23 return 24 } 25 26 func pagePath(root, path string) string { 27 path = strings.TrimPrefix(path, root) 28 if len(path) == 0 { 29 path = "/" 30 } else { 31 path = filepath.ToSlash(strings.TrimSuffix(path, filepath.Ext(path))) 32 } 33 return path 34 } 35 36 // Page is the data structure loaded from Content files/folders that 37 // gets passed to templates for execution after Content has been loaded. 38 // This is the data structure to reference when writing a template! 39 type Page struct { 40 Slug string 41 Path string 42 Nav Nav 43 Meta Meta 44 Contents []Content 45 Assets Assets 46 Updated string 47 } 48 49 type Assets struct { 50 All []string 51 Audio []*string 52 Image []*string 53 Video []*string 54 Misc []*string 55 } 56 57 // Nav is a struct that provides a set of pointers for navigating a 58 // across a set of pages. All values are initialised to nil and will only 59 // be populated manually or by calling `BuildSitemap`. 60 type Nav struct { 61 All []*Page 62 Root *Page 63 Parent *Page 64 Children []*Page 65 Crumbs []*Page 66 } 67 68 // Meta is the structure any metadata is parsed into (_.toml_, _.json_, etc) 69 type Meta map[string]interface{} 70 71 // MergeMeta merges `meta` into `m`. When there are matching keys in both, 72 // `overwrite` determines whether the existing value in `m` is overwritten. 73 func (m Meta) MergeMeta(meta Meta, overwrite bool) { 74 for k, v := range meta { 75 if _, ok := m[k]; ok && overwrite { 76 m[k] = v 77 } else if !ok { 78 m[k] = v 79 } 80 } 81 } 82 83 // NewPage returns a Page with init values. `.Path` will be set to `path`. 84 // Updated is set to time.Now(). Any other values will simply be initialised. 85 func NewPage(path string, updated time.Time) Page { 86 return Page{ 87 Slug: filepath.Base(path), 88 Path: path, 89 Nav: Nav{}, 90 Meta: Meta{"Title": titleFromPath(path)}, 91 Contents: make([]Content, 0), 92 Assets: Assets{}, 93 Updated: updated.Format(timefmt), 94 } 95 } 96 97 // GetTemplate will check if `p.Meta` has the key `template` or `Template` 98 // (in the order) and return the value of the first existing key as a string. 99 // If `.Meta` neither has the key `template` or `Template`, then it will 100 // return `DefaultTemplateName` from [./template.go]. 101 func (p *Page) TemplateName(defaultName string) string { 102 if v, ok := p.Meta["template"]; ok { 103 return v.(string) 104 } else if v, ok = p.Meta["Template"]; ok { 105 return v.(string) 106 } else { 107 return defaultName 108 } 109 } 110 111 // Build will run `t.Execute(p)` and write the result to 112 // `outDir/p.Path/index.html`. 113 func (p *Page) Build(outDir string, t suti.Template) (out string, err error) { 114 var buf bytes.Buffer 115 if buf, err = t.Execute(p); err == nil { 116 out = filepath.Join(outDir, p.Path, "index.html") 117 if err = os.MkdirAll(filepath.Dir(out), 0755); err == nil { 118 err = ioutil.WriteFile(out, buf.Bytes(), 0644) 119 } 120 } 121 return out, err 122 } 123 124 // call `NewContentFromFile` and append it to `p.Contents` 125 func (p *Page) NewContentFromFile(fpath string) (err error) { 126 var c Content 127 if c, err = NewContentFromFile(fpath); err == nil { 128 p.Contents = append(p.Contents, c) 129 } 130 return 131 } 132 133 func (page *Page) applyDefaults(defaultMetas map[string]Meta) { 134 for i, p := range page.Path { 135 if p != '/' { 136 continue 137 } 138 path := page.Path[:i] 139 if len(path) == 0 { 140 path = "/" 141 } 142 if meta, ok := defaultMetas[path]; ok { 143 page.Meta.MergeMeta(meta, false) 144 } 145 } 146 }