Skip to content

Commit a1d260b

Browse files
committed
hugolib: Extend the sections API
This commit adds some section related methods that have been asked for: * .CurrentSection * .IsDescendant * .IsAncestor Fixes #3591
1 parent dd9b1ba commit a1d260b

File tree

5 files changed

+155
-14
lines changed

5 files changed

+155
-14
lines changed

‎helpers/general.go‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,38 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
194194
return false
195195
}
196196

197+
// HasStringsPrefix tests whether the string slice s begins with prefix slice s.
198+
func HasStringsPrefix(s, prefix []string) bool {
199+
return len(s) >= len(prefix) && compareStringSlices(s[0:len(prefix)], prefix)
200+
}
201+
202+
// HasStringsSuffix tests whether the string slice s ends with suffix slice s.
203+
func HasStringsSuffix(s, suffix []string) bool {
204+
return len(s) >= len(suffix) && compareStringSlices(s[len(s)-len(suffix):], suffix)
205+
}
206+
207+
func compareStringSlices(a, b []string) bool {
208+
if a == nil && b == nil {
209+
return true
210+
}
211+
212+
if a == nil || b == nil {
213+
return false
214+
}
215+
216+
if len(a) != len(b) {
217+
return false
218+
}
219+
220+
for i := range a {
221+
if a[i] != b[i] {
222+
return false
223+
}
224+
}
225+
226+
return true
227+
}
228+
197229
// ThemeSet checks whether a theme is in use or not.
198230
func (p *PathSpec) ThemeSet() bool {
199231
return p.theme != ""

‎helpers/general_test.go‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,45 @@ func TestFirstUpper(t *testing.T) {
6464
}
6565
}
6666

