mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
hugolib: Extend the sections API
This commit adds some section related methods that have been asked for: * .CurrentSection * .IsDescendant * .IsAncestor Fixes #3591
This commit is contained in:
parent
dd9b1baab0
commit
a1d260b41a
5 changed files with 156 additions and 15 deletions
|
@ -194,6 +194,38 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasStringsPrefix tests whether the string slice s begins with prefix slice s.
|
||||||
|
func HasStringsPrefix(s, prefix []string) bool {
|
||||||
|
return len(s) >= len(prefix) && compareStringSlices(s[0:len(prefix)], prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasStringsSuffix tests whether the string slice s ends with suffix slice s.
|
||||||
|
func HasStringsSuffix(s, suffix []string) bool {
|
||||||
|
return len(s) >= len(suffix) && compareStringSlices(s[len(s)-len(suffix):], suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareStringSlices(a, b []string) bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ThemeSet checks whether a theme is in use or not.
|
// ThemeSet checks whether a theme is in use or not.
|
||||||
func (p *PathSpec) ThemeSet() bool {
|
func (p *PathSpec) ThemeSet() bool {
|
||||||
return p.theme != ""
|
return p.theme != ""
|
||||||
|
|
|
@ -64,6 +64,45 @@ func TestFirstUpper(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHasStringsPrefix(t *testing.T) {
|
||||||
|
for i, this := range []struct {
|
||||||
|
s []string
|
||||||
|
prefix []string
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{[]string{"a"}, []string{"a"}, true},
|
||||||
|
{[]string{}, []string{}, true},
|
||||||
|
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
|
||||||
|
{[]string{"d", "a", "b", "c"}, []string{"a", "b"}, false},
|
||||||
|
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, true},
|
||||||
|
{[]string{"abra", "ca"}, []string{"abra", "ca", "dabra"}, false},
|
||||||
|
} {
|
||||||
|
result := HasStringsPrefix(this.s, this.prefix)
|
||||||
|
if result != this.expect {
|
||||||
|
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasStringsSuffix(t *testing.T) {
|
||||||
|
for i, this := range []struct {
|
||||||
|
s []string
|
||||||
|
suffix []string
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{[]string{"a"}, []string{"a"}, true},
|
||||||
|
{[]string{}, []string{}, true},
|
||||||
|
{[]string{"a", "b", "c"}, []string{"b", "c"}, true},
|
||||||
|
{[]string{"abra", "ca", "dabra"}, []string{"abra", "ca"}, false},
|
||||||
|
{[]string{"abra", "ca", "dabra"}, []string{"ca", "dabra"}, true},
|
||||||
|
} {
|
||||||
|
result := HasStringsSuffix(this.s, this.suffix)
|
||||||
|
if result != this.expect {
|
||||||
|
t.Fatalf("[%d] got %t but expected %t", i, result, this.expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var containsTestText = (`На берегу пустынных волн
|
var containsTestText = (`На берегу пустынных волн
|
||||||
Стоял он, дум великих полн,
|
Стоял он, дум великих полн,
|
||||||
И вдаль глядел. Пред ним широко
|
И вдаль глядел. Пред ним широко
|
||||||
|
|
|
@ -186,7 +186,7 @@ func pageToPermalinkSection(p *Page, _ string) (string, error) {
|
||||||
func pageToPermalinkSections(p *Page, _ string) (string, error) {
|
func pageToPermalinkSections(p *Page, _ string) (string, error) {
|
||||||
// TODO(bep) we have some superflous URLize in this file, but let's
|
// TODO(bep) we have some superflous URLize in this file, but let's
|
||||||
// deal with that later.
|
// deal with that later.
|
||||||
return path.Join(p.current().sections...), nil
|
return path.Join(p.CurrentSection().sections...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
|
||||||
radix "github.com/hashicorp/go-immutable-radix"
|
radix "github.com/hashicorp/go-immutable-radix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,10 +44,9 @@ func (p *Page) Parent() *Page {
|
||||||
return p.parent
|
return p.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// current returns the page's current section.
|
// CurrentSection returns the page's current section or the page itself if home or a section.
|
||||||
// Note that this will return nil for pages that is not regular, home or section pages.
|
// Note that this will return nil for pages that is not regular, home or section pages.
|
||||||
// Note that for paginated sections and home pages, this will return the original page pointer.
|
func (p *Page) CurrentSection() *Page {
|
||||||
func (p *Page) current() *Page {
|
|
||||||
v := p
|
v := p
|
||||||
if v.origOnCopy != nil {
|
if v.origOnCopy != nil {
|
||||||
v = v.origOnCopy
|
v = v.origOnCopy
|
||||||
|
@ -65,20 +66,59 @@ func (p *Page) InSection(other interface{}) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if po, ok := other.(*PageOutput); ok {
|
pp, err := unwrapPage(other)
|
||||||
other = po.Page
|
if err != nil {
|
||||||
}
|
return false, err
|
||||||
|
|
||||||
pp, ok := other.(*Page)
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("%T not supported in InSection", other)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pp == nil {
|
if pp == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return pp.current() == p.current(), nil
|
return pp.CurrentSection() == p.CurrentSection(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDescendant returns whether the current page is a descendant of the given page.
|
||||||
|
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
|
||||||
|
func (p *Page) IsDescendant(other interface{}) (bool, error) {
|
||||||
|
pp, err := unwrapPage(other)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp.Kind == KindPage && len(p.sections) == len(pp.sections) {
|
||||||
|
// A regular page is never its section's descendant.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return helpers.HasStringsPrefix(p.sections, pp.sections), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAncestor returns whether the current page is an ancestor of the given page.
|
||||||
|
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
|
||||||
|
func (p *Page) IsAncestor(other interface{}) (bool, error) {
|
||||||
|
pp, err := unwrapPage(other)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Kind == KindPage && len(p.sections) == len(pp.sections) {
|
||||||
|
// A regular page is never its section's ancestor.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpers.HasStringsPrefix(pp.sections, p.sections), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrapPage(in interface{}) (*Page, error) {
|
||||||
|
if po, ok := in.(*PageOutput); ok {
|
||||||
|
in = po.Page
|
||||||
|
}
|
||||||
|
|
||||||
|
pp, ok := in.(*Page)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%T not supported", in)
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sections returns this section's subsections, if any.
|
// Sections returns this section's subsections, if any.
|
||||||
|
|
|
@ -166,7 +166,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
|
||||||
home := p.Parent()
|
home := p.Parent()
|
||||||
assert.True(home.IsHome())
|
assert.True(home.IsHome())
|
||||||
assert.Len(p.Sections(), 0)
|
assert.Len(p.Sections(), 0)
|
||||||
assert.Equal(home, home.current())
|
assert.Equal(home, home.CurrentSection())
|
||||||
active, err := home.InSection(home)
|
active, err := home.InSection(home)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.True(active)
|
assert.True(active)
|
||||||
|
@ -187,7 +187,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
|
||||||
assert.Len(p.Sections(), 1)
|
assert.Len(p.Sections(), 1)
|
||||||
|
|
||||||
for _, child := range p.Pages {
|
for _, child := range p.Pages {
|
||||||
assert.Equal(p, child.current())
|
assert.Equal(p, child.CurrentSection())
|
||||||
active, err := child.InSection(p)
|
active, err := child.InSection(p)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.True(active)
|
assert.True(active)
|
||||||
|
@ -197,9 +197,23 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
|
||||||
active, err = p.InSection(p.s.getPage(KindHome))
|
active, err = p.InSection(p.s.getPage(KindHome))
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.False(active)
|
assert.False(active)
|
||||||
|
|
||||||
|
isAncestor, err := p.IsAncestor(child)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.True(isAncestor)
|
||||||
|
isAncestor, err = child.IsAncestor(p)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.False(isAncestor)
|
||||||
|
|
||||||
|
isDescendant, err := p.IsDescendant(child)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.False(isDescendant)
|
||||||
|
isDescendant, err = child.IsDescendant(p)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.True(isDescendant)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(p, p.current())
|
assert.Equal(p, p.CurrentSection())
|
||||||
|
|
||||||
}},
|
}},
|
||||||
{"l1,l2_2", func(p *Page) {
|
{"l1,l2_2", func(p *Page) {
|
||||||
|
@ -214,6 +228,22 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
|
||||||
assert.Len(p.Pages, 2)
|
assert.Len(p.Pages, 2)
|
||||||
assert.Equal("T2_-1", p.Parent().Title)
|
assert.Equal("T2_-1", p.Parent().Title)
|
||||||
assert.Len(p.Sections(), 0)
|
assert.Len(p.Sections(), 0)
|
||||||
|
|
||||||
|
l1 := p.s.getPage(KindSection, "l1")
|
||||||
|
isDescendant, err := l1.IsDescendant(p)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.False(isDescendant)
|
||||||
|
isDescendant, err = p.IsDescendant(l1)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.True(isDescendant)
|
||||||
|
|
||||||
|
isAncestor, err := l1.IsAncestor(p)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.True(isAncestor)
|
||||||
|
isAncestor, err = p.IsAncestor(l1)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.False(isAncestor)
|
||||||
|
|
||||||
}},
|
}},
|
||||||
{"perm a,link", func(p *Page) {
|
{"perm a,link", func(p *Page) {
|
||||||
assert.Equal("T9_-1", p.Title)
|
assert.Equal("T9_-1", p.Title)
|
||||||
|
|
Loading…
Reference in a new issue