]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Add a page template func
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 25 Feb 2023 08:24:59 +0000 (09:24 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 25 Feb 2023 18:53:18 +0000 (19:53 +0100)
Fixes #9339

54 files changed:
commands/server.go
common/hreflect/helpers.go
hugolib/alias.go
hugolib/hugo_sites.go
hugolib/page__per_output.go
hugolib/site.go
hugolib/site_render.go
markup/converter/converter.go
markup/converter/hooks/hooks.go
markup/goldmark/codeblocks/render.go
markup/goldmark/render_hooks.go
markup/highlight/highlight.go
resources/errorResource.go
resources/page/pagegroup.go
resources/resource.go
resources/resource_transformers/templates/execute_as_template.go
resources/transform.go
tpl/cast/init.go
tpl/collections/apply.go
tpl/collections/init.go
tpl/compare/init.go
tpl/crypto/init.go
tpl/css/css.go
tpl/data/init.go
tpl/debug/init.go
tpl/diagrams/init.go
tpl/encoding/init.go
tpl/fmt/init.go
tpl/hugo/init.go
tpl/images/init.go
tpl/inflect/init.go
tpl/internal/go_templates/texttemplate/hugo_template.go
tpl/internal/templatefuncsRegistry.go
tpl/js/init.go
tpl/lang/init.go
tpl/math/init.go
tpl/openapi/openapi3/init.go
tpl/os/init.go
tpl/page/init.go [new file with mode: 0644]
tpl/page/integration_test.go [new file with mode: 0644]
tpl/partials/init.go
tpl/path/init.go
tpl/reflect/init.go
tpl/resources/init.go
tpl/resources/resources.go
tpl/safe/init.go
tpl/site/init.go
tpl/strings/init.go
tpl/template.go
tpl/templates/init.go
tpl/time/init.go
tpl/tplimpl/template.go
tpl/transform/init.go
tpl/urls/init.go

index 332e35c01b9e0b7a220e39c4bdebf3baf636756c..4f7d4db8f768ec77a519b1808dbab9e20f5e8fbd 100644 (file)
@@ -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
                },
        }
index 1b7e5acf7224514dc2263a4d323d6a1e569592fc..17afbf9127e96ce37efd57337256ed4f65a0058a 100644 (file)
@@ -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 {
index 2609cd6bb496325ef55243f2e53f113dec9277f9..071f73d412a191810ad4538bebaf8ca38d65b847 100644 (file)
@@ -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
        }
index cdc5d97fb786357e9c8915de70cf1897388b6cea..65cc23971db9ffec4c4df2b5103b87364e4be54c 100644 (file)
@@ -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)
 }
 
index ce3498e0ef49b37d7984f2e4e673b6e59e7ffa83..be65ad9e723d994e87aa48d2002af7739641cf70 100644 (file)
@@ -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,
index e90fa41ff633a1aa68022bc977ef4e529baf1be5..45b4643f03580c4d66ae31613ec8bb38b5c0aa98 100644 (file)
@@ -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)
index 51d638ddef0fec4a2b4ac25204084961187fa2f3..f105a1ae4c17e00ccf45e0baf0f47908a68b8470 100644 (file)
@@ -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 {
index e5a07f1a1346a64af4c6bdd3ce3f0fd664616f76..544d4841a8939ce88aca187e9990903a890ef1b4 100644 (file)
@@ -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
 
index 7eede07103341667935afb39c9d980334933f537..55d7c1127fc9aca782fdf9bc0a6f67b4b6bf75fb 100644 (file)
@@ -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
 }
 
index 739781de1c9c05d97de3f175aebbab4b75b7ad0e..cf5a0f2966c09c4f88532978ef55d17761839c85 100644 (file)
@@ -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,
        )
