Skip to content

Commit eaf5ace

Browse files
committed
Fix recent regression with cascading of params to content adapters
Fixes #13743
1 parent 9ad26b6 commit eaf5ace

File tree

7 files changed

+82
-72
lines changed

7 files changed

+82
-72
lines changed

config/allconfig/allconfig.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ type Config struct {
146146

147147
// The cascade configuration section contains the top level front matter cascade configuration options,
148148
// a slice of page matcher and params to apply to those pages.
149-
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
149+
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]] `mapstructure:"-"`
150150

151151
// The segments defines segments for the site. Used for partial/segmented builds.
152152
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
@@ -776,7 +776,7 @@ type Configs struct {
776776
}
777777

778778
func (c *Configs) Validate(logger loggers.Logger) error {
779-
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
779+
c.Base.Cascade.Config.Range(func(p page.PageMatcher, cfg page.PageMatcherParamsConfig) bool {
780780
page.CheckCascadePattern(logger, p)
781781
return true
782782
})

hugolib/cascade_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,3 +917,24 @@ title: p2
917917
b.AssertFileExists("public/sx/index.html", true) // failing
918918
b.AssertFileExists("public/sx/p2/index.html", true) // failing
919919
}
920+
921+
func TestCascadeGotmplIssue13743(t *testing.T) {
922+
t.Parallel()
923+
924+
files := `
925+
-- hugo.toml --
926+
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
927+
[cascade.params]
928+
foo = 'bar'
929+
[cascade.target]
930+
path = '/p1'
931+
-- content/_content.gotmpl --
932+
{{ .AddPage (dict "title" "p1" "path" "p1") }}
933+
-- layouts/all.html --
934+
{{ .Title }}|{{ .Params.foo }}
935+
`
936+
937+
b := Test(t, files)
938+
939+
b.AssertFileContent("public/p1/index.html", "p1|bar") // actual content is "p1|"
940+
}

hugolib/content_map_page.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
14121412
}
14131413

14141414
// Handle cascades first to get any default dates set.
1415-
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
1415+
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
14161416
if keyPage == "" {
14171417
// Home page gets it's cascade from the site config.
14181418
cascade = sa.conf.Cascade.Config
@@ -1424,7 +1424,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
14241424
} else {
14251425
_, data := pw.WalkContext.Data().LongestPrefix(paths.Dir(keyPage))
14261426
if data != nil {
1427-
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
1427+
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
14281428
}
14291429
}
14301430

