Skip to content

Commit 5b7cb25

Browse files
committed
Create default link and image render hooks
Fixes gohugoio#11933
1 parent 80595bb commit 5b7cb25

File tree

15 files changed

+229
-42
lines changed

15 files changed

+229
-42
lines changed

check_gofmt.sh

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
diff <(gofmt -d .) <(printf '')

common/paths/path.go

+5
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,11 @@ func ToSlashTrimLeading(s string) string {
387387
return strings.TrimPrefix(filepath.ToSlash(s), "/")
388388
}
389389

390+
// ToSlashTrimTrailing is just a filepath.ToSlash with an added / suffix trimmer.
391+
func ToSlashTrimTrailing(s string) string {
392+
return strings.TrimSuffix(filepath.ToSlash(s), "/")
393+
}
394+
390395
// ToSlashPreserveLeading converts the path given to a forward slash separated path
391396
// and preserves the leading slash if present trimming any trailing slash.
392397
func ToSlashPreserveLeading(s string) string {

common/types/types.go

+5
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,8 @@ type LowHigh struct {
107107

108108
// This is only used for debugging purposes.
109109
var InvocationCounter atomic.Int64
110+
111+
// NewTrue returns a pointer to b.
112+
func NewBool(b bool) *bool {
113+
return &b
114+
}

config/allconfig/allconfig.go

+13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/gohugoio/hugo/common/loggers"
3232
"github.com/gohugoio/hugo/common/maps"
3333
"github.com/gohugoio/hugo/common/paths"
34+
"github.com/gohugoio/hugo/common/types"
3435
"github.com/gohugoio/hugo/common/urls"
3536
"github.com/gohugoio/hugo/config"
3637
"github.com/gohugoio/hugo/config/privacy"
@@ -899,6 +900,18 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
899900
return nil, err
900901
}
901902

903+
// Adjust Goldmark config defaults for multilingual, single-host sites.
904+
if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
905+
if !clone.Markup.Goldmark.DuplicateResourceFiles {
906+
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
907+
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
908+
}
909+
if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
910+
clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
911+
}
912+
}
913+
}
914+
902915
langConfigMap[k] = clone
903916
case maps.ParamsMergeStrategy:
904917
default:

hugolib/content_render_hooks_test.go

+72
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package hugolib
1515

1616
import (
17+
"strings"
1718
"testing"
1819
)
1920

