summaryrefslogtreecommitdiff
path: root/modules/caddyhttp
diff options
context:
space:
mode:
authorTim Culverhouse <tim@timculverhouse.com>2021-09-17 14:00:36 -0500
committerGitHub <noreply@github.com>2021-09-17 13:00:36 -0600
commit5fda9610f907482497a7b32abb2ab2f85a4e6906 (patch)
tree847479be65b3267276ce2ea3a79be10c3e723c16 /modules/caddyhttp
parent3f2c3ecf85b590473030b21fd2b5fc9c8ef88744 (diff)
templates: Add 'import' action (#4321)
Related to (closed) Issue #2094 on template inheritance. This PR adds a new function called "import" which works like "include", except it only takes one argument and passes it to the referenced file to be used as "." in that file. * Update tplcontext.go Add {{ render "/path/to/file.ext" $data }} via funcRender * Update tplcontext.go * Refactor funcInclude, add funcImport to enable {{block}} and {{template}} * Fix funcImport return of nil showing up in html * Update godocs for and
Diffstat (limited to 'modules/caddyhttp')
-rw-r--r--modules/caddyhttp/templates/templates.go18
-rw-r--r--modules/caddyhttp/templates/tplcontext.go75
2 files changed, 69 insertions, 24 deletions
diff --git a/modules/caddyhttp/templates/templates.go b/modules/caddyhttp/templates/templates.go
index 6fed8c8..8f2ea2c 100644
--- a/modules/caddyhttp/templates/templates.go
+++ b/modules/caddyhttp/templates/templates.go
@@ -91,9 +91,25 @@ func init() {
// {{httpInclude "/foo/bar?q=val"}}
// ```
//
+// ##### `import`
+//
+// Imports the contents of another file and adds any template definitions to the template stack. If there are no defitions, the filepath will be the defition name. Any {{ define }} blocks will be accessible by {{ template }} or {{ block }}. Imports must happen before the template or block action is called
+//
+// **filename.html**
+// ```
+// {{ define "main" }}
+// content
+// {{ end }}
+//
+// **index.html**
+// ```
+// {{ import "/path/to/file.html" }}
+// {{ template "main" }}
+// ```
+//
// ##### `include`
//
-// Includes the contents of another file. Optionally can pass key-value pairs as arguments to be accessed by the included file.
+// Includes the contents of another file and renders in-place. Optionally can pass key-value pairs as arguments to be accessed by the included file.
//
// ```
// {{include "path/to/file.html"}} // no arguments
diff --git a/modules/caddyhttp/templates/tplcontext.go b/modules/caddyhttp/templates/tplcontext.go
index 14ac31b..71886c9 100644
--- a/modules/caddyhttp/templates/tplcontext.go
+++ b/modules/caddyhttp/templates/tplcontext.go
@@ -46,24 +46,26 @@ type TemplateContext struct {
RespHeader WrappedHeader
config *Templates
+ tpl *template.Template
}
// NewTemplate returns a new template intended to be evaluated with this
// context, as it is initialized with configuration from this context.
-func (c TemplateContext) NewTemplate(tplName string) *template.Template {
- tpl := template.New(tplName)
+func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
+ c.tpl = template.New(tplName)
// customize delimiters, if applicable
if c.config != nil && len(c.config.Delimiters) == 2 {
- tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
+ c.tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
}
// add sprig library
- tpl.Funcs(sprigFuncMap)
+ c.tpl.Funcs(sprigFuncMap)
// add our own library
- tpl.Funcs(template.FuncMap{
+ c.tpl.Funcs(template.FuncMap{
"include": c.funcInclude,
+ "import": c.funcImport,
"httpInclude": c.funcHTTPInclude,
"stripHTML": c.funcStripHTML,
"markdown": c.funcMarkdown,
@@ -74,8 +76,7 @@ func (c TemplateContext) NewTemplate(tplName string) *template.Template {
"fileExists": c.funcFileExists,
"httpError": c.funcHTTPError,
})
-
- return tpl
+ return c.tpl
}
// OriginalReq returns the original, unmodified, un-rewritten request as
@@ -85,18 +86,36 @@ func (c TemplateContext) OriginalReq() http.Request {
return or
}
-// funcInclude returns the contents of filename relative to the site root.
+// funcInclude returns the contents of filename relative to the site root and renders it in place.
// Note that included files are NOT escaped, so you should only include
// trusted files. If it is not trusted, be sure to use escaping functions
// in your template.
func (c TemplateContext) funcInclude(filename string, args ...interface{}) (string, error) {
+ bodyBuf, err := c.readFileToBuffer(filename)
+
+ if err != nil {
+ return "", err
+ }
+
+ c.Args = args
+
+ err = c.executeTemplateInBuffer(filename, bodyBuf)
+ if err != nil {
+ return "", err
+ }
+
+ return bodyBuf.String(), nil
+}
+
+// readFileToBuffer returns the contents of filename relative to root as a buffer
+func (c TemplateContext) readFileToBuffer(filename string) (*bytes.Buffer, error) {
if c.Root == nil {
- return "", fmt.Errorf("root file system not specified")
+ return nil, fmt.Errorf("root file system not specified")
}
file, err := c.Root.Open(filename)
if err != nil {
- return "", err
+ return nil, err
}
defer file.Close()
@@ -106,17 +125,10 @@ func (c TemplateContext) funcInclude(filename string, args ...interface{}) (stri
_, err = io.Copy(bodyBuf, file)
if err != nil {
- return "", err
- }
-
- c.Args = args
-
- err = c.executeTemplateInBuffer(filename, bodyBuf)
- if err != nil {
- return "", err
+ return nil, err
}
- return bodyBuf.String(), nil
+ return bodyBuf, nil
}
// funcHTTPInclude returns the body of a virtual (lightweight) request
@@ -167,17 +179,34 @@ func (c TemplateContext) funcHTTPInclude(uri string) (string, error) {
return buf.String(), nil
}
-func (c TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
- tpl := c.NewTemplate(tplName)
+// funcImport parses the filename into the current template stack. The imported
+// file will be rendered within the current template by calling {{ block }} or
+// {{ template }} from the standard template library. If the imported file has
+// no {{ define }} blocks, the name of the import will be the path
+func (c *TemplateContext) funcImport(filename string) (string, error) {
+ bodyBuf, err := c.readFileToBuffer(filename)
+ if err != nil {
+ return "", err
+ }
+
+ _, err = c.tpl.Parse(bodyBuf.String())
+ if err != nil {
+ return "", err
+ }
+ return "", nil
+}
+
+func (c *TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
+ c.NewTemplate(tplName)
- parsedTpl, err := tpl.Parse(buf.String())
+ _, err := c.tpl.Parse(buf.String())
if err != nil {
return err
}
buf.Reset() // reuse buffer for output
- return parsedTpl.Execute(buf, c)
+ return c.tpl.Execute(buf, c)
}
func (c TemplateContext) funcPlaceholder(name string) string {