aboutsummaryrefslogtreecommitdiffhomepage
path: root/hugolib/page.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2019-11-27 13:42:36 +0100
committerBjørn Erik Pedersen <[email protected]>2019-12-18 11:44:40 +0100
commite625088ef5a970388ad50e464e87db56b358dac4 (patch)
treef7b26dec1f3695411558d05ca7d0995817a42250 /hugolib/page.go
parent67f3aa72cf9aaf3d6e447fa6bc12de704d46adf7 (diff)
downloadhugo-e625088ef5a970388ad50e464e87db56b358dac4.tar.gz
hugo-e625088ef5a970388ad50e464e87db56b358dac4.zip
Add render template hooks for links and images
This commit also * revises the change detection for templates used by content files in server mode. * Adds a Page.RenderString method Fixes #6545 Fixes #4663 Closes #6043
Diffstat (limited to 'hugolib/page.go')
-rw-r--r--hugolib/page.go184
1 files changed, 169 insertions, 15 deletions
diff --git a/hugolib/page.go b/hugolib/page.go
index 56202f5e0..fb3b597be 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -23,6 +23,12 @@ import (
"sort"
"strings"
+ "github.com/mitchellh/mapstructure"
+
+ "github.com/gohugoio/hugo/tpl"
+
+ "github.com/gohugoio/hugo/identity"
+
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/common/maps"
@@ -43,9 +49,11 @@ import (
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/source"
+ "github.com/spf13/cast"
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/text"
+ "github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
@@ -59,7 +67,11 @@ var (
var (
pageTypesProvider = resource.NewResourceTypesProvider(media.OctetType, pageResourceType)
- nopPageOutput = &pageOutput{pagePerOutputProviders: nopPagePerOutput}
+ nopPageOutput = &pageOutput{
+ pagePerOutputProviders: nopPagePerOutput,
+ ContentProvider: page.NopPage,
+ TableOfContentsProvider: page.NopPage,
+ }
)
// pageContext provides contextual information about this page, for error
@@ -317,6 +329,54 @@ func (ps *pageState) initCommonProviders(pp pagePaths) error {
return nil
}
+func (p *pageState) createRenderHooks(f output.Format) (*hooks.Render, error) {
+
+ layoutDescriptor := p.getLayoutDescriptor()
+ layoutDescriptor.RenderingHook = true
+ layoutDescriptor.LayoutOverride = false
+ layoutDescriptor.Layout = ""
+
+ layoutDescriptor.Kind = "render-link"
+ linkLayouts, err := p.s.layoutHandler.For(layoutDescriptor, f)
+ if err != nil {
+ return nil, err
+ }
+
+ layoutDescriptor.Kind = "render-image"
+ imageLayouts, err := p.s.layoutHandler.For(layoutDescriptor, f)
+ if err != nil {
+ return nil, err
+ }
+
+ if linkLayouts == nil && imageLayouts == nil {
+ return nil, nil
+ }
+
+ var linkRenderer hooks.LinkRenderer
+ var imageRenderer hooks.LinkRenderer
+
+ if templ, found := p.s.lookupTemplate(linkLayouts...); found {
+ linkRenderer = contentLinkRenderer{
+ templateHandler: p.s.Tmpl,
+ Provider: templ.(tpl.Info),
+ templ: templ,
+ }
+ }
+
+ if templ, found := p.s.lookupTemplate(imageLayouts...); found {
+ imageRenderer = contentLinkRenderer{
+ templateHandler: p.s.Tmpl,
+ Provider: templ.(tpl.Info),
+ templ: templ,
+ }
+ }
+
+ return &hooks.Render{
+ LinkRenderer: linkRenderer,
+ ImageRenderer: imageRenderer,
+ }, nil
+}
+
func (p *pageState) getLayoutDescriptor() output.LayoutDescriptor {
p.layoutDescriptorInit.Do(func() {
var section string
@@ -464,11 +524,86 @@ func (p *pageState) AlternativeOutputFormats() page.OutputFormats {
return o
}
-func (p *pageState) Render(layout ...string) template.HTML {
+type renderStringOpts struct {
+ Display string
+ Markup string
+}
+
+var defualtRenderStringOpts = renderStringOpts{
+ Display: "inline",
+ Markup: "", // Will inherit the page's value when not set.
+}
+
+func (p *pageState) RenderString(args ...interface{}) (template.HTML, error) {
+ if len(args) < 1 || len(args) > 2 {
+ return "", errors.New("want 1 or 2 arguments")
+ }
+
+ var s string
+ opts := defualtRenderStringOpts
+ sidx := 1
+
+ if len(args) == 1 {
+ sidx = 0
+ } else {
+ m, ok := args[0].(map[string]interface{})
+ if !ok {
+ return "", errors.New("first argument must be a map")
+ }
+
+ if err := mapstructure.WeakDecode(m, &opts); err != nil {
+ return "", errors.WithMessage(err, "failed to decode options")
+ }
+ }
+
+ var err error
+ s, err = cast.ToStringE(args[sidx])
+ if err != nil {
+ return "", err
+ }
+
+ conv := p.getContentConverter()
+ if opts.Markup != "" && opts.Markup != p.m.markup {
+ var err error
+ // TODO(bep) consider cache
+ conv, err = p.m.newContentConverter(p, opts.Markup, nil)
+ if err != nil {
+ return "", p.wrapError(err)
+ }
+ }
+
+ c, err := p.pageOutput.cp.renderContentWithConverter(conv, []byte(s), false)
+ if err != nil {
+ return "", p.wrapError(err)
+ }
+
+ b := c.Bytes()
+
+ if opts.Display == "inline" {
+ // We may have to rethink this in the future when we get other
+ // renderers.
+ b = p.s.ContentSpec.TrimShortHTML(b)
+ }
+
+ return template.HTML(string(b)), nil
+}
+
+func (p *pageState) addDependency(dep identity.Provider) {
+ if !p.s.running() || p.pageOutput.cp == nil {
+ return
+ }
+ p.pageOutput.cp.dependencyTracker.Add(dep)
+}
+
+func (p *pageState) RenderWithTemplateInfo(info tpl.Info, layout ...string) (template.HTML, error) {
+ p.addDependency(info)
+ return p.Render(layout...)
+}
+
+func (p *pageState) Render(layout ...string) (template.HTML, error) {
l, err := p.getLayouts(layout...)
if err != nil {
- p.s.SendError(p.wrapError(errors.Errorf(".Render: failed to resolve layout %v", layout)))
- return ""
+ return "", p.wrapError(errors.Errorf("failed to resolve layout %v", layout))
}
for _, layout := range l {
@@ -479,17 +614,18 @@ func (p *pageState) Render(layout ...string) template.HTML {
// We default to good old HTML.
templ, _ = p.s.Tmpl.Lookup(layout + ".html")
}
+
if templ != nil {
+ p.addDependency(templ.(tpl.Info))
res, err := executeToString(p.s.Tmpl, templ, p)
if err != nil {
- p.s.SendError(p.wrapError(errors.Wrapf(err, ".Render: failed to execute template %q v", layout)))
- return ""
+ return "", p.wrapError(errors.Wrapf(err, "failed to execute template %q v", layout))
}
- return template.HTML(res)
+ return template.HTML(res), nil
}
}
- return ""
+ return "", nil
}
@@ -745,15 +881,33 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
p.pageOutput.paginator.reset()
}
- if idx > 0 {
- // Check if we can reuse content from one of the previous formats.
- for i := idx - 1; i >= 0; i-- {
- po := p.pageOutputs[i]
- if po.cp != nil && po.cp.reuse {
- p.pageOutput.cp = po.cp
- break
+ if isRenderingSite {
+ cp := p.pageOutput.cp
+ if cp == nil {
+
+ // Look for content to reuse.
+ for i := 0; i < len(p.pageOutputs); i++ {
+ if i == idx {
+ continue
+ }
+ po := p.pageOutputs[i]
+
+ if po.cp != nil && po.cp.reuse {
+ cp = po.cp
+ break
+ }
+ }
+ }
+
+ if cp == nil {
+ var err error
+ cp, err = newPageContentOutput(p, p.pageOutput)
+ if err != nil {
+ return err
}
}
+ p.pageOutput.initContentProvider(cp)
+ p.pageOutput.cp = cp
}
for _, r := range p.Resources().ByType(pageResourceType) {