Skip to content

Commit 30b9c19

Browse files
committed
tpl: Make any layout set in front matter higher priority
Fixes #13541
1 parent c871062 commit 30b9c19

7 files changed

+128
-102
lines changed

Diff for: hugolib/alias.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, err
5353
if ps, ok := p.(*pageState); ok {
5454
base, templateDesc = ps.GetInternalTemplateBasePathAndDescriptor()
5555
}
56-
templateDesc.Layout = ""
56+
templateDesc.LayoutFromUser = ""
5757
templateDesc.Kind = ""
5858
templateDesc.OutputFormat = output.AliasHTMLFormat.Name
5959

Diff for: hugolib/content_map_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ title: p1
538538
-- content/p1/c.html --
539539
<p>c</p>
540540
-- layouts/_default/single.html --
541+
Path: {{ .Path }}|{{.Kind }}
541542
|{{ (.Resources.Get "a.html").RelPermalink -}}
542543
|{{ (.Resources.Get "b.html").RelPermalink -}}
543544
|{{ (.Resources.Get "c.html").Publish }}

Diff for: hugolib/page.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -482,21 +482,21 @@ func (po *pageOutput) GetInternalTemplateBasePathAndDescriptor() (string, tplimp
482482
f := po.f
483483
base := p.PathInfo().BaseReTyped(p.m.pageConfig.Type)
484484
return base, tplimpl.TemplateDescriptor{
485-
Kind: p.Kind(),
486-
Lang: p.Language().Lang,
487-
Layout: p.Layout(),
488-
OutputFormat: f.Name,
489-
MediaType: f.MediaType.Type,
490-
IsPlainText: f.IsPlainText,
485+
Kind: p.Kind(),
486+
Lang: p.Language().Lang,
487+
LayoutFromUser: p.Layout(),
488+
OutputFormat: f.Name,
489+
MediaType: f.MediaType.Type,
490+
IsPlainText: f.IsPlainText,
491491
}
492492
}
493493

