tpl: Rework to handle both text and HTML templates

Before this commit, Hugo used `html/template` for all Go templates.

While this is a fine choice for HTML and maybe also RSS feeds, it is painful for plain text formats such as CSV, JSON etc.

This commit fixes that by using the `IsPlainText` attribute on the output format to decide what to use.

A couple of notes:

* The above requires a nonambiguous template name to type mapping. I.e. `/layouts/_default/list.json` will only work if there is only one JSON output format, `/layouts/_default/list.mytype.json` will always work.
* Ambiguous types will fall back to HTML.
* Partials inherits the text vs HTML identificator of the container template. This also means that plain text templates can only include plain text partials.
* Shortcode templates are, by definition, currently HTML templates only.

Fixes #3221
This commit is contained in:
Bjørn Erik Pedersen
2017-03-27 20:43:49 +02:00
parent 73c1c7b69d
commit 5c5efa03d2
31 changed files with 1208 additions and 840 deletions

View File

@@ -45,7 +45,6 @@ import (
"github.com/bep/inflect"
"github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
jww "github.com/spf13/jwalterweatherman"
@@ -55,22 +54,6 @@ import (
_ "image/png"
)
// Some of the template funcs are'nt entirely stateless.
type templateFuncster struct {
funcMap template.FuncMap
cachedPartials partialCache
image *imageHandler
*deps.Deps
}
func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
return &templateFuncster{
Deps: deps,
cachedPartials: partialCache{p: make(map[string]template.HTML)},
image: &imageHandler{fs: deps.Fs, imageConfigCache: map[string]image.Config{}},
}
}
// eq returns the boolean truth of arg1 == arg2.
func eq(x, y interface{}) bool {
normalize := func(v interface{}) interface{} {
@@ -1558,13 +1541,13 @@ func replace(a, b, c interface{}) (string, error) {
// partialCache represents a cache of partials protected by a mutex.
type partialCache struct {
sync.RWMutex
p map[string]template.HTML
p map[string]interface{}
}
// Get retrieves partial output from the cache based upon the partial name.
// If the partial is not found in the cache, the partial is rendered and added
// to the cache.
func (t *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
func (t *templateFuncster) Get(key, name string, context interface{}) (p interface{}, err error) {
var ok bool
t.cachedPartials.RLock()
@@ -1572,13 +1555,13 @@ func (t *templateFuncster) Get(key, name string, context interface{}) (p templat
t.cachedPartials.RUnlock()
if ok {
return p
return
}
t.cachedPartials.Lock()
if p, ok = t.cachedPartials.p[key]; !ok {
t.cachedPartials.Unlock()
p = t.Tmpl.Partial(name, context)
p, err = t.partial(name, context)
t.cachedPartials.Lock()
t.cachedPartials.p[key] = p
@@ -1586,14 +1569,14 @@ func (t *templateFuncster) Get(key, name string, context interface{}) (p templat
}
t.cachedPartials.Unlock()
return p
return
}
// partialCached executes and caches partial templates. An optional variant
// string parameter (a string slice actually, but be only use a variadic
// argument to make it optional) can be passed so that a given partial can have
// multiple uses. The cache is created with name+variant as the key.
func (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
func (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) (interface{}, error) {
key := name
if len(variant) > 0 {
for i := 0; i < len(variant); i++ {
@@ -2195,7 +2178,7 @@ func (t *templateFuncster) initFuncMap() {
"mul": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
"ne": ne,
"now": func() time.Time { return time.Now() },
"partial": t.Tmpl.Partial,
"partial": t.partial,
"partialCached": t.partialCached,
"plainify": plainify,
"pluralize": pluralize,
@@ -2249,5 +2232,5 @@ func (t *templateFuncster) initFuncMap() {
}
t.funcMap = funcMap
t.Tmpl.Funcs(funcMap)
t.Tmpl.setFuncs(funcMap)
}