shithub: hugo

Download patch

ref: 734b6508a12b29444ec78fc07d3f3805cf06ea3c
parent: e371ac0b6f56bdffe92b9e74dae571a003123912
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Oct 31 06:23:01 EDT 2016

node to page: Handle home

With refactored paginator handling.

Updates #2297

--- a/hugolib/node_as_page_test.go
+++ b/hugolib/node_as_page_test.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 	"testing"
 
+	"github.com/spf13/viper"
 	"github.com/stretchr/testify/require"
 )
 
@@ -54,6 +55,9 @@
 Index Title: {{ .Title }}
 Index Content: {{ .Content }}
 # Pages: {{ len .Data.Pages }}
+{{ range .Paginator.Pages }}
+	Pag: {{ .Title }}
+{{ end }}
 `)
 
 	writeSource(t, filepath.Join("layouts", "_default", "single.html"), `
@@ -70,6 +74,8 @@
 `, i, i))
 	}
 
+	viper.Set("paginate", 3)
+
 	s := newSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
@@ -80,6 +86,7 @@
 		"Index Title: Home Sweet Home!",
 		"Home <strong>Content!</strong>",
 		"# Pages: 10")
+
 	assertFileContent(t, filepath.Join("public", "regular1", "index.html"), false, "Single Title: Page 1", "Content Page 1")
 
 	h := s.owner
@@ -99,5 +106,12 @@
 	require.False(t, first.IsHome())
 	require.False(t, first.IsNode())
 	require.True(t, first.IsPage())
+
+	first.Paginator()
+
+	// Check paginator
+	assertFileContent(t, filepath.Join("public", "page", "3", "index.html"), false,
+		"Pag: Page 6",
+		"Pag: Page 7")
 
 }
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -1173,3 +1173,97 @@
 	return p.addLangFilepathPrefix(filepath.Join(strings.ToLower(
 		p.Site.pathSpec.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
 }
+
+// Pre render prepare steps
+
+func (p *Page) prepareLayouts() error {
+	// TODO(bep): Check the IsRenderable logic.
+	if p.NodeType == NodePage {
+		var layouts []string
+		if !p.IsRenderable() {
+			self := "__" + p.TargetPath()
+			_, err := p.Site.owner.tmpl.GetClone().New(self).Parse(string(p.Content))
+			if err != nil {
+				return err
+			}
+			layouts = append(layouts, self)
+		} else {
+			layouts = append(layouts, p.layouts()...)
+			layouts = append(layouts, "_default/single.html")
+		}
+		p.layoutsCalculated = layouts
+	}
+	return nil
+}
+
+func (p *Page) prepareData() error {
+	switch p.NodeType {
+	case NodePage:
+	case NodeHome:
+		p.Data = make(map[string]interface{})
+		// TODO(bep) np cache the below
+		// TODO(bep) np
+		p.Data["Pages"] = p.Site.owner.findPagesByNodeType(NodePage)
+	}
+
+	return nil
+}
+
+// renderPaginator must be run after the owning Page has been rendered.
+// TODO(bep) np
+func (p *Page) renderPaginator(s *Site) error {
+	if p.paginator != nil {
+		paginatePath := helpers.Config().GetString("paginatePath")
+
+		{
+			// write alias for page 1
+			// TODO(bep) ml all of these n.addLang ... fix.
+			permaLink, _ := p.Permalink()
+			s.writeDestAlias(p.addLangPathPrefix(helpers.PaginateAliasPath("", 1)), permaLink, nil)
+		}
+
+		pagers := p.paginator.Pagers()
+
+		for i, pager := range pagers {
+			if i == 0 {
+				// already created
+				continue
+			}
+
+			pagerNode := p.copy()
+
+			pagerNode.paginator = pager
+			if pager.TotalPages() > 0 {
+				first, _ := pager.page(0)
+				pagerNode.Date = first.Date
+				pagerNode.Lastmod = first.Lastmod
+			}
+
+			pageNumber := i + 1
+			htmlBase := fmt.Sprintf("/%s/%d", paginatePath, pageNumber)
+			htmlBase = p.addLangPathPrefix(htmlBase)
+			if err := s.renderAndWritePage(pagerNode.Title,
+				filepath.FromSlash(htmlBase), pagerNode, p.layouts()...); err != nil {
+				return err
+			}
+
+		}
+	}
+	return nil
+}
+
+// Page constains some sync.Once which have a mutex, so we cannot just
+// copy the Page by value. So for the situations where we need a copy,
+// the paginators etc., we do it manually here.
+// TODO(bep) np do better
+func (p *Page) copy() *Page {
+	c := &Page{Node: Node{NodeType: p.NodeType}}
+	c.Title = p.Title
+	c.Data = p.Data
+	c.Date = p.Date
+	c.Lastmod = p.Lastmod
+	c.language = p.language
+	c.lang = p.lang
+	c.URLPath = p.URLPath
+	return c
+}
--- a/hugolib/pagination.go
+++ b/hugolib/pagination.go
@@ -260,7 +260,9 @@
 // Paginator gets this Node's paginator if it's already created.
 // If it's not, one will be created with all pages in Data["Pages"].
 func (n *Node) Paginator(options ...interface{}) (*Pager, error) {
-
+	if !n.NodeType.IsNode() {
+		return nil, errors.New("Paginators not supported for content pages.")
+	}
 	pagerSize, err := resolvePagerSize(options...)
 
 	if err != nil {
@@ -297,20 +299,13 @@
 	return n.paginator, nil
 }
 
-// Paginator on Page isn't supported, calling this yields an error.
-func (p *Page) Paginator(options ...interface{}) (*Pager, error) {
-	return nil, errors.New("Paginators not supported for content pages.")
-}
-
-// Paginate on Page isn't supported, calling this yields an error.
-func (p *Page) Paginate(seq interface{}, options ...interface{}) (*Pager, error) {
-	return nil, errors.New("Paginators not supported for content pages.")
-}
-
 // Paginate gets this Node's paginator if it's already created.
 // If it's not, one will be created with the qiven sequence.
 // Note that repeated calls will return the same result, even if the sequence is different.
 func (n *Node) Paginate(seq interface{}, options ...interface{}) (*Pager, error) {
+	if !n.NodeType.IsNode() {
+		return nil, errors.New("Paginators not supported for content pages.")
+	}
 
 	pagerSize, err := resolvePagerSize(options...)
 
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -848,9 +848,15 @@
 		return
 	}
 	s.timerStep("render and write lists")
+
+	if err = s.preparePages(); err != nil {
+		return
+	}
+
 	if err = s.renderPages(); err != nil {
 		return
 	}
+
 	s.timerStep("render and write pages")
 	if err = s.renderHomePage(false); err != nil {
 		return
@@ -1620,6 +1626,25 @@
 	return nil
 }
 
+func (s *Site) preparePages() error {
+	var errors []error
+
+	for _, p := range s.Pages {
+		if err := p.prepareLayouts(); err != nil {
+			errors = append(errors, err)
+		}
+		if err := p.prepareData(); err != nil {
+			errors = append(errors, err)
+		}
+	}
+
+	if len(errors) != 0 {
+		return fmt.Errorf("Prepare pages failed: %.100q…", errors)
+	}
+
+	return nil
+}
+
 // renderPages renders pages each corresponding to a markdown file.
 func (s *Site) renderPages() error {
 
@@ -1631,26 +1656,6 @@
 
 	procs := getGoMaxProcs()
 
-	// this cannot be fanned out to multiple Go routines
-	// See issue #1601
-	// TODO(bep): Check the IsRenderable logic.
-	for _, p := range s.Pages {
-		var layouts []string
-		if !p.IsRenderable() {
-			self := "__" + p.TargetPath()
-			_, err := s.owner.tmpl.GetClone().New(self).Parse(string(p.Content))
-			if err != nil {
-				results <- err
-				continue
-			}
-			layouts = append(layouts, self)
-		} else {
-			layouts = append(layouts, p.layouts()...)
-			layouts = append(layouts, "_default/single.html")
-		}
-		p.layoutsCalculated = layouts
-	}
-
 	wg := &sync.WaitGroup{}
 
 	for i := 0; i < procs*4; i++ {
@@ -1678,23 +1683,15 @@
 func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.WaitGroup) {
 	defer wg.Done()
 	for p := range pages {
-		// TODO(bep) np paginator
-		s.preparePage(p)
-		err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...)
-		if err != nil {
+		if err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...); err != nil {
 			results <- err
 		}
-	}
-}
 
-func (s *Site) preparePage(p *Page) {
-	// TODO(bep) np the order of it all
-	switch p.NodeType {
-	case NodePage:
-	case NodeHome:
-		p.Data = make(map[string]interface{})
-		// TODO(bep) np cache the below
-		p.Data["Pages"] = s.owner.findPagesByNodeType(NodePage)
+		if p.NodeType.IsNode() {
+			if err := p.renderPaginator(s); err != nil {
+				results <- err
+			}
+		}
 	}
 }