node to page: Handle home
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 31 Oct 2016 09:23:01 +0000 (10:23 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 22 Nov 2016 08:57:03 +0000 (09:57 +0100)
With refactored paginator handling.

Updates #2297

hugolib/node_as_page_test.go
hugolib/page.go
hugolib/pagination.go
hugolib/site.go

index 0bbc99fd811016ecab8356998a71644917112cf9..722e5990cf3cec9a486ce293e9f69a68a47ebe15 100644 (file)
@@ -18,6 +18,7 @@ import (
        "path/filepath"
        "testing"
 
+       "github.com/spf13/viper"
        "github.com/stretchr/testify/require"
 )
 
@@ -54,6 +55,9 @@ Home **Content!**
 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 @@ Content Page %d
 `, i, i))
        }
 
+       viper.Set("paginate", 3)
+
        s := newSiteDefaultLang()
 
        if err := buildAndRenderSite(s); err != nil {
@@ -80,6 +86,7 @@ Content Page %d
                "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
@@ -100,4 +107,11 @@ Content Page %d
        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")
+
 }
index 4c4b0d29e8ba45798df95efefd8ecdd8beab87db..87f50fc912787a3a8c1bdd90966c37436bec17cb 100644 (file)
@@ -1173,3 +1173,97 @@ func (p *Page) TargetPath() (outfile string) {
        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
+}
index 81f3e0cda39c47c14fb348f6e35f2066bacaa066..65790d6ee7375c4de012754db47b9f3958c881f5 100644 (file)
@@ -260,7 +260,9 @@ func splitPageGroups(pageGroups PagesGroup, size int) []paginatedElement {
 // 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 @@ func (n *Node) Paginator(options ...interface{}) (*Pager, error) {
        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...)
 
index 55749aeb5cf509b3410e44602264c6dd0961b865..7d7ac438bb7f2b21c7259bbb895c2c91a7bfc86a 100644 (file)
@@ -848,9 +848,15 @@ func (s *Site) render() (err error) {
                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 @@ func (s *Site) renderAliases() error {
        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 @@ func (s *Site) renderPages() error {
 
        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 (s *Site) renderPages() error {
 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
+                       }
+               }
        }
 }