shithub: hugo

Download patch

ref: bde1bfd34a7b5e5959da6f59a497a190067534cb
parent: ec2d502b4f214b9505dcd57713d2236c606985e8
author: Bjørn Erik Pedersen <[email protected]>
date: Tue Nov 8 18:34:52 EST 2016

node to page: Handle aliases, 404, robots.txt, sitemap

Updates #2297

--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -37,12 +37,8 @@
 // Temporary feature flag to ease the refactoring of node vs page, see
 // https://github.com/spf13/hugo/issues/2297
 // TODO(bep) eventually remove
-var nodePageFeatureFlag bool
+var nodePageFeatureFlag bool = true
 
-func toggleNodePageFeatureFlag() {
-	nodePageFeatureFlag = !nodePageFeatureFlag
-}
-
 // HugoSites represents the sites to build. Each site represents a language.
 type HugoSites struct {
 	Sites []*Site
@@ -516,13 +512,15 @@
 
 // Move the new* methods after cleanup in site.go
 func (s *Site) newNodePage(typ NodeType) *Page {
-
-	return &Page{Node: Node{
-		NodeType: typ,
-		Data:     make(map[string]interface{}),
-		Site:     &s.Info,
-		language: s.Language,
-	}, site: s}
+	return &Page{
+		Node: Node{
+			Date:     s.Info.LastChange,
+			Lastmod:  s.Info.LastChange,
+			NodeType: typ,
+			Data:     make(map[string]interface{}),
+			Site:     &s.Info,
+			language: s.Language,
+		}, site: s}
 }
 
 func (s *Site) newHomePage() *Page {
--- a/hugolib/menu.go
+++ b/hugolib/menu.go
@@ -21,6 +21,8 @@
 	"github.com/spf13/cast"
 )
 
+// TODO(bep) np menu entries in section content etc.?
+
 // MenuEntry represents a menu item defined in either Page front matter
 // or in the site config.
 type MenuEntry struct {
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -477,10 +477,10 @@
 			&MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},
 	} {
 
-		n, _ := s.newTaxonomyNode(true, this.taxInfo, i)
+		p := s.newTaxonomyPage(this.taxInfo.plural, this.taxInfo.key)
 
-		isMenuCurrent := n.IsMenuCurrent(this.menu, this.menuItem)
-		hasMenuCurrent := n.HasMenuCurrent(this.menu, this.menuItem)
+		isMenuCurrent := p.IsMenuCurrent(this.menu, this.menuItem)
+		hasMenuCurrent := p.HasMenuCurrent(this.menu, this.menuItem)
 
 		if isMenuCurrent != this.isMenuCurrent {
 			t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)
--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -41,6 +41,12 @@
 	NodeSection
 	NodeTaxonomy
 	NodeTaxonomyTerms
+
+	// The following are (currently) temporary nodes,
+	// i.e. nodes we create just to render in isolation.
+	NodeSitemap
+	NodeRobotsTXT
+	Node404
 )
 
 func (p NodeType) String() string {
@@ -55,6 +61,12 @@
 		return "taxonomy list"
 	case NodeTaxonomyTerms:
 		return "taxonomy terms"
+	case NodeSitemap:
+		return "sitemap"
+	case NodeRobotsTXT:
+		return "robots.txt"
+	case Node404:
+		return "404 Not Found"
 	case NodeUnknown:
 		return "unknown"
 	default:
--- a/hugolib/node_as_page_test.go
+++ b/hugolib/node_as_page_test.go
@@ -34,9 +34,6 @@
 	//jww.SetStdoutThreshold(jww.LevelDebug)
 	jww.SetStdoutThreshold(jww.LevelFatal)
 
-	nodePageFeatureFlag = true
-	defer toggleNodePageFeatureFlag()
-
 	/* Will have to decide what to name the node content files, but:
 
 		Home page should have:
@@ -152,9 +149,6 @@
 	//jww.SetStdoutThreshold(jww.LevelDebug)
 	jww.SetStdoutThreshold(jww.LevelFatal)
 
-	nodePageFeatureFlag = true
-	defer toggleNodePageFeatureFlag()
-
 	testCommonResetState()
 
 	writeLayoutsForNodeAsPageTests(t)
@@ -219,9 +213,6 @@
 }
 
 func TestNodesAsPageMultilingual(t *testing.T) {
-
-	nodePageFeatureFlag = true
-	defer toggleNodePageFeatureFlag()
 
 	testCommonResetState()
 
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -1202,13 +1202,13 @@
 	// TODO(bep) np
 	switch p.NodeType {
 	case NodeHome:
-		return p.addLangFilepathPrefix("index.html")
+		return p.addLangFilepathPrefix("/")
 	case NodeSection:
-		return p.addLangFilepathPrefix(filepath.Join(p.sections[0], "index.html"))
+		return p.addLangFilepathPrefix(p.sections[0])
 	case NodeTaxonomy:
-		return p.addLangFilepathPrefix(filepath.Join(append(p.sections, "index.html")...))
+		return p.addLangFilepathPrefix(filepath.Join(p.sections...))
 	case NodeTaxonomyTerms:
-		return p.addLangFilepathPrefix(filepath.Join(append(p.sections, "index.html")...))
+		return p.addLangFilepathPrefix(filepath.Join(p.sections...))
 	}
 
 	// Always use URL if it's specified
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -838,36 +838,21 @@
 		return
 	}
 
-	if err = s.renderAliases(); err != nil {
-		return
-	}
-	s.timerStep("render and write aliases")
-	if err = s.renderTaxonomiesLists(false); err != nil {
-		return
-	}
-	s.timerStep("render and write taxonomies")
-	if err = s.renderListsOfTaxonomyTerms(false); err != nil {
-		return
-	}
-	s.timerStep("render & write taxonomy lists")
-	if err = s.renderSectionLists(false); err != nil {
-		return
-	}
-	s.timerStep("render and write lists")
-
 	if err = s.preparePages(); err != nil {
 		return
 	}
+	s.timerStep("prepare pages")
 
 	if err = s.renderPages(); err != nil {
 		return
 	}
-
 	s.timerStep("render and write pages")
-	if err = s.renderHomePage(false); err != nil {
+
+	if err = s.renderAliases(); err != nil {
 		return
 	}
-	s.timerStep("render and write homepage")
+	s.timerStep("render and write aliases")
+
 	if err = s.renderSitemap(); err != nil {
 		return
 	}
@@ -878,6 +863,11 @@
 	}
 	s.timerStep("render and write robots.txt")
 
+	if err = s.render404(); err != nil {
+		return
+	}
+	s.timerStep("render and write 404")
+
 	return
 }
 
@@ -1601,44 +1591,6 @@
 	return NodeSection
 }
 
-// renderAliases renders shell pages that simply have a redirect in the header.
-func (s *Site) renderAliases() error {
-	for _, p := range s.Pages {
-		if len(p.Aliases) == 0 {
-			continue
-		}
-
-		plink, err := p.Permalink()
-		if err != nil {
-			return err
-		}
-		for _, a := range p.Aliases {
-			if err := s.writeDestAlias(a, plink, p); err != nil {
-				return err
-			}
-		}
-	}
-
-	if s.owner.multilingual.enabled() {
-		mainLang := s.owner.multilingual.DefaultLang.Lang
-		if s.Info.defaultContentLanguageInSubdir {
-			mainLangURL := s.Info.pathSpec.AbsURL(mainLang, false)
-			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
-			if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
-				return err
-			}
-		} else {
-			mainLangURL := s.Info.pathSpec.AbsURL("", false)
-			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
-			if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
 func (s *Site) preparePages() error {
 	var errors []error
 
@@ -2085,21 +2037,8 @@
 		}
 	}
 
-	if viper.GetBool("disable404") {
-		return nil
-	}
-
-	node404 := s.newNode("404")
-	node404.Title = "404 Page not found"
-	node404.Data["Pages"] = s.Pages
-	s.setURLs(node404, "404.html")
-
-	nfLayouts := []string{"404.html"}
-	if nfErr := s.renderAndWritePage("404 page", "404.html", node404, s.appendThemeTemplates(nfLayouts)...); nfErr != nil {
-		return nfErr
-	}
-
 	return nil
+
 }
 
 func (s *Site) newHomeNode(prepare bool, counter int) *Node {
@@ -2115,80 +2054,6 @@
 	return n
 }
 
-func (s *Site) newPage() *Page {
-	page := &Page{}
-	page.language = s.Language
-	page.Date = s.Info.LastChange
-	page.Lastmod = s.Info.LastChange
-	page.Site = &s.Info
-	return page
-}
-
-func (s *Site) renderSitemap() error {
-	if viper.GetBool("disableSitemap") {
-		return nil
-	}
-
-	sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
-
-	n := s.newNode("sitemap")
-
-	// Prepend homepage to the list of pages
-	pages := make(Pages, 0)
-
-	page := s.newPage()
-	page.URLPath.URL = ""
-	page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
-	page.Sitemap.Priority = sitemapDefault.Priority
-
-	pages = append(pages, page)
-	pages = append(pages, s.Pages...)
-
-	n.Data["Pages"] = pages
-
-	for _, page := range pages {
-		if page.Sitemap.ChangeFreq == "" {
-			page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
-		}
-
-		if page.Sitemap.Priority == -1 {
-			page.Sitemap.Priority = sitemapDefault.Priority
-		}
-
-		if page.Sitemap.Filename == "" {
-			page.Sitemap.Filename = sitemapDefault.Filename
-		}
-	}
-
-	smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
-	addLanguagePrefix := n.Site.IsMultiLingual()
-	if err := s.renderAndWriteXML("sitemap", n.addLangPathPrefixIfFlagSet(page.Sitemap.Filename, addLanguagePrefix), n, s.appendThemeTemplates(smLayouts)...); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (s *Site) renderRobotsTXT() error {
-	if !viper.GetBool("enableRobotsTXT") {
-		return nil
-	}
-
-	n := s.newNode("robots")
-	n.Data["Pages"] = s.Pages
-
-	rLayouts := []string{"robots.txt", "_default/robots.txt", "_internal/_default/robots.txt"}
-	outBuffer := bp.GetBuffer()
-	defer bp.PutBuffer(outBuffer)
-	err := s.renderForLayouts("robots", n, outBuffer, s.appendThemeTemplates(rLayouts)...)
-
-	if err == nil {
-		err = s.writeDestFile("robots.txt", outBuffer)
-	}
-
-	return err
-}
-
 // Stats prints Hugo builds stats to the console.
 // This is what you see after a successful hugo build.
 func (s *Site) Stats() {
@@ -2223,15 +2088,19 @@
 		s.pathSpec.URLizeAndPrep(plink)).String()
 }
 
+// TODO(bep) np remove
 func (s *Site) newNode(nodeID string) *Node {
-	return s.nodeLookup(nodeID, 0, true)
+	return nil //s.nodeLookup(nodeID, 0, true)
 }
 
 func (s *Site) getNode(nodeID string) *Node {
-	return s.getOrAddNode(nodeID, false)
+	return nil //s.getOrAddNode(nodeID, false)
 }
 
 func (s *Site) getOrAddNode(nodeID string, add bool) *Node {
+	if true {
+		return nil
+	}
 	s.nodeCacheInit.Do(func() {
 		s.nodeCache = &nodeCache{m: make(map[string]*Node)}
 	})
@@ -2342,7 +2211,8 @@
 
 	var pageTarget target.Output
 
-	if p, ok := d.(*Page); ok && path.Ext(p.URLPath.URL) != "" {
+	// TODO(bep) np ugly urls vs frontmatter
+	if p, ok := d.(*Page); ok && p.IsPage() && path.Ext(p.URLPath.URL) != "" {
 		// user has explicitly set a URL with extension for this page
 		// make sure it sticks even if "ugly URLs" are turned off.
 		pageTarget = s.pageUglyTarget()
@@ -2361,7 +2231,7 @@
 	}
 
 	// For performance reasons we only inject the Hugo generator tag on the home page.
-	if n, ok := d.(*Node); ok && n.IsHome() {
+	if n, ok := d.(*Page); ok && n.IsHome() {
 		if !viper.GetBool("disableHugoGeneratorInject") {
 			transformLinks = append(transformLinks, transform.HugoGeneratorInject)
 		}
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -19,7 +19,9 @@
 	"path/filepath"
 	"sync"
 
+	bp "github.com/spf13/hugo/bufferpool"
 	"github.com/spf13/hugo/helpers"
+	"github.com/spf13/viper"
 
 	jww "github.com/spf13/jwalterweatherman"
 )
@@ -148,6 +150,128 @@
 
 	if err := s.renderAndWriteXML(rssNode.Title, rssNode.addLangFilepathPrefix(rssNode.URLPath.URL), rssNode, s.appendThemeTemplates(layouts)...); err != nil {
 		return err
+	}
+
+	return nil
+}
+
+func (s *Site) render404() error {
+	if viper.GetBool("disable404") {
+		return nil
+	}
+
+	p := s.newNodePage(Node404)
+	p.Title = "404 Page not found"
+	p.Data["Pages"] = s.Pages
+	s.setPageURLs(p, "404.html")
+
+	nfLayouts := []string{"404.html"}
+	if nfErr := s.renderAndWritePage("404 page", "404.html", p, s.appendThemeTemplates(nfLayouts)...); nfErr != nil {
+		return nfErr
+	}
+
+	return nil
+}
+
+func (s *Site) renderSitemap() error {
+	if viper.GetBool("disableSitemap") {
+		return nil
+	}
+
+	sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
+
+	n := s.newNodePage(NodeSitemap)
+
+	// Prepend homepage to the list of pages
+	pages := make(Pages, 0)
+
+	page := s.newNodePage(NodeSitemap)
+	page.URLPath.URL = ""
+	page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
+	page.Sitemap.Priority = sitemapDefault.Priority
+
+	pages = append(pages, page)
+	pages = append(pages, s.Pages...)
+
+	n.Data["Pages"] = pages
+
+	for _, page := range pages {
+		if page.Sitemap.ChangeFreq == "" {
+			page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
+		}
+
+		if page.Sitemap.Priority == -1 {
+			page.Sitemap.Priority = sitemapDefault.Priority
+		}
+
+		if page.Sitemap.Filename == "" {
+			page.Sitemap.Filename = sitemapDefault.Filename
+		}
+	}
+
+	smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
+	addLanguagePrefix := n.Site.IsMultiLingual()
+	if err := s.renderAndWriteXML("sitemap", n.addLangPathPrefixIfFlagSet(page.Sitemap.Filename, addLanguagePrefix), n, s.appendThemeTemplates(smLayouts)...); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *Site) renderRobotsTXT() error {
+	if !viper.GetBool("enableRobotsTXT") {
+		return nil
+	}
+
+	n := s.newNodePage(NodeRobotsTXT)
+	n.Data["Pages"] = s.Pages
+
+	rLayouts := []string{"robots.txt", "_default/robots.txt", "_internal/_default/robots.txt"}
+	outBuffer := bp.GetBuffer()
+	defer bp.PutBuffer(outBuffer)
+	err := s.renderForLayouts("robots", n, outBuffer, s.appendThemeTemplates(rLayouts)...)
+
+	if err == nil {
+		err = s.writeDestFile("robots.txt", outBuffer)
+	}
+
+	return err
+}
+
+// renderAliases renders shell pages that simply have a redirect in the header.
+// TODO(bep) np aliases of node types
+func (s *Site) renderAliases() error {
+	for _, p := range s.Pages {
+		if len(p.Aliases) == 0 {
+			continue
+		}
+
+		plink, err := p.Permalink()
+		if err != nil {
+			return err
+		}
+		for _, a := range p.Aliases {
+			if err := s.writeDestAlias(a, plink, p); err != nil {
+				return err
+			}
+		}
+	}
+
+	if s.owner.multilingual.enabled() {
+		mainLang := s.owner.multilingual.DefaultLang.Lang
+		if s.Info.defaultContentLanguageInSubdir {
+			mainLangURL := s.Info.pathSpec.AbsURL(mainLang, false)
+			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+			if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
+				return err
+			}
+		} else {
+			mainLangURL := s.Info.pathSpec.AbsURL("", false)
+			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+			if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
+				return err
+			}
+		}
 	}
 
 	return nil
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -393,6 +393,7 @@
 
 // Issue #1176
 func TestSectionNaming(t *testing.T) {
+	//jww.SetStdoutThreshold(jww.LevelDebug)
 
 	for _, canonify := range []bool{true, false} {
 		for _, uglify := range []bool{true, false} {
@@ -404,7 +405,6 @@
 }
 
 func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
-	hugofs.InitMemFs()
 	testCommonResetState()
 
 	viper.Set("baseURL", "http://auth/sub/")
@@ -427,12 +427,12 @@
 		{Name: filepath.FromSlash("ラーメン/doc3.html"), Content: []byte("doc3")},
 	}
 
-	s := &Site{
-		Source:   &source.InMemorySource{ByteSource: sources},
-		targets:  targetList{page: &target.PagePub{UglyURLs: uglify}},
-		Language: helpers.NewDefaultLanguage(),
+	for _, source := range sources {
+		writeSource(t, filepath.Join("content", source.Name), string(source.Content))
 	}
 
+	s := newSiteDefaultLang()
+
 	if err := buildAndRenderSite(s,
 		"_default/single.html", "{{.Content}}",
 		"_default/list.html", "{{ .Title }}"); err != nil {
@@ -453,20 +453,12 @@
 	}
 
 	for _, test := range tests {
-		file, err := hugofs.Destination().Open(test.doc)
-		if err != nil {
-			t.Fatalf("Did not find %s in target: %s", test.doc, err)
-		}
 
-		content := helpers.ReaderToString(file)
-
 		if test.pluralAware && pluralize {
 			test.expected = inflect.Pluralize(test.expected)
 		}
 
-		if content != test.expected {
-			t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
-		}
+		assertFileContent(t, filepath.Join("public", test.doc), true, test.expected)
 	}
 
 }