@@ -169,3 +170,74 @@ Self Fragments: [d e f]
169170
P1 Fragments: [b c z]
170171
`)
171172
}
173+
174+
func TestDefaultRenderHooksMultilingual(t *testing.T) {
175+
files := `
176+
-- hugo.toml --
177+
baseURL = "https://example.org"
178+
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT"]
179+
defaultContentLanguage = "nn"
180+
defaultContentLanguageInSubdir = true
181+
[markup]
182+
[markup.goldmark]
183+
duplicateResourceFiles = false
184+
[markup.goldmark.renderhooks]
185+
[markup.goldmark.renderhooks.link]
186+
#enableDefault = false
187+
[markup.goldmark.renderhooks.image]
188+
#enableDefault = false
189+
[languages]
190+
[languages.en]
191+
weight = 1
192+
[languages.nn]
193+
weight = 2
194+
-- content/p1/index.md --
195+
---
196+
title: "p1"
197+
---
198+
[P2](p2)
199+
![Pixel](pixel.png)
200+
-- content/p2/index.md --
201+
---
202+
title: "p2"
203+
---
204+
[P1](p1)
205+
![Pixel](pixel.jpg)
206+
-- content/p1/index.en.md --
207+
---
208+
title: "p1 en"
209+
---
210+
[P2](p2)
211+
![Pixel](pixel.png)
212+
-- content/p2/index.en.md --
213+
---
214+
title: "p2 en"
215+
---
216+
[P1](p1)
217+
![Pixel](pixel.png)
218+
219+
-- content/p1/pixel.nn.png --
220+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
221+
-- content/p2/pixel.png --
222+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
223+
-- layouts/_default/single.html --
224+
{{ .Title }}|{{ .Content }}|$
225+
226+
`
227+
228+
t.Run("Default multilingual", func(t *testing.T) {
229+
b := Test(t, files)
230+
231+
b.AssertFileContent("public/nn/p1/index.html",
232+
"p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
233+
b.AssertFileContent("public/en/p1/index.html",
234+
"p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
235+
})
236+
237+
t.Run("Disabled", func(t *testing.T) {
238+
b := Test(t, strings.ReplaceAll(files, "#enableDefault = false", "enableDefault = false"))
239+
240+
b.AssertFileContent("public/nn/p1/index.html",
241+
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
242+
})
243+
}

hugolib/page__per_output.go

+15
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,21 @@ func (pco *pageContentOutput) initRenderHooks() error {
470470
if err != nil {
471471
panic(err)
472472
}
473+
if found {
474+
if isitp, ok := templ.(tpl.IsInternalTemplateProvider); ok && isitp.IsInternalTemplate() {
475+
renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks
476+
switch templ.Name() {
477+
case "_default/_markup/render-link.html":
478+
if !renderHookConfig.Link.IsEnableDefault() {
479+
return nil, false
480+
}
481+
case "_default/_markup/render-image.html":
482+
if !renderHookConfig.Image.IsEnableDefault() {
483+
return nil, false
484+
}
485+
}
486+
}
487+
}
473488
return templ, found
474489
}
475490

hugolib/pagecollections.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func (c *pageFinder) getPageRef(context page.Page, ref string) (page.Page, error
5656
}
5757

5858
func (c *pageFinder) getPage(context page.Page, ref string) (page.Page, error) {
59-
n, err := c.getContentNode(context, false, filepath.ToSlash(ref))
59+
n, err := c.getContentNode(context, false, paths.ToSlashTrimTrailing(ref))
6060
if err != nil {
6161
return nil, err
6262
}

hugolib/pagecollections_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ title: p2
413413
func TestPageGetPageVariations(t *testing.T) {
414414
files := `
415415
-- hugo.toml --
416+
-- content/s1/_index.md --
417+
---
418+
title: s1 section
419+
---
416420
-- content/s1/p1/index.md --
417421
---
418422
title: p1
@@ -430,6 +434,8 @@ title: p3
430434
title: p2_root
431435
---
432436
-- layouts/index.html --
437+
/s1: {{ with .GetPage "/s1" }}{{ .Title }}{{ end }}|
438+
/s1/: {{ with .GetPage "/s1/" }}{{ .Title }}{{ end }}|
433439
/s1/p2.md: {{ with .GetPage "/s1/p2.md" }}{{ .Title }}{{ end }}|
434440
/s1/p2: {{ with .GetPage "/s1/p2" }}{{ .Title }}{{ end }}|
435441
/s1/p1/index.md: {{ with .GetPage "/s1/p1/index.md" }}{{ .Title }}{{ end }}|
@@ -444,6 +450,8 @@ p1/index.md: {{ with .GetPage "p1/index.md" }}{{ .Title }}{{ end }}|
444450
b := Test(t, files)
445451

