Skip to content

Commit

Permalink
Create a Node map to get proper node translations
Browse files Browse the repository at this point in the history
In a multi-language setup, before this commit the Node's Translations() method
would return some "dummy nodes" that would point to the correct page (Permalink),
but would not be the same as the node it points to -- it would not have the translated
title etc.

The node creation is, however, so mingled with rendering, whihc is too early to have any global state,
so the nodes has to be split in a prepare and a render phase. This commits does that with as small
a change as possible. This implementation is a temp solution until we fix #2297.

Updates #2309
  • Loading branch information
bep committed Aug 10, 2016
1 parent 2f03457 commit 45f980e
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 120 deletions.
51 changes: 49 additions & 2 deletions hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type HugoSites struct {
runMode runmode

multilingual *Multilingual

// Maps internalID to a set of nodes.
nodeMap map[string]Nodes
nodeMapMu sync.Mutex
}

// NewHugoSites creates a new collection of sites given the input sites, building
Expand All @@ -50,7 +54,7 @@ func newHugoSites(sites ...*Site) (*HugoSites, error) {
return nil, err
}

h := &HugoSites{multilingual: langConfig, Sites: sites}
h := &HugoSites{multilingual: langConfig, Sites: sites, nodeMap: make(map[string]Nodes)}

for _, s := range sites {
s.owner = h
Expand Down Expand Up @@ -92,14 +96,39 @@ func createSitesFromConfig() ([]*Site, error) {
return sites, nil
}

func (h *HugoSites) addNode(nodeID string, node *Node) {
h.nodeMapMu.Lock()

if nodes, ok := h.nodeMap[nodeID]; ok {
h.nodeMap[nodeID] = append(nodes, node)
} else {
h.nodeMap[nodeID] = Nodes{node}
}
h.nodeMapMu.Unlock()
}

func (h *HugoSites) getNodes(nodeID string) Nodes {
// At this point it is read only, so no need to lock.
if nodeID != "" {
if nodes, ok := h.nodeMap[nodeID]; ok {
return nodes
}
}
// Paginator pages will not have related nodes.
return Nodes{}
}

// Reset resets the sites, making it ready for a full rebuild.
func (h *HugoSites) reset() {
h.nodeMap = make(map[string]Nodes)
for i, s := range h.Sites {
h.Sites[i] = s.reset()
}
}

func (h *HugoSites) reCreateFromConfig() error {
h.nodeMap = make(map[string]Nodes)

sites, err := createSitesFromConfig()

if err != nil {
Expand Down Expand Up @@ -236,6 +265,7 @@ func (h *HugoSites) Rebuild(config BuildCfg, events ...fsnotify.Event) error {

firstSite := h.Sites[0]

h.nodeMap = make(map[string]Nodes)
for _, s := range h.Sites {
s.resetBuildState()
}
Expand Down Expand Up @@ -359,6 +389,23 @@ func (h *HugoSites) setupTranslations(master *Site) {
// Shortcode handling is the main task in here.
// TODO(bep) We need to look at the whole handler-chain construct witht he below in mind.
func (h *HugoSites) preRender() error {

for _, s := range h.Sites {
// Run "render prepare"
if err := s.renderHomePage(true); err != nil {
return err
}
if err := s.renderTaxonomiesLists(true); err != nil {
return err
}
if err := s.renderListsOfTaxonomyTerms(true); err != nil {
return err
}
if err := s.renderSectionLists(true); err != nil {
return err
}
}

pageChan := make(chan *Page)

wg := &sync.WaitGroup{}
Expand Down Expand Up @@ -418,7 +465,7 @@ func (h *HugoSites) preRender() error {
}

// Pages returns all pages for all sites.
func (h HugoSites) Pages() Pages {
func (h *HugoSites) Pages() Pages {
return h.Sites[0].AllPages
}

Expand Down
117 changes: 94 additions & 23 deletions hugolib/hugo_sites_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
t.Fatalf("Failed to build sites: %s", err)
}

require.Len(t, sites.Sites, 2)
require.Len(t, sites.Sites, 4)

enSite := sites.Sites[0]
frSite := sites.Sites[1]
Expand Down Expand Up @@ -177,8 +177,8 @@ func TestMultiSitesBuild(t *testing.T) {
if len(enSite.Pages) != 3 {
t.Fatal("Expected 3 english pages")
}
assert.Len(t, enSite.Source.Files(), 11, "should have 11 source files")
assert.Len(t, enSite.AllPages, 6, "should have 6 total pages (including translations)")
assert.Len(t, enSite.Source.Files(), 13, "should have 13 source files")
assert.Len(t, enSite.AllPages, 8, "should have 8 total pages (including translations)")

doc1en := enSite.Pages[0]
permalink, err := doc1en.Permalink()
Expand Down Expand Up @@ -230,7 +230,7 @@ func TestMultiSitesBuild(t *testing.T) {

assert.Equal(t, "fr", frSite.Language.Lang)
assert.Len(t, frSite.Pages, 3, "should have 3 pages")
assert.Len(t, frSite.AllPages, 6, "should have 6 total pages (including translations)")
assert.Len(t, frSite.AllPages, 8, "should have 8 total pages (including translations)")

for _, frenchPage := range frSite.Pages {
assert.Equal(t, "fr", frenchPage.Lang())
Expand All @@ -240,6 +240,36 @@ func TestMultiSitesBuild(t *testing.T) {
languageRedirect := readDestination(t, "public/index.html")
require.True(t, strings.Contains(languageRedirect, "0; url=http://example.com/blog/fr"), languageRedirect)

// Check node translations
homeEn := enSite.getNode("home-0")
require.NotNil(t, homeEn)
require.Len(t, homeEn.Translations(), 3)
require.Equal(t, "fr", homeEn.Translations()[0].Lang())
require.Equal(t, "nn", homeEn.Translations()[1].Lang())
require.Equal(t, "Nynorsk", homeEn.Translations()[1].Title)
require.Equal(t, "nb", homeEn.Translations()[2].Lang())
require.Equal(t, "Bokmål", homeEn.Translations()[2].Title)

sectFr := frSite.getNode("sect-sect-0")
require.NotNil(t, sectFr)

require.Equal(t, "fr", sectFr.Lang())
require.Len(t, sectFr.Translations(), 1)
require.Equal(t, "en", sectFr.Translations()[0].Lang())
require.Equal(t, "Sects", sectFr.Translations()[0].Title)

nnSite := sites.Sites[2]
require.Equal(t, "nn", nnSite.Language.Lang)
taxNn := nnSite.getNode("taxlist-lag-0")
require.NotNil(t, taxNn)
require.Len(t, taxNn.Translations(), 1)
require.Equal(t, "nb", taxNn.Translations()[0].Lang())

taxTermNn := nnSite.getNode("tax-lag-sogndal-0")
require.NotNil(t, taxTermNn)
require.Len(t, taxTermNn.Translations(), 1)
require.Equal(t, "nb", taxTermNn.Translations()[0].Lang())

// Check sitemap(s)
sitemapIndex := readDestination(t, "public/sitemap.xml")
require.True(t, strings.Contains(sitemapIndex, "<loc>http:/example.com/blog/en/sitemap.xml</loc>"), sitemapIndex)
Expand Down Expand Up @@ -338,7 +368,7 @@ func TestMultiSitesRebuild(t *testing.T) {
},
func(t *testing.T) {
assert.Len(t, enSite.Pages, 4)
assert.Len(t, enSite.AllPages, 8)
assert.Len(t, enSite.AllPages, 10)
assert.Len(t, frSite.Pages, 4)
assert.Equal(t, "new_fr_1", frSite.Pages[3].Title)
assert.Equal(t, "new_en_2", enSite.Pages[0].Title)
Expand Down Expand Up @@ -391,7 +421,7 @@ func TestMultiSitesRebuild(t *testing.T) {
[]fsnotify.Event{{Name: "layouts/_default/single.html", Op: fsnotify.Write}},
func(t *testing.T) {
assert.Len(t, enSite.Pages, 4)
assert.Len(t, enSite.AllPages, 8)
assert.Len(t, enSite.AllPages, 10)
assert.Len(t, frSite.Pages, 4)
doc1 := readDestination(t, "public/en/sect/doc1-slug/index.html")
assert.True(t, strings.Contains(doc1, "Template Changed"), doc1)
Expand All @@ -408,12 +438,18 @@ func TestMultiSitesRebuild(t *testing.T) {
[]fsnotify.Event{{Name: "i18n/fr.yaml", Op: fsnotify.Write}},
func(t *testing.T) {
assert.Len(t, enSite.Pages, 4)
assert.Len(t, enSite.AllPages, 8)
assert.Len(t, enSite.AllPages, 10)
assert.Len(t, frSite.Pages, 4)
docEn := readDestination(t, "public/en/sect/doc1-slug/index.html")
assert.True(t, strings.Contains(docEn, "Hello"), "No Hello")
docFr := readDestination(t, "public/fr/sect/doc1/index.html")
assert.True(t, strings.Contains(docFr, "Salut"), "No Salut")

homeEn := enSite.getNode("home-0")
require.NotNil(t, homeEn)
require.Len(t, homeEn.Translations(), 3)
require.Equal(t, "fr", homeEn.Translations()[0].Lang())

},
},
} {
Expand Down Expand Up @@ -469,12 +505,12 @@ func TestAddNewLanguage(t *testing.T) {

newConfig := multiSiteTomlConfig + `
[Languages.no]
[Languages.sv]
weight = 15
title = "Norsk"
title = "Svenska"
`

writeNewContentFile(t, "Norwegian Contentfile", "2016-01-01", "content/sect/doc1.no.md", 10)
writeNewContentFile(t, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10)
// replace the config
writeSource(t, "multilangconfig.toml", newConfig)

Expand All @@ -486,27 +522,32 @@ title = "Norsk"
t.Fatalf("Failed to rebuild sites: %s", err)
}

require.Len(t, sites.Sites, 3, fmt.Sprintf("Len %d", len(sites.Sites)))
require.Len(t, sites.Sites, 5, fmt.Sprintf("Len %d", len(sites.Sites)))

// The Norwegian site should be put in the middle (language weight=15)
// The Swedish site should be put in the middle (language weight=15)
enSite := sites.Sites[0]
noSite := sites.Sites[1]
svSite := sites.Sites[1]
frSite := sites.Sites[2]
require.True(t, enSite.Language.Lang == "en", enSite.Language.Lang)
require.True(t, noSite.Language.Lang == "no", noSite.Language.Lang)
require.True(t, svSite.Language.Lang == "sv", svSite.Language.Lang)
require.True(t, frSite.Language.Lang == "fr", frSite.Language.Lang)

homeEn := enSite.getNode("home-0")
require.NotNil(t, homeEn)
require.Len(t, homeEn.Translations(), 4)
require.Equal(t, "sv", homeEn.Translations()[0].Lang())

require.Len(t, enSite.Pages, 3)
require.Len(t, frSite.Pages, 3)

// Veriy Norwegian site
require.Len(t, noSite.Pages, 1)
noPage := noSite.Pages[0]
require.Equal(t, "Norwegian Contentfile", noPage.Title)
require.Equal(t, "no", noPage.Lang())
require.Len(t, noPage.Translations(), 2)
require.Len(t, noPage.AllTranslations(), 3)
require.Equal(t, "en", noPage.Translations()[0].Lang())
// Veriy Swedish site
require.Len(t, svSite.Pages, 1)
svPage := svSite.Pages[0]
require.Equal(t, "Swedish Contentfile", svPage.Title)
require.Equal(t, "sv", svPage.Lang())
require.Len(t, svPage.Translations(), 2)
require.Len(t, svPage.AllTranslations(), 3)
require.Equal(t, "en", svPage.Translations()[0].Lang())
//noFile := readDestination(t, "/public/no/doc1/index.html")
//require.True(t, strings.Contains("foo", noFile), noFile)

Expand Down Expand Up @@ -543,6 +584,18 @@ weight = 20
title = "Français"
[Languages.fr.Taxonomies]
plaque = "plaques"
[Languages.nn]
weight = 30
title = "Nynorsk"
[Languages.nn.Taxonomies]
lag = "lag"
[Languages.nb]
weight = 40
title = "Bokmål"
[Languages.nb.Taxonomies]
lag = "lag"
`

func createMultiTestSites(t *testing.T, tomlConfig string) *HugoSites {
Expand Down Expand Up @@ -686,6 +739,24 @@ publishdate: "2000-01-06"
draft: true
---
# Draft
`)},
{filepath.FromSlash("stats/tax.nn.md"), []byte(`---
title: Tax NN
publishdate: "2000-01-06"
weight: 1001
lag:
- Sogndal
---
# Tax NN
`)},
{filepath.FromSlash("stats/tax.nb.md"), []byte(`---
title: Tax NB
publishdate: "2000-01-06"
weight: 1002
lag:
- Sogndal
---
# Tax NB
`)},
}

Expand Down Expand Up @@ -713,7 +784,7 @@ draft: true
t.Fatalf("Failed to create sites: %s", err)
}

if len(sites.Sites) != 2 {
if len(sites.Sites) != 4 {
t.Fatalf("Got %d sites", len(sites.Sites))
}

Expand Down
16 changes: 11 additions & 5 deletions hugolib/menu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand Down Expand Up @@ -414,9 +415,13 @@ func doTestSectionPagesMenu(canonifyUrls bool, t *testing.T) {
fishySectionPages := s.Sections["fish-and-chips"]
assert.Equal(t, 1, len(fishySectionPages))

nodeFirst := s.newSectionListNode("First", "first", firstSectionPages)
nodeSecond := s.newSectionListNode("Second Section", "second-section", secondSectionPages)
nodeFishy := s.newSectionListNode("Fish and Chips", "fish-and-chips", fishySectionPages)
nodeFirst := s.getNode("sect-first-0")
require.NotNil(t, nodeFirst)
nodeSecond := s.getNode("sect-second-section-0")
require.NotNil(t, nodeSecond)
nodeFishy := s.getNode("sect-Fish and Chips-0")
require.NotNil(t, nodeFishy)

firstSectionMenuEntry := findTestMenuEntryByID(s, "spm", "first")
secondSectionMenuEntry := findTestMenuEntryByID(s, "spm", "second-section")
fishySectionMenuEntry := findTestMenuEntryByID(s, "spm", "Fish and Chips")
Expand Down Expand Up @@ -472,7 +477,7 @@ func TestTaxonomyNodeMenu(t *testing.T) {
&MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},
} {

n, _ := s.newTaxonomyNode(this.taxInfo)
n, _ := s.newTaxonomyNode(true, this.taxInfo, i)

isMenuCurrent := n.IsMenuCurrent(this.menu, this.menuItem)
hasMenuCurrent := n.HasMenuCurrent(this.menu, this.menuItem)
Expand Down Expand Up @@ -544,7 +549,8 @@ func TestHomeNodeMenu(t *testing.T) {

s := setupMenuTests(t, menuPageSources)

home := s.newHomeNode()
home := s.getNode("home-0")

homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL()}

for i, this := range []struct {
Expand Down
Loading

0 comments on commit 45f980e

Please sign in to comment.