node to page: Handle home

With refactored paginator handling.

Updates #2297
This commit is contained in:
Bjørn Erik Pedersen 2016-10-31 10:23:01 +01:00
parent e371ac0b6f
commit 734b6508a1
4 changed files with 145 additions and 45 deletions

View file

@ -18,6 +18,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -54,6 +55,9 @@ Home **Content!**
Index Title: {{ .Title }} Index Title: {{ .Title }}
Index Content: {{ .Content }} Index Content: {{ .Content }}
# Pages: {{ len .Data.Pages }} # Pages: {{ len .Data.Pages }}
{{ range .Paginator.Pages }}
Pag: {{ .Title }}
{{ end }}
`) `)
writeSource(t, filepath.Join("layouts", "_default", "single.html"), ` writeSource(t, filepath.Join("layouts", "_default", "single.html"), `
@ -70,6 +74,8 @@ Content Page %d
`, i, i)) `, i, i))
} }
viper.Set("paginate", 3)
s := newSiteDefaultLang() s := newSiteDefaultLang()
if err := buildAndRenderSite(s); err != nil { if err := buildAndRenderSite(s); err != nil {
@ -80,6 +86,7 @@ Content Page %d
"Index Title: Home Sweet Home!", "Index Title: Home Sweet Home!",
"Home <strong>Content!</strong>", "Home <strong>Content!</strong>",
"# Pages: 10") "# Pages: 10")
assertFileContent(t, filepath.Join("public", "regular1", "index.html"), false, "Single Title: Page 1", "Content Page 1") assertFileContent(t, filepath.Join("public", "regular1", "index.html"), false, "Single Title: Page 1", "Content Page 1")
h := s.owner h := s.owner
@ -100,4 +107,11 @@ Content Page %d
require.False(t, first.IsNode()) require.False(t, first.IsNode())
require.True(t, first.IsPage()) 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")
} }

View file

@ -1173,3 +1173,97 @@ func (p *Page) TargetPath() (outfile string) {
return p.addLangFilepathPrefix(filepath.Join(strings.ToLower( return p.addLangFilepathPrefix(filepath.Join(strings.ToLower(
p.Site.pathSpec.MakePath(p.Source.Dir())), strings.TrimSpace(outfile))) 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
}

View file

@ -260,7 +260,9 @@ func splitPageGroups(pageGroups PagesGroup, size int) []paginatedElement {
// Paginator gets this Node's paginator if it's already created. // 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"]. // If it's not, one will be created with all pages in Data["Pages"].
func (n *Node) Paginator(options ...interface{}) (*Pager, error) { 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...) pagerSize, err := resolvePagerSize(options...)
if err != nil { if err != nil {
@ -297,20 +299,13 @@ func (n *Node) Paginator(options ...interface{}) (*Pager, error) {
return n.paginator, nil 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. // Paginate gets this Node's paginator if it's already created.
// If it's not, one will be created with the qiven sequence. // 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. // 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) { 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...) pagerSize, err := resolvePagerSize(options...)

View file

@ -848,9 +848,15 @@ func (s *Site) render() (err error) {
return return
} }
s.timerStep("render and write lists") s.timerStep("render and write lists")
if err = s.preparePages(); err != nil {
return
}
if err = s.renderPages(); err != nil { if err = s.renderPages(); err != nil {
return return
} }
s.timerStep("render and write pages") s.timerStep("render and write pages")
if err = s.renderHomePage(false); err != nil { if err = s.renderHomePage(false); err != nil {
return return
@ -1620,6 +1626,25 @@ func (s *Site) renderAliases() error {
return nil 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. // renderPages renders pages each corresponding to a markdown file.
func (s *Site) renderPages() error { func (s *Site) renderPages() error {
@ -1631,26 +1656,6 @@ func (s *Site) renderPages() error {
procs := getGoMaxProcs() 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{} wg := &sync.WaitGroup{}
for i := 0; i < procs*4; i++ { 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) { func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
for p := range pages { for p := range pages {
// TODO(bep) np paginator if err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...); err != nil {
s.preparePage(p)
err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...)
if err != nil {
results <- err results <- err
} }
}
}
func (s *Site) preparePage(p *Page) { if p.NodeType.IsNode() {
// TODO(bep) np the order of it all if err := p.renderPaginator(s); err != nil {
switch p.NodeType { results <- err
case NodePage: }
case NodeHome: }
p.Data = make(map[string]interface{})
// TODO(bep) np cache the below
p.Data["Pages"] = s.owner.findPagesByNodeType(NodePage)
} }
} }