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