67+
func TestHasStringsPrefix(t *testing.T) {
68+
for i, this := range []struct {
69+
s []string
70+
prefix []string
71+
expect bool
72+
}{
73+
{[]string{"a"}, []string{"a"}, true},
74+
{[]string{}, []string{}, true},
75+
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
76+
{[]string{"d", "a", "b", "c"}, []string{"a", "b"}, false},
77+
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, true},
78+
{[]string{"abra", "ca"}, []string{"abra", "ca", "dabra"}, false},
79+
} {
80+
result := HasStringsPrefix(this.s, this.prefix)
81+
if result != this.expect {
82+
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
83+
}
84+
}
85+
}
86+
87+
func TestHasStringsSuffix(t *testing.T) {
88+
for i, this := range []struct {
89+
s []string
90+
suffix []string
91+
expect bool
92+
}{
93+
{[]string{"a"}, []string{"a"}, true},
94+
{[]string{}, []string{}, true},
95+
{[]string{"a", "b", "c"}, []string{"b", "c"}, true},
96+
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, false},
97+
{[]string{"abra", "ca", "dabra"}, []string{"ca", "dabra"}, true},
98+
} {
99+
result := HasStringsSuffix(this.s, this.suffix)
100+
if result != this.expect {
101+
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
102+
}
103+
}
104+
}
105+
67106
var containsTestText = (`На берегу пустынных волн
68107
Стоял он, дум великих полн,
69108
И вдаль глядел. Пред ним широко

‎hugolib/permalinks.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func pageToPermalinkSection(p *Page, _ string) (string, error) {
186186
func pageToPermalinkSections(p *Page, _ string) (string, error) {
187187
// TODO(bep) we have some superflous URLize in this file, but let's
188188
// deal with that later.
189-
return path.Join(p.current().sections...), nil
189+
return path.Join(p.CurrentSection().sections...), nil
190190
}
191191

192192
func init() {

‎hugolib/site_sections.go‎

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"strconv"
2020
"strings"
2121

22+
"github.com/gohugoio/hugo/helpers"
23+
2224
radix "github.com/hashicorp/go-immutable-radix"
2325
)
2426

@@ -42,10 +44,9 @@ func (p *Page) Parent() *Page {
4244
return p.parent
4345
}
4446

45-
// current returns the page's current section.
47+
// CurrentSection returns the page's current section or the page itself if home or a section.
4648
// Note that this will return nil for pages that is not regular, home or section pages.
47-
// Note that for paginated sections and home pages, this will return the original page pointer.
48-
func (p *Page) current() *Page {
49+
func (p *Page) CurrentSection() *Page {
4950
v := p
5051
if v.origOnCopy != nil {
5152
v = v.origOnCopy
@@ -65,20 +66,59 @@ func (p *Page) InSection(other interface{}) (bool, error) {
6566
return false, nil
6667
}
6768

68-
if po, ok := other.(*PageOutput); ok {
69-
other = po.Page
69+
pp, err := unwrapPage(other)
70+
if err != nil {
71+
return false, err
7072
}
7173

72-
pp, ok := other.(*Page)
73-
if !ok {
74-
return false, fmt.Errorf("%T not supported in InSection", other)
74+
if pp == nil {
75+
return false, nil
7576
}
7677

77-
if pp == nil {
78+
return pp.CurrentSection() == p.CurrentSection(), nil
79+
}
80+
81+
// IsDescendant returns whether the current page is a descendant of the given page.
82+
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
83+
func (p *Page) IsDescendant(other interface{}) (bool, error) {
84+
pp, err := unwrapPage(other)
85+
if err != nil {
86+
return false, err
87+
}
88+
89+
if pp.Kind == KindPage && len(p.sections) == len(pp.sections) {
90+
// A regular page is never its section's descendant.
7891
return false, nil
7992
}
93+
return helpers.HasStringsPrefix(p.sections, pp.sections), nil
94+
}
8095

81-
return pp.current() == p.current(), nil
96+
// IsAncestor returns whether the current page is an ancestor of the given page.
97+
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
98+
func (p *Page) IsAncestor(other interface{}) (bool, error) {
99+
pp, err := unwrapPage(other)
100+
if err != nil {
101+
return false, err
102+
}
103+
104+
if p.Kind == KindPage && len(p.sections) == len(pp.sections) {
105+
// A regular page is never its section's ancestor.
106+
return false, nil
107+
}
108+
109+
return helpers.HasStringsPrefix(pp.sections, p.sections), nil
110+
}
111+
112+
func unwrapPage(in interface{}) (*Page, error) {
113+
if po, ok := in.(*PageOutput); ok {
114+
in = po.Page
115+
}
116+
117+
pp, ok := in.(*Page)
118+
if !ok {
119+
return nil, fmt.Errorf("%T not supported", in)
120+
}
121+
return pp, nil
82122
}
83123

84124
// Sections returns this section's subsections, if any.

‎hugolib/site_sections_test.go‎

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
166166
home := p.Parent()
167167
assert.True(home.IsHome())
168168
assert.Len(p.Sections(), 0)
169-
assert.Equal(home, home.current())
169+
assert.Equal(home, home.CurrentSection())
170170
active, err := home.InSection(home)
171171
assert.NoError(err)
172172
assert.True(active)
@@ -187,7 +187,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
187187
assert.Len(p.Sections(), 1)
188188

189189
for _, child := range p.Pages {
190-
assert.Equal(p, child.current())
190+
assert.Equal(p, child.CurrentSection())
191191
active, err := child.InSection(p)
192192
assert.NoError(err)
193193
assert.True(active)
@@ -197,9 +197,23 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
197197
active, err = p.InSection(p.s.getPage(KindHome))
198198
assert.NoError(err)
199199
assert.False(active)
200+
201+
isAncestor, err := p.IsAncestor(child)
202+
assert.NoError(err)
203+
assert.True(isAncestor)
204+
isAncestor, err = child.IsAncestor(p)
205+
assert.NoError(err)
206+
assert.False(isAncestor)
207+
208+
isDescendant, err := p.IsDescendant(child)
209+
assert.NoError(err)
210+
assert.False(isDescendant)
211+
isDescendant, err = child.IsDescendant(p)
212+
assert.NoError(err)
213+
assert.True(isDescendant)
200214
}
201215

202-
assert.Equal(p, p.current())
216+
assert.Equal(p, p.CurrentSection())
203217

204218
}},
205219
{"l1,l2_2", func(p *Page) {
@@ -214,6 +228,22 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
214228
assert.Len(p.Pages, 2)
215229
assert.Equal("T2_-1", p.Parent().Title)
216230
assert.Len(p.Sections(), 0)
231+
232+
l1 := p.s.getPage(KindSection, "l1")
233+
isDescendant, err := l1.IsDescendant(p)
234+
assert.NoError(err)
235+
assert.False(isDescendant)
236+
isDescendant, err = p.IsDescendant(l1)
237+
assert.NoError(err)
238+
assert.True(isDescendant)
239+
240+
isAncestor, err := l1.IsAncestor(p)
241+
assert.NoError(err)
242+
assert.True(isAncestor)
243+
isAncestor, err = p.IsAncestor(l1)
244+
assert.NoError(err)
245+
assert.False(isAncestor)
246+
217247
}},
218248
{"perm a,link", func(p *Page) {
219249
assert.Equal("T9_-1", p.Title)

0 commit comments

Comments
 (0)