@marcel91200 / caddy Mirror · read-only / commits / e84e19a0
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 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,