hugolib: Use the new layout logic in Page
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 6 Mar 2017 12:18:33 +0000 (13:18 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 27 Mar 2017 13:43:56 +0000 (15:43 +0200)
hugolib/page.go
hugolib/page_test.go
hugolib/site.go
output/layout.go
output/layout_test.go

index 554bd30cd6d9f0537d2bd745fc51099a45465463..fb842ca37a8dc84dbea4361cc632977e3299df5f 100644 (file)
@@ -36,6 +36,8 @@ import (
        "time"
        "unicode/utf8"
 
+       "github.com/spf13/hugo/output"
+
        "github.com/spf13/cast"
        bp "github.com/spf13/hugo/bufferpool"
        "github.com/spf13/hugo/media"
@@ -204,6 +206,43 @@ type Page struct {
        // The media types this page will be rendered to.
        // TODO(bep) probably wrap this to add additional information like template evaluation?
        mediaTypes media.Types
+
+       // Used to pick the correct template(s)
+       layoutIdentifier pageLayoutIdentifier
+}
+
+// Implements layout.LayoutIdentifier
+type pageLayoutIdentifier struct {
+       *Page
+}
+
+// PageKind returns the page's kind.
+func (p pageLayoutIdentifier) PageKind() string {
+       return p.Kind
+}
+
+// PageLayout returns the page's layout, if set.
+func (p pageLayoutIdentifier) PageLayout() string {
+       return p.Layout
+}
+
+// PageType returns the page's type, if set.
+func (p pageLayoutIdentifier) PageType() string {
+       return p.Type()
+}
+
+// PageType returns the page's section in layout terms.
+// This will be empty for regular pages, the section for section pages,
+// and the singular term for taxonomy and taxonomy terms pages.
+func (p pageLayoutIdentifier) PageSection() string {
+       switch p.Kind {
+       case KindSection:
+               return p.sections[0]
+       case KindTaxonomy, KindTaxonomyTerm:
+               return p.s.taxonomiesPluralSingular[p.sections[0]]
+       default:
+               return ""
+       }
 }
 
 // pageInit lazy initializes different parts of the page. It is extracted
@@ -595,7 +634,7 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
 
 func (s *Site) newPage(filename string) *Page {
        sp := source.NewSourceSpec(s.Cfg, s.Fs)
-       page := Page{
+       p := &Page{
                pageInit:    &pageInit{},
                Kind:        kindFromFilename(filename),
                contentType: "",
@@ -607,9 +646,10 @@ func (s *Site) newPage(filename string) *Page {
                Site:         &s.Info,
                s:            s,
        }
+       p.layoutIdentifier = pageLayoutIdentifier{p}
 
-       s.Log.DEBUG.Println("Reading from", page.File.Path())
-       return &page
+       s.Log.DEBUG.Println("Reading from", p.File.Path())
+       return p
 }
 
 func (p *Page) IsRenderable() bool {
@@ -635,44 +675,23 @@ func (p *Page) Section() string {
        return p.Source.Section()
 }
 
-func (p *Page) layouts(l ...string) []string {
+func (p *Page) layouts(layouts ...string) []string {
        if len(p.layoutsCalculated) > 0 {
                return p.layoutsCalculated
        }
 
-       switch p.Kind {
-       case KindHome:
-               return p.s.appendThemeTemplates([]string{"index.html", "_default/list.html"})
-       case KindSection:
-               section := p.sections[0]
-               return p.s.appendThemeTemplates([]string{"section/" + section + ".html", section + "/list.html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"})
-       case KindTaxonomy:
-               singular := p.s.taxonomiesPluralSingular[p.sections[0]]
-               return p.s.appendThemeTemplates([]string{"taxonomy/" + singular + ".html", "indexes/" + singular + ".html", "_default/taxonomy.html", "_default/list.html"})
-       case KindTaxonomyTerm:
-               singular := p.s.taxonomiesPluralSingular[p.sections[0]]
-               return p.s.appendThemeTemplates([]string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"})
-       }
-
-       // Regular Page handled below
-
-       if p.Layout != "" {
-               return layouts(p.Type(), p.Layout)
+       layoutOverride := ""
+       if len(layouts) > 0 {
+               layoutOverride = layouts[0]
        }
 
-       layout := ""
-       if len(l) == 0 {
-               layout = "single"
-       } else {
-               layout = l[0]
-       }
-
-       return layouts(p.Type(), layout)
+       return p.s.layoutHandler.For(p.layoutIdentifier, layoutOverride, output.HTMLType)
 }
 
 // TODO(bep) consolidate and test these KindHome switches (see other layouts methods)s
 // rssLayouts returns RSS layouts to use for the RSS version of this page, nil
 // if no RSS should be rendered.
+// TODO(bep) output
 func (p *Page) rssLayouts() []string {
        switch p.Kind {
        case KindHome:
@@ -693,26 +712,6 @@ func (p *Page) rssLayouts() []string {
        return nil
 }
 
-func layouts(types string, layout string) (layouts []string) {
-       t := strings.Split(types, "/")
-
-       // Add type/layout.html
-       for i := range t {
-               search := t[:len(t)-i]
-               layouts = append(layouts, fmt.Sprintf("%s/%s.html", strings.ToLower(path.Join(search...)), layout))
-       }
-
-       // Add _default/layout.html
-       layouts = append(layouts, fmt.Sprintf("_default/%s.html", layout))
-
-       // Add theme/type/layout.html & theme/_default/layout.html
-       for _, l := range layouts {
-               layouts = append(layouts, "theme/"+l)
-       }
-
-       return
-}
-
 func (s *Site) NewPageFrom(buf io.Reader, name string) (*Page, error) {
        p, err := s.NewPage(name)
        if err != nil {
@@ -1360,14 +1359,8 @@ func (p *Page) Menus() PageMenus {
        return p.pageMenus
 }
 
-func (p *Page) Render(layout ...string) template.HTML {
-       var l []string
-
-       if len(layout) > 0 {
-               l = layouts(p.Type(), layout[0])
-       } else {
-               l = p.layouts()
-       }
+func (p *Page) Render(layouts ...string) template.HTML {
+       l := p.layouts(layouts...)
 
        return p.s.Tmpl.ExecuteTemplateToHTML(p, l...)
 }
index ab60876475abf01a8ff41099f87a222ed59b4e3c..82d6cbc8f6590aabda570027e9715c20171ee320 100644 (file)
@@ -1412,6 +1412,7 @@ func TestPageSimpleMethods(t *testing.T) {
 }
 
 func TestIndexPageSimpleMethods(t *testing.T) {
+       s := newTestSite(t)
        t.Parallel()
        for i, this := range []struct {
                assertFunc func(n *Page) bool
@@ -1422,9 +1423,10 @@ func TestIndexPageSimpleMethods(t *testing.T) {
                {func(n *Page) bool { return n.Scratch() != nil }},
                {func(n *Page) bool { return n.Hugo() != nil }},
                {func(n *Page) bool { return n.Now().Unix() == time.Now().Unix() }},
+               {func(n *Page) bool { return n.layoutIdentifier.Kind == KindHome }},
        } {
 
-               n := &Page{pageInit: &pageInit{}, Kind: KindHome}
+               n := s.newHomePage()
 
                n.RSSLink = "rssLink"
 
index 4c8aac01895c93bcd134163714154ee332657660..ee20ecc193fb19bb8ae1af8a14f12e6374ce5de5 100644 (file)
@@ -37,6 +37,7 @@ import (
        bp "github.com/spf13/hugo/bufferpool"
        "github.com/spf13/hugo/deps"
        "github.com/spf13/hugo/helpers"
+       "github.com/spf13/hugo/output"
        "github.com/spf13/hugo/parser"
        "github.com/spf13/hugo/source"
        "github.com/spf13/hugo/tpl"
@@ -100,6 +101,8 @@ type Site struct {
        // This is not a pointer by design.
        w siteWriter
 
+       layoutHandler *output.LayoutHandler
+
        draftCount   int
        futureCount  int
        expiredCount int
@@ -121,7 +124,7 @@ func (s *Site) isEnabled(kind string) bool {
 
 // reset returns a new Site prepared for rebuild.
 func (s *Site) reset() *Site {
-       return &Site{Deps: s.Deps, disabledKinds: s.disabledKinds, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
+       return &Site{Deps: s.Deps, layoutHandler: &output.LayoutHandler{}, disabledKinds: s.disabledKinds, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
 }
 
 // newSite creates a new site with the given configuration.
@@ -137,7 +140,7 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
                disabledKinds[disabled] = true
        }
 
-       s := &Site{PageCollections: c, Language: cfg.Language, disabledKinds: disabledKinds}
+       s := &Site{PageCollections: c, layoutHandler: &output.LayoutHandler{}, Language: cfg.Language, disabledKinds: disabledKinds}
 
        s.Info = newSiteInfo(siteBuilderCfg{s: s, pageCollections: c, language: s.Language})
 
@@ -2038,13 +2041,16 @@ func getGoMaxProcs() int {
 }
 
 func (s *Site) newNodePage(typ string) *Page {
-       return &Page{
+       p := &Page{
                language: s.Language,
                pageInit: &pageInit{},
                Kind:     typ,
                Data:     make(map[string]interface{}),
                Site:     &s.Info,
                s:        s}
+       p.layoutIdentifier = pageLayoutIdentifier{p}
+       return p
+
 }
 
 func (s *Site) newHomePage() *Page {
index 7646caf482d532f235436e36277796fdc70c1119..ba246237af3776e7fc73ded41ed14d65b489c67a 100644 (file)
@@ -19,6 +19,7 @@ import (
        "strings"
 )
 
+// LayoutIdentifier is used to pick the correct layout for a piece of content.
 type LayoutIdentifier interface {
        PageType() string
        PageSection() string // TODO(bep) name
@@ -28,7 +29,7 @@ type LayoutIdentifier interface {
 
 // Layout calculates the layout template to use to render a given output type.
 // TODO(bep) output improve names
-type Layout struct {
+type LayoutHandler struct {
 }
 
 // TODO(bep) output theme layouts
@@ -39,9 +40,15 @@ var (
        layoutTaxonomyTerm = "taxonomy/SECTION.terms.html _default/terms.html indexes/indexes.html"
 )
 
-func (l *Layout) For(id LayoutIdentifier, tp Type) []string {
+func (l *LayoutHandler) For(id LayoutIdentifier, layoutOverride string, tp Type) []string {
        var layouts []string
 
+       layout := id.PageLayout()
+
+       if layoutOverride != "" {
+               layout = layoutOverride
+       }
+
        switch id.PageKind() {
        // TODO(bep) move the Kind constants some common place.
        case "home":
@@ -53,7 +60,7 @@ func (l *Layout) For(id LayoutIdentifier, tp Type) []string {
        case "taxonomyTerm":
                layouts = strings.Fields(strings.Replace(layoutTaxonomyTerm, "SECTION", id.PageSection(), -1))
        case "page":
-               layouts = regularPageLayouts(id.PageType(), id.PageLayout())
+               layouts = regularPageLayouts(id.PageType(), layout)
        }
 
        for _, l := range layouts {
index 897b451c9bf234ac8daa53c517d57109b404f9d3..5b95e01d8b491e3f194611277305a4077f146179 100644 (file)
@@ -43,23 +43,25 @@ func (l testLayoutIdentifier) PageSection() string {
 }
 
 func TestLayout(t *testing.T) {
-       l := &Layout{}
+       l := &LayoutHandler{}
 
        for _, this := range []struct {
-               li     testLayoutIdentifier
-               tp     Type
-               expect []string
+               li             testLayoutIdentifier
+               layoutOverride string
+               tp             Type
+               expect         []string
        }{
-               {testLayoutIdentifier{"home", "", "", ""}, HTMLType, []string{"index.html", "_default/list.html", "theme/index.html", "theme/_default/list.html"}},
-               {testLayoutIdentifier{"section", "sect1", "", ""}, HTMLType, []string{"section/sect1.html", "sect1/list.html"}},
-               {testLayoutIdentifier{"taxonomy", "tag", "", ""}, HTMLType, []string{"taxonomy/tag.html", "indexes/tag.html"}},
-               {testLayoutIdentifier{"taxonomyTerm", "categories", "", ""}, HTMLType, []string{"taxonomy/categories.terms.html", "_default/terms.html"}},
-               {testLayoutIdentifier{"page", "", "", ""}, HTMLType, []string{"_default/single.html", "theme/_default/single.html"}},
-               {testLayoutIdentifier{"page", "", "mylayout", ""}, HTMLType, []string{"_default/mylayout.html"}},
-               {testLayoutIdentifier{"page", "", "mylayout", "myttype"}, HTMLType, []string{"myttype/mylayout.html", "_default/mylayout.html"}},
-               {testLayoutIdentifier{"page", "", "mylayout", "myttype/mysubtype"}, HTMLType, []string{"myttype/mysubtype/mylayout.html", "myttype/mylayout.html", "_default/mylayout.html"}},
+               {testLayoutIdentifier{"home", "", "", ""}, "", HTMLType, []string{"index.html", "_default/list.html", "theme/index.html", "theme/_default/list.html"}},
+               {testLayoutIdentifier{"section", "sect1", "", ""}, "", HTMLType, []string{"section/sect1.html", "sect1/list.html"}},
+               {testLayoutIdentifier{"taxonomy", "tag", "", ""}, "", HTMLType, []string{"taxonomy/tag.html", "indexes/tag.html"}},
+               {testLayoutIdentifier{"taxonomyTerm", "categories", "", ""}, "", HTMLType, []string{"taxonomy/categories.terms.html", "_default/terms.html"}},
+               {testLayoutIdentifier{"page", "", "", ""}, "", HTMLType, []string{"_default/single.html", "theme/_default/single.html"}},
+               {testLayoutIdentifier{"page", "", "mylayout", ""}, "", HTMLType, []string{"_default/mylayout.html"}},
+               {testLayoutIdentifier{"page", "", "mylayout", "myttype"}, "", HTMLType, []string{"myttype/mylayout.html", "_default/mylayout.html"}},
+               {testLayoutIdentifier{"page", "", "mylayout", "myttype/mysubtype"}, "", HTMLType, []string{"myttype/mysubtype/mylayout.html", "myttype/mylayout.html", "_default/mylayout.html"}},
+               {testLayoutIdentifier{"page", "", "mylayout", "myttype"}, "myotherlayout", HTMLType, []string{"myttype/myotherlayout.html", "_default/myotherlayout.html"}},
        } {
-               layouts := l.For(this.li, this.tp)
+               layouts := l.For(this.li, this.layoutOverride, this.tp)
                require.NotNil(t, layouts)
                require.True(t, len(layouts) >= len(this.expect))
                // Not checking the complete list for now ...