446452
b.AssertFileContent("public/index.html", `
453+
/s1: s1 section|
454+
/s1/: s1 section|
447455
/s1/p2.md: p2|
448456
/s1/p2: p2|
449457
/s1/p1/index.md: p1|

magefile.go

+9-32
Original file line numberDiff line numberDiff line change
@@ -185,42 +185,15 @@ func TestRace() error {
185185

186186
// Run gofmt linter
187187
func Fmt() error {
188-
if !isGoLatest() {
188+
if !isGoLatest() && !isUnix() {
189189
return nil
190190
}
191-
pkgs, err := hugoPackages()
191+
s, err := sh.Output("./check_gofmt.sh")
192192
if err != nil {
193-
return err
194-
}
195-
failed := false
196-
first := true
197-
for _, pkg := range pkgs {
198-
files, err := filepath.Glob(filepath.Join(pkg, "*.go"))
199-
if err != nil {
200-
return nil
201-
}
202-
for _, f := range files {
203-
// gofmt doesn't exit with non-zero when it finds unformatted code
204-
// so we have to explicitly look for output, and if we find any, we
205-
// should fail this target.
206-
s, err := sh.Output("gofmt", "-l", f)
207-
if err != nil {
208-
fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err)
209-
failed = true
210-
}
211-
if s != "" {
212-
if first {
213-
fmt.Println("The following files are not gofmt'ed:")
214-
first = false
215-
}
216-
failed = true
217-
fmt.Println(s)
218-
}
219-
}
220-
}
221-
if failed {
222-
return errors.New("improperly formatted go files")
193+
fmt.Println(s)
194+
return fmt.Errorf("gofmt needs to be run: %s", err)
223195
}
196+
224197
return nil
225198
}
226199

@@ -332,6 +305,10 @@ func isGoLatest() bool {
332305
return strings.Contains(runtime.Version(), "1.21")
333306
}
334307

308+
func isUnix() bool {
309+
return runtime.GOOS != "windows"
310+
}
311+
335312
func isCI() bool {
336313
return os.Getenv("CI") != ""
337314
}

markup/goldmark/goldmark_config/config.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,39 @@ var Default = Config{
7373

7474
// Config configures Goldmark.
7575
type Config struct {
76-
DuplicateResourceFiles bool
7776
Renderer Renderer
7877
Parser Parser
7978
Extensions Extensions
79+
DuplicateResourceFiles bool
80+
RenderHooks RenderHooks
81+
}
82+
83+
// RenderHooks contains configuration for Goldmark render hooks.
84+
type RenderHooks struct {
85+
Image ImageRenderHook
86+
Link LinkRenderHook
87+
}
88+
89+
// ImageRenderHook contains configuration for the image render hook.
90+
type ImageRenderHook struct {
91+
// Enable the default image render hook.
92+
// We need to know if it is set or not, hence the pointer.
93+
EnableDefault *bool
94+
}
95+
96+
func (h ImageRenderHook) IsEnableDefault() bool {
97+
return h.EnableDefault != nil && *h.EnableDefault
98+
}
99+
100+
// LinkRenderHook contains configuration for the link render hook.
101+
type LinkRenderHook struct {
102+
// Disable the default image render hook.
103+
// We need to know if it is set or not, hence the pointer.
104+
EnableDefault *bool
105+
}
106+
107+
func (h LinkRenderHook) IsEnableDefault() bool {
108+
return h.EnableDefault != nil && *h.EnableDefault
80109
}
81110

82111
type Extensions struct {

tpl/template_info.go

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type FileInfo interface {
2525
Filename() string
2626
}
2727

28+
type IsInternalTemplateProvider interface {
29+
IsInternalTemplate() bool
30+
}
31+
2832
type ParseInfo struct {
2933
// Set for shortcode templates with any {{ .Inner }}
3034
IsInner bool
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{- $u := urls.Parse .Destination -}}
2+
{{- $src := $u.String -}}
3+
{{- if not $u.IsAbs -}}
4+
{{- with or (.Page.Resources.Get $u.Path) (resources.Get $u.Path) -}}
5+
{{- $src = .RelPermalink -}}
6+
{{- end -}}
7+
{{- end -}}
8+
{{- $attributes := dict "alt" .Text "src" $src "title" .Title -}}
9+
<img
10+
{{- range $k, $v := $attributes -}}
11+
{{- if $v -}}
12+
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
13+
{{- end -}}
14+
{{- end -}}>
15+
{{- /**/ -}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{{- $u := urls.Parse .Destination -}}
2+
{{- $href := $u.String -}}
3+
{{- if not $u.IsAbs -}}
4+
{{- with or
5+
($.Page.GetPage $u.Path)
6+
($.Page.Resources.Get $u.Path)
7+
(resources.Get $u.Path)
8+
-}}
9+
{{- $href = .RelPermalink -}}
10+
{{- with $u.RawQuery -}}
11+
{{- $href = printf "%s?%s" $href . -}}
12+
{{- end -}}
13+
{{- with $u.Fragment -}}
14+
{{- $href = printf "%s#%s" $href . -}}
15+
{{- end -}}
16+
{{- end -}}
17+
{{- end -}}
18+
{{- $attributes := dict "href" $href "title" .Title -}}
19+
<a
20+
{{- range $k, $v := $attributes -}}
21+
{{- if $v -}}
22+
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
23+
{{- end -}}
24+
{{- end -}}
25+
>{{ .Text | safeHTML }}</a>
26+
{{- /**/ -}}

0 commit comments

Comments
 (0)