github.com/ruudk/buffalo@v0.11.0/render/template.go (about) 1 package render 2 3 import ( 4 "html/template" 5 "io" 6 "os" 7 "path/filepath" 8 "sort" 9 "strings" 10 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 14 // this blank import is here because dep doesn't 15 // handle transitive dependencies correctly 16 _ "github.com/russross/blackfriday" 17 ) 18 19 type templateRenderer struct { 20 *Engine 21 contentType string 22 names []string 23 } 24 25 func (s templateRenderer) ContentType() string { 26 return s.contentType 27 } 28 29 func (s templateRenderer) Render(w io.Writer, data Data) error { 30 var body template.HTML 31 var err error 32 for _, name := range s.names { 33 body, err = s.exec(name, data) 34 if err != nil { 35 return err 36 } 37 data["yield"] = body 38 } 39 w.Write([]byte(body)) 40 return nil 41 } 42 43 func (s templateRenderer) partial(name string, dd Data) (template.HTML, error) { 44 d, f := filepath.Split(name) 45 name = filepath.Join(d, "_"+f) 46 return s.exec(name, dd) 47 } 48 49 func (s templateRenderer) exec(name string, data Data) (template.HTML, error) { 50 ct := strings.ToLower(s.contentType) 51 data["contentType"] = ct 52 53 if filepath.Ext(name) == "" { 54 switch { 55 case strings.Contains(ct, "html"): 56 name += ".html" 57 case strings.Contains(ct, "javascript"): 58 name += ".js" 59 case strings.Contains(ct, "markdown"): 60 name += ".md" 61 } 62 } 63 64 // Try to use localized version 65 templateName := name 66 if languages, ok := data["languages"].([]string); ok { 67 ll := len(languages) 68 if ll > 0 { 69 // Default language is the last in the list 70 defaultLanguage := languages[ll-1] 71 ext := filepath.Ext(name) 72 rawName := strings.TrimSuffix(name, ext) 73 74 for _, l := range languages { 75 var candidateName string 76 if l == defaultLanguage { 77 break 78 } 79 candidateName = rawName + "." + strings.ToLower(l) + ext 80 if s.TemplatesBox.Has(candidateName) { 81 // Replace name with the existing suffixed version 82 templateName = candidateName 83 break 84 } 85 } 86 } 87 } 88 89 source, err := s.TemplatesBox.MustBytes(templateName) 90 if err != nil { 91 return "", err 92 } 93 94 helpers := map[string]interface{}{ 95 "partial": s.partial, 96 } 97 98 helpers = s.addAssetsHelpers(helpers) 99 100 for k, v := range s.Helpers { 101 helpers[k] = v 102 } 103 104 body := string(source) 105 for _, ext := range s.exts(name) { 106 te, ok := s.TemplateEngines[ext] 107 if !ok { 108 logrus.Errorf("could not find a template engine for %s\n", ext) 109 continue 110 } 111 body, err = te(body, data, helpers) 112 if err != nil { 113 return "", errors.Wrap(err, name) 114 } 115 } 116 117 return template.HTML(body), nil 118 } 119 120 func (s templateRenderer) exts(name string) []string { 121 exts := []string{} 122 for { 123 ext := filepath.Ext(name) 124 if ext == "" { 125 break 126 } 127 name = strings.TrimSuffix(name, ext) 128 exts = append(exts, strings.ToLower(ext[1:])) 129 } 130 if len(exts) == 0 { 131 return []string{"html"} 132 } 133 sort.Sort(sort.Reverse(sort.StringSlice(exts))) 134 return exts 135 } 136 137 func (s templateRenderer) assetPath(file string) (string, error) { 138 139 if len(assetMap) == 0 || os.Getenv("GO_ENV") != "production" { 140 manifest, err := s.AssetsBox.MustString("manifest.json") 141 142 if err != nil { 143 manifest, err = s.AssetsBox.MustString("assets/manifest.json") 144 if err != nil { 145 return assetPathFor(file), nil 146 } 147 } 148 149 err = loadManifest(manifest) 150 if err != nil { 151 return assetPathFor(file), errors.Wrap(err, "your manifest.json is not correct") 152 } 153 } 154 155 return assetPathFor(file), nil 156 } 157 158 // Template renders the named files using the specified 159 // content type and the github.com/gobuffalo/plush 160 // package for templating. If more than 1 file is provided 161 // the second file will be considered a "layout" file 162 // and the first file will be the "content" file which will 163 // be placed into the "layout" using "{{yield}}". 164 func Template(c string, names ...string) Renderer { 165 e := New(Options{}) 166 return e.Template(c, names...) 167 } 168 169 // Template renders the named files using the specified 170 // content type and the github.com/gobuffalo/plush 171 // package for templating. If more than 1 file is provided 172 // the second file will be considered a "layout" file 173 // and the first file will be the "content" file which will 174 // be placed into the "layout" using "{{yield}}". 175 func (e *Engine) Template(c string, names ...string) Renderer { 176 return templateRenderer{ 177 Engine: e, 178 contentType: c, 179 names: names, 180 } 181 }