Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tplimpl: Allow text partials in HTML templates #3347

Merged
merged 1 commit into from Apr 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 11 additions & 12 deletions tpl/tplimpl/template.go
Expand Up @@ -97,23 +97,21 @@ func (t *templateHandler) PrintErrors() {
// Lookup tries to find a template with the given name in both template
// collections: First HTML, then the plain text template collection.
func (t *templateHandler) Lookup(name string) *tpl.TemplateAdapter {
var te *tpl.TemplateAdapter

isTextTemplate := strings.HasPrefix(name, textTmplNamePrefix)

if isTextTemplate {
if strings.HasPrefix(name, textTmplNamePrefix) {
// The caller has explicitly asked for a text template, so only look
// in the text template collection.
// The templates are stored without the prefix identificator.
name = strings.TrimPrefix(name, textTmplNamePrefix)
te = t.text.Lookup(name)
} else {
te = t.html.Lookup(name)
return t.text.Lookup(name)
}

if te == nil {
return nil
// Look in both
if te := t.html.Lookup(name); te != nil {
return te
}

return te
return t.text.Lookup(name)
}

func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
Expand Down Expand Up @@ -459,9 +457,10 @@ func (t *templateHandler) loadTemplates(absPath string, prefix string) {

func (t *templateHandler) initFuncs() {

// The template funcs need separation between text and html templates.
// Both template types will get their own funcster instance, which
// in the current case contains the same set of funcs.
for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} {
funcster := newTemplateFuncster(t.Deps, funcsterHolder)
funcster := newTemplateFuncster(t.Deps)

// The URL funcs in the funcMap is somewhat language dependent,
// so we need to wait until the language and site config is loaded.
Expand Down
18 changes: 6 additions & 12 deletions tpl/tplimpl/templateFuncster.go
Expand Up @@ -17,6 +17,7 @@ import (
"fmt"
"html/template"
"strings"
texttemplate "text/template"

bp "github.com/spf13/hugo/bufferpool"

Expand All @@ -31,17 +32,12 @@ type templateFuncster struct {
cachedPartials partialCache
image *imageHandler

// Make sure each funcster gets its own TemplateFinder to get
// proper text and HTML template separation.
Tmpl templateFuncsterTemplater

*deps.Deps
}

func newTemplateFuncster(deps *deps.Deps, t templateFuncsterTemplater) *templateFuncster {
func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
return &templateFuncster{
Deps: deps,
Tmpl: t,
cachedPartials: partialCache{p: make(map[string]interface{})},
image: &imageHandler{fs: deps.Fs, imageConfigCache: map[string]image.Config{}},
}
Expand Down Expand Up @@ -75,14 +71,12 @@ func (t *templateFuncster) partial(name string, contextList ...interface{}) (int
return "", err
}

switch t.Tmpl.(type) {
case *htmlTemplates:
return template.HTML(b.String()), nil
case *textTemplates:
if _, ok := templ.Template.(*texttemplate.Template); ok {
return b.String(), nil
default:
panic("Unknown type")
}

return template.HTML(b.String()), nil

}
}

Expand Down
2 changes: 1 addition & 1 deletion tpl/tplimpl/template_funcs.go
Expand Up @@ -2221,5 +2221,5 @@ func (t *templateFuncster) initFuncMap() {
}

t.funcMap = funcMap
t.Tmpl.setFuncs(funcMap)
t.Tmpl.(*templateHandler).setFuncs(funcMap)
}
27 changes: 19 additions & 8 deletions tpl/tplimpl/template_funcs_test.go
Expand Up @@ -2869,20 +2869,27 @@ func TestPartialHTMLAndText(t *testing.T) {
}

config.WithTemplate = func(templ tpl.TemplateHandler) error {
if err := templ.AddTemplate("htmlTemplate.html", `HTML Test Partial: {{ partial "test.foo" . -}}`); err != nil {
if err := templ.AddTemplate("htmlTemplate.html", `HTML Test|HTML:{{ partial "test.html" . -}}|Text:{{ partial "test.txt" . }}
CSS plain: <style type="text/css">{{ partial "mystyles.css" . -}}</style>
CSS safe: <style type="text/css">{{ partial "mystyles.css" . | safeCSS -}}</style>
`); err != nil {
return err
}

if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test Partial: {{ partial "test.foo" . -}}`); err != nil {
if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test|HTML:{{ partial "test.html" . -}}|Text:{{ partial "test.txt" . }}
CSS plain: <style type="text/css">{{ partial "mystyles.css" . -}}</style>`); err != nil {
return err
}

// Use "foo" here to say that the extension doesn't really matter in this scenario.
// It will look for templates in "partials/test.foo" and "partials/test.foo.html".
if err := templ.AddTemplate("partials/test.foo", "HTML Name: {{ .Name }}"); err != nil {
if err := templ.AddTemplate("partials/test.html", "HTML Name: {{ .Name }}"); err != nil {
return err
}
if err := templ.AddTemplate("_text/partials/test.txt", "Text Name: {{ .Name }}"); err != nil {
return err
}
if err := templ.AddTemplate("_text/partials/test.foo", "Text Name: {{ .Name }}"); err != nil {
if err := templ.AddTemplate("_text/partials/mystyles.css",
`body { background-color: blue; }
`); err != nil {
return err
}

Expand All @@ -2903,8 +2910,12 @@ func TestPartialHTMLAndText(t *testing.T) {
resultText, err := templ.ExecuteToString(data)
require.NoError(t, err)

require.Contains(t, resultHTML, "HTML Test Partial: HTML Name: a&#43;b&#43;c")
require.Contains(t, resultText, "Text Test Partial: Text Name: a+b+c")
require.Contains(t, resultHTML, "HTML Test|HTML:HTML Name: a&#43;b&#43;c|Text:Text Name: a&#43;b&#43;c")
require.Contains(t, resultHTML, `CSS plain: <style type="text/css">ZgotmplZ</style>`)
require.Contains(t, resultHTML, `CSS safe: <style type="text/css">body { background-color: blue; }`)

require.Contains(t, resultText, "Text Test|HTML:HTML Name: a&#43;b&#43;c|Text:Text Name: a+b+c")
require.Contains(t, resultText, `CSS plain: <style type="text/css">body { background-color: blue; }`)

}

Expand Down