Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 18:00 UTC
templates: Add custom template function registration (#4757)
* Add custom template function registration * Rename TemplateFunctions to CustomFunctions * Add documentation * Document CustomFunctions interface * Preallocate custom functions map list * Fix interface name in error message
Tyler Kropp committed
May 2, 2022 at 16:55 UTC
e84e19a04eefccc743f0d397efe49ff42626f4b3
2 files changed
+38
-8
modules/caddyhttp/templates/templates.go
+28
-4
@@ -21,6 +21,7 @@ import (
21
"net/http"
22
"strconv"
23
"strings"
24
+ "text/template"
25
26
"github.com/caddyserver/caddy/v2"
27
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -36,6 +37,8 @@ func init() {
37
//
38
// ⚠️ Template functions/actions are still experimental, so they are subject to change.
39
//
40
+// Custom template functions can be registered by creating a plugin module under the `http.handlers.templates.functions.*` namespace that implements the `CustomFunctions` interface.
41
+//
42
// [All Sprig functions](https://masterminds.github.io/sprig/) are supported.
43
//
44
// In addition to the standard functions and the Sprig library, Caddy adds
@@ -249,6 +252,14 @@ type Templates struct {
252
// The template action delimiters. If set, must be precisely two elements:
253
// the opening and closing delimiters. Default: `["{{", "}}"]`
254
Delimiters []string `json:"delimiters,omitempty"`
255
+
256
+ customFuncs []template.FuncMap
257
+}
258
+
259
+// Customfunctions is the interface for registering custom template functions.
260
+type CustomFunctions interface {
261
+ // CustomTemplateFunctions should return the mapping from custom function names to implementations.
262
+ CustomTemplateFunctions() template.FuncMap
263
}
264
265
// CaddyModule returns the Caddy module information.
@@ -261,6 +272,18 @@ func (Templates) CaddyModule() caddy.ModuleInfo {
272
273
// Provision provisions t.
274
func (t *Templates) Provision(ctx caddy.Context) error {
275
+ fnModInfos := caddy.GetModules("http.handlers.templates.functions")
276
+ customFuncs := make([]template.FuncMap, len(fnModInfos), 0)
277
+ for _, modInfo := range fnModInfos {
278
+ mod := modInfo.New()
279
+ fnMod, ok := mod.(CustomFunctions)
280
+ if !ok {
281
+ return fmt.Errorf("module %q does not satisfy the CustomFunctions interface", modInfo.ID)
282
+ }
283
+ customFuncs = append(customFuncs, fnMod.CustomTemplateFunctions())
284
+ }
285
+ t.customFuncs = customFuncs
286
+
287
if t.MIMETypes == nil {
288
t.MIMETypes = defaultMIMETypes
289
}
@@ -331,10 +354,11 @@ func (t *Templates) executeTemplate(rr caddyhttp.ResponseRecorder, r *http.Reque
354
}
355
356
ctx := &TemplateContext{
334
- Root: fs,
335
- Req: r,
336
- RespHeader: WrappedHeader{rr.Header()},
337
- config: t,
357
+ Root: fs,
358
+ Req: r,
359
+ RespHeader: WrappedHeader{rr.Header()},
360
+ config: t,
361
+ CustomFuncs: t.customFuncs,
362
}
363
364
err := ctx.executeTemplateInBuffer(r.URL.Path, rr.Buffer())
modules/caddyhttp/templates/tplcontext.go
+10
-4
@@ -40,10 +40,11 @@ import (
40
41
// TemplateContext is the TemplateContext with which HTTP templates are executed.
42
type TemplateContext struct {
43
- Root http.FileSystem
44
- Req *http.Request
45
- Args []interface{} // defined by arguments to funcInclude
46
- RespHeader WrappedHeader
43
+ Root http.FileSystem
44
+ Req *http.Request
45
+ Args []interface{} // defined by arguments to funcInclude
46
+ RespHeader WrappedHeader
47
+ CustomFuncs []template.FuncMap // functions added by plugins
48
49
config *Templates
50
tpl *template.Template
@@ -62,6 +63,11 @@ func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
63
// add sprig library
64
c.tpl.Funcs(sprigFuncMap)
65
66
+ // add all custom functions
67
+ for _, funcMap := range c.CustomFuncs {
68
+ c.tpl.Funcs(funcMap)
69
+ }
70
+
71
// add our own library
72
c.tpl.Funcs(template.FuncMap{
73
"include": c.funcInclude,