summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
Diffstat (limited to 'tpl')
-rw-r--r--tpl/fmt/fmt_integration_test.go16
-rw-r--r--tpl/resources/resources.go3
-rw-r--r--tpl/template.go32
-rw-r--r--tpl/templates/defer_integration_test.go202
-rw-r--r--tpl/templates/init.go10
-rw-r--r--tpl/templates/templates.go68
-rw-r--r--tpl/tplimpl/embedded/templates/_default/_markup/render-image.html11
-rw-r--r--tpl/tplimpl/embedded/templates/_default/_markup/render-link.html9
-rw-r--r--tpl/tplimpl/embedded/templates/_default/rss.xml2
-rw-r--r--tpl/tplimpl/embedded/templates/opengraph.html12
-rw-r--r--tpl/tplimpl/embedded/templates/schema.html4
-rw-r--r--tpl/tplimpl/embedded/templates/shortcodes/youtube.html20
-rw-r--r--tpl/tplimpl/embedded/templates/twitter_cards.html24
-rw-r--r--tpl/tplimpl/render_hook_integration_test.go33
-rw-r--r--tpl/tplimpl/template.go98
-rw-r--r--tpl/tplimpl/template_ast_transformers.go69
-rw-r--r--tpl/tplimpl/template_ast_transformers_test.go2
-rw-r--r--tpl/tplimpl/template_funcs.go10
-rw-r--r--tpl/tplimpl/tplimpl_integration_test.go265
-rw-r--r--tpl/transform/unmarshal.go8
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">`,
+ )
}
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 63dc29662..04ccdaad2 100644
--- a/