github.com/haraldLmueller/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  }