@@ -1506,11 +1506,11 @@ func (sa *sitePagesAssembler) applyAggregates() error {
15061506
pageResource := rs.r.(*pageState)
15071507
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
15081508
pageResource.m.resourcePath = relPath
1509-
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
1509+
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
15101510
// Apply cascade (if set) to the page.
15111511
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
15121512
if data != nil {
1513-
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
1513+
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
15141514
}
15151515
if err := pageResource.setMetaPost(cascade); err != nil {
15161516
return false, err
@@ -1574,10 +1574,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
15741574
const eventName = "dates"
15751575

15761576
if p.Kind() == kinds.KindTerm {
1577-
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
1577+
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
15781578
_, data := pw.WalkContext.Data().LongestPrefix(s)
15791579
if data != nil {
1580-
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
1580+
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
15811581
}
15821582
if err := p.setMetaPost(cascade); err != nil {
15831583
return false, err

hugolib/page__meta.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ type pageMetaParams struct {
8383

8484
// These are only set in watch mode.
8585
datesOriginal pagemeta.Dates
86-
paramsOriginal map[string]any // contains the original params as defined in the front matter.
87-
cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter.
86+
paramsOriginal map[string]any // contains the original params as defined in the front matter.
87+
cascadeOriginal *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // contains the original cascade as defined in the front matter.
8888
}
8989

9090
func (m *pageMetaParams) init(preserveOriginal bool) {
@@ -291,7 +291,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
291291
return nil
292292
}
293293

294-
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error {
294+
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) error {
295295
ps.m.setMetaPostCount++
296296
var cascadeHashPre uint64
297297
if ps.m.setMetaPostCount > 1 {
@@ -303,15 +303,20 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
303303
// Apply cascades first so they can be overridden later.
304304
if cascade != nil {
305305
if ps.m.pageConfig.CascadeCompiled != nil {
306-
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
306+
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
307307
vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
308308
if !found {
309309
ps.m.pageConfig.CascadeCompiled.Set(k, v)
310310
} else {
311311
// Merge
312-
for ck, cv := range v {
313-
if _, found := vv[ck]; !found {
314-
vv[ck] = cv
312+
for ck, cv := range v.Params {
313+
if _, found := vv.Params[ck]; !found {
314+
vv.Params[ck] = cv
315+
}
316+
}
317+
for ck, cv := range v.Fields {
318+
if _, found := vv.Fields[ck]; !found {
319+
vv.Fields[ck] = cv
315320
}
316321
}
317322
}
@@ -341,11 +346,17 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
341346

342347
// Cascade is also applied to itself.
343348
var err error
344-
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
349+
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
345350
if !k.Matches(ps) {
346351
return true
347352
}
348-
for kk, vv := range v {
353+
for kk, vv := range v.Params {
354+
if _, found := ps.m.pageConfig.Params[kk]; !found {
355+
ps.m.pageConfig.Params[kk] = vv
356+
}
357+
}
358+
359+
for kk, vv := range v.Fields {
349360
if ps.m.pageConfig.IsFromContentAdapter {
350361
if _, found := ps.m.pageConfig.ContentAdapterData[kk]; !found {
351362
ps.m.pageConfig.ContentAdapterData[kk] = vv

resources/page/page_matcher.go

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ func CheckCascadePattern(logger loggers.Logger, m PageMatcher) {
105105
}
106106
}
107107

108-
func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]], error) {
109-
buildConfig := func(in any) (*maps.Ordered[PageMatcher, maps.Params], any, error) {
110-
cascade := maps.NewOrdered[PageMatcher, maps.Params]()
108+
func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]], error) {
109+
buildConfig := func(in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], any, error) {
110+
cascade := maps.NewOrdered[PageMatcher, PageMatcherParamsConfig]()
111111
if in == nil {
112112
return cascade, []map[string]any{}, nil
113113
}
@@ -124,11 +124,7 @@ func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any)
124124
c PageMatcherParamsConfig
125125
err error
126126
)
127-
if handleLegacyFormat {
128-
c, err = mapToPageMatcherParamsConfigLegacy(m)
129-
} else {
130-
c, err = mapToPageMatcherParamsConfig(m)
131-
}
127+
c, err = mapToPageMatcherParamsConfig(m)
132128
if err != nil {
133129
return nil, nil, err
134130
}
@@ -147,23 +143,28 @@ func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any)
147143
if found {
148144
// Merge
149145
for k, v := range cfg.Params {
150-
if _, found := c[k]; !found {
151-
c[k] = v
146+
if _, found := c.Params[k]; !found {
147+
c.Params[k] = v
148+
}
149+
}
150+
for k, v := range cfg.Fields {
151+
if _, found := c.Fields[k]; !found {
152+
c.Fields[k] = v
152153
}
153154
}
154155
} else {
155-
cascade.Set(m, cfg.Params)
156+
cascade.Set(m, cfg)
156157
}
157158
}
158159

159160
return cascade, cfgs, nil
160161
}
161162

162-
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]](in, buildConfig)
163+
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]](in, buildConfig)
163164
}
164165

165166
// DecodeCascade decodes in which could be either a map or a slice of maps.
166-
func DecodeCascade(logger loggers.Logger, handleLegacyFormat bool, in any) (*maps.Ordered[PageMatcher, maps.Params], error) {
167+
func DecodeCascade(logger loggers.Logger, handleLegacyFormat bool, in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], error) {
167168
conf, err := DecodeCascadeConfig(logger, handleLegacyFormat, in)
168169
if err != nil {
169170
return nil, err
@@ -181,47 +182,22 @@ func mapToPageMatcherParamsConfig(m map[string]any) (PageMatcherParamsConfig, er
181182
return pcfg, err
182183
}
183184
pcfg.Target = target
184-
default:
185+
case "params":
185186
if pcfg.Params == nil {
186187
pcfg.Params = make(maps.Params)
187188
}
188-
pcfg.Params[k] = v
189-
}
190-
}
191-
return pcfg, pcfg.init()
192-
}
193-
194-
func mapToPageMatcherParamsConfigLegacy(m map[string]any) (PageMatcherParamsConfig, error) {
195-
var pcfg PageMatcherParamsConfig
196-
for k, v := range m {
197-
switch strings.ToLower(k) {
198-
case "params":
199-
// We simplified the structure of the cascade config in Hugo 0.111.0.
200-
// There is a small chance that someone has used the old structure with the params keyword,
201-
// those values will now be moved to the top level.
202-
// This should be very unlikely as it would lead to constructs like .Params.params.foo,
203-
// and most people see params as an Hugo internal keyword.
204189
params := maps.ToStringMap(v)
205-
if pcfg.Params == nil {
206-
pcfg.Params = params
207-
} else {
208-
for k, v := range params {
209-
if _, found := pcfg.Params[k]; !found {
210-
pcfg.Params[k] = v
211-
}
190+
for k, v := range params {
191+
if _, found := pcfg.Params[k]; !found {
192+
pcfg.Params[k] = v
212193
}
213194
}
214-
case "_target", "target":
215-
var target PageMatcher
216-
if err := decodePageMatcher(v, &target); err != nil {
217-
return pcfg, err
218-
}
219-
pcfg.Target = target
220195
default:
221-
if pcfg.Params == nil {
222-
pcfg.Params = make(maps.Params)
196+
if pcfg.Fields == nil {
197+
pcfg.Fields = make(maps.Params)
223198
}
224-
pcfg.Params[k] = v
199+
200+
pcfg.Fields[k] = v
225201
}
226202
}
227203
return pcfg, pcfg.init()
@@ -250,10 +226,14 @@ func decodePageMatcher(m any, v *PageMatcher) error {
250226
type PageMatcherParamsConfig struct {
251227
// Apply Params to all Pages matching Target.
252228
Params maps.Params
229+
// Fields holds all fields but Params.
230+
Fields maps.Params
231+
// Target is the PageMatcher that this config applies to.
253232
Target PageMatcher
254233
}
255234

256235
func (p *PageMatcherParamsConfig) init() error {
257236
maps.PrepareParams(p.Params)
237+
maps.PrepareParams(p.Fields)
258238
return nil
259239
}

resources/page/page_matcher_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,17 @@ func TestPageMatcher(t *testing.T) {
8484

8585
c.Run("mapToPageMatcherParamsConfig", func(c *qt.C) {
8686
fn := func(m map[string]any) PageMatcherParamsConfig {
87-
v, err := mapToPageMatcherParamsConfigLegacy(m)
87+
v, err := mapToPageMatcherParamsConfig(m)
8888
c.Assert(err, qt.IsNil)
8989
return v
9090
}
91-
// Legacy.
9291
c.Assert(fn(map[string]any{"_target": map[string]any{"kind": "page"}, "foo": "bar"}), qt.DeepEquals, PageMatcherParamsConfig{
93-
Params: maps.Params{
92+
Fields: maps.Params{
9493
"foo": "bar",
9594
},
9695
Target: PageMatcher{Path: "", Kind: "page", Lang: "", Environment: ""},
9796
})
9897

99-
// Current format.
10098
c.Assert(fn(map[string]any{"target": map[string]any{"kind": "page"}, "params": map[string]any{"foo": "bar"}}), qt.DeepEquals, PageMatcherParamsConfig{
10199
Params: maps.Params{
102100
"foo": "bar",
@@ -134,7 +132,7 @@ func TestDecodeCascadeConfig(t *testing.T) {
134132
c.Assert(err, qt.IsNil)
135133
c.Assert(got, qt.IsNotNil)
136134
c.Assert(got.Config.Keys(), qt.DeepEquals, []PageMatcher{{Kind: "page", Environment: "production"}, {Kind: "page"}})
137-
c.Assert(got.Config.Values(), qt.DeepEquals, []maps.Params{{"a": string("av")}, {"b": string("bv")}})
135+
138136
c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{
139137
{
140138
Params: maps.Params{"a": string("av")},

resources/page/pagemeta/page_frontmatter.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ type PageConfig struct {
125125
ContentAdapterData map[string]any `mapstructure:"-" json:"-"`
126126

127127
// Compiled values.
128-
CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"`
129-
ContentMediaType media.Type `mapstructure:"-" json:"-"`
130-
ConfiguredOutputFormats output.Formats `mapstructure:"-" json:"-"`
131-
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
128+
CascadeCompiled *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] `mapstructure:"-" json:"-"`
129+
ContentMediaType media.Type `mapstructure:"-" json:"-"`
130+
ConfiguredOutputFormats output.Formats `mapstructure:"-" json:"-"`
131+
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
132132
}
133133

134134
func ClonePageConfigForRebuild(p *PageConfig, params map[string]any) *PageConfig {

0 commit comments

Comments
 (0)