hugolib: Fix panic for unused taxonomy content files
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 15 Apr 2019 07:38:14 +0000 (09:38 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 15 Apr 2019 11:36:05 +0000 (13:36 +0200)
In Hugo 0.55 we connected the taxonomy nodes with their owning Page.

This failed if you had, say, a content file for a author that did not author anything in the site:

```
content/authors/silent-persin/_index.md
```

Fixes #5847

hugolib/hugo_sites.go
hugolib/page.go
hugolib/site.go
hugolib/taxonomy.go
hugolib/taxonomy_test.go

index 8c80e189c854f9e71c532ff752e30f8be19adca5..6f95dbb12c3f2b280215d4d3c1998437ab7968f5 100644 (file)
@@ -14,7 +14,9 @@
 package hugolib
 
 import (
+       "fmt"
        "io"
+       "path"
        "path/filepath"
        "sort"
        "strings"
@@ -650,7 +652,19 @@ func (h *HugoSites) createMissingPages() error {
 
                        // Make them navigable from WeightedPage etc.
                        for _, p := range taxonomyPages {
-                               p.getTaxonomyNodeInfo().TransferValues(p)
+                               ni := p.getTaxonomyNodeInfo()
+                               if ni == nil {
+                                       // This can be nil for taxonomies, e.g. an author,
+                                       // with a content file, but no actual usage.
+                                       // Create one.
+                                       sections := p.SectionsEntries()
+                                       if len(sections) < 2 {
+                                               // Invalid state
+                                               panic(fmt.Sprintf("invalid taxonomy state for %q with sections %v", p.pathOrTitle(), sections))
+                                       }
+                                       ni = p.s.taxonomyNodes.GetOrAdd(sections[0], path.Join(sections[1:]...))
+                               }
+                               ni.TransferValues(p)
                        }
                        for _, p := range taxonomyTermsPages {
                                p.getTaxonomyNodeInfo().TransferValues(p)
index 2ed828df6bd00b2242a305926d09ea2d8e35aa0e..ec4b23d901214dccc194dc5e00c855c76b3ca2cf 100644 (file)
@@ -746,8 +746,10 @@ func (p *pageState) getTaxonomyNodeInfo() *taxonomyNodeInfo {
        info := p.s.taxonomyNodes.Get(p.SectionsEntries()...)
 
        if info == nil {
-               // This should never happpen
-               panic(fmt.Sprintf("invalid taxonomy state for %q with sections %v", p.pathOrTitle(), p.SectionsEntries()))
+               // There can be unused content pages for taxonomies (e.g. author that
+               // has not written anything, yet), and these will not have a taxonomy
+               // node created in the assemble taxonomies step.
+               return nil
        }
 
        return info
index 2653479ea090d1f1f87f155ac2fd5b077bf0a39f..47ca77016b19bbc9b3b6ccf3983ab11616e3881e 100644 (file)
@@ -94,7 +94,7 @@ type Site struct {
 
        Taxonomies TaxonomyList
 
-       taxonomyNodes taxonomyNodeInfos
+       taxonomyNodes *taxonomyNodeInfos
 
        Sections Taxonomy
        Info     SiteInfo
@@ -1566,24 +1566,23 @@ func (s *Site) assembleTaxonomies() error {
                s.Taxonomies[plural] = make(Taxonomy)
        }
 
-       s.taxonomyNodes = make(taxonomyNodeInfos)
+       s.taxonomyNodes = &taxonomyNodeInfos{
+               m:      make(map[string]*taxonomyNodeInfo),
+               getKey: s.getTaxonomyKey,
+       }
 
        s.Log.INFO.Printf("found taxonomies: %#v\n", taxonomies)
 
        for singular, plural := range taxonomies {
-               parent := s.taxonomyNodes.GetOrCreate(plural, "", "")
+               parent := s.taxonomyNodes.GetOrCreate(plural, "")
                parent.singular = singular
 
                addTaxonomy := func(plural, term string, weight int, p page.Page) {
                        key := s.getTaxonomyKey(term)
 
-                       n := s.taxonomyNodes.GetOrCreate(plural, key, term)
+                       n := s.taxonomyNodes.GetOrCreate(plural, term)
                        n.parent = parent
 
-                       // There may be different spellings before normalization, so the
-                       // last one will win, e.g. "hugo" vs "Hugo".
-                       n.term = term
-
                        w := page.NewWeightedPage(weight, p, n.owner)
 
                        s.Taxonomies[plural].add(key, w)
index b91318ff1878fefe4c7c0515bfb7ba91bd6b0566..e6c80161a1de868ed54b7e75ceb217b0b8848812 100644 (file)
@@ -193,16 +193,33 @@ func (t *taxonomyNodeInfo) TransferValues(p *pageState) {
 
 // Maps either plural or plural/term to a taxonomy node.
 // TODO(bep) consolidate somehow with s.Taxonomies
-type taxonomyNodeInfos map[string]*taxonomyNodeInfo
+type taxonomyNodeInfos struct {
+       m      map[string]*taxonomyNodeInfo
+       getKey func(string) string
+}
 
+// map[string]*taxonomyNodeInfo
 func (t taxonomyNodeInfos) key(parts ...string) string {
        return path.Join(parts...)
 }
 
-func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNodeInfo {
+// GetOrAdd will get or create and add a new taxonomy node to the parent identified with plural.
+// It will panic if the parent does not exist.
+func (t taxonomyNodeInfos) GetOrAdd(plural, term string) *taxonomyNodeInfo {
+       parent := t.GetOrCreate(plural, "")
+       if parent == nil {
+               panic(fmt.Sprintf("no parent found with plural %q", plural))
+       }
+       child := t.GetOrCreate(plural, term)
+       child.parent = parent
+       return child
+}
+
+func (t taxonomyNodeInfos) GetOrCreate(plural, term string) *taxonomyNodeInfo {
+       termKey := t.getKey(term)
        key := t.key(plural, termKey)
 
-       n, found := t[key]
+       n, found := t.m[key]
        if found {
                return n
        }
@@ -214,7 +231,7 @@ func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNo
                owner:   &page.PageWrapper{}, // Page will be assigned later.
        }
 
-       t[key] = n
+       t.m[key] = n
 
        return n
 }
@@ -222,7 +239,7 @@ func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNo
 func (t taxonomyNodeInfos) Get(sections ...string) *taxonomyNodeInfo {
        key := t.key(sections...)
 
-       n, found := t[key]
+       n, found := t.m[key]
        if found {
                return n
        }
index 2501ed2e4decd0191afd9f537f21a0c39cbaac0f..f4902ae8d653b57737e1862a5b17c1d3ce957553 100644 (file)
@@ -117,6 +117,9 @@ permalinkeds:
        writeNewContentFile(t, fs.Source, "Category Terms", "2017-01-01", "content/categories/_index.md", 10)
        writeNewContentFile(t, fs.Source, "Tag1 List", "2017-01-01", "content/tags/Tag1/_index.md", 10)
 
+       // https://github.com/gohugoio/hugo/issues/5847
+       writeNewContentFile(t, fs.Source, "Unused Tag List", "2018-01-01", "content/tags/not-used/_index.md", 10)
+
        err := h.Build(BuildCfg{})
 
        require.NoError(t, err)
@@ -159,7 +162,7 @@ permalinkeds:
        // Make sure that each page.KindTaxonomyTerm page has an appropriate number
        // of page.KindTaxonomy pages in its Pages slice.
        taxonomyTermPageCounts := map[string]int{
-               "tags":         2,
+               "tags":         3,
                "categories":   2,
                "others":       2,
                "empties":      0,