index f36f9f4e6908a11abf13725b2fbeda38d9033f91..0bd800dc0d94a7f2bc60f4f32e9f9253fe5471aa 100644 (file)
@@ -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,
index b749977005511f732bbf8dda56b4fe45bed5e5f8..410beb7406882b61b3dc12e520b22307c640dff5 100644 (file)
@@ -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()
index 42edb0bd0a75be5c548e9741587254f6f0935d3a..c8c32dfc3135d6f144fb21e11afa26d7f39ce342 100644 (file)
@@ -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)
+}
index bac5d8327eacb405920b2586385c7a9b7ef92e8f..99f1af3ff7e11cbbbdad8bc55cee6a62e728230d 100644 (file)
@@ -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
index 8a524247ae3f1a06b52787c86a61e706c47179a1..7ccc5da39de9ba6e8b718154af2518402a2b3d9f 100644 (file)
@@ -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 {
index 5fe4230f13361e44120adcb4184919f243d0fa9d..4d4aba39607693855eadf4ae152168b1a265a266 100644 (file)
@@ -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,
index 3477c710f34b7d09b3548d368a0776b812610203..4ab51485e1bae2f723b0d45d6c7300d2a06bf315 100644 (file)
@@ -69,6 +69,7 @@ func newResourceAdapter(spec *Spec, lazyPublish bool, target transformableResour
        return &resourceAdapter{
                resourceTransformations: &resourceTransformations{},
                resourceAdapterInner: &resourceAdapterInner{
+                       ctx:         context.TODO(),
                        spec:        spec,
                        publishOnce: po,
                        target:      target,
@@ -84,6 +85,9 @@ type ResourceTransformation interface {
 }
 
 type ResourceTransformationCtx struct {
+       // The context that started the transformation.
+       Ctx context.Context
+
        // The content to transform.
        From io.Reader
 
@@ -180,6 +184,7 @@ func (r *resourceAdapter) Data() any {
 func (r resourceAdapter) cloneTo(targetPath string) resource.Resource {
        newtTarget := r.target.cloneTo(targetPath)
        newInner := &resourceAdapterInner{
+               ctx:    r.ctx,
                spec:   r.spec,
                target: newtTarget.(transformableResource),
        }
@@ -278,11 +283,17 @@ func (r *resourceAdapter) Title() string {
 }
 
 func (r resourceAdapter) Transform(t ...ResourceTransformation) (ResourceTransformer, error) {
+       return r.TransformWithContext(context.Background(), t...)
+}
+
+func (r resourceAdapter) TransformWithContext(ctx context.Context, t ...ResourceTransformation) (ResourceTransformer, error) {
+
        r.resourceTransformations = &resourceTransformations{
                transformations: append(r.transformations, t...),
        }
 
        r.resourceAdapterInner = &resourceAdapterInner{
+               ctx:         ctx,
                spec:        r.spec,
                publishOnce: &publishOnce{},
                target:      r.target,
@@ -377,6 +388,7 @@ func (r *resourceAdapter) transform(publish, setContent bool) error {
        defer bp.PutBuffer(b2)
 
        tctx := &ResourceTransformationCtx{
+               Ctx:                   r.ctx,
                Data:                  make(map[string]any),
                OpenResourcePublisher: r.target.openPublishFileForWriting,
        }
@@ -599,6 +611,9 @@ func (r *resourceAdapter) initTransform(publish, setContent bool) {
 }
 
 type resourceAdapterInner struct {
+       // The context that started this transformation.
+       ctx context.Context
+
        target transformableResource
 
        spec *Spec
index f1badf993c6173127d0b7211acf6559d784c5301..84211a00b0a00fa02e77ce4dd1cd282c3562d384 100644 (file)
@@ -14,6 +14,8 @@
 package cast
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.ToInt,
index 74ecc5b196f0147fbfbfcbc793d42a16c23cef48..1dc09c8e51ac53e5f77c1430a6c3d8854b4c3d5a 100644 (file)
@@ -40,7 +40,7 @@ func (ns *Namespace) Apply(ctx context.Context, c any, fname string, args ...any
                return nil, errors.New("can't iterate over a nil value")
        }
 
-       fnv, found := ns.lookupFunc(fname)
+       fnv, found := ns.lookupFunc(ctx, fname)
        if !found {
                return nil, errors.New("can't find function " + fname)
        }
@@ -106,7 +106,7 @@ func applyFnToThis(ctx context.Context, fn, this reflect.Value, args ...any) (re
        return reflect.ValueOf(nil), res[1].Interface().(error)
 }
 
-func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) {
+func (ns *Namespace) lookupFunc(ctx context.Context, fname string) (reflect.Value, bool) {
        namespace, methodName, ok := strings.Cut(fname, ".")
        if !ok {
                templ := ns.deps.Tmpl().(tpl.TemplateFuncGetter)
@@ -114,16 +114,16 @@ func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) {
        }
 
        // Namespace
-       nv, found := ns.lookupFunc(namespace)
+       nv, found := ns.lookupFunc(ctx, namespace)
        if !found {
                return reflect.Value{}, false
        }
 
-       fn, ok := nv.Interface().(func(...any) (any, error))
+       fn, ok := nv.Interface().(func(context.Context, ...any) (any, error))
        if !ok {
                return reflect.Value{}, false
        }
-       v, err := fn()
+       v, err := fn(ctx)
        if err != nil {
                panic(err)
        }
index c992d3fb2a178c7f12c3aa3611a55a3be4a98be1..8801422ac5dae1292286cdd3bee1198dd1893ee6 100644 (file)
@@ -14,6 +14,8 @@
 package collections
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.After,
index 98c07f41b8d2e63cc8931e0848afa7582b64bddd..f080647b18f801fc913e3a9fe840ed626e8a8451 100644 (file)
@@ -14,6 +14,8 @@
 package compare
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/langs"
        "github.com/gohugoio/hugo/tpl/internal"
@@ -31,7 +33,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Default,
index dddc5585a9ed3c8183e224ae3d3044c35cfda6a7..418fbd9fb472ae2cb551970d2d9f22a98f8f8834 100644 (file)
@@ -14,6 +14,8 @@
 package crypto
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.MD5,
index e1783334ee816473fc0c991127246bbda267c037..5fc6130119285ea66f16074d713ef1ad4fa72771 100644 (file)
@@ -1,6 +1,8 @@
 package css
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/common/types/css"
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
@@ -31,7 +33,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                return ns
index 22e685fc8486e8c0b2c0f351078a23de4bd1bcfb..507e0d43e9c97f92f270984a3a143222ad19ce3e 100644 (file)
@@ -14,6 +14,8 @@
 package data
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.GetCSV,
index 12a99783d2c93077b967c7ac641642a4c262e246..796a34bfcfcd28873c32dbfc9a264794c07a5084 100644 (file)
@@ -14,6 +14,8 @@
 package debug
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Dump,
index 1ed308c57304ef52e7e5bbc68106fce6f8e6c087..e6356ce9c45448bd4d7edba7c5a2ad38637f4137 100644 (file)
@@ -15,6 +15,8 @@
 package diagrams
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -29,7 +31,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                return ns
index 1d42b4e3771cbc18bfe99857d8b1d22ccbdf8a6f..1c3322d6ed558d688369ec17e516b9e3000aa98f 100644 (file)
@@ -14,6 +14,8 @@
 package encoding
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Base64Decode,
index b0683f06174df637cb46c1cebf5fcfff7e3d21f3..8000627e211fcb24fa1ff81aa2b4ff5f0f150fa7 100644 (file)
@@ -14,6 +14,8 @@
 package fmt
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Print,
index e2b4ae7af5aad7f523de43bb10f07a50e63cfe19..ad589722c36f1b9f7d5222e45324dddee4ff2b99 100644 (file)
@@ -15,6 +15,8 @@
 package hugo
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -27,7 +29,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return h, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return h, nil },
                }
 
                // We just add the Hugo struct as the namespace here. No method mappings.
index d9b9af4e7db28f0390a28a8b38d5ddb8b2f003fd..a350d5b9d2a8ac230834d818cd9abf20d1fdd04e 100644 (file)
@@ -14,6 +14,8 @@
 package images
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Config,
index a2d28f6bfd7b22b68fd76e58a647b58695af0a14..736e9fbd66e9d4242dd9e9fde613aa306c8be91c 100644 (file)
@@ -14,6 +14,8 @@
 package inflect
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Humanize,
index 96f526005ed06a8c78f9ec439817b095d06ed7cc..23bc18e42a03881aabb0943a070c35cce854a17e 100644 (file)
@@ -60,29 +60,29 @@ func NewExecuter(helper ExecHelper) Executer {
 }
 
 type (
-       dataContextKeyType    string
+       pageContextKeyType    string
        hasLockContextKeyType string
        stackContextKeyType   string
 )
 
 const (
-       // The data object passed to Execute or ExecuteWithContext gets stored with this key if not already set.
-       DataContextKey = dataContextKeyType("data")
+       // The data page passed to ExecuteWithContext gets stored with this key.
+       PageContextKey = pageContextKeyType("page")
        // Used in partialCached to signal to nested templates that a lock is already taken.
        HasLockContextKey = hasLockContextKeyType("hasLock")
 )
 
 // Note: The context is currently not fully implemeted in Hugo. This is a work in progress.
 func (t *executer) ExecuteWithContext(ctx context.Context, p Preparer, wr io.Writer, data any) error {
+       if ctx == nil {
+               panic("nil context")
+       }
+
        tmpl, err := p.Prepare()
        if err != nil {
                return err
        }
 
-       if v := ctx.Value(DataContextKey); v == nil {
-               ctx = context.WithValue(ctx, DataContextKey, data)
-       }
-
        value, ok := data.(reflect.Value)
        if !ok {
                value = reflect.ValueOf(data)
@@ -102,28 +102,6 @@ func (t *executer) ExecuteWithContext(ctx context.Context, p Preparer, wr io.Wri
        return tmpl.executeWithState(state, value)
 }
 
-func (t *executer) Execute(p Preparer, wr io.Writer, data any) error {
-       tmpl, err := p.Prepare()
-       if err != nil {
-               return err
-       }
-
-       value, ok := data.(reflect.Value)
-       if !ok {
-               value = reflect.ValueOf(data)
-       }
-
-       state := &state{
-               helper: t.helper,
-               prep:   p,
-               tmpl:   tmpl,
-               wr:     wr,
-               vars:   []variable{{"$", value}},
-       }
-
-       return tmpl.executeWithState(state, value)
-}
-
 // Prepare returns a template ready for execution.
 func (t *Template) Prepare() (*Template, error) {
        return t, nil
index 84e0e25faf4b87741fcc42b26954e7b876224cf7..363f6d82f33ff5b50d29b0e2b96e4a125e187c86 100644 (file)
@@ -17,6 +17,7 @@ package internal
 
 import (
        "bytes"
+       "context"
        "encoding/json"
        "fmt"
        "go/doc"
@@ -49,7 +50,7 @@ type TemplateFuncsNamespace struct {
        Name string
 
        // This is the method receiver.
-       Context func(v ...any) (any, error)
+       Context func(ctx context.Context, v ...any) (any, error)
 
        // Additional info, aliases and examples, per method name.
        MethodMappings map[string]TemplateFuncMethodMapping
@@ -172,7 +173,7 @@ func (namespaces TemplateFuncsNamespaces) MarshalJSON() ([]byte, error) {
                if i != 0 {
                        buf.WriteString(",")
                }
-               b, err := ns.toJSON()
+               b, err := ns.toJSON(context.TODO())
                if err != nil {
                        return nil, err
                }
@@ -188,7 +189,7 @@ var ignoreFuncs = map[string]bool{
        "Reset": true,
 }
 
-func (t *TemplateFuncsNamespace) toJSON() ([]byte, error) {
+func (t *TemplateFuncsNamespace) toJSON(ctx context.Context) ([]byte, error) {
        var buf bytes.Buffer
 
        godoc := getGetTplPackagesGoDoc()[t.Name]
@@ -197,11 +198,11 @@ func (t *TemplateFuncsNamespace) toJSON() ([]byte, error) {
 
        buf.WriteString(fmt.Sprintf(`%q: {`, t.Name))
 
-       ctx, err := t.Context()
+       tctx, err := t.Context(ctx)
        if err != nil {
                return nil, err
        }
-       ctxType := reflect.TypeOf(ctx)
+       ctxType := reflect.TypeOf(tctx)
        for i := 0; i < ctxType.NumMethod(); i++ {
                method := ctxType.Method(i)
                if ignoreFuncs[method.Name] {
index d57e0fdcbcf524e9723bbac175fb0819a54ebfb5..16e6c7efad126452cb1fd2d2652e2dbc39d88b92 100644 (file)
@@ -14,6 +14,8 @@
 package js
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                return ns
index 3f7b57ffca7e23d23f39b55956a754562d0ddd65..62c3a56a0499387f5c6c173a17cec11be6340ae4 100644 (file)
@@ -14,6 +14,8 @@
 package lang
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/langs"
        "github.com/gohugoio/hugo/tpl/internal"
@@ -27,7 +29,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Translate,
index 19905fd3acaa8da6284777c387a2bdfcf296afef..67aa95f41641d02709d959ab32a0122b6d7fbf79 100644 (file)
@@ -14,6 +14,8 @@
 package math
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Add,
index 8597e3294e20eafe018d12a29c173464176f097d..4d88e148e036e5a24ab12d8bfff68f05bd7bb88f 100644 (file)
@@ -14,6 +14,8 @@
 package openapi3
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Unmarshal,
index cd9e370cd08ea997e5dc8550d9794413ec186397..d7afba16fbc736f09c293dbb63fcf8ac959f599d 100644 (file)
@@ -14,6 +14,8 @@
 package os
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Getenv,
diff --git a/tpl/page/init.go b/tpl/page/init.go
new file mode 100644 (file)
index 0000000..52aeaaf
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package page provides template functions for accessing the current Page object,
+// the entry level context for the current template.
+package page
+
+import (
+       "context"
+
+       "github.com/gohugoio/hugo/deps"
+       "github.com/gohugoio/hugo/resources/page"
+       "github.com/gohugoio/hugo/tpl"
+
+       "github.com/gohugoio/hugo/tpl/internal"
+)
+
+const name = "page"
+
+func init() {
+       f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+               ns := &internal.TemplateFuncsNamespace{
+                       Name: name,
+                       Context: func(ctx context.Context, args ...interface{}) (interface{}, error) {
+                               v := tpl.GetPageFromContext(ctx)
+                               if v == nil {
+                                       // The multilingual sitemap does not have a page as its context.
+                                       return nil, nil
+                               }
+
+                               return v.(page.Page), nil
+                       },
+               }
+
+               return ns
+       }
+
+       internal.AddTemplateFuncsNamespace(f)
+}
diff --git a/tpl/page/integration_test.go b/tpl/page/integration_test.go
new file mode 100644 (file)
index 0000000..30a4885
--- /dev/null
@@ -0,0 +1,179 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package page_test
+
+import (
+       "fmt"
+       "strings"
+       "testing"
+
+       "github.com/gohugoio/hugo/hugolib"
+)
+
+func TestThatPageIsAvailableEverywhere(t *testing.T) {
+       t.Parallel()
+
+       filesTemplate := `
+-- config.toml --
+baseURL = 'http://example.com/'
+disableKinds = ["taxonomy", "term"]
+enableInlineShortcodes = true
+paginate = 1
+enableRobotsTXT = true
+LANG_CONFIG
+-- content/_index.md --
+---
+title: "Home"
+aliases: ["/homealias/"]
+---
+{{< shortcode "Angled Brackets" >}}
+{{% shortcode "Percentage" %}}
+
+{{< outer >}}
+{{< inner >}}
+{{< /outer >}}
+
+{{< foo.inline >}}{{ if page.IsHome }}Shortcode Inline OK.{{ end }}{{< /foo.inline >}}
+
+## Heading
+
+[I'm an inline-style link](https://www.google.com)
+
+![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 1")
+
+$$$bash
+echo "hello";
+$$$
+
+-- content/p1.md --
+-- content/p2/index.md --
+-- content/p2/p2_1.md --
+---
+title: "P2_1"
+---
+{{< foo.inline >}}{{ if page.IsHome }}Shortcode in bundled page OK.{{ else}}Failed.{{ end }}{{< /foo.inline >}}
+-- content/p3.md --
+-- layouts/_default/_markup/render-heading.html --
+{{ if page.IsHome }}
+Heading OK.
+{{ end }}
+-- layouts/_default/_markup/render-image.html --
+{{ if page.IsHome }}
+Image OK.
+{{ end }}
+-- layouts/_default/_markup/render-link.html --
+{{ if page.IsHome }}
+Link OK.
+{{ end }}
+-- layouts/_default/myview.html
+{{ if page.IsHome }}
+Render OK.
+{{ end }}
+-- layouts/_default/_markup/render-codeblock.html --
+{{ if page.IsHome }}
+Codeblock OK.
+{{ end }}
+-- layouts/_default/single.html --
+Single.
+-- layouts/index.html --
+{{ if eq page . }}Page OK.{{ end }}
+{{ $r := "{{ if page.IsHome }}ExecuteAsTemplate OK.{{ end }}" | resources.FromString "foo.html" |  resources.ExecuteAsTemplate "foo.html" . }}
+{{ $r.Content }}
+{{ .RenderString "{{< renderstring.inline >}}{{ if page.IsHome }}RenderString OK.{{ end }}{{< /renderstring.inline >}}}}"}}
+{{ .Render "myview" }}
+{{ .Content }}
+partial: {{ partials.Include "foo.html" . }}
+{{ $pag := (.Paginate site.RegularPages) }}
+PageNumber: {{ $pag.PageNumber }}/{{ $pag.TotalPages }}|
+{{ $p2 := site.GetPage "p2" }}
+{{ $p2_1 := index $p2.Resources 0 }}
+Bundled page: {{ $p2_1.Content }}
+-- layouts/alias.html --
+{{ if eq page .Page }}Alias OK.{{ else }}Failed.{{ end }}
+-- layouts/404.html --
+{{ if eq page . }}404 Page OK.{{ else }}Failed.{{ end }}
+-- layouts/partials/foo.html --
+{{ if page.IsHome }}Partial OK.{{ else }}Failed.{{ end }}
+-- layouts/shortcodes/outer.html --
+{{ .Inner }}
+-- layouts/shortcodes/inner.html --
+{{ if page.IsHome }}Shortcode Inner OK.{{ else }}Failed.{{ end }}
+-- layouts/shortcodes/shortcode.html --
+{{ if page.IsHome }}Shortcode {{ .Get 0 }} OK.{{ else }}Failed.{{ end }}
+-- layouts/sitemap.xml --
+HRE?{{ if eq page . }}Sitemap OK.{{ else }}Failed.{{ end }}
+-- layouts/robots.txt --
+{{ if eq page . }}Robots OK.{{ else }}Failed.{{ end }}
+-- layouts/sitemapindex.xml --
+{{ if not page }}SitemapIndex OK.{{ else }}Failed.{{ end }}
+
+  `
+
+       for _, multilingual := range []bool{false, true} {
+               t.Run(fmt.Sprintf("multilingual-%t", multilingual), func(t *testing.T) {
+                       // Fenced code blocks.
+                       files := strings.ReplaceAll(filesTemplate, "$$$", "```")
+
+                       if multilingual {
+                               files = strings.ReplaceAll(files, "LANG_CONFIG", `
+[languages]
+[languages.en]
+weight = 1
+[languages.no]
+weight = 2
+`)
+                       } else {
+                               files = strings.ReplaceAll(files, "LANG_CONFIG", "")
+                       }
+
+                       b := hugolib.NewIntegrationTestBuilder(
+                               hugolib.IntegrationTestConfig{
+                                       T:           t,
+                                       TxtarString: files,
+                               },
+                       ).Build()
+
+                       b.AssertFileContent("public/index.html", `
+Heading OK.
+Image OK.
+Link OK.
+Codeblock OK.
+Page OK.
+Partial OK.
+Shortcode Angled Brackets OK.
+Shortcode Percentage OK.
+Shortcode Inner OK.
+Shortcode Inline OK.
+ExecuteAsTemplate OK.
+RenderString OK.
+Render OK.
+Shortcode in bundled page OK.
+       `)
+
+                       b.AssertFileContent("public/404.html", `404 Page OK.`)
+                       b.AssertFileContent("public/robots.txt", `Robots OK.`)
+                       b.AssertFileContent("public/homealias/index.html", `Alias OK.`)
+                       b.AssertFileContent("public/page/1/index.html", `Alias OK.`)
+                       b.AssertFileContent("public/page/2/index.html", `Page OK.`)
+                       if multilingual {
+                               b.AssertFileContent("public/sitemap.xml", `SitemapIndex OK.`)
+                       } else {
+                               b.AssertFileContent("public/sitemap.xml", `Sitemap OK.`)
+                       }
+
+               })
+
+       }
+
+}
index 2662b8894d1249254e9f7c8c22d19956275366c7..e9d901bbf63432d74c838fcae3a0bf58c900fdff 100644 (file)
@@ -14,6 +14,8 @@
 package partials
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    namespaceName,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Include,
index 0d81f61819b3b6cd77c6b4e71a7bd4ff2619cf5c..97a479d3f01e737652c4df5faa0261e2e5544ac7 100644 (file)
@@ -14,6 +14,7 @@
 package path
 
 import (
+       "context"
        "fmt"
        "path/filepath"
 
@@ -29,7 +30,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Split,
index 3af6dfa119ec92fa0ef05606d8ef6d197c7ce8ad..259f97323f2e9291c4ad14759869be37c6b8c909 100644 (file)
@@ -15,6 +15,8 @@
 package reflect
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -27,7 +29,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.IsMap,
index 73a7b8f42ea22606e45ab3c23dea16c7ac4248e5..db51b0287e570077268a27ed5276200f444b6dbe 100644 (file)
@@ -14,6 +14,8 @@
 package resources
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -30,7 +32,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Get,
index 85323f057f69330abb77e90be745a5a67f25dd43..cc5310277f7ed19982dd345cce9df9439aa7206a 100644 (file)
 package resources
 
 import (
+       "context"
        "fmt"
        "sync"
 
-       "github.com/gohugoio/hugo/common/herrors"
-
        "errors"
 
        "github.com/gohugoio/hugo/common/maps"
@@ -227,7 +226,6 @@ func (ns *Namespace) ByType(typ any) resource.Resources {
 //
 // See Match for a more complete explanation about the rules used.
 func (ns *Namespace) Match(pattern any) resource.Resources {
-       defer herrors.Recover()
        patternStr, err := cast.ToStringE(pattern)
        if err != nil {
                panic(err)
@@ -283,7 +281,7 @@ func (ns *Namespace) FromString(targetPathIn, contentIn any) (resource.Resource,
 
 // ExecuteAsTemplate creates a Resource from a Go template, parsed and executed with
 // the given data, and published to the relative target path.
-func (ns *Namespace) ExecuteAsTemplate(args ...any) (resource.Resource, error) {
+func (ns *Namespace) ExecuteAsTemplate(ctx context.Context, args ...any) (resource.Resource, error) {
        if len(args) != 3 {
                return nil, fmt.Errorf("must provide targetPath, the template data context and a Resource object")
        }
@@ -298,7 +296,7 @@ func (ns *Namespace) ExecuteAsTemplate(args ...any) (resource.Resource, error) {
                return nil, fmt.Errorf("type %T not supported in Resource transformations", args[2])
        }
 
-       return ns.templatesClient.ExecuteAsTemplate(r, targetPath, data)
+       return ns.templatesClient.ExecuteAsTemplate(ctx, r, targetPath, data)
 }
 
 // Fingerprint transforms the given Resource with a MD5 hash of the content in
index 794c9d6f0f2bd5e6e723673bcf519f6294782811..8fc0e82eaa56ff9ade61f531f1c05414cbc19242 100644 (file)
@@ -14,6 +14,8 @@
 package safe
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.CSS,
index 34ea7309f704345240e92e6cb98558b39a0170e0..3d293f3fe2ffca313fcf39df0b2b6dc365f0c48e 100644 (file)
@@ -15,6 +15,8 @@
 package site
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
 
        "github.com/gohugoio/hugo/tpl/internal"
@@ -27,7 +29,7 @@ func init() {
                s := d.Site
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return s, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return s, nil },
                }
 
                if s == nil {
index 503ec6a25e254fcd7776bd1dae3e49a4d43ed1b7..37a48912852d0d5b958c573a22257493d5aa6693 100644 (file)
@@ -14,6 +14,8 @@
 package strings
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Chomp,
index dd9249adef54d004776c4091ab59fbab710627f8..f71de8bb2a0999c62e42ea594688fc9461a8d3d4 100644 (file)
@@ -59,7 +59,6 @@ type UnusedTemplatesProvider interface {
 // TemplateHandler finds and executes templates.
 type TemplateHandler interface {
        TemplateFinder
-       Execute(t Template, wr io.Writer, data any) error
        ExecuteWithContext(ctx context.Context, t Template, wr io.Writer, data any) error
        LookupLayout(d output.LayoutDescriptor, f output.Format) (Template, bool, error)
        HasTemplate(name string) bool
@@ -153,10 +152,18 @@ type TemplateFuncGetter interface {
        GetFunc(name string) (reflect.Value, bool)
 }
 
-// GetDataFromContext returns the template data context (usually .Page) from ctx if set.
-// NOte: This is not fully implemented yet.
-func GetDataFromContext(ctx context.Context) any {
-       return ctx.Value(texttemplate.DataContextKey)
+// GetPageFromContext returns the top level Page.
+func GetPageFromContext(ctx context.Context) any {
+       return ctx.Value(texttemplate.PageContextKey)
+}
+
+// SetPageInContext sets the top level Page.
+func SetPageInContext(ctx context.Context, p page) context.Context {
+       return context.WithValue(ctx, texttemplate.PageContextKey, p)
+}
+
+type page interface {
+       IsNode() bool
 }
 
 func GetHasLockFromContext(ctx context.Context) bool {
index e068fca81c27a298dd7a3a95276215740bf43df8..ff6acdabd0bde8633a4c57cfd562691593bbb3bc 100644 (file)
@@ -14,6 +14,8 @@
 package templates
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Exists,
index 4bb2ddf67bfb444d38c365faa4250bec2142ade9..583dacd4acde8ba4abd892144f7319f7b4b36fad 100644 (file)
@@ -14,6 +14,7 @@
 package time
 
 import (
+       "context"
        "errors"
 
        "github.com/gohugoio/hugo/deps"
@@ -32,7 +33,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name: name,
-                       Context: func(args ...any) (any, error) {
+                       Context: func(cctx context.Context, args ...any) (any, error) {
                                // Handle overlapping "time" namespace and func.
                                //
                                // If no args are passed to `time`, assume namespace usage and
index c01863ebb4b8dd9644759761553bbf44c0f08904..70541e0768220cb9aa53096191e67f21b514b91e 100644 (file)
@@ -232,6 +232,10 @@ func (t templateExec) Clone(d *deps.Deps) *templateExec {
 }
 
 func (t *templateExec) Execute(templ tpl.Template, wr io.Writer, data any) error {
+       // TOD1
+       if true {
+               //panic("not implemented")
+       }
        return t.ExecuteWithContext(context.Background(), templ, wr, data)
 }
 
index dd31446bdd28103b17e6e33d185a3f396c7f0d43..00ae8f89d048011b8fc88dc954878ade39a656b3 100644 (file)
@@ -14,6 +14,8 @@
 package transform
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.Emojify,
index 3597e87c52a9bd04b43c8543b66bce0abfa82155..ec954640d2915535b6937c9a018f3629cbd9bf84 100644 (file)
@@ -14,6 +14,8 @@
 package urls
 
 import (
+       "context"
+
        "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/tpl/internal"
 )
@@ -26,7 +28,7 @@ func init() {
 
                ns := &internal.TemplateFuncsNamespace{
                        Name:    name,
-                       Context: func(args ...any) (any, error) { return ctx, nil },
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
                }
 
                ns.AddMethodMapping(ctx.AbsURL,