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 {