mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 10:12:29 +08: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:
		@ -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 {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user