494494
func (p *pageState) resolveTemplate(layouts ...string) (*tplimpl.TemplInfo, bool, error) {
495495
dir, d := p.GetInternalTemplateBasePathAndDescriptor()
496496

497497
if len(layouts) > 0 {
498-
d.Layout = layouts[0]
499-
d.LayoutMustMatch = true
498+
d.LayoutFromUser = layouts[0]
499+
d.LayoutFromUserMustMatch = true
500500
}
501501

502502
q := tplimpl.TemplateQuery{

Diff for: tpl/tplimpl/templatedescriptor.go

+41-41
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ const baseNameBaseof = "baseof"
2222
// This is used both as a key and in lookups.
2323
type TemplateDescriptor struct {
2424
// Group 1.
25-
Kind string // page, home, section, taxonomy, term (and only those)
26-
Layout string // list, single, baseof, mycustomlayout.
25+
Kind string // page, home, section, taxonomy, term (and only those)
26+
LayoutFromTemplate string // list, single, all,mycustomlayout
27+
LayoutFromUser string // custom layout set in front matter, e.g. list, single, all, mycustomlayout
2728

2829
// Group 2.
2930
OutputFormat string // rss, csv ...
@@ -34,23 +35,21 @@ type TemplateDescriptor struct {
3435
Variant2 string // contextual variant, e.g. "id" in render.
3536

3637
// Misc.
37-
LayoutMustMatch bool // If set, we only look for the exact layout.
38-
IsPlainText bool // Whether this is a plain text template.
38+
LayoutFromUserMustMatch bool // If set, we only look for the exact layout.
39+
IsPlainText bool // Whether this is a plain text template.
3940
}
4041

4142
func (d *TemplateDescriptor) normalizeFromFile() {
42-
// fmt.Println("normalizeFromFile", "kind:", d.Kind, "layout:", d.Layout, "of:", d.OutputFormat)
43-
44-
if d.Layout == d.OutputFormat {
45-
d.Layout = ""
43+
if d.LayoutFromTemplate == d.OutputFormat {
44+
d.LayoutFromTemplate = ""
4645
}
4746

4847
if d.Kind == kinds.KindTemporary {
4948
d.Kind = ""
5049
}
5150

52-
if d.Layout == d.Kind {
53-
d.Layout = ""
51+
if d.LayoutFromTemplate == d.Kind {
52+
d.LayoutFromTemplate = ""
5453
}
5554
}
5655

@@ -61,7 +60,7 @@ type descriptorHandler struct {
6160
// Note that this in this setup is usually a descriptor constructed from a page,
6261
// so we want to find the best match for that page.
6362
func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool, this, other TemplateDescriptor) weight {
64-
if this.LayoutMustMatch && this.Layout != other.Layout {
63+
if this.LayoutFromUserMustMatch && this.LayoutFromUser != other.LayoutFromTemplate {
6564
return weightNoMatch
6665
}
6766

@@ -94,20 +93,15 @@ func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, oth
9493
return w
9594
}
9695

97-
if other.Layout != "" && other.Layout != layoutAll && other.Layout != this.Layout {
98-
if isLayoutCustom(this.Layout) {
99-
if this.Kind == "" {
100-
this.Layout = ""
101-
} else if this.Kind == kinds.KindPage {
102-
this.Layout = layoutSingle
103-
} else {
104-
this.Layout = layoutList
96+
if other.LayoutFromTemplate != "" && other.LayoutFromTemplate != layoutAll {
97+
if this.LayoutFromUser == "" {
98+
if other.LayoutFromTemplate != this.LayoutFromTemplate {
99+
return w
100+
}
101+
} else if isLayoutStandard(this.LayoutFromUser) {
102+
if other.LayoutFromTemplate != this.LayoutFromUser {
103+
return w
105104
}
106-
}
107-
108-
// Test again.
109-
if other.Layout != this.Layout {
110-
return w
111105
}
112106
}
113107

@@ -123,7 +117,11 @@ func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, oth
123117
// We want e.g. home page in amp output format (media type text/html) to
124118
// find a template even if one isn't specified for that output format,
125119
// when one exist for the html output format (same media type).
126-
if category != CategoryBaseof && (this.Kind == "" || (this.Kind != other.Kind && (this.Layout != other.Layout && other.Layout != layoutAll))) {
120+
skip := category != CategoryBaseof && (this.Kind == "" || (this.Kind != other.Kind && (this.LayoutFromTemplate != other.LayoutFromTemplate && other.LayoutFromTemplate != layoutAll)))
121+
if this.LayoutFromUser != "" {
122+
skip = skip && (this.LayoutFromUser != other.LayoutFromTemplate)
123+
}
124+
if skip {
127125
return w
128126
}
129127

@@ -148,14 +146,14 @@ func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, oth
148146
}
149147

150148
const (
151-
weightKind = 3 // page, home, section, taxonomy, term (and only those)
152-
weightcustomLayout = 4 // custom layout (mylayout, set in e.g. front matter)
153-
weightLayout = 2 // standard layouts (single,list,all)
154-
weightOutputFormat = 2 // a configured output format (e.g. rss, html, json)
155-
weightMediaType = 1 // a configured media type (e.g. text/html, text/plain)
156-
weightLang = 1 // a configured language (e.g. en, nn, fr, ...)
157-
weightVariant1 = 4 // currently used for render hooks, e.g. "link", "image"
158-
weightVariant2 = 2 // currently used for render hooks, e.g. the language "go" in code blocks.
149+
weightKind = 3 // page, home, section, taxonomy, term (and only those)
150+
weightcustomLayout = 4 // custom layout (mylayout, set in e.g. front matter)
151+
weightLayoutStandard = 2 // standard layouts (single,list,all)
152+
weightOutputFormat = 2 // a configured output format (e.g. rss, html, json)
153+
weightMediaType = 1 // a configured media type (e.g. text/html, text/plain)
154+
weightLang = 1 // a configured language (e.g. en, nn, fr, ...)
155+
weightVariant1 = 4 // currently used for render hooks, e.g. "link", "image"
156+
weightVariant2 = 2 // currently used for render hooks, e.g. the language "go" in code blocks.
159157

160158
// We will use the values for group 2 and 3
161159
// if the distance up to the template is shorter than
@@ -179,14 +177,16 @@ func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, oth
179177
w.w2 = weight2Group1
180178
}
181179

182-
if other.Layout != "" && other.Layout == this.Layout || other.Layout == layoutAll {
183-
if isLayoutCustom(this.Layout) {
184-
w.w1 += weightcustomLayout
185-
w.w2 = weight2Group2
186-
} else {
187-
w.w1 += weightLayout
188-
w.w2 = weight2Group1
189-
}
180+
if this.LayoutFromUser == "" && other.LayoutFromTemplate != "" && (other.LayoutFromTemplate == this.LayoutFromTemplate || other.LayoutFromTemplate == layoutAll) {
181+
w.w1 += weightLayoutStandard
182+
w.w2 = weight2Group1
183+
184+
}
185+
186+
// LayoutCustom is only set in this (usually from Page.Layout).
187+
if this.LayoutFromUser != "" && this.LayoutFromUser == other.LayoutFromTemplate {
188+
w.w1 += weightcustomLayout
189+
w.w2 = weight2Group2
190190
}
191191

192192
if other.Lang != "" && other.Lang == this.Lang {

Diff for: tpl/tplimpl/templatedescriptor_test.go

+15-15
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,29 @@ func TestTemplateDescriptorCompare(t *testing.T) {
3838
check(
3939

4040
CategoryBaseof,
41-
TemplateDescriptor{Kind: "", Layout: "", Lang: "", OutputFormat: "404", MediaType: "text/html"},
42-
TemplateDescriptor{Kind: "", Layout: "", Lang: "", OutputFormat: "html", MediaType: "text/html"},
41+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", Lang: "", OutputFormat: "404", MediaType: "text/html"},
42+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", Lang: "", OutputFormat: "html", MediaType: "text/html"},
4343
false,
4444
)
4545

4646
check(
4747
CategoryLayout,
4848
TemplateDescriptor{Kind: "", Lang: "en", OutputFormat: "404", MediaType: "text/html"},
49-
TemplateDescriptor{Kind: "", Layout: "", Lang: "", OutputFormat: "alias", MediaType: "text/html"},
49+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", Lang: "", OutputFormat: "alias", MediaType: "text/html"},
5050
true,
5151
)
5252

5353
less(
5454
CategoryLayout,
55-
TemplateDescriptor{Kind: kinds.KindHome, Layout: "list", OutputFormat: "html"},
56-
TemplateDescriptor{Layout: "list", OutputFormat: "html"},
55+
TemplateDescriptor{Kind: kinds.KindHome, LayoutFromTemplate: "list", OutputFormat: "html"},
56+
TemplateDescriptor{LayoutFromTemplate: "list", OutputFormat: "html"},
5757
TemplateDescriptor{Kind: kinds.KindHome, OutputFormat: "html"},
5858
)
5959

6060
check(
6161
CategoryLayout,
62-
TemplateDescriptor{Kind: kinds.KindHome, Layout: "list", OutputFormat: "html", MediaType: "text/html"},
63-
TemplateDescriptor{Kind: kinds.KindHome, Layout: "list", OutputFormat: "myformat", MediaType: "text/html"},
62+
TemplateDescriptor{Kind: kinds.KindHome, LayoutFromTemplate: "list", OutputFormat: "html", MediaType: "text/html"},
63+
TemplateDescriptor{Kind: kinds.KindHome, LayoutFromTemplate: "list", OutputFormat: "myformat", MediaType: "text/html"},
6464
false,
6565
)
6666
}
@@ -78,20 +78,20 @@ func BenchmarkCompareDescriptors(b *testing.B) {
7878
d1, d2 TemplateDescriptor
7979
}{
8080
{
81-
TemplateDescriptor{Kind: "", Layout: "", OutputFormat: "404", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
82-
TemplateDescriptor{Kind: "", Layout: "", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
81+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", OutputFormat: "404", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
82+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
8383
},
8484
{
85-
TemplateDescriptor{Kind: "page", Layout: "single", OutputFormat: "html", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
86-
TemplateDescriptor{Kind: "", Layout: "list", OutputFormat: "", MediaType: "application/rss+xml", Lang: "", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
85+
TemplateDescriptor{Kind: "page", LayoutFromTemplate: "single", OutputFormat: "html", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
86+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "list", OutputFormat: "", MediaType: "application/rss+xml", Lang: "", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
8787
},
8888
{
89-
TemplateDescriptor{Kind: "page", Layout: "single", OutputFormat: "html", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
90-
TemplateDescriptor{Kind: "", Layout: "", OutputFormat: "alias", MediaType: "text/html", Lang: "", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
89+
TemplateDescriptor{Kind: "page", LayoutFromTemplate: "single", OutputFormat: "html", MediaType: "text/html", Lang: "en", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
90+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "", OutputFormat: "alias", MediaType: "text/html", Lang: "", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
9191
},
9292
{
93-
TemplateDescriptor{Kind: "page", Layout: "single", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "en", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
94-
TemplateDescriptor{Kind: "", Layout: "single", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "nn", Variant1: "", Variant2: "", LayoutMustMatch: false, IsPlainText: false},
93+
TemplateDescriptor{Kind: "page", LayoutFromTemplate: "single", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "en", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
94+
TemplateDescriptor{Kind: "", LayoutFromTemplate: "single", OutputFormat: "rss", MediaType: "application/rss+xml", Lang: "nn", Variant1: "", Variant2: "", LayoutFromUserMustMatch: false, IsPlainText: false},
9595
},
9696
}
9797

0 commit comments

Comments
 (0)