Skip to content

Commit 7b7a0f3

Browse files
committed
resources/page: Revise the new contentbasename permalinks tokens
* Make it work for all pages, including those created from content adapters and not backed by a file. * Allow the `slug` to win, so the new tokens are: `:contentbasename`: 1. ContentBaseName `:slugorcontentbasename`: 1. Slug 2. ContentBaseName Note that a page will always have a `ContentBaseName`, so no need to fall back to e.g. the title. Closes #11722
1 parent cb7a433 commit 7b7a0f3

File tree

6 files changed

+145
-60
lines changed

6 files changed

+145
-60
lines changed

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

-8
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,6 @@ Use these tokens when defining the URL pattern. You can also use these tokens wh
317317
`:slugorfilename`
318318
: The slug as defined in front matter, else the content's file name without extension, applicable to the `page` page kind.
319319
320-
`:contentbasename`
321-
: The content base name, as defined in [`File.ContentBaseName`], applicable to pages backed by a file.
322-
323-
`:contentbasenameorslug`
324-
: The content base name, else the slug as defined above.
325-
326-
[`File.ContentBaseName`]: /methods/page/file/#contentbasename
327-
328320
For time-related values, you can also use the layout string components defined in Go's [time package]. For example:
329321
330322
[time package]: https://pkg.go.dev/time#pkg-constants

resources/page/permalinks.go

+8-11
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]ma
9393
"slugorfilename": p.pageToPermalinkSlugElseFilename,
9494
"filename": p.pageToPermalinkFilename,
9595
"contentbasename": p.pageToPermalinkContentBaseName,
96-
"contentbasenameorslug": p.pageToPermalinkContentBaseNameOrSlug,
96+
"slugorcontentbasename": p.pageToPermalinkSlugOrContentBaseName,
9797
}
9898

