diff options
author | Bjørn Erik Pedersen <[email protected]> | 2024-04-11 17:46:18 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2024-04-15 09:49:57 +0200 |
commit | df11327ba90179747be2b25574ac48c2f336b298 (patch) | |
tree | 70f7487cee6815109fc325ed0619130bb29834ca /hugolib | |
parent | a18e2bcb9a2c556e03dc7e790b0343c83163877a (diff) | |
download | hugo-df11327ba90179747be2b25574ac48c2f336b298.tar.gz hugo-df11327ba90179747be2b25574ac48c2f336b298.zip |
Pass .RenderShortcodes' Page to render hooks as .PageInner
The main use case for this is to resolve links and resources (e.g. images) relative to the included `Page`.
A typical `include` would similar to this:
```handlebars
{{ with site.GetPage (.Get 0) }}
{{ .RenderShortcodes }}
{{ end }}
```
And when used in a Markdown file:
```markdown
{{% include "/posts/p1" %}}
```
Any render hook triggered while rendering `/posts/p1` will get `/posts/p1` when calling `.PageInner`.
Note that
* This is only relevant for shortcodes included with `{{%` that calls `.RenderShortcodes`.
* `.PageInner` is available in all render hooks that, before this commit, received `.Page`.
* `.PageInner` will fall back to the value of `.Page` if not relevant and will always have a value.
Fixes #12356
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/content_map_page.go | 2 | ||||
-rw-r--r-- | hugolib/page__content.go | 5 | ||||
-rw-r--r-- | hugolib/page__meta.go | 24 | ||||
-rw-r--r-- | hugolib/page__per_output.go | 17 | ||||
-rw-r--r-- | hugolib/rendershortcodes_test.go | 96 | ||||
-rw-r--r-- | hugolib/shortcode.go | 6 |
6 files changed, 142 insertions, 8 deletions
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 336fe197c..aa32b5320 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1603,7 +1603,7 @@ func (sa *sitePagesAssembler) assembleResources() error { targetPaths := ps.targetPaths() baseTarget := targetPaths.SubResourceBaseTarget duplicateResourceFiles := true - if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.pageConfig.Markup) { + if ps.m.pageConfig.IsGoldmark { duplicateResourceFiles = ps.s.ContentSpec.Converters.GetMarkupConfig().Goldmark.DuplicateResourceFiles } diff --git a/hugolib/page__content.go b/hugolib/page__content.go index 799fc89b6..f10c25d7b 100644 --- a/hugolib/page__content.go +++ b/hugolib/page__content.go @@ -522,6 +522,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp if err != nil { return nil, err } + if !ok { return nil, errors.New("invalid state: astDoc is set but RenderContent returned false") } @@ -626,8 +627,10 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) ( return nil, err } - // Callback called from above (e.g. in .RenderString) + // Callback called from below (e.g. in .RenderString) ctxCallback := func(cp2 *pageContentOutput, ct2 contentTableOfContents) { + cp.otherOutputs[cp2.po.p.pid] = cp2 + // Merge content placeholders for k, v := range ct2.contentPlaceholders { ct.contentPlaceholders[k] = v diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index ebf57f3b3..d8203fe75 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -737,6 +737,8 @@ func (p *pageMeta) applyDefaultValues() error { } } + p.pageConfig.IsGoldmark = p.s.ContentSpec.Converters.IsGoldmark(p.pageConfig.Markup) + if p.pageConfig.Title == "" && p.f == nil { switch p.Kind() { case kinds.KindHome: @@ -794,12 +796,26 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter. path = p.Path() } + doc := newPageForRenderHook(ps) + + documentLookup := func(id uint64) any { + if id == ps.pid { + // This prevents infinite recursion in some cases. + return doc + } + if v, ok := ps.pageOutput.pco.otherOutputs[id]; ok { + return v.po.p + } + return nil + } + cpp, err := cp.New( converter.DocumentContext{ - Document: newPageForRenderHook(ps), - DocumentID: id, - DocumentName: path, - Filename: filename, + Document: doc, + DocumentLookup: documentLookup, + DocumentID: id, + DocumentName: path, + Filename: filename, }, ) if err != nil { diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index fac719ea9..4ff67c074 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -30,6 +30,7 @@ import ( "github.com/spf13/cast" "github.com/gohugoio/hugo/markup/converter/hooks" + "github.com/gohugoio/hugo/markup/goldmark/hugocontext" "github.com/gohugoio/hugo/markup/highlight/chromalexers" "github.com/gohugoio/hugo/markup/tableofcontents" @@ -68,8 +69,9 @@ var ( func newPageContentOutput(po *pageOutput) (*pageContentOutput, error) { cp := &pageContentOutput{ - po: po, - renderHooks: &renderHooks{}, + po: po, + renderHooks: &renderHooks{}, + otherOutputs: make(map[uint64]*pageContentOutput), } return cp, nil } @@ -83,6 +85,10 @@ type renderHooks struct { type pageContentOutput struct { po *pageOutput + // Other pages involved in rendering of this page, + // typically included with .RenderShortcodes. + otherOutputs map[uint64]*pageContentOutput + contentRenderedVersion int // Incremented on reset. contentRendered bool // Set on content render. @@ -165,6 +171,13 @@ func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HT cb(pco, ct) } + if tpl.Context.IsInGoldmark.Get(ctx) { + // This content will be parsed and rendered by Goldmark. + // Wrap it in a special Hugo markup to assign the correct Page from + // the stack. + c = hugocontext.Wrap(c, pco.po.p.pid) + } + return helpers.BytesToHTML(c), nil } diff --git a/hugolib/rendershortcodes_test.go b/hugolib/rendershortcodes_test.go index 696ed8f41..67b1a85ef 100644 --- a/hugolib/rendershortcodes_test.go +++ b/hugolib/rendershortcodes_test.go @@ -200,3 +200,99 @@ Myshort Original. b.Build() b.AssertFileContent("public/p1/index.html", "Edited") } + +func TestRenderShortcodesNestedPageContextIssue12356(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "rss", "sitemap", "robotsTXT", "404"] +-- layouts/_default/_markup/render-image.html -- +{{- with .PageInner.Resources.Get .Destination -}}Image: {{ .RelPermalink }}|{{- end -}} +-- layouts/_default/_markup/render-link.html -- +{{- with .PageInner.GetPage .Destination -}}Link: {{ .RelPermalink }}|{{- end -}} +-- layouts/_default/_markup/render-heading.html -- +Heading: {{ .PageInner.Title }}: {{ .PlainText }}| +-- layouts/_default/_markup/render-codeblock.html -- +CodeBlock: {{ .PageInner.Title }}: {{ .Type }}| +-- layouts/_default/list.html -- +Content:{{ .Content }}| +Fragments: {{ with .Fragments }}{{.Identifiers }}{{ end }}| +-- layouts/_default/single.html -- +Content:{{ .Content }}| +-- layouts/shortcodes/include.html -- +{{ with site.GetPage (.Get 0) }} + {{ .RenderShortcodes }} +{{ end }} +-- content/markdown/_index.md -- +--- +title: "Markdown" +--- +# H1 +|{{% include "/posts/p1" %}}| +![kitten](pixel3.png "Pixel 3") + +§§§go +fmt.Println("Hello") +§§§ + +-- content/markdown2/_index.md -- +--- +title: "Markdown 2" +--- +|{{< include "/posts/p1" >}}| +-- content/html/_index.html -- +--- +title: "HTML" +--- +|{{% include "/posts/p1" %}}| + +-- content/posts/p1/index.md -- +--- +title: "p1" +--- +## H2-p1 +![kitten](pixel1.png "Pixel 1") +![kitten](pixel2.png "Pixel 2") +[p2](p2) + +§§§bash +echo "Hello" +§§§ + +-- content/posts/p2/index.md -- +--- +title: "p2" +--- +-- content/posts/p1/pixel1.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== +-- content/posts/p1/pixel2.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== +-- content/markdown/pixel3.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== +-- content/html/pixel4.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== + +` + + b := Test(t, files) + + b.AssertFileContent("public/markdown/index.html", + // Images. + "Image: /posts/p1/pixel1.png|\nImage: /posts/p1/pixel2.png|\n|\nImage: /markdown/pixel3.png|</p>\n|", + // Links. + "Link: /posts/p2/|", + // Code blocks + "CodeBlock: p1: bash|", "CodeBlock: Markdown: go|", + // Headings. + "Heading: Markdown: H1|", "Heading: p1: H2-p1|", + // Fragments. + "Fragments: [h1 h2-p1]|", + // Check that the special context markup is not rendered. + "! hugo_ctx", + ) + + b.AssertFileContent("public/markdown2/index.html", "! hugo_ctx", "Content:<p>|\n ![kitten](pixel1.png \"Pixel 1\")\n![kitten](pixel2.png \"Pixel 2\")\n|</p>\n|") + + b.AssertFileContent("public/html/index.html", "! hugo_ctx") +} diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 7cc2cba27..af4454a89 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -321,10 +321,16 @@ func prepareShortcode( // Allow the caller to delay the rendering of the shortcode if needed. var fn shortcodeRenderFunc = func(ctx context.Context) ([]byte, bool, error) { + if p.m.pageConfig.IsGoldmark && sc.doMarkup { + // Signal downwards that the content rendered will be + // parsed and rendered by Goldmark. + ctx = tpl.Context.IsInGoldmark.Set(ctx, true) + } r, err := doRenderShortcode(ctx, level, s, tplVariants, sc, parent, p, isRenderString) if err != nil { return nil, false, toParseErr(err) } + b, hasVariants, err := r.renderShortcode(ctx) if err != nil { return nil, false, toParseErr(err) |