Skip to content

Commit 4a6393c

Browse files
committed
Add render template hooks for links and images
This commit also revises the change detection for templates used by content files in server mode. Fixes gohugoio#6545 Fixes gohugoio#4663 Closes gohugoio#6043
1 parent 92c7f7a commit 4a6393c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1389
-220
lines changed

deps/deps.go

+4
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,10 @@ func (d Deps) ForLanguage(cfg DepsCfg, onCreated func(d *Deps) error) (*Deps, er
286286
return nil, err
287287
}
288288

289+
if err != nil {
290+
return nil, err
291+
}
292+
289293
d.Site = cfg.Site
290294

291295
// The resource cache is global so reuse.

docs/content/en/content-management/formats.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ You can put any file type into your `/content` directories, but Hugo uses the `m
2323
* [Shortcodes](/content-management/shortcodes/) processed
2424
* Layout applied
2525

26-
## List of content formats
26+
{{< deleteme >}}
27+
28+
29+
## List of content formats.
30+
31+
32+
2733

2834
The current list of content formats in Hugo:
2935

docs/content/en/getting-started/quick-start.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ This quick start uses `macOS` in the examples. For instructions about how to ins
2424
It is recommended to have [Git](https://git-scm.com/downloads) installed to run this tutorial.
2525
{{% /note %}}
2626

27+
![Drag Racing](/images/Dragster.jpg "image title")
2728

2829

30+
![Drag Racing](/images/Dragster2.jpg "image title")
31+
2932
## Step 1: Install Hugo
3033

3134
{{% note %}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{{ $url := "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Mating_pair_of_Castalius_rosimon_WLB_DSC_2823.jpg/1024px-Mating_pair_of_Castalius_rosimon_WLB_DSC_2823.jpg" | safeURL }}
2+
<img src="{{ $url }}" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<a href="{{ .Destination | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }}>😉😉{{ .Text | safeHTML }} 😉😉</a>

docs/layouts/partials/deleteme.html

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
THIS IS PARTIAL!!!

docs/layouts/shortcodes/deleteme.html

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DELETEME PARTIAL:
2+
3+
{{ partial "deleteme" }}

helpers/content.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ import (
2525

2626
"github.com/gohugoio/hugo/common/loggers"
2727

28+
"github.com/spf13/afero"
29+
2830
"github.com/gohugoio/hugo/markup/converter"
2931

3032
"github.com/gohugoio/hugo/markup"
3133

3234
bp "github.com/gohugoio/hugo/bufferpool"
3335
"github.com/gohugoio/hugo/config"
34-
"github.com/spf13/afero"
3536

3637
"strings"
3738
)
@@ -78,6 +79,7 @@ func NewContentSpec(cfg config.Provider, logger *loggers.Logger, contentFs afero
7879
ContentFs: contentFs,
7980
Logger: logger,
8081
})
82+
8183
if err != nil {
8284
return nil, err
8385
}

helpers/general_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ import (
2121

2222
"github.com/spf13/viper"
2323

24-
"github.com/gohugoio/hugo/common/loggers"
25-
2624
qt "github.com/frankban/quicktest"
25+
"github.com/gohugoio/hugo/common/loggers"
2726
"github.com/spf13/afero"
2827
)
2928

hugolib/content_render_hooks_test.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2019 The Hugo Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package hugolib
15+
16+
import "testing"
17+
18+
func TestRenderHooks(t *testing.T) {
19+
// TODO1 markdownify
20+
config := `
21+
baseURL="https://example.org"
22+
workingDir="/mywork"
23+
`
24+
b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
25+
b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
26+
b.WithTemplatesAdded("shortcodes/myshortcode1.html", `{{ partial "mypartial1" }}`)
27+
b.WithTemplatesAdded("shortcodes/myshortcode2.html", `{{ partial "mypartial2" }}`)
28+
b.WithTemplatesAdded("shortcodes/myshortcode3.html", `SHORT3|`)
29+
b.WithTemplatesAdded("shortcodes/myshortcode4.html", `
30+
<div class="foo">
31+
{{ .Inner | markdownify }}
32+
</div>
33+
`)
34+
b.WithTemplatesAdded("shortcodes/myshortcode5.html", `
35+
<div class="foo">
36+
{{ .Inner | .Page.RenderString }}
37+
</div>
38+
`)
39+
40+
b.WithTemplatesAdded("partials/mypartial1.html", `PARTIAL1`)
41+
b.WithTemplatesAdded("partials/mypartial2.html", `PARTIAL2 {{ partial "mypartial3.html" }}`)
42+
b.WithTemplatesAdded("partials/mypartial3.html", `PARTIAL3`)
43+
b.WithTemplatesAdded("_default/_markup/render-link.html", `{{ with .Page }}{{ .Title }}{{ end }}|{{ .Destination | safeURL }}|Title: {{ .Title | safeHTML }}|Text: {{ .Text | safeHTML }}|END`)
44+
b.WithTemplatesAdded("docs/_markup/render-link.html", `Link docs section: {{ .Text | safeHTML }}|END`)
45+
b.WithTemplatesAdded("_default/_markup/render-image.html", `IMAGE: {{ .Page.Title }}||{{ .Destination | safeURL }}|Title: {{ .Title | safeHTML }}|Text: {{ .Text | safeHTML }}|END`)
46+
47+
b.WithContent("blog/p1.md", `---
48+
title: Cool Page
49+
---
50+
51+
[First Link](https://www.google.com "Google's Homepage")
52+
53+
{{< myshortcode3 >}}
54+
55+
[Second Link](https://www.google.com "Google's Homepage")
56+
57+
Image:
58+
59+
![Drag Racing](/images/Dragster.jpg "image title")
60+
61+
62+
`, "blog/p2.md", `---
63+
title: Cool Page2
64+
layout: mylayout
65+
---
66+
67+
{{< myshortcode1 >}}
68+
69+
[Some Text](https://www.google.com "Google's Homepage")
70+
71+
72+
73+
`, "blog/p3.md", `---
74+
title: Cool Page3
75+
---
76+
77+
{{< myshortcode2 >}}
78+
79+
80+
`, "docs/docs1.md", `---
81+
title: Docs 1
82+
---
83+
84+
85+
[Docs 1](https://www.google.com "Google's Homepage")
86+
87+
88+
`, "blog/p4.md", `---
89+
title: Cool Page With Image
90+
---
91+
92+
Image:
93+
94+
![Drag Racing](/images/Dragster.jpg "image title")
95+
96+
97+
`, "blog/p5.md", `---
98+
title: Cool Page With Markdownify
99+
---
100+
101+
{{< myshortcode4 >}}
102+
Inner Link: [Inner Link](https://www.google.com "Google's Homepage")
103+
{{< /myshortcode4 >}}
104+
105+
`, "blog/p6.md", `---
106+
title: With RenderString
107+
---
108+
109+
{{< myshortcode5 >}}Inner Link: [Inner Link](https://www.gohugo.io "Hugo's Homepage"){{< /myshortcode5 >}}
110+
111+
`)
112+
b.Build(BuildCfg{})
113+
b.AssertFileContent("public/blog/p1/index.html", `
114+
<p>Cool Page|https://www.google.com|Title: Google's Homepage|Text: First Link|END</p>
115+
Text: Second
116+
SHORT3|
117+
<p>IMAGE: Cool Page||/images/Dragster.jpg|Title: image title|Text: Drag Racing|END</p>
118+
`)
119+
b.AssertFileContent("public/blog/p2/index.html", `PARTIAL`)
120+
b.AssertFileContent("public/blog/p3/index.html", `PARTIAL3`)
121+
b.AssertFileContent("public/docs/docs1/index.html", `Link docs section: Docs 1|END`)
122+
b.AssertFileContent("public/blog/p4/index.html", `<p>IMAGE: Cool Page With Image||/images/Dragster.jpg|Title: image title|Text: Drag Racing|END</p>`)
123+
// The regular markdownify func currently gets regular links.
124+
b.AssertFileContent("public/blog/p5/index.html", "Inner Link: <a href=\"https://www.google.com\" title=\"Google's Homepage\">Inner Link</a>\n</div>")
125+
126+
b.AssertFileContent("public/blog/p6/index.html", "<div class=\"foo\">\n<p>Inner Link: With RenderString|https://www.gohugo.io|Title: Hugo's Homepage|Text: Inner Link|END</p>\n\n</div>")
127+
128+
b.EditFiles(
129+
"layouts/_default/_markup/render-link.html", `EDITED: {{ .Destination | safeURL }}|`,
130+
"layouts/_default/_markup/render-image.html", `IMAGE EDITED: {{ .Destination | safeURL }}|`,
131+
"layouts/docs/_markup/render-link.html", `DOCS EDITED: {{ .Destination | safeURL }}|`,
132+
"layouts/partials/mypartial1.html", `PARTIAL1_EDITED`,
133+
"layouts/partials/mypartial3.html", `PARTIAL3_EDITED`,
134+
"layouts/shortcodes/myshortcode3.html", `SHORT3_EDITED|`,
135+
)
136+
b.Build(BuildCfg{})
137+
b.AssertFileContent("public/blog/p1/index.html", `<p>EDITED: https://www.google.com|</p>`, "SHORT3_EDITED|")
138+
b.AssertFileContent("public/blog/p2/index.html", `PARTIAL1_EDITED`)
139+
b.AssertFileContent("public/blog/p3/index.html", `PARTIAL3_EDITED`)
140+
b.AssertFileContent("public/docs/docs1/index.html", `DOCS EDITED: https://www.google.com|</p>`)
141+
b.AssertFileContent("public/blog/p4/index.html", `IMAGE EDITED: /images/Dragster.jpg|`)
142+
b.AssertFileContent("public/blog/p6/index.html", "<p>Inner Link: EDITED: https://www.gohugo.io|</p>")
143+
144+
}

hugolib/filesystems/basefs.go

+41-8
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,28 @@ type SourceFilesystems struct {
126126
StaticDirs []hugofs.FileMetaInfo
127127
}
128128

129+
// FileSystems returns the FileSystems relevant for the change detection
130+
// in server mode.
131+
// Note: This does currently not return any static fs.
132+
func (s *SourceFilesystems) FileSystems() []*SourceFilesystem {
133+
return []*SourceFilesystem{
134+
s.Content,
135+
s.Data,
136+
s.I18n,
137+
s.Layouts,
138+
s.Archetypes,
139+
// TODO(bep) static
140+
}
141+
142+
}
143+
129144
// A SourceFilesystem holds the filesystem for a given source type in Hugo (data,
130145
// i18n, layouts, static) and additional metadata to be able to use that filesystem
131146
// in server mode.
132147
type SourceFilesystem struct {
148+
// Name matches one in files.ComponentFolders
149+
Name string
150+
133151
// This is a virtual composite filesystem. It expects path relative to a context.
134152
Fs afero.Fs
135153

@@ -275,6 +293,19 @@ func (d *SourceFilesystem) Contains(filename string) bool {
275293
return false
276294
}
277295

296+
// Path returns the relative path to the given filename if it is a member of
297+
// of the current filesystem, an empty string if not.
298+
func (d *SourceFilesystem) Path(filename string) string {
299+
for _, dir := range d.Dirs {
300+
meta := dir.Meta()
301+
if strings.HasPrefix(filename, meta.Filename()) {
302+
p := strings.TrimPrefix(strings.TrimPrefix(filename, meta.Filename()), filePathSeparator)
303+
return p
304+
}
305+
}
306+
return ""
307+
}
308+
278309
// RealDirs gets a list of absolute paths to directories starting from the given
279310
// path.
280311
func (d *SourceFilesystem) RealDirs(from string) []string {
@@ -349,12 +380,14 @@ func newSourceFilesystemsBuilder(p *paths.Paths, logger *loggers.Logger, b *Base
349380
return &sourceFilesystemsBuilder{p: p, logger: logger, sourceFs: sourceFs, theBigFs: b.theBigFs, result: &SourceFilesystems{}}
350381
}
351382

352-
func (b *sourceFilesystemsBuilder) newSourceFilesystem(fs afero.Fs, dirs []hugofs.FileMetaInfo) *SourceFilesystem {
383+
func (b *sourceFilesystemsBuilder) newSourceFilesystem(name string, fs afero.Fs, dirs []hugofs.FileMetaInfo) *SourceFilesystem {
353384
return &SourceFilesystem{
385+
Name: name,
354386
Fs: fs,
355387
Dirs: dirs,
356388
}
357389
}
390+
358391
func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
359392

360393
if b.theBigFs == nil {
@@ -369,12 +402,12 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
369402

370403
createView := func(componentID string) *SourceFilesystem {
371404
if b.theBigFs == nil || b.theBigFs.overlayMounts == nil {
372-
return b.newSourceFilesystem(hugofs.NoOpFs, nil)
405+
return b.newSourceFilesystem(componentID, hugofs.NoOpFs, nil)
373406
}
374407

375408
dirs := b.theBigFs.overlayDirs[componentID]
376409

377-
return b.newSourceFilesystem(afero.NewBasePathFs(b.theBigFs.overlayMounts, componentID), dirs)
410+
return b.newSourceFilesystem(componentID, afero.NewBasePathFs(b.theBigFs.overlayMounts, componentID), dirs)
378411

379412
}
380413

@@ -392,14 +425,14 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
392425
return nil, err
393426
}
394427

395-
b.result.Data = b.newSourceFilesystem(dataFs, dataDirs)
428+
b.result.Data = b.newSourceFilesystem(files.ComponentFolderData, dataFs, dataDirs)
396429

397430
i18nDirs := b.theBigFs.overlayDirs[files.ComponentFolderI18n]
398431
i18nFs, err := hugofs.NewSliceFs(i18nDirs...)
399432
if err != nil {
400433
return nil, err
401434
}
402-
b.result.I18n = b.newSourceFilesystem(i18nFs, i18nDirs)
435+
b.result.I18n = b.newSourceFilesystem(files.ComponentFolderI18n, i18nFs, i18nDirs)
403436

404437
contentDirs := b.theBigFs.overlayDirs[files.ComponentFolderContent]
405438
contentBfs := afero.NewBasePathFs(b.theBigFs.overlayMountsContent, files.ComponentFolderContent)
@@ -409,7 +442,7 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
409442
return nil, errors.Wrap(err, "create content filesystem")
410443
}
411444

412-
b.result.Content = b.newSourceFilesystem(contentFs, contentDirs)
445+
b.result.Content = b.newSourceFilesystem(files.ComponentFolderContent, contentFs, contentDirs)
413446

414447
b.result.Work = afero.NewReadOnlyFs(b.theBigFs.overlayFull)
415448

@@ -421,13 +454,13 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
421454
if b.theBigFs.staticPerLanguage != nil {
422455
// Multihost mode
423456
for k, v := range b.theBigFs.staticPerLanguage {
424-
sfs := b.newSourceFilesystem(v, b.result.StaticDirs)
457+
sfs := b.newSourceFilesystem(files.ComponentFolderStatic, v, b.result.StaticDirs)
425458
sfs.PublishFolder = k
426459
ms[k] = sfs
427460
}
428461
} else {
429462
bfs := afero.NewBasePathFs(b.theBigFs.overlayMountsStatic, files.ComponentFolderStatic)
430-
ms[""] = b.newSourceFilesystem(bfs, b.result.StaticDirs)
463+
ms[""] = b.newSourceFilesystem(files.ComponentFolderStatic, bfs, b.result.StaticDirs)
431464
}
432465

433466
return b.result, nil

hugolib/hugo_modules_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ import (
4040

4141
// TODO(bep) this fails when testmodBuilder is also building ...
4242
func TestHugoModules(t *testing.T) {
43+
if !isCI() {
44+
t.Skip("skip (relative) long running modules test when running locally")
45+
}
4346
t.Parallel()
4447

4548
if !isCI() || hugo.GoMinorVersion() < 12 {

0 commit comments

Comments
 (0)