diff options
Diffstat (limited to 'tpl')
20 files changed, 810 insertions, 88 deletions
diff --git a/tpl/fmt/fmt_integration_test.go b/tpl/fmt/fmt_integration_test.go index 74322770e..87a89943c 100644 --- a/tpl/fmt/fmt_integration_test.go +++ b/tpl/fmt/fmt_integration_test.go @@ -27,16 +27,22 @@ func TestErroridf(t *testing.T) { files := ` -- hugo.toml -- disableKinds = ['page','rss','section','sitemap','taxonomy','term'] -ignoreErrors = ['error-b'] +ignoreErrors = ['error-b','error-C'] -- layouts/index.html -- {{ erroridf "error-a" "%s" "a"}} {{ erroridf "error-b" "%s" "b"}} +{{ erroridf "error-C" "%s" "C"}} +{{ erroridf "error-c" "%s" "c"}} + {{ erroridf "error-d" "%s" "D"}} ` b, err := hugolib.TestE(t, files) b.Assert(err, qt.IsNotNil) - b.AssertLogMatches(`^ERROR a\nYou can suppress this error by adding the following to your site configuration:\nignoreLogs = \['error-a'\]\n$`) + b.AssertLogMatches(`ERROR a\nYou can suppress this error by adding the following to your site configuration:\nignoreLogs = \['error-a'\]`) + b.AssertLogMatches(`ERROR D`) + b.AssertLogMatches(`! ERROR C`) + b.AssertLogMatches(`! ERROR c`) } func TestWarnidf(t *testing.T) { @@ -45,13 +51,15 @@ func TestWarnidf(t *testing.T) { files := ` -- hugo.toml -- disableKinds = ['page','rss','section','sitemap','taxonomy','term'] -ignoreLogs = ['warning-b'] +ignoreLogs = ['warning-b', 'WarniNg-C'] -- layouts/index.html -- {{ warnidf "warning-a" "%s" "a"}} {{ warnidf "warning-b" "%s" "b"}} +{{ warnidf "warNing-C" "%s" "c"}} ` b := hugolib.Test(t, files, hugolib.TestOptWarn()) b.AssertLogContains("WARN a", "You can suppress this warning", "ignoreLogs", "['warning-a']") - b.AssertLogNotContains("['warning-b']") + b.AssertLogContains("! ['warning-b']") + b.AssertLogContains("! ['warning-c']") } diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go index 04af756ef..34b4464be 100644 --- a/tpl/resources/resources.go +++ b/tpl/resources/resources.go @@ -369,7 +369,7 @@ func (ns *Namespace) ToCSS(args ...any) (resource.Resource, error) { } if m != nil { - if t, found := maps.LookupEqualFold(m, "transpiler"); found { + if t, _, found := maps.LookupEqualFold(m, "transpiler"); found { switch t { case transpilerDart, transpilerLibSass: transpiler = cast.ToString(t) @@ -440,7 +440,6 @@ func (ns *Namespace) Babel(args ...any) (resource.Resource, error) { var options babel.Options if m != nil { options, err = babel.DecodeOptions(m) - if err != nil { return nil, err } diff --git a/tpl/template.go b/tpl/template.go index 5ef0eecb8..cb8d2b321 100644 --- a/tpl/template.go +++ b/tpl/template.go @@ -20,11 +20,13 @@ import ( "reflect" "regexp" "strings" + "sync" "unicode" bp "github.com/gohugoio/hugo/bufferpool" "github.com/gohugoio/hugo/common/hcontext" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/output/layouts" "github.com/gohugoio/hugo/output" @@ -65,10 +67,14 @@ type TemplateHandlers struct { TxtTmpl TemplateParseFinder } +type TemplateExecutor interface { + ExecuteWithContext(ctx context.Context, t Template, wr io.Writer, data any) error +} + // TemplateHandler finds and executes templates. type TemplateHandler interface { TemplateFinder - ExecuteWithContext(ctx context.Context, t Template, wr io.Writer, data any) error + TemplateExecutor LookupLayout(d layouts.LayoutDescriptor, f output.Format) (Template, bool, error) HasTemplate(name string) bool GetIdentity(name string) (identity.Identity, bool) @@ -156,6 +162,11 @@ type TemplateFuncGetter interface { GetFunc(name string) (reflect.Value, bool) } +type RenderingContext struct { + Site site + SiteOutIdx int +} + type contextKey string // Context manages values passed in the context to templates. @@ -187,6 +198,15 @@ type page interface { IsNode() bool } +type site interface { + Language() *langs.Language +} + +const ( + HugoDeferredTemplatePrefix = "__hdeferred/" + HugoDeferredTemplateSuffix = "__d=" +) + const hugoNewLinePlaceholder = "___hugonl_" var stripHTMLReplacerPre = strings.NewReplacer("\n", " ", "</p>", hugoNewLinePlaceholder, "<br>", hugoNewLinePlaceholder, "<br />", hugoNewLinePlaceholder) @@ -224,3 +244,13 @@ func StripHTML(s string) string { return s } + +type DeferredExecution struct { + Mu sync.Mutex + Ctx context.Context + TemplateName string + Data any + + Executed bool + Result string +} diff --git a/tpl/templates/defer_integration_test.go b/tpl/templates/defer_integration_test.go new file mode 100644 index 000000000..2c2bf0d80 --- /dev/null +++ b/tpl/templates/defer_integration_test.go @@ -0,0 +1,202 @@ +// Copyright 2024 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 templates_test + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/gohugoio/hugo/hugolib" +) + +const deferFilesCommon = ` +-- hugo.toml -- +disableLiveReload = true +disableKinds = ["taxonomy", "term", "rss", "sitemap", "robotsTXT", "404", "section"] +[languages] +[languages.en] +weight = 1 +[languages.nn] +weight = 2 +-- i18n/en.toml -- +[hello] +other = "Hello" +-- i18n/nn.toml -- +[hello] +other = "Hei" +-- content/_index.en.md -- +--- +title: "Home" +outputs: ["html", "amp"] +--- +-- content/_index.nn.md -- +--- +title: "Heim" +outputs: ["html", "amp"] +--- +-- assets/mytext.txt -- +Hello. +-- layouts/baseof.html -- +HTML|{{ block "main" . }}{{ end }}$ +-- layouts/index.html -- +{{ define "main" }} +EDIT_COUNTER_OUTSIDE_0 +{{ .Store.Set "hello" "Hello" }} +{{ $data := dict "page" . }} +{{ with (templates.Defer (dict "data" $data) ) }} +{{ $mytext := resources.Get "mytext.txt" }} +REPLACE_ME|Title: {{ .page.Title }}|{{ .page.RelPermalink }}|Hello: {{ T "hello" }}|Hello Store: {{ .page.Store.Get "hello" }}|Mytext: {{ $mytext.Content }}| +EDIT_COUNTER_DEFER_0 +{{ end }}$ +{{ end }} +-- layouts/index.amp.html -- +AMP. +{{ $data := dict "page" . }} +{{ with (templates.Defer (dict "data" $data) ) }}Title AMP: {{ .page.Title }}|{{ .page.RelPermalink }}|Hello: {{ T "hello" }}{{ end }}$ + +` + +func TestDeferBasic(t *testing.T) { + t.Parallel() + + b := hugolib.Test(t, deferFilesCommon) + + b.AssertFileContent("public/index.html", "Title: Home|/|Hello: Hello|Hello Store: Hello|Mytext: Hello.|") + b.AssertFileContent("public/amp/index.html", "Title AMP: Home|/amp/|Hello: Hello") + b.AssertFileContent("public/nn/index.html", "Title: Heim|/nn/|Hello: Hei") + b.AssertFileContent("public/nn/amp/index.html", "Title AMP: Heim|/nn/amp/|Hello: Hei") +} + +func TestDeferRepeatedBuildsEditOutside(t *testing.T) { + t.Parallel() + + b := hugolib.TestRunning(t, deferFilesCommon) + + for i := 0; i < 5; i++ { + old := fmt.Sprintf("EDIT_COUNTER_OUTSIDE_%d", i) + new := fmt.Sprintf("EDIT_COUNTER_OUTSIDE_%d", i+1) + b.EditFileReplaceAll("layouts/index.html", old, new).Build() + b.AssertFileContent("public/index.html", new) + } +} + +func TestDeferRepeatedBuildsEditDefer(t *testing.T) { + t.Parallel() + + b := hugolib.TestRunning(t, deferFilesCommon) + + for i := 0; i < 8; i++ { + old := fmt.Sprintf("EDIT_COUNTER_DEFER_%d", i) + new := fmt.Sprintf("EDIT_COUNTER_DEFER_%d", i+1) + b.EditFileReplaceAll("layouts/index.html", old, new).Build() + b.AssertFileContent("public/index.html", new) + } +} + +func TestDeferErrorParse(t *testing.T) { + t.Parallel() + + b, err := hugolib.TestE(t, strings.ReplaceAll(deferFilesCommon, "Title AMP: {{ .page.Title }}", "{{ .page.Title }")) + + b.Assert(err, qt.Not(qt.IsNil)) + b.Assert(err.Error(), qt.Contains, `index.amp.html:3: unexpected "}" in operand`) +} + +func TestDeferErrorRuntime(t *testing.T) { + t.Parallel() + + b, err := hugolib.TestE(t, strings.ReplaceAll(deferFilesCommon, "Title AMP: {{ .page.Title }}", "{{ .page.Titles }}")) + + b.Assert(err, qt.Not(qt.IsNil)) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`/layouts/index.amp.html:3:57`)) + b.Assert(err.Error(), qt.Contains, `execute of template failed: template: index.amp.html:3:57: executing at <.page.Titles>: can't evaluate field Titles`) +} + +func TestDeferEditDeferBlock(t *testing.T) { + t.Parallel() + + b := hugolib.TestRunning(t, deferFilesCommon) + b.AssertRenderCountPage(4) + b.EditFileReplaceAll("layouts/index.html", "REPLACE_ME", "Edited.").Build() + b.AssertFileContent("public/index.html", "Edited.") + b.AssertRenderCountPage(2) +} + +// + +func TestDeferEditResourceUsedInDeferBlock(t *testing.T) { + t.Parallel() + + b := hugolib.TestRunning(t, deferFilesCommon) + b.AssertRenderCountPage(4) + b.EditFiles("assets/mytext.txt", "Mytext Hello Edited.").Build() + b.AssertFileContent("public/index.html", "Mytext Hello Edited.") + b.AssertRenderCountPage(2) +} + +func TestDeferMountPublic(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[module] +[[module.mounts]] +source = "content" +target = "content" +[[module.mounts]] +source = "layouts" +target = "layouts" +[[module.mounts]] +source = 'public' +target = 'assets/public' +disableWatch = true +-- layouts/index.html -- +Home. +{{ $mydata := dict "v1" "v1value" }} +{{ $json := resources.FromString "mydata/data.json" ($mydata | jsonify ) }} +{{ $nop := $json.RelPermalink }} +{{ with (templates.Defer (dict "key" "foo")) }} + {{ $jsonFilePublic := resources.Get "public/mydata/data.json" }} + {{ with $jsonFilePublic }} + {{ $m := $jsonFilePublic | transform.Unmarshal }} + v1: {{ $m.v1 }} + {{ end }} +{{ end }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "v1: v1value") +} + +func TestDeferFromContentAdapterShouldFail(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- content/_content.gotmpl -- +{{ with (templates.Defer (dict "key" "foo")) }} + Foo. +{{ end }} +` + + b, err := hugolib.TestE(t, files) + + b.Assert(err, qt.Not(qt.IsNil)) + b.Assert(err.Error(), qt.Contains, "error calling Defer: this method cannot be called before the site is fully initialized") +} diff --git a/tpl/templates/init.go b/tpl/templates/init.go index ff6acdabd..7bd1f50c5 100644 --- a/tpl/templates/init.go +++ b/tpl/templates/init.go @@ -39,6 +39,16 @@ func init() { }, ) + ns.AddMethodMapping(ctx.Defer, + nil, // No aliases to keep the AST parsing simple. + [][2]string{}, + ) + + ns.AddMethodMapping(ctx.DoDefer, + []string{"doDefer"}, + [][2]string{}, + ) + return ns } diff --git a/tpl/templates/templates.go b/tpl/templates/templates.go index 8e40f3443..91e96ed8e 100644 --- a/tpl/templates/templates.go +++ b/tpl/templates/templates.go @@ -15,14 +15,24 @@ package templates import ( + "context" + "fmt" + "strconv" + "sync/atomic" + "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/tpl" + "github.com/mitchellh/mapstructure" ) // New returns a new instance of the templates-namespaced template functions. func New(deps *deps.Deps) *Namespace { - return &Namespace{ + ns := &Namespace{ deps: deps, } + + return ns } // Namespace provides template functions for the "templates" namespace. @@ -36,3 +46,59 @@ type Namespace struct { func (ns *Namespace) Exists(name string) bool { return ns.deps.Tmpl().HasTemplate(name) } + +// Defer defers the execution of a template block. +func (ns *Namespace) Defer(args ...any) (bool, error) { + // Prevent defer from being used in content adapters, + // that just doesn't work. + ns.deps.Site.CheckReady() + + if len(args) != 0 { + return false, fmt.Errorf("Defer does not take any arguments") + } + return true, nil +} + +var defferedIDCounter atomic.Uint64 + +type DeferOpts struct { + // Optional cache key. If set, the deferred block will be executed + // once per unique key. + Key string + + // Optional data context to use when executing the deferred block. + Data any +} + +// DoDefer defers the execution of a template block. +// For internal use only. +func (ns *Namespace) DoDefer(ctx context.Context, id string, optsv any) string { + var opts DeferOpts + if optsv != nil { + if err := mapstructure.WeakDecode(optsv, &opts); err != nil { + panic(err) + } + } + + templateName := id + var key string + if opts.Key != "" { + key = helpers.MD5String(opts.Key) + } else { + key = strconv.FormatUint(defferedIDCounter.Add(1), 10) + } + + id = fmt.Sprintf("%s_%s%s", id, key, tpl.HugoDeferredTemplateSuffix) + + _ = ns.deps.BuildState.DeferredExecutions.Executions.GetOrCreate(id, + func() *tpl.DeferredExecution { + return &tpl.DeferredExecution{ + TemplateName: templateName, + Ctx: ctx, + Data: opts.Data, + Executed: false, + } + }) + + return id +} diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html index 013e31235..89514cb83 100644 --- a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html +++ b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html @@ -1,11 +1,18 @@ {{- $u := urls.Parse .Destination -}} {{- $src := $u.String -}} {{- if not $u.IsAbs -}} - {{- with or (.PageInner.Resources.Get $u.Path) (resources.Get $u.Path) -}} + {{- $path := strings.TrimPrefix "./" $u.Path }} + {{- with or (.PageInner.Resources.Get $path) (resources.Get $path) -}} {{- $src = .RelPermalink -}} + {{- with $u.RawQuery -}} + {{- $src = printf "%s?%s" $src . -}} + {{- end -}} + {{- with $u.Fragment -}} + {{- $src = printf "%s#%s" $src . -}} + {{- end -}} {{- end -}} {{- end -}} -{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" .Title) -}} +{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" (.Title | transform.HTMLEscape)) -}} <img {{- range $k, $v := $attributes -}} {{- if $v -}} diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html index 8903d3dfb..daf3f11e1 100644 --- a/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html +++ b/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html @@ -3,10 +3,11 @@ {{- if strings.HasPrefix $u.String "#" }} {{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment }} {{- else if not $u.IsAbs -}} + {{- $path := strings.TrimPrefix "./" $u.Path }} {{- with or - ($.PageInner.GetPage $u.Path) - ($.PageInner.Resources.Get $u.Path) - (resources.Get $u.Path) + ($.PageInner.GetPage $path) + ($.PageInner.Resources.Get $path) + (resources.Get $path) -}} {{- $href = .RelPermalink -}} {{- with $u.RawQuery -}} @@ -17,7 +18,7 @@ {{- end -}} {{- end -}} {{- end -}} -{{- $attributes := dict "href" $href "title" .Title -}} +{{- $attributes := dict "href" $href "title" (.Title | transform.HTMLEscape) -}} <a {{- range $k, $v := $attributes -}} {{- if $v -}} diff --git a/tpl/tplimpl/embedded/templates/_default/rss.xml b/tpl/tplimpl/embedded/templates/_default/rss.xml index a3cade657..2e505f1bc 100644 --- a/tpl/tplimpl/embedded/templates/_default/rss.xml +++ b/tpl/tplimpl/embedded/templates/_default/rss.xml @@ -48,7 +48,7 @@ <title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title> <link>{{ .Permalink }}</link> <description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description> - <generator>Hugo {{ hugo.Version }}</generator> + <generator>Hugo</generator> <language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }} <managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }} <webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster>{{ end }}{{ with .Site.Copyright }} diff --git a/tpl/tplimpl/embedded/templates/opengraph.html b/tpl/tplimpl/embedded/templates/opengraph.html index c245e5bd1..59e63d6be 100644 --- a/tpl/tplimpl/embedded/templates/opengraph.html +++ b/tpl/tplimpl/embedded/templates/opengraph.html @@ -4,21 +4,23 @@ <meta property="og:site_name" content="{{ . }}"> {{- end }} -{{- with or .Title site.Title site.Params.title | plainify}} +{{- with or .Title site.Title site.Params.title | plainify }} <meta property="og:title" content="{{ . }}"> {{- end }} -{{- with or .Description .Summary site.Params.description | plainify }} +{{- with or .Description .Summary site.Params.description | plainify | htmlUnescape | chomp }} <meta property="og:description" content="{{ . }}"> {{- end }} -{{- with or .Params.locale site.Language.LanguageCode site.Language.Lang }} - <meta property="og:locale" content="{{ . }}"> +{{- with or .Params.locale site.Language.LanguageCode }} + <meta property="og:locale" content="{{ replace . `-` `_` }}"> {{- end }} {{- if .IsPage }} <meta property="og:type" content="article"> - <meta property="article:section" content="{{ .Section }}"> + {{- with .Section }} + <meta property="article:section" content="{{ . }}"> + {{- end }} {{- $ISO8601 := "2006-01-02T15:04:05-07:00" }} {{- with .PublishDate }} <meta property="article:published_time" {{ .Format $ISO8601 | printf "content=%q" | safeHTMLAttr }}> diff --git a/tpl/tplimpl/embedded/templates/schema.html b/tpl/tplimpl/embedded/templates/schema.html index c4e89abd6..2b3c5425a 100644 --- a/tpl/tplimpl/embedded/templates/schema.html +++ b/tpl/tplimpl/embedded/templates/schema.html @@ -1,8 +1,8 @@ -{{- with or .Title site.Title }} +{{- with or .Title site.Title | plainify }} <meta itemprop="name" content="{{ . }}"> {{- end }} -{{- with or .Description .Summary site.Params.Description }} +{{- with or .Description .Summary site.Params.description | plainify | htmlUnescape | chomp }} <meta itemprop="description" content="{{ . }}"> {{- end }} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html index bf72be45b..93fa18197 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html @@ -8,10 +8,10 @@ Renders an embedded YouTube video. @param {int} [end] The time, measured in seconds from the start of the video, when the player should stop playing the video. @param {string} [id] The video id. Optional if the id is provided as first positional argument. @param {string} [loading=eager] The loading attribute of the iframe element. -@param {bool} [loop=false] Whether to indefinitely repeat the video. +@param {bool} [loop=false] Whether to indefinitely repeat the video. Ignores the start and end arguments after the first play. @param {bool} [mute=false] Whether to mute the video. Always true when autoplay is true. @param {int} [start] The time, measured in seconds from the start of the video, when the player should start playing the video. -@param {string} [title] The title attribute of the iframe element. Defaults to the title returned by YouTube oEmbed API. +@param {string} [title] The title attribute of the iframe element. Defaults to "YouTube video". @returns {template.HTML} @@ -26,20 +26,6 @@ Renders an embedded YouTube video. {{- if not $pc.Disable }} {{- with $id := or (.Get "id") (.Get 0) }} - {{- /* Get data from the YouTube oEmbed API. */}} - {{- $q := querify "url" (printf "https://www.youtube.com/watch?v=%s" $id) "format" "json" }} - {{- $url := printf "https://www.youtube.com/oembed?%s" $q }} - {{- $data := dict }} - {{- with resources.GetRemote $url }} - {{- with .Err }} - {{- erroridf $remoteErrID "The %q shortcode was unable to get remote resource %q. %s. See %s" $.Name $url . $.Position }} - {{- else }} - {{- $data = .Content | transform.Unmarshal }} - {{- end }} - {{- else }} - {{- erroridf $remoteErrID "The %q shortcode was unable to get remote resource %q. See %s" $.Name $url $.Position }} - {{- end }} - {{/* Set defaults. */}} {{- $allowFullScreen := "allowfullscreen" }} {{- $autoplay := 0 }} @@ -50,7 +36,7 @@ Renders an embedded YouTube video. {{- $loop := 0 }} {{- $mute := 0 }} {{- $start := 0 }} - {{- $title := $data.title }} + {{- $title := "YouTube video" }} {{- /* Get arguments. */}} {{- if in (slice "false" false 0) ($.Get "allowFullScreen") }} diff --git a/tpl/tplimpl/embedded/templates/twitter_cards.html b/tpl/tplimpl/embedded/templates/twitter_cards.html index 14c92274b..6f156c7a7 100644 --- a/tpl/tplimpl/embedded/templates/twitter_cards.html +++ b/tpl/tplimpl/embedded/templates/twitter_cards.html @@ -1,12 +1,18 @@ -{{- $images := partial "_funcs/get-page-images" . -}} -{{- with index $images 0 -}} -<meta name="twitter:card" content="summary_large_image"> -<meta name="twitter:image" content="{{ .Permalink }}"> -{{- else -}} -<meta name="twitter:card" content="summary"> -{{- end -}} -<meta name="twitter:title" content="{{ .Title }}"> -<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"> +{{- $images := partial "_funcs/get-page-images" . }} +{{- with index $images 0 }} + <meta name="twitter:card" content="summary_large_image"> + <meta name="twitter:image" content="{{ .Permalink }}"> +{{- else }} + <meta name="twitter:card" content="summary"> +{{- end }} + +{{- with or .Title site.Title site.Params.title | plainify }} + <meta name="twitter:title" content="{{ . }}"> +{{- end }} + +{{- with or .Description .Summary site.Params.description | plainify | htmlUnescape | chomp }} + <meta name="twitter:description" content="{{ . }}"> +{{- end }} {{- $twitterSite := "" }} {{- with site.Params.social }} diff --git a/tpl/tplimpl/render_hook_integration_test.go b/tpl/tplimpl/render_hook_integration_test.go index 07c24108d..b91358227 100644 --- a/tpl/tplimpl/render_hook_integration_test.go +++ b/tpl/tplimpl/render_hook_integration_test.go @@ -51,7 +51,8 @@ title: s1/p1 title: s1/p2 --- [500](a.txt) // global resource -[600](b.txt) // page resource +[510](b.txt) // page resource +[520](./b.txt) // page resource -- content/s1/p2/b.txt -- irrelevant -- content/s1/p3.md -- @@ -125,35 +126,49 @@ title: s1/p3 b.AssertFileContent("public/s1/p2/index.html", `<a href="/a.txt">500</a>`, - `<a href="/s1/p2/b.txt">600</a>`, + `<a href="/s1/p2/b.txt">510</a>`, + `<a href="/s1/p2/b.txt">520</a>`, ) } // Issue 12203 -func TestEmbeddedImageRenderHookMarkdownAttributes(t *testing.T) { +// Issue 12468 +// Issue 12514 +func TestEmbeddedImageRenderHook(t *testing.T) { t.Parallel() files := ` -- config.toml -- -disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +baseURL = 'https://example.org/dir/' +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] [markup.goldmark.parser] wrapStandAloneImageWithinParagraph = false [markup.goldmark.parser.attribute] block = false [markup.goldmark.renderHooks.image] enableDefault = true --- content/_index.md -- -![alt](a.jpg) +-- content/p1/index.md -- +![alt1](./pixel.png) + +![alt2](pixel.png?a=b&c=d#fragment) {.foo #bar} --- layouts/index.html -- +-- content/p1/pixel.png -- +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== +-- layouts/_default/single.html -- {{ .Content }} ` b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", `<img alt="alt" src="a.jpg">`) + b.AssertFileContent("public/p1/index.html", + `<img alt="alt1" src="/dir/p1/pixel.png">`, + `<img alt="alt2" src="/dir/p1/pixel.png?a=b&c=d#fragment">`, + ) files = strings.Replace(files, "block = false", "block = true", -1) b = hugolib.Test(t, files) - b.AssertFileContent("public/index.html", `<img alt="alt" class="foo" id="bar" src="a.jpg">`) + b.AssertFileContent("public/p1/index.html", + `<img alt="alt1" src="/dir/p1/pixel.png">`, + `<img alt="alt2" class="foo" id="bar" src="/dir/p1/pixel.png?a=b&c=d#fragment">`, + ) } |