shithub: hugo

Download patch

ref: a1d260b41a6673adef679ec4e262c5f390432cf5
parent: dd9b1baab0cb860a3eb32fd9043bac18cab3f9f0
author: Bjørn Erik Pedersen <[email protected]>
date: Sun Jul 2 16:14:06 EDT 2017

hugolib: Extend the sections API

This commit adds some section related methods that have been asked for:

* .CurrentSection
* .IsDescendant
* .IsAncestor

Fixes #3591

--- a/helpers/general.go
+++ b/helpers/general.go
@@ -194,6 +194,38 @@
 	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 != ""
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -64,6 +64,45 @@
 	}
 }
 
+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 = (`На берегу пустынных волн
 Стоял он, дум великих полн,
 И вдаль глядел. Пред ним широко
--- a/hugolib/permalinks.go
+++ b/hugolib/permalinks.go
@@ -186,7 +186,7 @@
 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() {
--- a/hugolib/site_sections.go
+++ b/hugolib/site_sections.go
@@ -19,6 +19,8 @@
 	"strconv"
 	"strings"
 
+	"github.com/gohugoio/hugo/helpers"
+
 	radix "github.com/hashicorp/go-immutable-radix"
 )
 
@@ -42,10 +44,9 @@
 	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 @@
 		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.
--- a/hugolib/site_sections_test.go
+++ b/hugolib/site_sections_test.go
@@ -166,7 +166,7 @@
 			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 @@
 			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 @@
 				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 @@
 			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)