Skip to content

Commit

Permalink
Create default link and image render hooks
Browse files Browse the repository at this point in the history
Fixes #11933
  • Loading branch information
bep committed Jan 30, 2024
1 parent 80595bb commit 5b7cb25
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 42 deletions.
2 changes: 2 additions & 0 deletions check_gofmt.sh
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
diff <(gofmt -d .) <(printf '')
5 changes: 5 additions & 0 deletions common/paths/path.go
Expand Up @@ -387,6 +387,11 @@ func ToSlashTrimLeading(s string) string {
return strings.TrimPrefix(filepath.ToSlash(s), "/")
}

// ToSlashTrimTrailing is just a filepath.ToSlash with an added / suffix trimmer.
func ToSlashTrimTrailing(s string) string {
return strings.TrimSuffix(filepath.ToSlash(s), "/")
}

// ToSlashPreserveLeading converts the path given to a forward slash separated path
// and preserves the leading slash if present trimming any trailing slash.
func ToSlashPreserveLeading(s string) string {
Expand Down
5 changes: 5 additions & 0 deletions common/types/types.go
Expand Up @@ -107,3 +107,8 @@ type LowHigh struct {

// This is only used for debugging purposes.
var InvocationCounter atomic.Int64

// NewTrue returns a pointer to b.
func NewBool(b bool) *bool {
return &b
}
13 changes: 13 additions & 0 deletions config/allconfig/allconfig.go
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/privacy"
Expand Down Expand Up @@ -899,6 +900,18 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
return nil, err
}

// Adjust Goldmark config defaults for multilingual, single-host sites.
if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
if !clone.Markup.Goldmark.DuplicateResourceFiles {
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
}
if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
}
}
}

langConfigMap[k] = clone
case maps.ParamsMergeStrategy:
default:
Expand Down
72 changes: 72 additions & 0 deletions hugolib/content_render_hooks_test.go
Expand Up @@ -14,6 +14,7 @@
package hugolib

import (
"strings"
"testing"
)

Expand Down Expand Up @@ -169,3 +170,74 @@ Self Fragments: [d e f]
P1 Fragments: [b c z]
`)
}

func TestDefaultRenderHooksMultilingual(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.org"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT"]
defaultContentLanguage = "nn"
defaultContentLanguageInSubdir = true
[markup]
[markup.goldmark]
duplicateResourceFiles = false
[markup.goldmark.renderhooks]
[markup.goldmark.renderhooks.link]
#enableDefault = false
[markup.goldmark.renderhooks.image]
#enableDefault = false
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
-- content/p1/index.md --
---
title: "p1"
---
[P2](p2)
![Pixel](pixel.png)
-- content/p2/index.md --
---
title: "p2"
---
[P1](p1)
![Pixel](pixel.jpg)
-- content/p1/index.en.md --
---
title: "p1 en"
---
[P2](p2)
![Pixel](pixel.png)
-- content/p2/index.en.md --
---
title: "p2 en"
---
[P1](p1)
![Pixel](pixel.png)
-- content/p1/pixel.nn.png --
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
-- content/p2/pixel.png --
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
-- layouts/_default/single.html --
{{ .Title }}|{{ .Content }}|$
`

