From 7ce033a89d411036e40dbb42325e30e12248bda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 17 May 2023 16:29:06 +0200 Subject: [PATCH] Support, but warn, about top level language custom params Updates #10947 --- common/maps/params.go | 24 ++++++++--------- common/maps/params_test.go | 4 +-- config/allconfig/allconfig.go | 50 ++++++++++++++++++++++++++++++++++- hugolib/config_test.go | 41 +++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 16 deletions(-) diff --git a/common/maps/params.go b/common/maps/params.go index eb60fbbfc94..a3e607b8935 100644 --- a/common/maps/params.go +++ b/common/maps/params.go @@ -70,7 +70,7 @@ func (p Params) IsZero() bool { } for k := range p { - return k == mergeStrategyKey + return k == MergeStrategyKey } return false @@ -103,7 +103,7 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) { for k, v := range pp { - if k == mergeStrategyKey { + if k == MergeStrategyKey { continue } vv, found := p[k] @@ -124,7 +124,7 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) { // For internal use. func (p Params) GetMergeStrategy() (ParamsMergeStrategy, bool) { - if v, found := p[mergeStrategyKey]; found { + if v, found := p[MergeStrategyKey]; found { if s, ok := v.(ParamsMergeStrategy); ok { return s, true } @@ -134,8 +134,8 @@ func (p Params) GetMergeStrategy() (ParamsMergeStrategy, bool) { // For internal use. func (p Params) DeleteMergeStrategy() bool { - if _, found := p[mergeStrategyKey]; found { - delete(p, mergeStrategyKey) + if _, found := p[MergeStrategyKey]; found { + delete(p, MergeStrategyKey) return true } return false @@ -148,7 +148,7 @@ func (p Params) SetMergeStrategy(s ParamsMergeStrategy) { default: panic(fmt.Sprintf("invalid merge strategy %q", s)) } - p[mergeStrategyKey] = s + p[MergeStrategyKey] = s } func getNested(m map[string]any, indices []string) (any, string, map[string]any) { @@ -242,7 +242,7 @@ const ( // Add new keys, merge existing. ParamsMergeStrategyDeep ParamsMergeStrategy = "deep" - mergeStrategyKey = "_merge" + MergeStrategyKey = "_merge" ) // CleanConfigStringMapString removes any processing instructions from m, @@ -251,13 +251,13 @@ func CleanConfigStringMapString(m map[string]string) map[string]string { if m == nil || len(m) == 0 { return m } - if _, found := m[mergeStrategyKey]; !found { + if _, found := m[MergeStrategyKey]; !found { return m } // Create a new map and copy all the keys except the merge strategy key. m2 := make(map[string]string, len(m)-1) for k, v := range m { - if k != mergeStrategyKey { + if k != MergeStrategyKey { m2[k] = v } } @@ -270,13 +270,13 @@ func CleanConfigStringMap(m map[string]any) map[string]any { if m == nil || len(m) == 0 { return m } - if _, found := m[mergeStrategyKey]; !found { + if _, found := m[MergeStrategyKey]; !found { return m } // Create a new map and copy all the keys except the merge strategy key. m2 := make(map[string]any, len(m)-1) for k, v := range m { - if k != mergeStrategyKey { + if k != MergeStrategyKey { m2[k] = v } switch v2 := v.(type) { @@ -313,7 +313,7 @@ func PrepareParams(m Params) { for k, v := range m { var retyped bool lKey := strings.ToLower(k) - if lKey == mergeStrategyKey { + if lKey == MergeStrategyKey { v = toMergeStrategy(v) retyped = true } else { diff --git a/common/maps/params_test.go b/common/maps/params_test.go index 7e1dbbae7fe..578b2a576e9 100644 --- a/common/maps/params_test.go +++ b/common/maps/params_test.go @@ -75,7 +75,7 @@ func TestParamsSetAndMerge(t *testing.T) { createParamsPair := func() (Params, Params) { p1 := Params{"a": "av", "c": "cv", "nested": Params{"al2": "al2v", "cl2": "cl2v"}} - p2 := Params{"b": "bv", "a": "abv", "nested": Params{"bl2": "bl2v", "al2": "al2bv"}, mergeStrategyKey: ParamsMergeStrategyDeep} + p2 := Params{"b": "bv", "a": "abv", "nested": Params{"bl2": "bl2v", "al2": "al2bv"}, MergeStrategyKey: ParamsMergeStrategyDeep} return p1, p2 } @@ -92,7 +92,7 @@ func TestParamsSetAndMerge(t *testing.T) { "bl2": "bl2v", }, "b": "bv", - mergeStrategyKey: ParamsMergeStrategyDeep, + MergeStrategyKey: ParamsMergeStrategyDeep, }) p1, p2 = createParamsPair() diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 6731b9003f6..a5191477253 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -63,6 +63,31 @@ type InternalConfig struct { LiveReloadPort int } +// All non-params config keys for language. +var configLanguageKeys map[string]bool + +func init() { + skip := map[string]bool{ + "internal": true, + "c": true, + "rootconfig": true, + } + configLanguageKeys = make(map[string]bool) + addKeys := func(v reflect.Value) { + for i := 0; i < v.NumField(); i++ { + name := strings.ToLower(v.Type().Field(i).Name) + if skip[name] { + continue + } + configLanguageKeys[name] = true + } + } + addKeys(reflect.ValueOf(Config{})) + addKeys(reflect.ValueOf(RootConfig{})) + addKeys(reflect.ValueOf(config.CommonDirs{})) + addKeys(reflect.ValueOf(langs.LanguageConfig{})) +} + type Config struct { // For internal use only. Internal InternalConfig `mapstructure:"-" json:"-"` @@ -328,7 +353,7 @@ type ConfigCompiled struct { Clock time.Time // This is set to the last transient error found during config compilation. - // With themes/modules we compule the configuration in multiple passes, and + // With themes/modules we compute the configuration in multiple passes, and // errors with missing output format definitions may resolve itself. transientErr error } @@ -659,7 +684,30 @@ func FromLoadConfigResult(fs afero.Fs, res config.LoadConfigResult) (*Configs, e var differentRootKeys []string switch x := v.(type) { case maps.Params: + var params maps.Params + pv, found := x["params"] + if found { + params = pv.(maps.Params) + } else { + params = maps.Params{ + maps.MergeStrategyKey: maps.ParamsMergeStrategyDeep, + } + x["params"] = params + } + for kk, vv := range x { + if kk == "_merge" { + continue + } + if kk != maps.MergeStrategyKey && !configLanguageKeys[kk] { + // This should have been placed below params. + // We accidently allowed it in the past, so we need to support it a little longer, + // But log a warning. + if _, found := params[kk]; !found { + helpers.Deprecated(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), false) + params[kk] = vv + } + } if kk == "baseurl" { // baseURL configure don the language level is a multihost setup. isMultiHost = true diff --git a/hugolib/config_test.go b/hugolib/config_test.go index e27068da699..1baaf71963c 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -778,6 +778,43 @@ Home. } +func TestConfigParamSetOnLanguageLevel(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT"] +[languages] +[languages.en] +title = "English Title" +thisIsAParam = "thisIsAParamValue" +[languages.en.params] +myparam = "enParamValue" +[languages.sv] +title = "Svensk Title" +[languages.sv.params] +myparam = "svParamValue" +-- layouts/index.html -- +MyParam: {{ site.Params.myparam }} +ThisIsAParam: {{ site.Params.thisIsAParam }} + + +` + + b, err := NewIntegrationTestBuilder( + IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNil) + b.AssertFileContent("public/index.html", ` +MyParam: enParamValue +ThisIsAParam: thisIsAParamValue +`) +} + func TestReproCommentsIn10947(t *testing.T) { t.Parallel() @@ -817,7 +854,9 @@ title: "My Swedish Section" }, ).Build() - b.Assert(b.H.Log.LogCounters().WarnCounter.Count(), qt.Equals, uint64(2)) + { + b.Assert(b.H.Log.LogCounters().WarnCounter.Count(), qt.Equals, uint64(2)) + } b.AssertFileContent("public/index.html", ` AllPages: 4| Sections: true|