hugolib: Extend the sections API - hugo - [fork] hugo port for 9front
 (HTM) git clone git@git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit a1d260b41a6673adef679ec4e262c5f390432cf5
 (DIR) parent dd9b1baab0cb860a3eb32fd9043bac18cab3f9f0
 (HTM) Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Sun,  2 Jul 2017 20:14:06 +0200
       
       hugolib: Extend the sections API
       
       This commit adds some section related methods that have been asked for:
       
       * .CurrentSection
       * .IsDescendant
       * .IsAncestor
       
       Fixes #3591
       
       Diffstat:
         M helpers/general.go                  |      32 +++++++++++++++++++++++++++++++
         M helpers/general_test.go             |      39 +++++++++++++++++++++++++++++++
         M hugolib/permalinks.go               |       2 +-
         M hugolib/site_sections.go            |      60 ++++++++++++++++++++++++++------
         M hugolib/site_sections_test.go       |      36 ++++++++++++++++++++++++++++---
       
       5 files changed, 155 insertions(+), 14 deletions(-)
       ---
 (DIR) diff --git a/helpers/general.go b/helpers/general.go
       @@ -194,6 +194,38 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
                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.
        func (p *PathSpec) ThemeSet() bool {
                return p.theme != ""
 (DIR) diff --git a/helpers/general_test.go b/helpers/general_test.go
       @@ -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 = (`На берегу пустынных волн
        Стоял он, дум великих полн,
        И вдаль глядел. Пред ним широко
 (DIR) diff --git a/hugolib/permalinks.go b/hugolib/permalinks.go
       @@ -186,7 +186,7 @@ func pageToPermalinkSection(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
                // deal with that later.
       -        return path.Join(p.current().sections...), nil
       +        return path.Join(p.CurrentSection().sections...), nil
        }
        
        func init() {
 (DIR) diff --git a/hugolib/site_sections.go b/hugolib/site_sections.go
       @@ -19,6 +19,8 @@ import (
                "strconv"
                "strings"
        
       +        "github.com/gohugoio/hugo/helpers"
       +
                radix "github.com/hashicorp/go-immutable-radix"
        )
        
       @@ -42,10 +44,9 @@ func (p *Page) Parent() *Page {
                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 for paginated sections and home pages, this will return the original page pointer.
       -func (p *Page) current() *Page {
       +func (p *Page) CurrentSection() *Page {
                v := p
                if v.origOnCopy != nil {
                        v = v.origOnCopy
       @@ -65,20 +66,59 @@ func (p *Page) InSection(other interface{}) (bool, error) {
                        return false, nil
                }
        
       -        if po, ok := other.(*PageOutput); ok {
       -                other = po.Page
       +        pp, err := unwrapPage(other)
       +        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 {
       +                return false, nil
                }
        
       -        if pp == 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
       +}
        
       -        return pp.current() == p.current(), 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.
 (DIR) diff --git a/hugolib/site_sections_test.go b/hugolib/site_sections_test.go
       @@ -166,7 +166,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                home := p.Parent()
                                assert.True(home.IsHome())
                                assert.Len(p.Sections(), 0)
       -                        assert.Equal(home, home.current())
       +                        assert.Equal(home, home.CurrentSection())
                                active, err := home.InSection(home)
                                assert.NoError(err)
                                assert.True(active)
       @@ -187,7 +187,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                assert.Len(p.Sections(), 1)
        
                                for _, child := range p.Pages {
       -                                assert.Equal(p, child.current())
       +                                assert.Equal(p, child.CurrentSection())
                                        active, err := child.InSection(p)
                                        assert.NoError(err)
                                        assert.True(active)
       @@ -197,9 +197,23 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                        active, err = p.InSection(p.s.getPage(KindHome))
                                        assert.NoError(err)
                                        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) {
       @@ -214,6 +228,22 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                assert.Len(p.Pages, 2)
                                assert.Equal("T2_-1", p.Parent().Title)
                                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) {
                                assert.Equal("T9_-1", p.Title)