From 734b6508a12b29444ec78fc07d3f3805cf06ea3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 31 Oct 2016 10:23:01 +0100 Subject: [PATCH] node to page: Handle home With refactored paginator handling. Updates #2297 --- hugolib/node_as_page_test.go | 14 ++++++ hugolib/page.go | 94 ++++++++++++++++++++++++++++++++++++ hugolib/pagination.go | 17 +++---- hugolib/site.go | 65 ++++++++++++------------- 4 files changed, 145 insertions(+), 45 deletions(-) diff --git a/hugolib/node_as_page_test.go b/hugolib/node_as_page_test.go index 0bbc99fd8..722e5990c 100644 --- a/hugolib/node_as_page_test.go +++ b/hugolib/node_as_page_test.go @@ -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 Content!", "# 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") + } diff --git a/hugolib/page.go b/hugolib/page.go index 4c4b0d29e..87f50fc91 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -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 +} diff --git a/hugolib/pagination.go b/hugolib/pagination.go index 81f3e0cda..65790d6ee 100644 --- a/hugolib/pagination.go +++ b/hugolib/pagination.go @@ -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...) diff --git a/hugolib/site.go b/hugolib/site.go index 55749aeb5..7d7ac438b 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -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 + } + } } }