From 566fe7ba12e7ecabadffa9ec76b319d04063c78b Mon Sep 17 00:00:00 2001 From: n1xx1 Date: Thu, 1 Aug 2024 12:14:29 +0200 Subject: resources/page: Expand parmalinks tokens in `url` This change allows to use permalink tokens in url front matter fields. This should be useful to target more specific pages instead of using a global permalink configuration. It's expected to be used with cascade. Fixes #9714 --- resources/page/permalinks.go | 44 ++++++++++++++++++++------- resources/page/permalinks_integration_test.go | 39 ++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 11 deletions(-) (limited to 'resources') diff --git a/resources/page/permalinks.go b/resources/page/permalinks.go index 67c63c4b2..05911f0ea 100644 --- a/resources/page/permalinks.go +++ b/resources/page/permalinks.go @@ -40,6 +40,8 @@ type PermalinkExpander struct { expanders map[string]map[string]func(Page) (string, error) urlize func(uri string) string + + patternCache *maps.Cache[string, func(Page) (string, error)] } // Time for checking date formats. Every field is different than the @@ -71,7 +73,10 @@ func (p PermalinkExpander) callback(attr string) (pageToPermaAttribute, bool) { // NewPermalinkExpander creates a new PermalinkExpander configured by the given // urlize func. func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]map[string]string) (PermalinkExpander, error) { - p := PermalinkExpander{urlize: urlize} + p := PermalinkExpander{ + urlize: urlize, + patternCache: maps.NewCache[string, func(Page) (string, error)](), + } p.knownPermalinkAttributes = map[string]pageToPermaAttribute{ "year": p.pageToPermalinkDate, @@ -102,6 +107,16 @@ func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]ma return p, nil } +// ExpandPattern expands the path in p with the specified expand pattern. +func (l PermalinkExpander) ExpandPattern(pattern string, p Page) (string, error) { + expander, err := l.getOrParsePattern(pattern) + if err != nil { + return "", err + } + + return expander(p) +} + // Expand expands the path in p according to the rules defined for the given key. // If no rules are found for the given key, an empty string is returned. func (l PermalinkExpander) Expand(key string, p Page) (string, error) { @@ -129,17 +144,11 @@ func init() { } } -func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Page) (string, error), error) { - expanders := make(map[string]func(Page) (string, error)) - - for k, pattern := range patterns { - k = strings.Trim(k, sectionCutSet) - +func (l PermalinkExpander) getOrParsePattern(pattern string) (func(Page) (string, error), error) { + return l.patternCache.GetOrCreate(pattern, func() (func(Page) (string, error), error) { if !l.validate(pattern) { return nil, &permalinkExpandError{pattern: pattern, err: errPermalinkIllFormed} } - - pattern := pattern matches := attributeRegexp.FindAllStringSubmatch(pattern, -1) callbacks := make([]pageToPermaAttribute, len(matches)) @@ -157,7 +166,7 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa callbacks[i] = callback } - expanders[k] = func(p Page) (string, error) { + return func(p Page) (string, error) { if matches == nil { return pattern, nil } @@ -173,12 +182,25 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa } newField = strings.Replace(newField, replacement, newAttr, 1) - } return newField, nil + }, nil + }) +} + +func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Page) (string, error), error) { + expanders := make(map[string]func(Page) (string, error)) + + for k, pattern := range patterns { + k = strings.Trim(k, sectionCutSet) + + expander, err := l.getOrParsePattern(pattern) + if err != nil { + return nil, err } + expanders[k] = expander } return expanders, nil diff --git a/resources/page/permalinks_integration_test.go b/resources/page/permalinks_integration_test.go index 9a76ac602..2b9e878b1 100644 --- a/resources/page/permalinks_integration_test.go +++ b/resources/page/permalinks_integration_test.go @@ -193,3 +193,42 @@ List. b.AssertFileContent("public/libros/fiction/index.html", "List.") b.AssertFileContent("public/libros/fiction/2023/book1/index.html", "Single.") } + +func TestPermalinksUrlCascade(t *testing.T) { + t.Parallel() + + files := ` +-- layouts/_default/list.html -- +List|{{ .Kind }}|{{ .RelPermalink }}| +-- layouts/_default/single.html -- +Single|{{ .Kind }}|{{ .RelPermalink }}| +-- hugo.toml -- +-- content/cooking/delicious-recipes/_index.md -- +--- +url: /delicious-recipe/ +cascade: + url: /delicious-recipe/:slug/ +--- +-- content/cooking/delicious-recipes/example1.md -- +--- +title: Recipe 1 +--- +-- content/cooking/delicious-recipes/example2.md -- +--- +title: Recipe 2 +slug: custom-recipe-2 +--- +` + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + LogLevel: logg.LevelWarn, + }).Build() + + t.Log(b.LogString()) + b.Assert(b.H.Log.LoggCount(logg.LevelWarn), qt.Equals, 0) + b.AssertFileContent("public/delicious-recipe/index.html", "List|section|/delicious-recipe/") + b.AssertFileContent("public/delicious-recipe/recipe-1/index.html", "Single|page|/delicious-recipe/recipe-1/") + b.AssertFileContent("public/delicious-recipe/custom-recipe-2/index.html", "Single|page|/delicious-recipe/custom-recipe-2/") +} -- cgit v1.2.3