if err == nil {
if q.IncludeSelf {
- if n := m.treePages.Get(q.Path); n != nil {
+ if n := m.treePages.GetWithFallback(q.Path); n != nil {
if p, ok := n.(*pageState); ok && include(p) {
pas = append(pas, p)
}
var (
best contentNodeForSite = nil
bestDistance int
+ bestWeight int
)
for n := range candidates {
if m := n.(contentNodeLookupContentNodes).lookupContentNodes(q, fallback); m != nil {
for nn := range m {
vec := nn.siteVector()
- if q == vec {
- // Exact match.
+ var w int
+ if wp, ok := nn.(contentNodeContentWeightProvider); ok {
+ w = wp.contentWeight()
+ }
+
+ if q == vec && w > 0 {
+ // Exact match backed by a file.
return nn
}
distance := q.Distance(vec)
+ distanceAbs := absint(distance)
if best == nil {
best = nn
bestDistance = distance
+ bestWeight = w
} else {
- distanceAbs := absint(distance)
bestDistanceAbs := absint(bestDistance)
- if distanceAbs < bestDistanceAbs {
- // Closer is better.
- best = nn
- bestDistance = distance
- } else if distanceAbs == bestDistanceAbs && distance > 0 {
- // Positive distance is better than negative.
+ if w > 0 && bestWeight == 0 {
+ // Prefer file-backed pages over auto pages.
best = nn
bestDistance = distance
+ bestWeight = w
+ } else if (w > 0) == (bestWeight > 0) {
+ // Both file-backed or both auto: use distance.
+ if distanceAbs < bestDistanceAbs {
+ best = nn
+ bestDistance = distance
+ bestWeight = w
+ } else if distanceAbs == bestDistanceAbs && distance > 0 {
+ best = nn
+ bestDistance = distance
+ bestWeight = w
+ }
}
}
}
}
func (s *contentNodeShifter) Shift(n contentNode, siteVector sitesmatrix.Vector, fallback bool) (contentNode, bool) {
+ var exact contentNode
switch v := n.(type) {
case contentNodeLookupContentNode:
- if vv := v.lookupContentNode(siteVector); vv != nil {
- return vv, true
- }
+ exact = v.lookupContentNode(siteVector)
default:
panic(fmt.Sprintf("Shift: unknown type %T for %q", n, n.Path()))
}
+ if exact != nil {
+ if !fallback {
+ return exact, true
+ }
+ // If the exact match is backed by a file, return it directly.
+ if wp, ok := exact.(contentNodeContentWeightProvider); ok && wp.contentWeight() > 0 {
+ return exact, true
+ }
+ // The exact match is an auto page (not backed by a file).
+ // Check if there's a file-backed complement that should take precedence.
+ if vvv := cnh.findContentNodeForSiteVector(siteVector, fallback, contentNodeToSeq(n)); vvv != nil {
+ return vvv, true
+ }
+ return exact, true
+ }
+
if !fallback {
- // Done
return nil, false
}
return t
}
+// GetWithFallback is like Get but uses the fallback/complement mechanism
+// when finding a node for the current site vector.
+func (r *NodeShiftTree[T]) GetWithFallback(s string) T {
+ s = cleanKey(s)
+ v, ok := r.tree.Get(s)
+ if !ok {
+ var t T
+ return t
+ }
+ if v, ok := r.shift(v, true); ok {
+ return v
+ }
+ var t T
+ return t
+}
+
func (r *NodeShiftTree[T]) ForEeachInDimension(s string, dims sitesmatrix.Vector, d int, f func(T) bool) {
s = cleanKey(s)
v, ok := r.tree.Get(s)
b.AssertFileContent("public/v1/index.html", "Edited version: v1|")
}
+
+// Issue 14540
+func TestSiteComplementsOverrideAutomaticSectionPages(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+disableKinds = ['rss', 'sitemap', 'taxonomy', 'term']
+defaultContentLanguageInSubdir = true
+
+[languages.en]
+ weight = 1
+
+[languages.de]
+ weight = 2
+
+[[module.mounts]]
+ source = 'content/en'
+ target = 'content'
+ [module.mounts.sites.matrix]
+ languages = ['en']
+ [module.mounts.sites.complements]
+ languages = ['de']
+
+[[module.mounts]]
+ source = 'content/de'
+ target = 'content'
+ [module.mounts.sites.matrix]
+ languages = ['de']
+-- layouts/home.html --
+<ul>
+{{ range $k, $_ := site.Pages -}}
+ <li>[{{ $k }}] <a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
+{{ end -}}
+</ul>
+-- layouts/page.html --
+{{ .Title }}|
+-- layouts/section.html --
+{{ .Title }}|
+-- content/en/_index.md --
+---
+title: home en
+---
+-- content/en/s1/_index.md --
+---
+title: s1 en
+---
+-- content/en/s1/p1.md --
+---
+title: p1 en
+---
+-- content/en/s2/_index.md --
+---
+title: s2 en
+---
+-- content/en/s2/p2.md --
+---
+title: p2 en
+---
+-- content/de/s1/p1.md --
+---
+title: p1 de
+---
+`
+
+ b := hugolib.Test(t, files)
+
+ b.AssertFileContent("public/en/index.html", `
+<ul>
+<li>[0] <a href="/en/">home en</a></li>
+<li>[1] <a href="/en/s1/p1/">p1 en</a></li>
+<li>[2] <a href="/en/s2/p2/">p2 en</a></li>
+<li>[3] <a href="/en/s1/">s1 en</a></li>
+<li>[4] <a href="/en/s2/">s2 en</a></li>
+</ul>
+ `)
+
+ b.AssertFileContent("public/de/index.html", `
+<ul>
+<li>[0] <a href="/en/">home en</a></li>
+<li>[1] <a href="/de/s1/p1/">p1 de</a></li>
+<li>[2] <a href="/en/s2/p2/">p2 en</a></li>
+<li>[3] <a href="/en/s1/">s1 en</a></li>
+<li>[4] <a href="/en/s2/">s2 en</a></li>
+</ul>
+ `)
+}