]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
resources/page: Revise the new contentbasename permalinks tokens
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 12 Feb 2025 21:36:24 +0000 (22:36 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 17 Feb 2025 08:41:49 +0000 (09:41 +0100)
* 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

docs/content/en/content-management/urls.md
resources/page/permalinks.go
resources/page/permalinks_integration_test.go
resources/page/permalinks_test.go
resources/page/testhelpers_test.go
source/fileInfo.go

index 62ccfb6669f47892895c82bdc3c218edee36afa2..ab0c7148a428404ab24b86f3744311e36899fca9 100644 (file)
@@ -317,14 +317,6 @@ Use these tokens when defining the URL pattern. You can also use these tokens wh
 `:slugorfilename`
 : The slug as defined in front matter, else the content's file name without extension, applicable to the `page` page kind.
 
-`:contentbasename`
-: The content base name, as defined in [`File.ContentBaseName`], applicable to pages backed by a file.
-
-`:contentbasenameorslug`
-: The content base name, else the slug as defined above.
-
-[`File.ContentBaseName`]: /methods/page/file/#contentbasename
-
 For time-related values, you can also use the layout string components defined in Go's [time package]. For example:
 
 [time package]: https://pkg.go.dev/time#pkg-constants
index 56dfff46f30882bcaf0fe4165bbd351d74cc96b6..953f4a2e8e0eccb0cb9b382dcce7780038f47780 100644 (file)
@@ -93,7 +93,7 @@ func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]ma
                "slugorfilename":        p.pageToPermalinkSlugElseFilename,
                "filename":              p.pageToPermalinkFilename,
                "contentbasename":       p.pageToPermalinkContentBaseName,
-               "contentbasenameorslug": p.pageToPermalinkContentBaseNameOrSlug,
+               "slugorcontentbasename": p.pageToPermalinkSlugOrContentBaseName,
        }
 
        p.expanders = make(map[string]map[string]func(Page) (string, error))