9999
p.expanders = make(map[string]map[string]func(Page) (string, error))
@@ -311,22 +311,19 @@ func (l PermalinkExpander) pageToPermalinkSections(p Page, _ string) (string, er
311311

312312
// pageToPermalinkContentBaseName returns the URL-safe form of the content base name.
313313
func (l PermalinkExpander) pageToPermalinkContentBaseName(p Page, _ string) (string, error) {
314-
if p.File() == nil {
315-
return "", nil
316-
}
317-
return l.urlize(p.File().ContentBaseName()), nil
314+
return l.urlize(p.PathInfo().BaseNameNoIdentifier()), nil
318315
}
319316

320-
// pageToPermalinkContentBaseNameOrSlug returns the URL-safe form of the content base name, or the slug.
321-
func (l PermalinkExpander) pageToPermalinkContentBaseNameOrSlug(p Page, a string) (string, error) {
317+
// pageToPermalinkSlugOrContentBaseName returns the URL-safe form of the slug, content base name.
318+
func (l PermalinkExpander) pageToPermalinkSlugOrContentBaseName(p Page, a string) (string, error) {
319+
if p.Slug() != "" {
320+
return l.urlize(p.Slug()), nil
321+
}
322322
name, err := l.pageToPermalinkContentBaseName(p, a)
323323
if err != nil {
324324
return "", nil
325325
}
326-
if name != "" {
327-
return name, nil
328-
}
329-
return l.pageToPermalinkSlugElseTitle(p, a)
326+
return name, nil
330327
}
331328

332329
func (l PermalinkExpander) translationBaseName(p Page) string {

resources/page/permalinks_integration_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,69 @@ title: p2
277277
// We strip colons from paths constructed by Hugo (they are not supported on Windows).
278278
b.AssertFileExists("public/cd/p2/index.html", true)
279279
}
280+
281+
func TestPermalinksContentbasenameContentAdapter(t *testing.T) {
282+
t.Parallel()
283+
284+
files := `
285+
-- hugo.toml --
286+
[permalinks]
287+
[permalinks.page]
288+
a = "/:slugorcontentbasename/"
289+
b = "/:sections/:contentbasename/"
290+
-- content/_content.gotmpl --
291+
{{ $.AddPage (dict "kind" "page" "path" "a/b/contentbasename1" "title" "My A Page No Slug") }}
292+
{{ $.AddPage (dict "kind" "page" "path" "a/b/contentbasename2" "slug" "myslug" "title" "My A Page With Slug") }}
293+
{{ $.AddPage (dict "kind" "section" "path" "b/c" "title" "My B Section") }}
294+
{{ $.AddPage (dict "kind" "page" "path" "b/c/contentbasename3" "title" "My B Page No Slug") }}
295+
-- layouts/_default/single.html --
296+
{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
297+
`
298+
b := hugolib.Test(t, files)
299+
300+
b.AssertFileContent("public/contentbasename1/index.html", "My A Page No Slug|/contentbasename1/|page|")
301+
b.AssertFileContent("public/myslug/index.html", "My A Page With Slug|/myslug/|page|")
302+
}
303+
304+
func TestPermalinksContentbasenameWithAndWithoutFile(t *testing.T) {
305+
t.Parallel()
306+
307+
files := `
308+
-- hugo.toml --
309+
[permalinks.section]
310+
a = "/mya/:contentbasename/"
311+
[permalinks.page]
312+
a = "/myapage/:contentbasename/"
313+
[permalinks.term]
314+
categories = "/myc/:slugorcontentbasename/"
315+
-- content/b/c/_index.md --
316+
---
317+
title: "C section"
318+
---
319+
-- content/a/b/index.md --
320+
---
321+
title: "My Title"
322+
categories: ["c1", "c2"]
323+
---
324+
-- content/categories/c2/_index.md --
325+
---
326+
title: "C2"
327+
slug: "c2slug"
328+
---
329+
-- layouts/_default/single.html --
330+
{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
331+
-- layouts/_default/list.html --
332+
{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
333+
`
334+
b := hugolib.Test(t, files)
335+
336+
// Sections.
337+
b.AssertFileContent("public/mya/a/index.html", "As|/mya/a/|section|")
338+
339+
// Pages.
340+
b.AssertFileContent("public/myapage/b/index.html", "My Title|/myapage/b/|page|")
341+
342+
// Taxonomies.
343+
b.AssertFileContent("public/myc/c1/index.html", "C1|/myc/c1/|term|")
344+
b.AssertFileContent("public/myc/c2slug/index.html", "C2|/myc/c2slug/|term|")
345+
}

resources/page/permalinks_test.go

+54-32
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,52 @@ import (
2222
"time"
2323

2424
qt "github.com/frankban/quicktest"
25+
"github.com/gohugoio/hugo/source"
2526
)
2627

2728
// testdataPermalinks is used by a couple of tests; the expandsTo content is
2829
// subject to the data in simplePageJSON.
2930
var testdataPermalinks = []struct {
3031
spec string
3132
valid bool
33+
withPage func(p *testPage)
3234
expandsTo string
3335
}{
34-
{":title", true, "spf13-vim-3.0-release-and-new-website"},
35-
{"/:year-:month-:title", true, "/2012-04-spf13-vim-3.0-release-and-new-website"},
36-
{"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, "/2012/97/04/April/06/5/Friday/"}, // Dates
37-
{"/:section/", true, "/blue/"}, // Section
38-
{"/:title/", true, "/spf13-vim-3.0-release-and-new-website/"}, // Title
39-
{"/:slug/", true, "/the-slug/"}, // Slug
40-
{"/:slugorfilename/", true, "/the-slug/"}, // Slug or filename
41-
{"/:filename/", true, "/test-page/"}, // Filename
42-
{"/:06-:1-:2-:Monday", true, "/12-4-6-Friday"}, // Dates with Go formatting
43-
{"/:2006_01_02_15_04_05.000", true, "/2012_04_06_03_01_59.000"}, // Complicated custom date format
44-
{"/:sections/", true, "/a/b/c/"}, // Sections
45-
{"/:sections[last]/", true, "/c/"}, // Sections
46-
{"/:sections[0]/:sections[last]/", true, "/a/c/"}, // Sections
47-
{"/\\:filename", true, "/:filename"}, // Escape sequence
48-
{"/special\\::slug/", true, "/special:the-slug/"}, // Escape sequence
49-
{"/:contentbasename/", true, "/index/"}, // Content base name
50-
{"/:contentbasenameorslug/", true, "/index/"}, // Content base name or slug
51-
36+
{":title", true, nil, "spf13-vim-3.0-release-and-new-website"},
37+
{"/:year-:month-:title", true, nil, "/2012-04-spf13-vim-3.0-release-and-new-website"},
38+
{"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, nil, "/2012/97/04/April/06/5/Friday/"}, // Dates
39+
{"/:section/", true, nil, "/blue/"}, // Section
40+
{"/:title/", true, nil, "/spf13-vim-3.0-release-and-new-website/"}, // Title
41+
{"/:slug/", true, nil, "/the-slug/"}, // Slug
42+
{"/:slugorfilename/", true, nil, "/the-slug/"}, // Slug or filename
43+
{"/:filename/", true, nil, "/test-page/"}, // Filename
44+
{"/:06-:1-:2-:Monday", true, nil, "/12-4-6-Friday"}, // Dates with Go formatting
45+
{"/:2006_01_02_15_04_05.000", true, nil, "/2012_04_06_03_01_59.000"}, // Complicated custom date format
46+
{"/:sections/", true, nil, "/a/b/c/"}, // Sections
47+
{"/:sections[last]/", true, nil, "/c/"}, // Sections
48+
{"/:sections[0]/:sections[last]/", true, nil, "/a/c/"}, // Sections
49+
{"/\\:filename", true, nil, "/:filename"}, // Escape sequence
50+
{"/special\\::slug/", true, nil, "/special:the-slug/"},
51+
// contentbasename. // Escape sequence
52+
{"/:contentbasename/", true, nil, "/test-page/"},
53+
// slug, contentbasename. // Content base name
54+
{"/:slugorcontentbasename/", true, func(p *testPage) {
55+
p.slug = ""
56+
}, "/test-page/"},
57+
{"/:slugorcontentbasename/", true, func(p *testPage) {
58+
p.slug = "myslug"
59+
}, "/myslug/"},
60+
{"/:slugorcontentbasename/", true, func(p *testPage) {
61+
p.slug = ""
62+
p.title = "mytitle"
63+
p.file = source.NewContentFileInfoFrom("/", "_index.md")
64+
}, "/test-page/"},
5265
// Failures
53-
{"/blog/:fred", false, ""},
54-
{"/:year//:title", false, ""},
55-
{"/:TITLE", false, ""}, // case is not normalized
56-
{"/:2017", false, ""}, // invalid date format
57-
{"/:2006-01-02", false, ""}, // valid date format but invalid attribute name
66+
{"/blog/:fred", false, nil, ""},
67+
{"/:year//:title", false, nil, ""},
68+
{"/:TITLE", false, nil, ""}, // case is not normalized
69+
{"/:2017", false, nil, ""}, // invalid date format
70+
{"/:2006-01-02", false, nil, ""}, // valid date format but invalid attribute name
5871
}
5972

6073
func urlize(uri string) string {
@@ -67,21 +80,30 @@ func TestPermalinkExpansion(t *testing.T) {
6780

6881
c := qt.New(t)
6982

70-
page := newTestPageWithFile("/test-page/index.md")
71-
page.title = "Spf13 Vim 3.0 Release and new website"
72-
d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59")
73-
page.date = d
74-
page.section = "blue"
75-
page.slug = "The Slug"
76-
page.kind = "page"
83+
newPage := func() *testPage {
84+
page := newTestPageWithFile("/test-page/index.md")
85+
page.title = "Spf13 Vim 3.0 Release and new website"
86+
d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59")
87+
page.date = d
88+
page.section = "blue"
89+
page.slug = "The Slug"
90+
page.kind = "page"
91+
// page.pathInfo
92+
return page
93+
}
7794

78-
for _, item := range testdataPermalinks {
95+
for i, item := range testdataPermalinks {
7996
if !item.valid {
8097
continue
8198
}
8299

100+
page := newPage()
101+
if item.withPage != nil {
102+
item.withPage(page)
103+
}
104+
83105
specNameCleaner := regexp.MustCompile(`[\:\/\[\]]`)
84-
name := specNameCleaner.ReplaceAllString(item.spec, "")
106+
name := fmt.Sprintf("[%d] %s", i, specNameCleaner.ReplaceAllString(item.spec, "_"))
85107

86108
c.Run(name, func(c *qt.C) {
87109
patterns := map[string]map[string]string{

resources/page/testhelpers_test.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func newTestPage() *testPage {
5252

5353
func newTestPageWithFile(filename string) *testPage {
5454
filename = filepath.FromSlash(filename)
55-
file := source.NewFileInfoFrom(filename, filename)
55+
file := source.NewContentFileInfoFrom(filename, filename)
5656

5757
l, err := langs.NewLanguage(
5858
"en",
@@ -67,9 +67,10 @@ func newTestPageWithFile(filename string) *testPage {
6767
}
6868

6969
return &testPage{
70-
params: make(map[string]any),
71-
data: make(map[string]any),
72-
file: file,
70+
params: make(map[string]any),
71+
data: make(map[string]any),
72+
file: file,
73+
pathInfo: file.FileInfo().Meta().PathInfo,
7374
currentSection: &testPage{
7475
sectionEntries: []string{"a", "b", "c"},
7576
},
@@ -90,7 +91,8 @@ type testPage struct {
9091

9192
fuzzyWordCount int
9293

93-
path string
94+
path string
95+
pathInfo *paths.Path
9496

9597
slug string
9698

@@ -406,7 +408,7 @@ func (p *testPage) Path() string {
406408
}
407409

408410
func (p *testPage) PathInfo() *paths.Path {
409-
panic("testpage: not implemented")
411+
return p.pathInfo
410412
}
411413

412414
func (p *testPage) Permalink() string {

source/fileInfo.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/gohugoio/hugo/common/hashing"
2323
"github.com/gohugoio/hugo/common/hugo"
2424
"github.com/gohugoio/hugo/common/paths"
25-
"github.com/gohugoio/hugo/media"
25+
"github.com/gohugoio/hugo/hugofs/files"
2626

2727
"github.com/gohugoio/hugo/common/hugio"
2828

@@ -132,11 +132,17 @@ func (fi *File) p() *paths.Path {
132132
return fi.fim.Meta().PathInfo.Unnormalized()
133133
}
134134

135+
var contentPathParser = &paths.PathParser{
136+
IsContentExt: func(ext string) bool {
137+
return true
138+
},
139+
}
140+
135141
// Used in tests.
136-
func NewFileInfoFrom(path, filename string) *File {
142+
func NewContentFileInfoFrom(path, filename string) *File {
137143
meta := &hugofs.FileMeta{
138144
Filename: filename,
139-
PathInfo: media.DefaultPathParser.Parse("", filepath.ToSlash(path)),
145+
PathInfo: contentPathParser.Parse(files.ComponentFolderContent, filepath.ToSlash(path)),
140146
}
141147

142148
return NewFileInfo(hugofs.NewFileMetaInfo(nil, meta))

0 commit comments

Comments
 (0)