diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-25 09:24:59 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-25 19:53:18 +0100 |
commit | ce524d0b5ebaef05d29fa368465f31358f26dcda (patch) | |
tree | e5df54a5deeefacbff4916d3619f85c2cb341b01 | |
parent | 2662faf61ff0240be1ee0d6c496b6b4a6ed55fb4 (diff) |
Add a page template func
Fixes #9339
54 files changed, 436 insertions, 108 deletions
diff --git a/commands/server.go b/commands/server.go index 332e35c01..4f7d4db8f 100644 --- a/commands/server.go +++ b/commands/server.go @@ -577,7 +577,7 @@ func (c *commandeer) serve(s *serverCmd) error { // to cached values if nil. templ, handler := getErrorTemplateAndHandler(c.hugoTry()) b := &bytes.Buffer{} - err := handler.Execute(templ, b, ctx) + err := handler.ExecuteWithContext(context.Background(), templ, b, ctx) return b, err }, } diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index 1b7e5acf7..17afbf912 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -208,6 +208,23 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) { return time.Time{}, false } +func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value { + fn := v.MethodByName(name) + var args []reflect.Value + tp := fn.Type() + if tp.NumIn() > 0 { + if tp.NumIn() > 1 { + panic("not supported") + } + first := tp.In(0) + if first.Implements(ContextInterface) { + args = append(args, reflect.ValueOf(cxt)) + } + } + + return fn.Call(args) +} + // Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931 func indirectInterface(v reflect.Value) reflect.Value { if v.Kind() != reflect.Interface { diff --git a/hugolib/alias.go b/hugolib/alias.go index 2609cd6bb..071f73d41 100644 --- a/hugolib/alias.go +++ b/hugolib/alias.go @@ -15,6 +15,7 @@ package hugolib import ( "bytes" + "context" "errors" "fmt" "io" @@ -64,8 +65,10 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, err p, } + ctx := tpl.SetPageInContext(context.Background(), p) + buffer := new(bytes.Buffer) - err := a.t.Execute(templ, buffer, data) + err := a.t.ExecuteWithContext(ctx, templ, buffer, data) if err != nil { return nil, err } diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index cdc5d97fb..65cc23971 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -767,10 +767,11 @@ func (h *HugoSites) renderCrossSitesSitemap() error { } s := h.Sites[0] + // We don't have any page context to pass in here. + ctx := context.Background() templ := s.lookupLayouts("sitemapindex.xml", "_default/sitemapindex.xml", "_internal/_default/sitemapindex.xml") - - return s.renderAndWriteXML(&s.PathSpec.ProcessingStats.Sitemaps, "sitemapindex", + return s.renderAndWriteXML(ctx, &s.PathSpec.ProcessingStats.Sitemaps, "sitemapindex", s.siteCfg.sitemap.Filename, h.toSiteInfos(), templ) } diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index ce3498e0e..be65ad9e7 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -740,6 +740,7 @@ func (cp *pageContentOutput) ParseContent(ctx context.Context, content []byte) ( return nil, ok, nil } rctx := converter.RenderContext{ + Ctx: ctx, Src: content, RenderTOC: true, GetRenderer: cp.renderHooks.getRenderer, @@ -758,6 +759,7 @@ func (cp *pageContentOutput) RenderContent(ctx context.Context, content []byte, return nil, ok, nil } rctx := converter.RenderContext{ + Ctx: ctx, Src: content, RenderTOC: true, GetRenderer: cp.renderHooks.getRenderer, @@ -777,6 +779,7 @@ func (cp *pageContentOutput) RenderContent(ctx context.Context, content []byte, func (cp *pageContentOutput) renderContentWithConverter(ctx context.Context, c converter.Converter, content []byte, renderTOC bool) (converter.ResultRender, error) { r, err := c.Convert( converter.RenderContext{ + Ctx: ctx, Src: content, RenderTOC: renderTOC, GetRenderer: cp.renderHooks.getRenderer, diff --git a/hugolib/site.go b/hugolib/site.go index e90fa41ff..45b4643f0 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -1710,12 +1710,12 @@ func (s *Site) lookupLayouts(layouts ...string) tpl.Template { return nil } -func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath string, d any, templ tpl.Template) error { +func (s *Site) renderAndWriteXML(ctx context.Context, statCounter *uint64, name string, targetPath string, d any, templ tpl.Template) error { s.Log.Debugf("Render XML for %q to %q", name, targetPath) renderBuffer := bp.GetBuffer() defer bp.PutBuffer(renderBuffer) - if err := s.renderForTemplate(name, "", d, renderBuffer, templ); err != nil { + if err := s.renderForTemplate(ctx, name, "", d, renderBuffer, templ); err != nil { return err } @@ -1739,8 +1739,9 @@ func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath s defer bp.PutBuffer(renderBuffer) of := p.outputFormat() + ctx := tpl.SetPageInContext(context.Background(), p) - if err := s.renderForTemplate(p.Kind(), of.Name, p, renderBuffer, templ); err != nil { + if err := s.renderForTemplate(ctx, p.Kind(), of.Name, p, renderBuffer, templ); err != nil { return err } @@ -1797,16 +1798,16 @@ type hookRendererTemplate struct { resolvePosition func(ctx any) text.Position } -func (hr hookRendererTemplate) RenderLink(w io.Writer, ctx hooks.LinkContext) error { - return hr.templateHandler.Execute(hr.templ, w, ctx) +func (hr hookRendererTemplate) RenderLink(cctx context.Context, w io.Writer, ctx hooks.LinkContext) error { + return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx) } -func (hr hookRendererTemplate) RenderHeading(w io.Writer, ctx hooks.HeadingContext) error { - return hr.templateHandler.Execute(hr.templ, w, ctx) +func (hr hookRendererTemplate) RenderHeading(cctx context.Context, w io.Writer, ctx hooks.HeadingContext) error { + return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx) } -func (hr hookRendererTemplate) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error { - return hr.templateHandler.Execute(hr.templ, w, ctx) +func (hr hookRendererTemplate) RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx hooks.CodeblockContext) error { + return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx) } func (hr hookRendererTemplate) ResolvePosition(ctx any) text.Position { @@ -1817,13 +1818,15 @@ func (hr hookRendererTemplate) IsDefaultCodeBlockRenderer() bool { return false } -func (s *Site) renderForTemplate(name, outputFormat string, d any, w io.Writer, templ tpl.Template) (err error) { +func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, d any, w io.Writer, templ tpl.Template) (err error) { if templ == nil { s.logMissingLayout(name, "", "", outputFormat) return nil } - ctx := context.Background() + if ctx == nil { + panic("nil context") + } if err = s.Tmpl().ExecuteWithContext(ctx, templ, w, d); err != nil { return fmt.Errorf("render of %q failed: %w", name, err) diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 51d638dde..f105a1ae4 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "fmt" "path" "strings" @@ -197,7 +198,7 @@ func (s *Site) renderPaginator(p *pageState, templ tpl.Template) error { d.Addends = fmt.Sprintf("/%s/%d", paginatePath, 1) targetPaths := page.CreateTargetPaths(d) - if err := s.writeDestAlias(targetPaths.TargetFilename, p.Permalink(), f, nil); err != nil { + if err := s.writeDestAlias(targetPaths.TargetFilename, p.Permalink(), f, p); err != nil { return err } } @@ -278,6 +279,7 @@ func (s *Site) renderSitemap() error { } targetPath := p.targetPaths().TargetFilename + ctx := tpl.SetPageInContext(context.Background(), p) if targetPath == "" { return errors.New("failed to create targetPath for sitemap") @@ -285,7 +287,7 @@ func (s *Site) renderSitemap() error { templ := s.lookupLayouts("sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml") - return s.renderAndWriteXML(&s.PathSpec.ProcessingStats.Sitemaps, "sitemap", targetPath, p, templ) + return s.renderAndWriteXML(ctx, &s.PathSpec.ProcessingStats.Sitemaps, "sitemap", targetPath, p, templ) } func (s *Site) renderRobotsTXT() error { diff --git a/markup/converter/converter.go b/markup/converter/converter.go index e5a07f1a1..544d4841a 100644 --- a/markup/converter/converter.go +++ b/markup/converter/converter.go @@ -15,6 +15,7 @@ package converter import ( "bytes" + "context" "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/loggers" @@ -141,6 +142,9 @@ type DocumentContext struct { // RenderContext holds contextual information about the content to render. type RenderContext struct { + // Ctx is the context.Context for the current Page render. + Ctx context.Context + // Src is the content to render. Src []byte diff --git a/markup/converter/hooks/hooks.go b/markup/converter/hooks/hooks.go index 7eede0710..55d7c1127 100644 --- a/markup/converter/hooks/hooks.go +++ b/markup/converter/hooks/hooks.go @@ -14,6 +14,7 @@ package hooks import ( + "context" "io" "github.com/gohugoio/hugo/common/hugio" @@ -85,12 +86,12 @@ type AttributesOptionsSliceProvider interface { } type LinkRenderer interface { - RenderLink(w io.Writer, ctx LinkContext) error + RenderLink(cctx context.Context, w io.Writer, ctx LinkContext) error identity.Provider } type CodeBlockRenderer interface { - RenderCodeblock(w hugio.FlexiWriter, ctx CodeblockContext) error + RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx CodeblockContext) error identity.Provider } @@ -119,7 +120,7 @@ type HeadingContext interface { // HeadingRenderer describes a uniquely identifiable rendering hook. type HeadingRenderer interface { // Render writes the rendered content to w using the data in w. - RenderHeading(w io.Writer, ctx HeadingContext) error + RenderHeading(cctx context.Context, w io.Writer, ctx HeadingContext) error identity.Provider } diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go index 739781de1..cf5a0f296 100644 --- a/markup/goldmark/codeblocks/render.go +++ b/markup/goldmark/codeblocks/render.go @@ -124,6 +124,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No cr := renderer.(hooks.CodeBlockRenderer) err := cr.RenderCodeblock( + ctx.RenderContext().Ctx, w, cbctx, ) diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go index f36f9f4e6..0bd800dc0 100644 --- a/markup/goldmark/render_hooks.go +++ b/markup/goldmark/render_hooks.go @@ -181,6 +181,7 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N attrs := r.filterInternalAttributes(n.Attributes()) err := lr.RenderLink( + ctx.RenderContext().Ctx, w, imageLinkContext{ linkContext: linkContext{ @@ -271,6 +272,7 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No ctx.Buffer.Truncate(pos) err := lr.RenderLink( + ctx.RenderContext().Ctx, w, linkContext{ page: ctx.DocumentContext().Document, @@ -340,6 +342,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as } err := lr.RenderLink( + ctx.RenderContext().Ctx, w, linkContext{ page: ctx.DocumentContext().Document, @@ -428,6 +431,7 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast anchor := anchori.([]byte) err := hr.RenderHeading( + ctx.RenderContext().Ctx, w, headingContext{ page: ctx.DocumentContext().Document, diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go index b74997700..410beb740 100644 --- a/markup/highlight/highlight.go +++ b/markup/highlight/highlight.go @@ -14,6 +14,7 @@ package highlight import ( + "context" "fmt" gohtml "html" "html/template" @@ -122,7 +123,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a }, nil } -func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error { +func (h chromaHighlighter) RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx hooks.CodeblockContext) error { cfg := h.cfg attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice() diff --git a/resources/errorResource.go b/resources/errorResource.go index 42edb0bd0..c8c32dfc3 100644 --- a/resources/errorResource.go +++ b/resources/errorResource.go @@ -135,3 +135,7 @@ func (e *errorResource) DecodeImage() (image.Image, error) { func (e *errorResource) Transform(...ResourceTransformation) (ResourceTransformer, error) { panic(e.ResourceError) } + +func (e *errorResource) TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) { + panic(e.ResourceError) +} diff --git a/resources/page/pagegroup.go b/resources/page/pagegroup.go index bac5d8327..99f1af3ff 100644 --- a/resources/page/pagegroup.go +++ b/resources/page/pagegroup.go @@ -159,12 +159,7 @@ func (p Pages) GroupBy(ctx context.Context, key string, order ...string) (PagesG case reflect.StructField: fv = ppv.Elem().FieldByName(key) case reflect.Method: - var args []reflect.Value - fn := hreflect.GetMethodByName(ppv, key) - if fn.Type().NumIn() > 0 && fn.Type().In(0).Implements(hreflect.ContextInterface) { - args = []reflect.Value{reflect.ValueOf(ctx)} - } - fv = fn.Call(args)[0] + fv = hreflect.CallMethodByName(ctx, key, ppv)[0] } if !fv.IsValid() { continue diff --git a/resources/resource.go b/resources/resource.go index 8a524247a..7ccc5da39 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -104,6 +104,7 @@ type ResourceTransformer interface { type Transformer interface { Transform(...ResourceTransformation) (ResourceTransformer, error) + TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) } func NewFeatureNotAvailableTransformer(key string, elements ...any) ResourceTransformation { diff --git a/resources/resource_transformers/templates/execute_as_template.go b/resources/resource_transformers/templates/execute_as_template.go index 5fe4230f1..4d4aba396 100644 --- a/resources/resource_transformers/templates/execute_as_template.go +++ b/resources/resource_transformers/templates/execute_as_template.go @@ -15,6 +15,7 @@ package templates import ( + "context" "fmt" "github.com/gohugoio/hugo/helpers" @@ -61,11 +62,11 @@ func (t *executeAsTemplateTransform) Transform(ctx *resources.ResourceTransforma ctx.OutPath = t.targetPath - return t.t.Tmpl().Execute(templ, ctx.To, t.data) + return t.t.Tmpl().ExecuteWithContext(ctx.Ctx, templ, ctx.To, t.data) } -func (c *Client) ExecuteAsTemplate(res resources.ResourceTransformer, targetPath string, data any) (resource.Resource, error) { - return res.Transform(&executeAsTemplateTransform{ +func (c *Client) ExecuteAsTemplate(ctx context.Context, res resources.ResourceTransformer, targetPath string, data any) (resource.Resource, error) { + return res.TransformWithContext(ctx, &executeAsTemplateTransform{ rs: c.rs, targetPath: helpers.ToSlashTrimLeading(targetPath), t: c.t, diff --git a/resources/transform.go b/resources/transform.go index 3477c710f..4ab51485e 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -69,6 +69,7 @@ func newResourceAdapter(spec *Spec, lazyPublish bool, target transformableResour return &resourceAdapter{ resourceTransfor |