@@ -311,22 +311,19 @@ func (l PermalinkExpander) pageToPermalinkSections(p Page, _ string) (string, er
 
 // pageToPermalinkContentBaseName returns the URL-safe form of the content base name.
 func (l PermalinkExpander) pageToPermalinkContentBaseName(p Page, _ string) (string, error) {
-       if p.File() == nil {
-               return "", nil
-       }
-       return l.urlize(p.File().ContentBaseName()), nil
+       return l.urlize(p.PathInfo().BaseNameNoIdentifier()), nil
 }
 
-// pageToPermalinkContentBaseNameOrSlug returns the URL-safe form of the content base name, or the slug.
-func (l PermalinkExpander) pageToPermalinkContentBaseNameOrSlug(p Page, a string) (string, error) {
+// pageToPermalinkSlugOrContentBaseName returns the URL-safe form of the slug, content base name.
+func (l PermalinkExpander) pageToPermalinkSlugOrContentBaseName(p Page, a string) (string, error) {
+       if p.Slug() != "" {
+               return l.urlize(p.Slug()), nil
+       }
        name, err := l.pageToPermalinkContentBaseName(p, a)
        if err != nil {
                return "", nil
        }
-       if name != "" {
-               return name, nil
-       }
-       return l.pageToPermalinkSlugElseTitle(p, a)
+       return name, nil
 }
 
 func (l PermalinkExpander) translationBaseName(p Page) string {
index 52bcd6860804176702432948cc1e6f14d5cebccd..6ef450edd0ddead28a974f32888a38a8a25759c2 100644 (file)
@@ -277,3 +277,69 @@ title: p2
        // We strip colons from paths constructed by Hugo (they are not supported on Windows).
        b.AssertFileExists("public/cd/p2/index.html", true)
 }
+
+func TestPermalinksContentbasenameContentAdapter(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+[permalinks]
+[permalinks.page]
+a = "/:slugorcontentbasename/"
+b = "/:sections/:contentbasename/"
+-- content/_content.gotmpl --
+{{ $.AddPage  (dict "kind" "page" "path" "a/b/contentbasename1" "title" "My A Page No Slug")  }}
+{{ $.AddPage (dict "kind" "page" "path" "a/b/contentbasename2" "slug" "myslug"  "title" "My A Page With Slug")  }}
+ {{ $.AddPage  (dict "kind" "section" "path" "b/c" "title" "My B Section")  }}
+{{ $.AddPage  (dict "kind" "page" "path" "b/c/contentbasename3" "title" "My B Page No Slug")  }}
+-- layouts/_default/single.html --
+{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
+`
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContent("public/contentbasename1/index.html", "My A Page No Slug|/contentbasename1/|page|")
+       b.AssertFileContent("public/myslug/index.html", "My A Page With Slug|/myslug/|page|")
+}
+
+func TestPermalinksContentbasenameWithAndWithoutFile(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+[permalinks.section]
+a = "/mya/:contentbasename/"
+[permalinks.page]
+a = "/myapage/:contentbasename/"
+[permalinks.term]
+categories = "/myc/:slugorcontentbasename/"
+-- content/b/c/_index.md --
+---
+title: "C section"
+---
+-- content/a/b/index.md --
+---
+title: "My Title"
+categories: ["c1", "c2"]
+---
+-- content/categories/c2/_index.md --
+---
+title: "C2"
+slug: "c2slug"
+---
+-- layouts/_default/single.html --
+{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
+-- layouts/_default/list.html --
+{{ .Title }}|{{ .RelPermalink }}|{{ .Kind }}|
+`
+       b := hugolib.Test(t, files)
+
+       // Sections.
+       b.AssertFileContent("public/mya/a/index.html", "As|/mya/a/|section|")
+
+       // Pages.
+       b.AssertFileContent("public/myapage/b/index.html", "My Title|/myapage/b/|page|")
+
+       // Taxonomies.
+       b.AssertFileContent("public/myc/c1/index.html", "C1|/myc/c1/|term|")
+       b.AssertFileContent("public/myc/c2slug/index.html", "C2|/myc/c2slug/|term|")
+}
index 71c5ded65029aaf60dd54ab9d4782b151c94e094..191259252ddc898a7a8400e5065d01331b3c37ef 100644 (file)
@@ -22,6 +22,7 @@ import (
        "time"
 
        qt "github.com/frankban/quicktest"
+       "github.com/gohugoio/hugo/source"
 )
 
 // testdataPermalinks is used by a couple of tests; the expandsTo content is
@@ -29,32 +30,44 @@ import (
 var testdataPermalinks = []struct {
        spec      string
        valid     bool
+       withPage  func(p *testPage)
        expandsTo string
 }{
-       {":title", true, "spf13-vim-3.0-release-and-new-website"},
-       {"/:year-:month-:title", true, "/2012-04-spf13-vim-3.0-release-and-new-website"},
-       {"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, "/2012/97/04/April/06/5/Friday/"}, // Dates
-       {"/:section/", true, "/blue/"},                                  // Section
-       {"/:title/", true, "/spf13-vim-3.0-release-and-new-website/"},   // Title
-       {"/:slug/", true, "/the-slug/"},                                 // Slug
-       {"/:slugorfilename/", true, "/the-slug/"},                       // Slug or filename
-       {"/:filename/", true, "/test-page/"},                            // Filename
-       {"/:06-:1-:2-:Monday", true, "/12-4-6-Friday"},                  // Dates with Go formatting
-       {"/:2006_01_02_15_04_05.000", true, "/2012_04_06_03_01_59.000"}, // Complicated custom date format
-       {"/:sections/", true, "/a/b/c/"},                                // Sections
-       {"/:sections[last]/", true, "/c/"},                              // Sections
-       {"/:sections[0]/:sections[last]/", true, "/a/c/"},               // Sections
-       {"/\\:filename", true, "/:filename"},                            // Escape sequence
-       {"/special\\::slug/", true, "/special:the-slug/"},               // Escape sequence
-       {"/:contentbasename/", true, "/index/"},                         // Content base name
-       {"/:contentbasenameorslug/", true, "/index/"},                   // Content base name or slug
-
+       {":title", true, nil, "spf13-vim-3.0-release-and-new-website"},
+       {"/:year-:month-:title", true, nil, "/2012-04-spf13-vim-3.0-release-and-new-website"},
+       {"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, nil, "/2012/97/04/April/06/5/Friday/"}, // Dates
+       {"/:section/", true, nil, "/blue/"},                                                                            // Section
+       {"/:title/", true, nil, "/spf13-vim-3.0-release-and-new-website/"},                                             // Title
+       {"/:slug/", true, nil, "/the-slug/"},                                                                           // Slug
+       {"/:slugorfilename/", true, nil, "/the-slug/"},                                                                 // Slug or filename
+       {"/:filename/", true, nil, "/test-page/"},                                                                      // Filename
+       {"/:06-:1-:2-:Monday", true, nil, "/12-4-6-Friday"},                                                            // Dates with Go formatting
+       {"/:2006_01_02_15_04_05.000", true, nil, "/2012_04_06_03_01_59.000"},                                           // Complicated custom date format
+       {"/:sections/", true, nil, "/a/b/c/"},                                                                          // Sections
+       {"/:sections[last]/", true, nil, "/c/"},                                                                        // Sections
+       {"/:sections[0]/:sections[last]/", true, nil, "/a/c/"},                                                         // Sections
+       {"/\\:filename", true, nil, "/:filename"},                                                                      // Escape sequence
+       {"/special\\::slug/", true, nil, "/special:the-slug/"},
+       // contentbasename.                                                      // Escape sequence
+       {"/:contentbasename/", true, nil, "/test-page/"},
+       // slug, contentbasename.                                               // Content base name
+       {"/:slugorcontentbasename/", true, func(p *testPage) {
+               p.slug = ""
+       }, "/test-page/"},
+       {"/:slugorcontentbasename/", true, func(p *testPage) {
+               p.slug = "myslug"
+       }, "/myslug/"},
+       {"/:slugorcontentbasename/", true, func(p *testPage) {
+               p.slug = ""
+               p.title = "mytitle"
+               p.file = source.NewContentFileInfoFrom("/", "_index.md")
+       }, "/test-page/"},
        // Failures
-       {"/blog/:fred", false, ""},
-       {"/:year//:title", false, ""},
-       {"/:TITLE", false, ""},      // case is not normalized
-       {"/:2017", false, ""},       // invalid date format
-       {"/:2006-01-02", false, ""}, // valid date format but invalid attribute name
+       {"/blog/:fred", false, nil, ""},
+       {"/:year//:title", false, nil, ""},
+       {"/:TITLE", false, nil, ""},      // case is not normalized
+       {"/:2017", false, nil, ""},       // invalid date format
+       {"/:2006-01-02", false, nil, ""}, // valid date format but invalid attribute name
 }
 
 func urlize(uri string) string {
@@ -67,21 +80,30 @@ func TestPermalinkExpansion(t *testing.T) {
 
        c := qt.New(t)
 
-       page := newTestPageWithFile("/test-page/index.md")
-       page.title = "Spf13 Vim 3.0 Release and new website"
-       d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59")
-       page.date = d
-       page.section = "blue"
-       page.slug = "The Slug"
-       page.kind = "page"
+       newPage := func() *testPage {
+               page := newTestPageWithFile("/test-page/index.md")
+               page.title = "Spf13 Vim 3.0 Release and new website"
+               d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59")
+               page.date = d
+               page.section = "blue"
+               page.slug = "The Slug"
+               page.kind = "page"
+               // page.pathInfo
+               return page
+       }
 
-       for _, item := range testdataPermalinks {
+       for i, item := range testdataPermalinks {
                if !item.valid {
                        continue
                }
 
+               page := newPage()
+               if item.withPage != nil {
+                       item.withPage(page)
+               }
+
                specNameCleaner := regexp.MustCompile(`[\:\/\[\]]`)
-               name := specNameCleaner.ReplaceAllString(item.spec, "")
+               name := fmt.Sprintf("[%d] %s", i, specNameCleaner.ReplaceAllString(item.spec, "_"))
 
                c.Run(name, func(c *qt.C) {
                        patterns := map[string]map[string]string{
index 8e6dfb79ac2ff57586c8dc7259e9cfb21aaa0632..59b8cf0e8e17a57e8e58177df6cebd385b0ea21b 100644 (file)
@@ -52,7 +52,7 @@ func newTestPage() *testPage {
 
 func newTestPageWithFile(filename string) *testPage {
        filename = filepath.FromSlash(filename)
-       file := source.NewFileInfoFrom(filename, filename)
+       file := source.NewContentFileInfoFrom(filename, filename)
 
        l, err := langs.NewLanguage(
                "en",
@@ -67,9 +67,10 @@ func newTestPageWithFile(filename string) *testPage {
        }
 
        return &testPage{
-               params: make(map[string]any),
-               data:   make(map[string]any),
-               file:   file,
+               params:   make(map[string]any),
+               data:     make(map[string]any),
+               file:     file,
+               pathInfo: file.FileInfo().Meta().PathInfo,
                currentSection: &testPage{
                        sectionEntries: []string{"a", "b", "c"},
                },
@@ -90,7 +91,8 @@ type testPage struct {
 
        fuzzyWordCount int
 
-       path string
+       path     string
+       pathInfo *paths.Path
 
        slug string
 
@@ -406,7 +408,7 @@ func (p *testPage) Path() string {
 }
 
 func (p *testPage) PathInfo() *paths.Path {
-       panic("testpage: not implemented")
+       return p.pathInfo
 }
 
 func (p *testPage) Permalink() string {
index 8403c8088940832bb5fd57f80b27f19122cf3bdc..dfa5cda26af665cc55f7142b5b2efd0bb372a43e 100644 (file)
@@ -22,7 +22,7 @@ import (
        "github.com/gohugoio/hugo/common/hashing"
        "github.com/gohugoio/hugo/common/hugo"
        "github.com/gohugoio/hugo/common/paths"
-       "github.com/gohugoio/hugo/media"
+       "github.com/gohugoio/hugo/hugofs/files"
 
        "github.com/gohugoio/hugo/common/hugio"
 
@@ -132,11 +132,17 @@ func (fi *File) p() *paths.Path {
        return fi.fim.Meta().PathInfo.Unnormalized()
 }
 
+var contentPathParser = &paths.PathParser{
+       IsContentExt: func(ext string) bool {
+               return true
+       },
+}
+
 // Used in tests.
-func NewFileInfoFrom(path, filename string) *File {
+func NewContentFileInfoFrom(path, filename string) *File {
        meta := &hugofs.FileMeta{
                Filename: filename,
-               PathInfo: media.DefaultPathParser.Parse("", filepath.ToSlash(path)),
+               PathInfo: contentPathParser.Parse(files.ComponentFolderContent, filepath.ToSlash(path)),
        }
 
        return NewFileInfo(hugofs.NewFileMetaInfo(nil, meta))