From 25c7c18f9f8862f756835d3ac3faa4f114c344f6 Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:33:15 +0100 Subject: [PATCH] resources/page: Fix slugorcontentbasename for section pages MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The slugorcontentbasename permalink token was creating an extra subdirectory for section pages (_index.md files). This was because pageToPermalinkContentBaseName did not have the same special handling for _index.md files as pageToPermalinkFilename. This commit adds the special handling to return an empty string for section pages, ensuring backward compatibility with slugorfilename. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Fixes #14104 --- resources/page/permalinks.go | 5 +++ resources/page/permalinks_integration_test.go | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/resources/page/permalinks.go b/resources/page/permalinks.go index 1d6078b4a..49dc815d1 100644 --- a/resources/page/permalinks.go +++ b/resources/page/permalinks.go @@ -336,6 +336,11 @@ func (l PermalinkExpander) pageToPermalinkSectionSlugs(p Page, attr string) (str // pageToPermalinkContentBaseName returns the URL-safe form of the content base name. func (l PermalinkExpander) pageToPermalinkContentBaseName(p Page, _ string) (string, error) { + // For section pages with _index.md files, return empty string to match the behavior of pageToPermalinkFilename. + // Sections without files should use their directory name. + if p.PathInfo().IsBranchBundle() && p.File() != nil { + return "", nil + } return l.urlize(p.PathInfo().Unnormalized().BaseNameNoIdentifier()), nil } diff --git a/resources/page/permalinks_integration_test.go b/resources/page/permalinks_integration_test.go index 74df38820..c1b179cd5 100644 --- a/resources/page/permalinks_integration_test.go +++ b/resources/page/permalinks_integration_test.go @@ -457,3 +457,39 @@ title: aBc b = hugolib.Test(t, files) b.AssertFileExists("public/aBc/index.html", true) } + +// Issue 14104 +func TestIssue14104(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[permalinks.page] +foo = "/:sections[1:]/:slugorcontentbasename/" +[permalinks.section] +foo = "/:sections[1:]/:slugorcontentbasename/" +-- content/foo/_index.md -- +--- +title: Foo +--- +-- content/foo/bar/_index.md -- +--- +title: Bar +--- +-- content/foo/bar/somepage.md -- +--- +title: Some Page +--- +-- layouts/_default/list.html -- +List|{{ .Kind }}|{{ .RelPermalink }}| +-- layouts/_default/single.html -- +Single|{{ .Kind }}|{{ .RelPermalink }}| +` + + b := hugolib.Test(t, files) + + // Section page should be at /bar/index.html, not /bar/bar/index.html + b.AssertFileContent("public/bar/index.html", "List|section|/bar/|") + // Regular page should be at /bar/somepage/index.html + b.AssertFileContent("public/bar/somepage/index.html", "Single|page|/bar/somepage/|") +} -- 2.39.5