aboutsummaryrefslogtreecommitdiffhomepage
path: root/hugolib
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2024-04-11 17:46:18 +0200
committerBjørn Erik Pedersen <[email protected]>2024-04-15 09:49:57 +0200
commitdf11327ba90179747be2b25574ac48c2f336b298 (patch)
tree70f7487cee6815109fc325ed0619130bb29834ca /hugolib
parenta18e2bcb9a2c556e03dc7e790b0343c83163877a (diff)
downloadhugo-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.go2
-rw-r--r--hugolib/page__content.go5
-rw-r--r--hugolib/page__meta.go24
-rw-r--r--hugolib/page__per_output.go17
-rw-r--r--hugolib/rendershortcodes_test.go96
-rw-r--r--hugolib/shortcode.go6
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)