mirror of
https://github.com/caddyserver/caddy.git
synced 2025-06-01 04:34:21 -04:00
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
This commit is contained in:
parent
3f2c3ecf85
commit
5fda9610f9
@ -91,9 +91,25 @@ func init() {
|
|||||||
// {{httpInclude "/foo/bar?q=val"}}
|
// {{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`
|
// ##### `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
|
// {{include "path/to/file.html"}} // no arguments
|
||||||
|
@ -46,24 +46,26 @@ type TemplateContext struct {
|
|||||||
RespHeader WrappedHeader
|
RespHeader WrappedHeader
|
||||||
|
|
||||||
config *Templates
|
config *Templates
|
||||||
|
tpl *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTemplate returns a new template intended to be evaluated with this
|
// NewTemplate returns a new template intended to be evaluated with this
|
||||||
// context, as it is initialized with configuration from this context.
|
// context, as it is initialized with configuration from this context.
|
||||||
func (c TemplateContext) NewTemplate(tplName string) *template.Template {
|
func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
|
||||||
tpl := template.New(tplName)
|
c.tpl = template.New(tplName)
|
||||||
|
|
||||||
// customize delimiters, if applicable
|
// customize delimiters, if applicable
|
||||||
if c.config != nil && len(c.config.Delimiters) == 2 {
|
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
|
// add sprig library
|
||||||
tpl.Funcs(sprigFuncMap)
|
c.tpl.Funcs(sprigFuncMap)
|
||||||
|
|
||||||
// add our own library
|
// add our own library
|
||||||
tpl.Funcs(template.FuncMap{
|
c.tpl.Funcs(template.FuncMap{
|
||||||
"include": c.funcInclude,
|
"include": c.funcInclude,
|
||||||
|
"import": c.funcImport,
|
||||||
"httpInclude": c.funcHTTPInclude,
|
"httpInclude": c.funcHTTPInclude,
|
||||||
"stripHTML": c.funcStripHTML,
|
"stripHTML": c.funcStripHTML,
|
||||||
"markdown": c.funcMarkdown,
|
"markdown": c.funcMarkdown,
|
||||||
@ -74,8 +76,7 @@ func (c TemplateContext) NewTemplate(tplName string) *template.Template {
|
|||||||
"fileExists": c.funcFileExists,
|
"fileExists": c.funcFileExists,
|
||||||
"httpError": c.funcHTTPError,
|
"httpError": c.funcHTTPError,
|
||||||
})
|
})
|
||||||
|
return c.tpl
|
||||||
return tpl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OriginalReq returns the original, unmodified, un-rewritten request as
|
// OriginalReq returns the original, unmodified, un-rewritten request as
|
||||||
@ -85,26 +86,13 @@ func (c TemplateContext) OriginalReq() http.Request {
|
|||||||
return or
|
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
|
// 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
|
// trusted files. If it is not trusted, be sure to use escaping functions
|
||||||
// in your template.
|
// in your template.
|
||||||
func (c TemplateContext) funcInclude(filename string, args ...interface{}) (string, error) {
|
func (c TemplateContext) funcInclude(filename string, args ...interface{}) (string, error) {
|
||||||
if c.Root == nil {
|
bodyBuf, err := c.readFileToBuffer(filename)
|
||||||
return "", fmt.Errorf("root file system not specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := c.Root.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
bodyBuf := bufPool.Get().(*bytes.Buffer)
|
|
||||||
bodyBuf.Reset()
|
|
||||||
defer bufPool.Put(bodyBuf)
|
|
||||||
|
|
||||||
_, err = io.Copy(bodyBuf, file)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -119,6 +107,30 @@ func (c TemplateContext) funcInclude(filename string, args ...interface{}) (stri
|
|||||||
return bodyBuf.String(), nil
|
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 nil, fmt.Errorf("root file system not specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := c.Root.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
bodyBuf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
bodyBuf.Reset()
|
||||||
|
defer bufPool.Put(bodyBuf)
|
||||||
|
|
||||||
|
_, err = io.Copy(bodyBuf, file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bodyBuf, nil
|
||||||
|
}
|
||||||
|
|
||||||
// funcHTTPInclude returns the body of a virtual (lightweight) request
|
// funcHTTPInclude returns the body of a virtual (lightweight) request
|
||||||
// to the given URI on the same server. Note that included bodies
|
// to the given URI on the same server. Note that included bodies
|
||||||
// are NOT escaped, so you should only include trusted resources.
|
// are NOT escaped, so you should only include trusted resources.
|
||||||
@ -167,17 +179,34 @@ func (c TemplateContext) funcHTTPInclude(uri string) (string, error) {
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
|
// funcImport parses the filename into the current template stack. The imported
|
||||||
tpl := c.NewTemplate(tplName)
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
parsedTpl, err := tpl.Parse(buf.String())
|
_, 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)
|
||||||
|
|
||||||
|
_, err := c.tpl.Parse(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Reset() // reuse buffer for output
|
buf.Reset() // reuse buffer for output
|
||||||
|
|
||||||
return parsedTpl.Execute(buf, c)
|
return c.tpl.Execute(buf, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c TemplateContext) funcPlaceholder(name string) string {
|
func (c TemplateContext) funcPlaceholder(name string) string {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user