@marcel91200 / caddy Mirror · read-only / commits / 5fda9610
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 04:00 UTC

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

Tim Culverhouse committed Sep 17, 2021 at 14:00 UTC 5fda9610f907482497a7b32abb2ab2f85a4e6906
2 files changed +69 -24
modules/caddyhttp/templates/templates.go
+17 -1
@@ -91,9 +91,25 @@ func init() {
91 // {{httpInclude "/foo/bar?q=val"}}
92 // ```
93 //
94 +// ##### `import`
95 +//
96 +// 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
97 +//
98 +// **filename.html**
99 +// ```
100 +// {{ define "main" }}
101 +// content
102 +// {{ end }}
103 +//
104 +// **index.html**
105 +// ```
106 +// {{ import "/path/to/file.html" }}
107 +// {{ template "main" }}
108 +// ```
109 +//
110 // ##### `include`
111 //
96 -// Includes the contents of another file. Optionally can pass key-value pairs as arguments to be accessed by the included file.
112 +// 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.
113 //
114 // ```
115 // {{include "path/to/file.html"}} // no arguments
modules/caddyhttp/templates/tplcontext.go
+52 -23
@@ -46,24 +46,26 @@ type TemplateContext struct {
46 RespHeader WrappedHeader
47
48 config *Templates
49 + tpl *template.Template
50 }
51
52 // NewTemplate returns a new template intended to be evaluated with this
53 // context, as it is initialized with configuration from this context.
53 -func (c TemplateContext) NewTemplate(tplName string) *template.Template {
54 - tpl := template.New(tplName)
54 +func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
55 + c.tpl = template.New(tplName)
56
57 // customize delimiters, if applicable
58 if c.config != nil && len(c.config.Delimiters) == 2 {
58 - tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
59 + c.tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
60 }
61
62 // add sprig library
62 - tpl.Funcs(sprigFuncMap)
63 + c.tpl.Funcs(sprigFuncMap)
64
65 // add our own library
65 - tpl.Funcs(template.FuncMap{
66 + c.tpl.Funcs(template.FuncMap{
67 "include": c.funcInclude,
68 + "import": c.funcImport,
69 "httpInclude": c.funcHTTPInclude,
70 "stripHTML": c.funcStripHTML,
71 "markdown": c.funcMarkdown,
@@ -74,8 +76,7 @@ func (c TemplateContext) NewTemplate(tplName string) *template.Template {
76 "fileExists": c.funcFileExists,
77 "httpError": c.funcHTTPError,
78 })
77 -
78 - return tpl
79 + return c.tpl
80 }
81
82 // OriginalReq returns the original, unmodified, un-rewritten request as
@@ -85,18 +86,36 @@ func (c TemplateContext) OriginalReq() http.Request {
86 return or
87 }
88
88 -// funcInclude returns the contents of filename relative to the site root.
89 +// funcInclude returns the contents of filename relative to the site root and renders it in place.
90 // Note that included files are NOT escaped, so you should only include
91 // trusted files. If it is not trusted, be sure to use escaping functions
92 // in your template.
93 func (c TemplateContext) funcInclude(filename string, args ...interface{}) (string, error) {
94 + bodyBuf, err := c.readFileToBuffer(filename)
95 +
96 + if err != nil {
97 + return "", err
98 + }
99 +
100 + c.Args = args
101 +
102 + err = c.executeTemplateInBuffer(filename, bodyBuf)
103 + if err != nil {
104 + return "", err
105 + }
106 +
107 + return bodyBuf.String(), nil
108 +}
109 +
110 +// readFileToBuffer returns the contents of filename relative to root as a buffer
111 +func (c TemplateContext) readFileToBuffer(filename string) (*bytes.Buffer, error) {
112 if c.Root == nil {
94 - return "", fmt.Errorf("root file system not specified")
113 + return nil, fmt.Errorf("root file system not specified")
114 }
115
116 file, err := c.Root.Open(filename)
117 if err != nil {
99 - return "", err
118 + return nil, err
119 }
120 defer file.Close()
121
@@ -106,17 +125,10 @@ func (c TemplateContext) funcInclude(filename string, args ...interface{}) (stri
125
126 _, err = io.Copy(bodyBuf, file)
127 if err != nil {
109 - return "", err
110 - }
111 -
112 - c.Args = args
113 -
114 - err = c.executeTemplateInBuffer(filename, bodyBuf)
115 - if err != nil {
116 - return "", err
128 + return nil, err
129 }
130
119 - return bodyBuf.String(), nil
131 + return bodyBuf, nil
132 }
133
134 // funcHTTPInclude returns the body of a virtual (lightweight) request
@@ -167,17 +179,34 @@ func (c TemplateContext) funcHTTPInclude(uri string) (string, error) {
179 return buf.String(), nil
180 }
181
170 -func (c TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
171 - tpl := c.NewTemplate(tplName)
182 +// funcImport parses the filename into the current template stack. The imported
183 +// file will be rendered within the current template by calling {{ block }} or
184 +// {{ template }} from the standard template library. If the imported file has
185 +// no {{ define }} blocks, the name of the import will be the path
186 +func (c *TemplateContext) funcImport(filename string) (string, error) {
187 + bodyBuf, err := c.readFileToBuffer(filename)
188 + if err != nil {
189 + return "", err
190 + }
191 +
192 + _, err = c.tpl.Parse(bodyBuf.String())
193 + if err != nil {
194 + return "", err
195 + }
196 + return "", nil
197 +}
198 +
199 +func (c *TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
200 + c.NewTemplate(tplName)
201
173 - parsedTpl, err := tpl.Parse(buf.String())
202 + _, err := c.tpl.Parse(buf.String())
203 if err != nil {
204 return err
205 }
206
207 buf.Reset() // reuse buffer for output
208
180 - return parsedTpl.Execute(buf, c)
209 + return c.tpl.Execute(buf, c)
210 }
211
212 func (c TemplateContext) funcPlaceholder(name string) string {