t.Run("Default multilingual", func(t *testing.T) {
b := Test(t, files)

b.AssertFileContent("public/nn/p1/index.html",
"p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
b.AssertFileContent("public/en/p1/index.html",
"p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
})

t.Run("Disabled", func(t *testing.T) {
b := Test(t, strings.ReplaceAll(files, "#enableDefault = false", "enableDefault = false"))

b.AssertFileContent("public/nn/p1/index.html",
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
})
}
15 changes: 15 additions & 0 deletions hugolib/page__per_output.go
Expand Up @@ -470,6 +470,21 @@ func (pco *pageContentOutput) initRenderHooks() error {
if err != nil {
panic(err)
}
if found {
if isitp, ok := templ.(tpl.IsInternalTemplateProvider); ok && isitp.IsInternalTemplate() {
renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks
switch templ.Name() {
case "_default/_markup/render-link.html":
if !renderHookConfig.Link.IsEnableDefault() {
return nil, false
}
case "_default/_markup/render-image.html":
if !renderHookConfig.Image.IsEnableDefault() {
return nil, false
}
}
}
}
return templ, found
}

Expand Down
2 changes: 1 addition & 1 deletion hugolib/pagecollections.go
Expand Up @@ -56,7 +56,7 @@ func (c *pageFinder) getPageRef(context page.Page, ref string) (page.Page, error
}

func (c *pageFinder) getPage(context page.Page, ref string) (page.Page, error) {
n, err := c.getContentNode(context, false, filepath.ToSlash(ref))
n, err := c.getContentNode(context, false, paths.ToSlashTrimTrailing(ref))
if err != nil {
return nil, err
}
Expand Down
8 changes: 8 additions & 0 deletions hugolib/pagecollections_test.go
Expand Up @@ -413,6 +413,10 @@ title: p2
func TestPageGetPageVariations(t *testing.T) {
files := `
-- hugo.toml --
-- content/s1/_index.md --
---
title: s1 section
---
-- content/s1/p1/index.md --
---
title: p1
Expand All @@ -430,6 +434,8 @@ title: p3
title: p2_root
---
-- layouts/index.html --
/s1: {{ with .GetPage "/s1" }}{{ .Title }}{{ end }}|
/s1/: {{ with .GetPage "/s1/" }}{{ .Title }}{{ end }}|
/s1/p2.md: {{ with .GetPage "/s1/p2.md" }}{{ .Title }}{{ end }}|
/s1/p2: {{ with .GetPage "/s1/p2" }}{{ .Title }}{{ end }}|
/s1/p1/index.md: {{ with .GetPage "/s1/p1/index.md" }}{{ .Title }}{{ end }}|
Expand All @@ -444,6 +450,8 @@ p1/index.md: {{ with .GetPage "p1/index.md" }}{{ .Title }}{{ end }}|
b := Test(t, files)

b.AssertFileContent("public/index.html", `
/s1: s1 section|
/s1/: s1 section|
/s1/p2.md: p2|
/s1/p2: p2|
/s1/p1/index.md: p1|
Expand Down
41 changes: 9 additions & 32 deletions magefile.go
Expand Up @@ -185,42 +185,15 @@ func TestRace() error {

// Run gofmt linter
func Fmt() error {
if !isGoLatest() {
if !isGoLatest() && !isUnix() {
return nil
}
pkgs, err := hugoPackages()
s, err := sh.Output("./check_gofmt.sh")
if err != nil {
return err
}
failed := false
first := true
for _, pkg := range pkgs {
files, err := filepath.Glob(filepath.Join(pkg, "*.go"))
if err != nil {
return nil
}
for _, f := range files {
// gofmt doesn't exit with non-zero when it finds unformatted code
// so we have to explicitly look for output, and if we find any, we
// should fail this target.
s, err := sh.Output("gofmt", "-l", f)
if err != nil {
fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err)
failed = true
}
if s != "" {
if first {
fmt.Println("The following files are not gofmt'ed:")
first = false
}
failed = true
fmt.Println(s)
}
}
}
if failed {
return errors.New("improperly formatted go files")
fmt.Println(s)
return fmt.Errorf("gofmt needs to be run: %s", err)
}

return nil
}

Expand Down Expand Up @@ -332,6 +305,10 @@ func isGoLatest() bool {
return strings.Contains(runtime.Version(), "1.21")
}

func isUnix() bool {
return runtime.GOOS != "windows"
}

func isCI() bool {
return os.Getenv("CI") != ""
}
Expand Down
31 changes: 30 additions & 1 deletion markup/goldmark/goldmark_config/config.go
Expand Up @@ -73,10 +73,39 @@ var Default = Config{

// Config configures Goldmark.
type Config struct {
DuplicateResourceFiles bool
Renderer Renderer
Parser Parser
Extensions Extensions
DuplicateResourceFiles bool
RenderHooks RenderHooks
}

// RenderHooks contains configuration for Goldmark render hooks.
type RenderHooks struct {
Image ImageRenderHook
Link LinkRenderHook
}

// ImageRenderHook contains configuration for the image render hook.
type ImageRenderHook struct {
// Enable the default image render hook.
// We need to know if it is set or not, hence the pointer.
EnableDefault *bool
}

func (h ImageRenderHook) IsEnableDefault() bool {
return h.EnableDefault != nil && *h.EnableDefault
}

// LinkRenderHook contains configuration for the link render hook.
type LinkRenderHook struct {
// Disable the default image render hook.
// We need to know if it is set or not, hence the pointer.
EnableDefault *bool
}

func (h LinkRenderHook) IsEnableDefault() bool {
return h.EnableDefault != nil && *h.EnableDefault
}

type Extensions struct {
Expand Down
4 changes: 4 additions & 0 deletions tpl/template_info.go
Expand Up @@ -25,6 +25,10 @@ type FileInfo interface {
Filename() string
}

type IsInternalTemplateProvider interface {
IsInternalTemplate() bool
}

type ParseInfo struct {
// Set for shortcode templates with any {{ .Inner }}
IsInner bool
Expand Down
15 changes: 15 additions & 0 deletions tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
@@ -0,0 +1,15 @@
{{- $u := urls.Parse .Destination -}}
{{- $src := $u.String -}}
{{- if not $u.IsAbs -}}
{{- with or (.Page.Resources.Get $u.Path) (resources.Get $u.Path) -}}
{{- $src = .RelPermalink -}}
{{- end -}}
{{- end -}}
{{- $attributes := dict "alt" .Text "src" $src "title" .Title -}}
<img
{{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
{{- end -}}
{{- end -}}>
{{- /**/ -}}
26 changes: 26 additions & 0 deletions tpl/tplimpl/embedded/templates/_default/_markup/render-link.html
@@ -0,0 +1,26 @@
{{- $u := urls.Parse .Destination -}}
{{- $href := $u.String -}}
{{- if not $u.IsAbs -}}
{{- with or
($.Page.GetPage $u.Path)
($.Page.Resources.Get $u.Path)
(resources.Get $u.Path)
-}}
{{- $href = .RelPermalink -}}
{{- with $u.RawQuery -}}
{{- $href = printf "%s?%s" $href . -}}
{{- end -}}
{{- with $u.Fragment -}}
{{- $href = printf "%s#%s" $href . -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $attributes := dict "href" $href "title" .Title -}}
<a
{{- range $k, $v := $attributes -}}
{{- if $v -}}
{{- printf " %s=%q" $k $v | safeHTMLAttr -}}
{{- end -}}
{{- end -}}
>{{ .Text | safeHTML }}</a>
{{- /**/ -}}

0 comments on commit 5b7cb25

Please sign in to comment.