Revise the fix for shortcode vs output format nilpointer

We do lazy initialization and (potentially) reuse of an output format's rendered content. We do this evaluation when we
start a new rendering a new output format. There are, however, situation where these borders gets crossed (e.g.
accessing content from another output format). We have a check for this in place for most cases, but not the content
rendering of inner markdown blocks inside shortcodes. This patch applies that same logic to the newly introduced
RenderContent method (which is not available from the templates).

Fixes #10391
This commit is contained in:
Bjørn Erik Pedersen 2022-10-26 10:09:38 +02:00
parent e5d2a8f6a3
commit 631d768be9
7 changed files with 47 additions and 17 deletions

View file

@ -909,6 +909,7 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
} }
return cp, nil return cp, nil
}) })
p.pageOutput.contentRenderer = lcp
p.pageOutput.ContentProvider = lcp p.pageOutput.ContentProvider = lcp
p.pageOutput.TableOfContentsProvider = lcp p.pageOutput.TableOfContentsProvider = lcp
p.pageOutput.PageRenderProvider = lcp p.pageOutput.PageRenderProvider = lcp

View file

@ -81,6 +81,7 @@ type pageOutput struct {
// These interface provides the functionality that is specific for this // These interface provides the functionality that is specific for this
// output format. // output format.
contentRenderer page.ContentRenderer
pagePerOutputProviders pagePerOutputProviders
page.ContentProvider page.ContentProvider
page.TableOfContentsProvider page.TableOfContentsProvider
@ -94,10 +95,12 @@ func (p *pageOutput) initContentProvider(cp *pageContentOutput) {
if cp == nil { if cp == nil {
return return
} }
p.contentRenderer = cp
p.ContentProvider = cp p.ContentProvider = cp
p.TableOfContentsProvider = cp p.TableOfContentsProvider = cp
p.PageRenderProvider = cp p.PageRenderProvider = cp
p.cp = cp p.cp = cp
} }
func (p *pageOutput) enablePlaceholders() { func (p *pageOutput) enablePlaceholders() {

View file

@ -123,7 +123,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
isHTML := cp.p.m.markup == "html" isHTML := cp.p.m.markup == "html"
if !isHTML { if !isHTML {
r, err := cp.renderContent(cp.workContent, true) r, err := po.contentRenderer.RenderContent(cp.workContent, true)
if err != nil { if err != nil {
return err return err
} }
@ -183,7 +183,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
} }
} }
} else if cp.p.m.summary != "" { } else if cp.p.m.summary != "" {
b, err := cp.renderContent([]byte(cp.p.m.summary), false) b, err := po.contentRenderer.RenderContent([]byte(cp.p.m.summary), false)
if err != nil { if err != nil {
return err return err
} }
@ -629,7 +629,7 @@ func (p *pageContentOutput) setAutoSummary() error {
return nil return nil
} }
func (cp *pageContentOutput) renderContent(content []byte, renderTOC bool) (converter.Result, error) { func (cp *pageContentOutput) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
if err := cp.initRenderHooks(); err != nil { if err := cp.initRenderHooks(); err != nil {
return nil, err return nil, err
} }

View file

@ -381,19 +381,8 @@ func renderShortcode(
// Pre Hugo 0.55 this was the behaviour even for the outer-most // Pre Hugo 0.55 this was the behaviour even for the outer-most
// shortcode. // shortcode.
if sc.doMarkup && (level > 0 || sc.configVersion() == 1) { if sc.doMarkup && (level > 0 || sc.configVersion() == 1) {
cp := p.pageOutput.cp
if cp == nil {
var err error var err error
cp, err = newPageContentOutput(p, p.pageOutput) b, err := p.pageOutput.contentRenderer.RenderContent([]byte(inner), false)
if err != nil {
return "", false, err
}
p.pageOutput.initContentProvider(cp)
}
var err error
b, err := p.pageOutput.cp.renderContent([]byte(inner), false)
if err != nil { if err != nil {
return "", false, err return "", false, err
} }

View file

@ -19,6 +19,7 @@ import (
"html/template" "html/template"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/converter"
"github.com/bep/gitmap" "github.com/bep/gitmap"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
@ -105,6 +106,11 @@ type ContentProvider interface {
Len() int Len() int
} }
// ContentRenderer provides the content rendering methods for some content.
type ContentRenderer interface {
RenderContent(content []byte, renderTOC bool) (converter.Result, error)
}
// FileProvider provides the source file. // FileProvider provides the source file.
type FileProvider interface { type FileProvider interface {
File() source.File File() source.File

View file

@ -17,6 +17,7 @@ import (
"html/template" "html/template"
"github.com/gohugoio/hugo/lazy" "github.com/gohugoio/hugo/lazy"
"github.com/gohugoio/hugo/markup/converter"
) )
// OutputFormatContentProvider represents the method set that is "outputFormat aware" and that we // OutputFormatContentProvider represents the method set that is "outputFormat aware" and that we
@ -24,6 +25,14 @@ import (
// Note that this set is currently not complete, but should cover the most common use cases. // Note that this set is currently not complete, but should cover the most common use cases.
// For the others, the implementation will be from the page.NoopPage. // For the others, the implementation will be from the page.NoopPage.
type OutputFormatContentProvider interface { type OutputFormatContentProvider interface {
OutputFormatPageContentProvider
// for internal use.
ContentRenderer
}
// OutputFormatPageContentProvider holds the exported methods from Page that are "outputFormat aware".
type OutputFormatPageContentProvider interface {
ContentProvider ContentProvider
TableOfContentsProvider TableOfContentsProvider
PageRenderProvider PageRenderProvider
@ -46,7 +55,7 @@ type LazyContentProvider struct {
func NewLazyContentProvider(f func() (OutputFormatContentProvider, error)) *LazyContentProvider { func NewLazyContentProvider(f func() (OutputFormatContentProvider, error)) *LazyContentProvider {
lcp := LazyContentProvider{ lcp := LazyContentProvider{
init: lazy.New(), init: lazy.New(),
cp: NopPage, cp: NopCPageContentRenderer,
} }
lcp.init.Add(func() (any, error) { lcp.init.Add(func() (any, error) {
cp, err := f() cp, err := f()
@ -122,3 +131,8 @@ func (lcp *LazyContentProvider) TableOfContents() template.HTML {
lcp.init.Do() lcp.init.Do()
return lcp.cp.TableOfContents() return lcp.cp.TableOfContents()
} }
func (lcp *LazyContentProvider) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
lcp.init.Do()
return lcp.cp.RenderContent(content, renderTOC)
}

View file

@ -16,10 +16,12 @@
package page package page
import ( import (
"bytes"
"html/template" "html/template"
"time" "time"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/tpl"
@ -42,6 +44,14 @@ import (
var ( var (
NopPage Page = new(nopPage) NopPage Page = new(nopPage)
NopContentRenderer ContentRenderer = new(nopContentRenderer)
NopCPageContentRenderer = struct {
OutputFormatPageContentProvider
ContentRenderer
}{
NopPage,
NopContentRenderer,
}
NilPage *nopPage NilPage *nopPage
) )
@ -513,3 +523,10 @@ func (p *nopPage) WordCount() int {
func (p *nopPage) GetIdentity() identity.Identity { func (p *nopPage) GetIdentity() identity.Identity {
return identity.NewPathIdentity("content", "foo/bar.md") return identity.NewPathIdentity("content", "foo/bar.md")
} }
type nopContentRenderer int
func (r *nopContentRenderer) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
b := &bytes.Buffer{}
return b, nil
}