1
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-16 21:56:40 -05:00

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
This commit is contained in:
Tyler Kropp 2022-05-02 16:55:34 -04:00 committed by GitHub
parent 4a223f5203
commit e84e19a04e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 8 deletions

View file

@ -21,6 +21,7 @@ import (
"net/http"
"strconv"
"strings"
"text/template"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
@ -36,6 +37,8 @@ func init() {
//
// ⚠️ Template functions/actions are still experimental, so they are subject to change.
//
// Custom template functions can be registered by creating a plugin module under the `http.handlers.templates.functions.*` namespace that implements the `CustomFunctions` interface.
//
// [All Sprig functions](https://masterminds.github.io/sprig/) are supported.
//
// In addition to the standard functions and the Sprig library, Caddy adds
@ -249,6 +252,14 @@ type Templates struct {
// The template action delimiters. If set, must be precisely two elements:
// the opening and closing delimiters. Default: `["{{", "}}"]`
Delimiters []string `json:"delimiters,omitempty"`
customFuncs []template.FuncMap
}
// Customfunctions is the interface for registering custom template functions.
type CustomFunctions interface {
// CustomTemplateFunctions should return the mapping from custom function names to implementations.
CustomTemplateFunctions() template.FuncMap
}
// CaddyModule returns the Caddy module information.
@ -261,6 +272,18 @@ func (Templates) CaddyModule() caddy.ModuleInfo {
// Provision provisions t.
func (t *Templates) Provision(ctx caddy.Context) error {
fnModInfos := caddy.GetModules("http.handlers.templates.functions")
customFuncs := make([]template.FuncMap, len(fnModInfos), 0)
for _, modInfo := range fnModInfos {
mod := modInfo.New()
fnMod, ok := mod.(CustomFunctions)
if !ok {
return fmt.Errorf("module %q does not satisfy the CustomFunctions interface", modInfo.ID)
}
customFuncs = append(customFuncs, fnMod.CustomTemplateFunctions())
}
t.customFuncs = customFuncs
if t.MIMETypes == nil {
t.MIMETypes = defaultMIMETypes
}
@ -331,10 +354,11 @@ func (t *Templates) executeTemplate(rr caddyhttp.ResponseRecorder, r *http.Reque
}
ctx := &TemplateContext{
Root: fs,
Req: r,
RespHeader: WrappedHeader{rr.Header()},
config: t,
Root: fs,
Req: r,
RespHeader: WrappedHeader{rr.Header()},
config: t,
CustomFuncs: t.customFuncs,
}
err := ctx.executeTemplateInBuffer(r.URL.Path, rr.Buffer())

View file

@ -40,10 +40,11 @@ import (
// TemplateContext is the TemplateContext with which HTTP templates are executed.
type TemplateContext struct {
Root http.FileSystem
Req *http.Request
Args []interface{} // defined by arguments to funcInclude
RespHeader WrappedHeader
Root http.FileSystem
Req *http.Request
Args []interface{} // defined by arguments to funcInclude
RespHeader WrappedHeader
CustomFuncs []template.FuncMap // functions added by plugins
config *Templates
tpl *template.Template
@ -62,6 +63,11 @@ func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
// add sprig library
c.tpl.Funcs(sprigFuncMap)
// add all custom functions
for _, funcMap := range c.CustomFuncs {
c.tpl.Funcs(funcMap)
}
// add our own library
c.tpl.Funcs(template.FuncMap{
"include": c.funcInclude,