diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-04-02 14:20:34 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-04-02 14:20:34 +0200 |
commit | 7eb71ee06419f9ceedfd701ab2a27513ef448829 (patch) | |
tree | 254ba6c75575e067df76b4abe4e566936619adcb /tpl | |
parent | c97dae40d9cd24c467f5b8cfbe2ac06f3cdef1d2 (diff) |
Revert "tpl: Rework to handle both text and HTML templates"
Will have to take another stab at this ...
This reverts commit 5c5efa03d2512749950b0d05a7d4bde35ecbdc37.
Closes #3260
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/template.go | 111 | ||||
-rw-r--r-- | tpl/tplimpl/ace.go | 51 | ||||
-rw-r--r-- | tpl/tplimpl/amber_compiler.go | 4 | ||||
-rw-r--r-- | tpl/tplimpl/template.go | 770 | ||||
-rw-r--r-- | tpl/tplimpl/templateFuncster.go | 86 | ||||
-rw-r--r-- | tpl/tplimpl/templateProvider.go | 59 | ||||
-rw-r--r-- | tpl/tplimpl/template_ast_transformers.go | 50 | ||||
-rw-r--r-- | tpl/tplimpl/template_ast_transformers_test.go | 12 | ||||
-rw-r--r-- | tpl/tplimpl/template_embedded.go | 57 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs.go | 33 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs_test.go | 106 | ||||
-rw-r--r-- | tpl/tplimpl/template_test.go | 232 |
12 files changed, 644 insertions, 927 deletions
diff --git a/tpl/template.go b/tpl/template.go index 617aa84ec..b94fc3242 100644 --- a/tpl/template.go +++ b/tpl/template.go @@ -1,103 +1,28 @@ -// Copyright 2017-present The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package tpl import ( - "io" - - "text/template/parse" - "html/template" - texttemplate "text/template" - - bp "github.com/spf13/hugo/bufferpool" -) - -var ( - _ TemplateExecutor = (*TemplateAdapter)(nil) + "io" ) -// TemplateHandler manages the collection of templates. -type TemplateHandler interface { - TemplateFinder +// TODO(bep) make smaller +type Template interface { + ExecuteTemplate(wr io.Writer, name string, data interface{}) error + ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML + Lookup(name string) *template.Template + Templates() []*template.Template + New(name string) *template.Template + GetClone() *template.Template + RebuildClone() *template.Template + LoadTemplates(absPath string) + LoadTemplatesWithPrefix(absPath, prefix string) AddTemplate(name, tpl string) error - AddLateTemplate(name, tpl string) error - LoadTemplates(absPath, prefix string) + AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error + AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error + AddInternalTemplate(prefix, name, tpl string) error + AddInternalShortcode(name, tpl string) error + Partial(name string, contextList ...interface{}) template.HTML PrintErrors() - + Funcs(funcMap template.FuncMap) MarkReady() - RebuildClone() -} - -// TemplateFinder finds templates. -type TemplateFinder interface { - Lookup(name string) *TemplateAdapter -} - -// Template is the common interface between text/template and html/template. -type Template interface { - Execute(wr io.Writer, data interface{}) error - Name() string -} - -// TemplateExecutor adds some extras to Template. -type TemplateExecutor interface { - Template - ExecuteToString(data interface{}) (string, error) - Tree() string -} - -// TemplateAdapter implements the TemplateExecutor interface. -type TemplateAdapter struct { - Template -} - -// ExecuteToString executes the current template and returns the result as a -// string. -func (t *TemplateAdapter) ExecuteToString(data interface{}) (string, error) { - b := bp.GetBuffer() - defer bp.PutBuffer(b) - if err := t.Execute(b, data); err != nil { - return "", err - } - return b.String(), nil -} - -// Tree returns the template Parse tree as a string. -// Note: this isn't safe for parallel execution on the same template -// vs Lookup and Execute. -func (t *TemplateAdapter) Tree() string { - var tree *parse.Tree - switch tt := t.Template.(type) { - case *template.Template: - tree = tt.Tree - case *texttemplate.Template: - tree = tt.Tree - default: - panic("Unknown template") - } - - if tree.Root == nil { - return "" - } - s := tree.Root.String() - - return s -} - -// TemplateTestMocker adds a way to override some template funcs during tests. -// The interface is named so it's not used in regular application code. -type TemplateTestMocker interface { - SetFuncs(funcMap map[string]interface{}) } diff --git a/tpl/tplimpl/ace.go b/tpl/tplimpl/ace.go deleted file mode 100644 index fc3a1e1b1..000000000 --- a/tpl/tplimpl/ace.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017-present The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tplimpl - -import ( - "path/filepath" - - "strings" - - "github.com/yosssi/ace" -) - -func (t *templateHandler) addAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error { - t.checkState() - var base, inner *ace.File - name = name[:len(name)-len(filepath.Ext(innerPath))] + ".html" - - // Fixes issue #1178 - basePath = strings.Replace(basePath, "\\", "/", -1) - innerPath = strings.Replace(innerPath, "\\", "/", -1) - - if basePath != "" { - base = ace.NewFile(basePath, baseContent) - inner = ace.NewFile(innerPath, innerContent) - } else { - base = ace.NewFile(innerPath, innerContent) - inner = ace.NewFile("", []byte{}) - } - parsed, err := ace.ParseSource(ace.NewSource(base, inner, []*ace.File{}), nil) - if err != nil { - t.errors = append(t.errors, &templateErr{name: name, err: err}) - return err - } - templ, err := ace.CompileResultWithTemplate(t.html.t.New(name), parsed, nil) - if err != nil { - t.errors = append(t.errors, &templateErr{name: name, err: err}) - return err - } - return applyTemplateTransformersToHMLTTemplate(templ) -} diff --git a/tpl/tplimpl/amber_compiler.go b/tpl/tplimpl/amber_compiler.go index 10ed0443c..252c39ffb 100644 --- a/tpl/tplimpl/amber_compiler.go +++ b/tpl/tplimpl/amber_compiler.go @@ -19,7 +19,7 @@ import ( "github.com/eknkc/amber" ) -func (t *templateHandler) compileAmberWithTemplate(b []byte, path string, templ *template.Template) (*template.Template, error) { +func (gt *GoHTMLTemplate) CompileAmberWithTemplate(b []byte, path string, t *template.Template) (*template.Template, error) { c := amber.New() if err := c.ParseData(b, path); err != nil { @@ -32,7 +32,7 @@ func (t *templateHandler) compileAmberWithTemplate(b []byte, path string, templ return nil, err } - tpl, err := templ.Funcs(t.amberFuncMap).Parse(data) + tpl, err := t.Funcs(gt.amberFuncMap).Parse(data) if err != nil { return nil, err diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 49be4a769..ea12cde7a 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -1,4 +1,4 @@ -// Copyright 2017-present The Hugo Authors. All rights reserved. +// Copyright 2016 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,39 +15,23 @@ package tplimpl import ( "html/template" - "strings" - texttemplate "text/template" - - "github.com/eknkc/amber" - + "io" "os" - - "github.com/spf13/hugo/output" - "path/filepath" + "strings" + "sync" + "github.com/eknkc/amber" "github.com/spf13/afero" + bp "github.com/spf13/hugo/bufferpool" "github.com/spf13/hugo/deps" "github.com/spf13/hugo/helpers" - "github.com/spf13/hugo/tpl" -) - -const ( - textTmplNamePrefix = "_text/" + "github.com/spf13/hugo/output" + "github.com/yosssi/ace" ) -var ( - _ tpl.TemplateHandler = (*templateHandler)(nil) - _ tpl.TemplateTestMocker = (*templateHandler)(nil) - _ tpl.TemplateFinder = (*htmlTemplates)(nil) - _ tpl.TemplateFinder = (*textTemplates)(nil) - _ templateLoader = (*htmlTemplates)(nil) - _ templateLoader = (*textTemplates)(nil) - _ templateLoader = (*templateHandler)(nil) - _ templateFuncsterTemplater = (*htmlTemplates)(nil) - _ templateFuncsterTemplater = (*textTemplates)(nil) -) +// TODO(bep) globals get rid of the rest of the jww.ERR etc. // Protecting global map access (Amber) var amberMu sync.Mutex @@ -57,539 +41,340 @@ type templateErr struct { err error } -type templateLoader interface { - handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error - addTemplate(name, tpl string) error - addLateTemplate(name, tpl string) error -} - -type templateFuncsterTemplater interface { - tpl.TemplateFinder - setFuncs(funcMap map[string]interface{}) - setTemplateFuncster(f *templateFuncster) -} +type GoHTMLTemplate struct { + *template.Template -// templateHandler holds the templates in play. -// It implements the templateLoader and tpl.TemplateHandler interfaces. -type templateHandler struct { - // text holds all the pure text templates. - text *textTemplates - html *htmlTemplates + // This looks, and is, strange. + // The clone is used by non-renderable content pages, and these need to be + // re-parsed on content change, and to avoid the + // "cannot Parse after Execute" error, we need to re-clone it from the original clone. + clone *template.Template + cloneClone *template.Template - amberFuncMap template.FuncMap + // a separate storage for the overlays created from cloned master templates. + // note: No mutex protection, so we add these in one Go routine, then just read. + overlays map[string]*template.Template errors []*templateErr + funcster *templateFuncster + + amberFuncMap template.FuncMap + *deps.Deps } -func (t *templateHandler) addError(name string, err error) { - t.errors = append(t.errors, &templateErr{name, err}) -} +type TemplateProvider struct{} + +var DefaultTemplateProvider *TemplateProvider -// PrintErrors prints the accumulated errors as ERROR to the log. -func (t *templateHandler) PrintErrors() { - for _, e := range t.errors { - t.Log.ERROR.Println(e.name, ":", e.err) +// Update updates the Hugo Template System in the provided Deps. +// with all the additional features, templates & functions +func (*TemplateProvider) Update(deps *deps.Deps) error { + tmpl := &GoHTMLTemplate{ + Template: template.New(""), + overlays: make(map[string]*template.Template), + errors: make([]*templateErr, 0), + Deps: deps, } -} -// 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 + deps.Tmpl = tmpl - isTextTemplate := strings.HasPrefix(name, textTmplNamePrefix) + tmpl.initFuncs(deps) - if isTextTemplate { - // The templates are stored without the prefix identificator. - name = strings.TrimPrefix(name, textTmplNamePrefix) - te = t.text.Lookup(name) - } else { - te = t.html.Lookup(name) - } + tmpl.LoadEmbedded() + + if deps.WithTemplate != nil { + err := deps.WithTemplate(tmpl) + if err != nil { + tmpl.errors = append(tmpl.errors, &templateErr{"init", err}) + } - if te == nil { - return nil } - return te + tmpl.MarkReady() + + return nil + } -func (t *templateHandler) clone(d *deps.Deps) *templateHandler { - c := &templateHandler{ - Deps: d, - html: &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template)}, - text: &textTemplates{t: texttemplate.Must(t.text.t.Clone()), overlays: make(map[string]*texttemplate.Template)}, - errors: make([]*templateErr, 0), - } +// Clone clones +func (*TemplateProvider) Clone(d *deps.Deps) error { - d.Tmpl = c + t := d.Tmpl.(*GoHTMLTemplate) - c.initFuncs() + // 1. Clone the clone with new template funcs + // 2. Clone any overlays with new template funcs + + tmpl := &GoHTMLTemplate{ + Template: template.Must(t.Template.Clone()), + overlays: make(map[string]*template.Template), + errors: make([]*templateErr, 0), + Deps: d, + } - for k, v := range t.html.overlays { + d.Tmpl = tmpl + tmpl.initFuncs(d) + + for k, v := range t.overlays { vc := template.Must(v.Clone()) // The extra lookup is a workaround, see // * https://github.com/golang/go/issues/16101 // * https://github.com/spf13/hugo/issues/2549 vc = vc.Lookup(vc.Name()) - vc.Funcs(t.html.funcster.funcMap) - c.html.overlays[k] = vc - } - - for k, v := range t.text.overlays { - vc := texttemplate.Must(v.Clone()) - vc = vc.Lookup(vc.Name()) - vc.Funcs(texttemplate.FuncMap(t.text.funcster.funcMap)) - c.text.overlays[k] = vc + vc.Funcs(tmpl.funcster.funcMap) + tmpl.overlays[k] = vc } - return c + tmpl.MarkReady() + return nil } -func newTemplateAdapter(deps *deps.Deps) *templateHandler { - htmlT := &htmlTemplates{ - t: template.New(""), - overlays: make(map[string]*template.Template), - } - textT := &textTemplates{ - t: texttemplate.New(""), - overlays: make(map[string]*texttemplate.Template), - } - return &templateHandler{ - Deps: deps, - html: htmlT, - text: textT, - errors: make([]*templateErr, 0), - } +func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) { -} + t.funcster = newTemplateFuncster(d) -type htmlTemplates struct { - funcster *templateFuncster + // The URL funcs in the funcMap is somewhat language dependent, + // so we need to wait until the language and site config is loaded. + t.funcster.initFuncMap() - t *template.Template + t.amberFuncMap = template.FuncMap{} - // This looks, and is, strange. - // The clone is used by non-renderable content pages, and these need to be - // re-parsed on content change, and to avoid the - // "cannot Parse after Execute" error, we need to re-clone it from the original clone. - clone *template.Template - cloneClone *template.Template + amberMu.Lock() + for k, v := range amber.FuncMap { + t.amberFuncMap[k] = v + } - // a separate storage for the overlays created from cloned master templates. - // note: No mutex protection, so we add these in one Go routine, then just read. - overlays map[string]*template.Template -} + for k, v := range t.funcster.funcMap { + t.amberFuncMap[k] = v + // Hacky, but we need to make sure that the func names are in the global map. + amber.FuncMap[k] = func() string { + panic("should never be invoked") + } + } + amberMu.Unlock() -func (t *htmlTemplates) setTemplateFuncster(f *templateFuncster) { - t.funcster = f } -func (t *htmlTemplates) Lookup(name string) *tpl.TemplateAdapter { - templ := t.lookup(name) - if templ == nil { - return nil - } - return &tpl.TemplateAdapter{templ} +func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) { + t.Template.Funcs(funcMap) } -func (t *htmlTemplates) lookup(name string) *template.Template { - if templ := t.t.Lookup(name); templ != nil { - return templ - } - if t.overlays != nil { - if templ, ok := t.overlays[name]; ok { - return templ - } +func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML { + if strings.HasPrefix("partials/", name) { + name = name[8:] } + var context interface{} - if t.clone != nil { - return t.clone.Lookup(name) + if len(contextList) == 0 { + context = nil + } else { + context = contextList[0] } - - return nil + return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name) } -type textTemplates struct { - funcster *templateFuncster - - t *texttemplate.Template - - clone *texttemplate.Template - cloneClone *texttemplate.Template +func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) { + var worked bool + for _, layout := range layouts { + templ := t.Lookup(layout) + if templ == nil { + // TODO(bep) output + layout += ".html" + templ = t.Lookup(layout) + } - overlays map[string]*texttemplate.Template + if templ != nil { + if err := templ.Execute(w, context); err != nil { + helpers.DistinctErrorLog.Println(layout, err) + } + worked = true + break + } + } + if !worked { + t.Log.ERROR.Println("Unable to render", layouts) + t.Log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts) + } } -func (t *textTemplates) setTemplateFuncster(f *templateFuncster) { - t.funcster = f +func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML { + b := bp.GetBuffer() + defer bp.PutBuffer(b) + t.executeTemplate(context, b, layouts...) + return template.HTML(b.String()) } -func (t *textTemplates) Lookup(name string) *tpl.TemplateAdapter { - templ := t.lookup(name) - if templ == nil { - return nil - } - return &tpl.TemplateAdapter{templ} -} +func (t *GoHTMLTemplate) Lookup(name string) *template.Template { -func (t *textTemplates) lookup(name string) *texttemplate.Template { - if templ := t.t.Lookup(name); templ != nil { + if templ := t.Template.Lookup(name); templ != nil { return templ } + if t.overlays != nil { if templ, ok := t.overlays[name]; ok { return templ } } + // The clone is used for the non-renderable HTML pages (p.IsRenderable == false) that is parsed + // as Go templates late in the build process. if t.clone != nil { - return t.clone.Lookup(name) + if templ := t.clone.Lookup(name); templ != nil { + return templ + } } return nil -} - -func (t *templateHandler) setFuncs(funcMap map[string]interface{}) { - t.html.setFuncs(funcMap) - t.text.setFuncs(funcMap) -} -// SetFuncs replaces the funcs in the func maps with new definitions. -// This is only used in tests. -func (t *templateHandler) SetFuncs(funcMap map[string]interface{}) { - t.setFuncs(funcMap) } -func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) { - t.t.Funcs(funcMap) +func (t *GoHTMLTemplate) GetClone() *template.Template { + return t.clone } -func (t *textTemplates) setFuncs(funcMap map[string]interface{}) { - t.t.Funcs(funcMap) +func (t *GoHTMLTemplate) RebuildClone() *template.Template { + t.clone = template.Must(t.cloneClone.Clone()) + return t.clone } -// LoadTemplates loads the templates, starting from the given absolute path. -// A prefix can be given to indicate a template namespace to load the templates -// into, i.e. "_internal" etc. -func (t *templateHandler) LoadTemplates(absPath, prefix string) { - // TODO(bep) output formats. Will have to get to complete list when that is ready. - t.loadTemplates(absPath, prefix, output.Formats{output.HTMLFormat, output.RSSFormat, output.CalendarFormat, output.AMPFormat, output.JSONFormat}) - +func (t *GoHTMLTemplate) LoadEmbedded() { + t.EmbedShortcodes() + t.EmbedTemplates() } -func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) error { - templ, err := tt.New(name).Parse(tpl) - if err != nil { - return err +// MarkReady marks the template as "ready for execution". No changes allowed +// after this is set. +// TODO(bep) if this proves to be resource heavy, we could detect +// earlier if we really need this, or make it lazy. +func (t *GoHTMLTemplate) MarkReady() { + if t.clone == nil { + t.clone = template.Must(t.Template.Clone()) + t.cloneClone = template.Must(t.clone.Clone()) } +} - if err := applyTemplateTransformersToHMLTTemplate(templ); err != nil { - return err +func (t *GoHTMLTemplate) checkState() { + if t.clone != nil { + panic("template is cloned and cannot be modfified") } - - return nil } -func (t *htmlTemplates) addTemplate(name, tpl string) error { - return t.addTemplateIn(t.t, name, tpl) +func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error { + if prefix != "" { + return t.AddTemplate("_internal/"+prefix+"/"+name, tpl) + } + return t.AddTemplate("_internal/"+name, tpl) } -func (t *htmlTemplates) addLateTemplate(name, tpl string) error { - return t.addTemplateIn(t.clone, name, tpl) +func (t *GoHTMLTemplate) AddInternalShortcode(name, content string) error { + return t.AddInternalTemplate("shortcodes", name, content) } -func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error { - name = strings.TrimPrefix(name, textTmplNamePrefix) - templ, err := tt.New(name).Parse(tpl) +func (t *GoHTMLTemplate) AddTemplate(name, tpl string) error { + t.checkState() + templ, err := t.New(name).Parse(tpl) if err != nil { + t.errors = append(t.errors, &templateErr{name: name, err: err}) return err } - - if err := applyTemplateTransformersToTextTemplate(templ); err != nil { - return err - } - - return nil -} - -func (t *textTemplates) addTemplate(name, tpl string) error { - return t.addTemplateIn(t.t, name, tpl) -} - -func (t *textTemplates) addLateTemplate(name, tpl string) error { - return t.addTemplateIn(t.clone, name, tpl) -} - -func (t *templateHandler) addTemplate(name, tpl string) error { - return t.AddTemplate(name, tpl) -} - -func (t *templateHandler) addLateTemplate(name, tpl string) error { - return t.AddLateTemplate(name, tpl) -} - -// AddLateTemplate is used to add a template late, i.e. after the -// regular templates have started its execution. -func (t *templateHandler) AddLateTemplate(name, tpl string) error { - h := t.getTemplateHandler(name) - if err := h.addLateTemplate(name, tpl); err != nil { - t.addError(name, err) + if err := applyTemplateTransformers(templ); err != nil { return err } - return nil -} -// AddTemplate parses and adds a template to the collection. -// Templates with name prefixed with "_text" will be handled as plain -// text templates. -func (t *templateHandler) AddTemplate(name, tpl string) error { - h := t.getTemplateHandler(name) - if err := h.addTemplate(name, tpl); err != nil { - t.addError(name, err) - return err - } return nil } -// MarkReady marks the templates as "ready for execution". No changes allowed -// after this is set. -// TODO(bep) if this proves to be resource heavy, we could detect -// earlier if we really need this, or make it lazy. -func (t *templateHandler) MarkReady() { - if t.html.clone == nil { - t.html.clone = template.Must(t.html.t.Clone()) - t.html.cloneClone = template.Must(t.html.clone.Clone()) - } - if t.text.clone == nil { - t.text.clone = texttemplate.Must(t.text.t.Clone()) - t.text.cloneClone = texttemplate.Must(t.text.clone.Clone()) - } -} - -// RebuildClone rebuilds the cloned templates. Used for live-reloads. -func (t *templateHandler) RebuildClone() { - t.html.clone = template.Must(t.html.cloneClone.Clone()) - t.text.clone = texttemplate.Must(t.text.cloneClone.Clone()) -} - -func (t *templateHandler) loadTemplates(absPath string, prefix string, formats output.Formats) { - t.Log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix) - walker := func(path string, fi os.FileInfo, err error) error { - if err != nil { - return nil - } - - t.Log.DEBUG.Println("Template path", path) - if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - link, err := filepath.EvalSymlinks(absPath) - if err != nil { - t.Log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err) - return nil - } - - linkfi, err := t.Fs.Source.Stat(link) - if err != nil { - t.Log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err) - return nil - } - - if !linkfi.Mode().IsRegular() { - t.Log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath) - } - return nil - } - - if !fi.IsDir() { - if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) { - return nil - } - - var ( - workingDir = t.PathSpec.WorkingDir() - themeDir = t.PathSpec.GetThemeDir() - layoutDir = t.PathSpec.LayoutDir() - ) - - if themeDir != "" && strings.HasPrefix(absPath, themeDir) { - workingDir = themeDir - layoutDir = "layouts" - } - - li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1 - relPath := path[li:] - - descriptor := output.TemplateLookupDescriptor{ - WorkingDir: workingDir, - LayoutDir: layoutDir, - RelPath: relPath, - Prefix: prefix, - Theme: t.PathSpec.Theme(), - OutputFormats: formats, - FileExists: func(filename string) (bool, error) { - return helpers.Exists(filename, t.Fs.Source) - }, - ContainsAny: func(filename string, subslices [][]byte) (bool, error) { - return helpers.FileContainsAny(filename, subslices, t.Fs.Source) - }, - } - - tplID, err := output.CreateTemplateNames(descriptor) - if err != nil { - t.Log.ERROR.Printf("Failed to resolve template in path %q: %s", path, err) - - return nil - } - - if err := t.addTemplateFile(tplID.Name, tplID.MasterFilename, tplID.OverlayFilename); err != nil { - t.Log.ERROR.Printf("Failed to add template %q in path %q: %s", tplID.Name, path, err) - } - - } - return nil - } - if err := helpers.SymbolicWalk(t.Fs.Source, absPath, walker); err != nil { - t.Log.ERROR.Printf("Failed to load templates: %s", err) - } -} - -func (t *templateHandler) initFuncs() { - - // The template funcs need separation between text and html templates. - for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} { - funcster := newTemplateFuncster(t.Deps, funcsterHolder) - - // The URL funcs in the funcMap is somewhat language dependent, - // so we need to wait until the language and site config is loaded. - funcster.initFuncMap() - - funcsterHolder.setTemplateFuncster(funcster) - - } +func (t *GoHTMLTemplate) |