shithub: hugo

Download patch

ref: 089fe49309cd9cadba3ae9a5eddfd69622941a70
parent: 118b83d74b5caaae6646ec031d59b8ab3491c6b2
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Nov 20 05:34:30 EST 2017

hugolib: Make sure everything ends up in its lang root in multihost mode

Fixes #4105

--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -254,7 +254,6 @@
 
 	doc3 := enSite.RegularPages[2]
 	permalink = doc3.Permalink()
-	require.NoError(t, err, "permalink call failed")
 	// Note that /superbob is a custom URL set in frontmatter.
 	// We respect that URL literally (it can be /search.json)
 	// and do no not do any language code prefixing.
@@ -1155,6 +1154,7 @@
 title: doc3
 weight: 3
 publishdate: "2000-01-03"
+aliases: [/en/al/alias1,/al/alias2/]
 tags:
  - tag2
  - tag1
--- a/hugolib/hugo_sites_multihost_test.go
+++ b/hugolib/hugo_sites_multihost_test.go
@@ -68,6 +68,22 @@
 	assert.Len(s1h.Translations(), 2)
 	assert.Equal("https://example.com/", s1h.Permalink())
 
+	// For “regular multilingual” we kept the aliases pages with url in front matter
+	// as a literal value that we use as is.
+	// There is an ambiguity in the guessing.
+	// For multihost, we never want any content in the root.
+	//
+	// check url in front matter:
+	pageWithURLInFrontMatter := s1.getPage(KindPage, "sect/doc3.en.md")
+	assert.NotNil(pageWithURLInFrontMatter)
+	assert.Equal("/superbob", pageWithURLInFrontMatter.URL())
+	assert.Equal("/superbob/", pageWithURLInFrontMatter.RelPermalink())
+	th.assertFileContent("public/en/superbob/index.html", "doc3|Hello|en")
+
+	// check alias:
+	th.assertFileContent("public/en/al/alias1/index.html", `content="0; url=https://example.com/superbob/"`)
+	th.assertFileContent("public/en/al/alias2/index.html", `content="0; url=https://example.com/superbob/"`)
+
 	s2 := sites.Sites[1]
 	assert.Equal([]string{"s1", "s2", "frs1", "frs2"}, s2.StaticDirs())
 
--- a/hugolib/page_paths.go
+++ b/hugolib/page_paths.go
@@ -53,6 +53,9 @@
 	// language subdir.
 	LangPrefix string
 
+	// Whether this is a multihost multilingual setup.
+	IsMultihost bool
+
 	// Page.URLPath.URL. Will override any Slug etc. for regular pages.
 	URL string
 
@@ -81,12 +84,13 @@
 func (p *Page) initTargetPathDescriptor() error {
 
 	d := &targetPathDescriptor{
-		PathSpec: p.s.PathSpec,
-		Kind:     p.Kind,
-		Sections: p.sections,
-		UglyURLs: p.s.Info.uglyURLs,
-		Dir:      filepath.ToSlash(p.Source.Dir()),
-		URL:      p.URLPath.URL,
+		PathSpec:    p.s.PathSpec,
+		Kind:        p.Kind,
+		Sections:    p.sections,
+		UglyURLs:    p.s.Info.uglyURLs,
+		Dir:         filepath.ToSlash(p.Source.Dir()),
+		URL:         p.URLPath.URL,
+		IsMultihost: p.s.owner.IsMultihost(),
 	}
 
 	if p.Slug != "" {
@@ -177,7 +181,11 @@
 	if d.Kind == KindPage {
 		// Always use URL if it's specified
 		if d.URL != "" {
-			pagePath = filepath.Join(pagePath, d.URL)
+			if d.IsMultihost && d.LangPrefix != "" && !strings.HasPrefix(d.URL, "/"+d.LangPrefix) {
+				pagePath = filepath.Join(d.LangPrefix, pagePath, d.URL)
+			} else {
+				pagePath = filepath.Join(pagePath, d.URL)
+			}
 			if strings.HasSuffix(d.URL, "/") || !strings.Contains(d.URL, ".") {
 				pagePath = filepath.Join(pagePath, d.Type.BaseName+d.Type.MediaType.FullSuffix())
 			}
--- a/hugolib/page_paths_test.go
+++ b/hugolib/page_paths_test.go
@@ -40,151 +40,156 @@
 		BaseName:  "_redirects",
 	}
 
-	for _, langPrefix := range []string{"", "no"} {
-		for _, uglyURLs := range []bool{false, true} {
-			t.Run(fmt.Sprintf("langPrefix=%q,uglyURLs=%t", langPrefix, uglyURLs),
-				func(t *testing.T) {
+	for _, multiHost := range []bool{false, true} {
+		for _, langPrefix := range []string{"", "no"} {
+			for _, uglyURLs := range []bool{false, true} {
+				t.Run(fmt.Sprintf("multihost=%t,langPrefix=%q,uglyURLs=%t", multiHost, langPrefix, uglyURLs),
+					func(t *testing.T) {
 
-					tests := []struct {
-						name     string
-						d        targetPathDescriptor
-						expected string
-					}{
-						{"JSON home", targetPathDescriptor{Kind: KindHome, Type: output.JSONFormat}, "/index.json"},
-						{"AMP home", targetPathDescriptor{Kind: KindHome, Type: output.AMPFormat}, "/amp/index.html"},
-						{"HTML home", targetPathDescriptor{Kind: KindHome, BaseName: "_index", Type: output.HTMLFormat}, "/index.html"},
-						{"Netlify redirects", targetPathDescriptor{Kind: KindHome, BaseName: "_index", Type: noExtDelimFormat}, "/_redirects"},
-						{"HTML section list", targetPathDescriptor{
-							Kind:     KindSection,
-							Sections: []string{"sect1"},
-							BaseName: "_index",
-							Type:     output.HTMLFormat}, "/sect1/index.html"},
-						{"HTML taxonomy list", targetPathDescriptor{
-							Kind:     KindTaxonomy,
-							Sections: []string{"tags", "hugo"},
-							BaseName: "_index",
-							Type:     output.HTMLFormat}, "/tags/hugo/index.html"},
-						{"HTML taxonomy term", targetPathDescriptor{
-							Kind:     KindTaxonomy,
-							Sections: []string{"tags"},
-							BaseName: "_index",
-							Type:     output.HTMLFormat}, "/tags/index.html"},
-						{
-							"HTML page", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/a/b",
-								BaseName: "mypage",
-								Sections: []string{"a"},
-								Type:     output.HTMLFormat}, "/a/b/mypage/index.html"},
-
-						{
-							// Issue #3396
-							"HTML page with index as base", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/a/b",
-								BaseName: "index",
-								Sections: []string{"a"},
-								Type:     output.HTMLFormat}, "/a/b/index.html"},
-
-						{
-							"HTML page with special chars", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/a/b",
-								BaseName: "My Page!",
-								Type:     output.HTMLFormat}, "/a/b/My-Page/index.html"},
-						{"RSS home", targetPathDescriptor{Kind: kindRSS, Type: output.RSSFormat}, "/index.xml"},
-						{"RSS section list", targetPathDescriptor{
-							Kind:     kindRSS,
-							Sections: []string{"sect1"},
-							Type:     output.RSSFormat}, "/sect1/index.xml"},
-						{
-							"AMP page", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/a/b/c",
-								BaseName: "myamp",
-								Type:     output.AMPFormat}, "/amp/a/b/c/myamp/index.html"},
-						{
-							"AMP page with URL with suffix", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/sect/",
-								BaseName: "mypage",
-								URL:      "/some/other/url.xhtml",
-								Type:     output.HTMLFormat}, "/some/other/url.xhtml"},
-						{
-							"JSON page with URL without suffix", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/sect/",
-								BaseName: "mypage",
-								URL:      "/some/other/path/",
-								Type:     output.JSONFormat}, "/some/other/path/index.json"},
-						{
-							"JSON page with URL without suffix and no trailing slash", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/sect/",
-								BaseName: "mypage",
-								URL:      "/some/other/path",
-								Type:     output.JSONFormat}, "/some/other/path/index.json"},
-						{
-							"HTML page with expanded permalink", targetPathDescriptor{
-								Kind:              KindPage,
-								Dir:               "/a/b",
-								BaseName:          "mypage",
-								ExpandedPermalink: "/2017/10/my-title",
-								Type:              output.HTMLFormat}, "/2017/10/my-title/index.html"},
-						{
-							"Paginated HTML home", targetPathDescriptor{
-								Kind:     KindHome,
+						tests := []struct {
+							name     string
+							d        targetPathDescriptor
+							expected string
+						}{
+							{"JSON home", targetPathDescriptor{Kind: KindHome, Type: output.JSONFormat}, "/index.json"},
+							{"AMP home", targetPathDescriptor{Kind: KindHome, Type: output.AMPFormat}, "/amp/index.html"},
+							{"HTML home", targetPathDescriptor{Kind: KindHome, BaseName: "_index", Type: output.HTMLFormat}, "/index.html"},
+							{"Netlify redirects", targetPathDescriptor{Kind: KindHome, BaseName: "_index", Type: noExtDelimFormat}, "/_redirects"},
+							{"HTML section list", targetPathDescriptor{
+								Kind:     KindSection,
+								Sections: []string{"sect1"},
 								BaseName: "_index",
-								Type:     output.HTMLFormat,
-								Addends:  "page/3"}, "/page/3/index.html"},
-						{
-							"Paginated Taxonomy list", targetPathDescriptor{
+								Type:     output.HTMLFormat}, "/sect1/index.html"},
+							{"HTML taxonomy list", targetPathDescriptor{
 								Kind:     KindTaxonomy,
-								BaseName: "_index",
 								Sections: []string{"tags", "hugo"},
-								Type:     output.HTMLFormat,
-								Addends:  "page/3"}, "/tags/hugo/page/3/index.html"},
-						{
-							"Regular page with addend", targetPathDescriptor{
-								Kind:     KindPage,
-								Dir:      "/a/b",
-								BaseName: "mypage",
-								Addends:  "c/d/e",
-								Type:     output.HTMLFormat}, "/a/b/mypage/c/d/e/index.html"},
-					}
+								BaseName: "_index",
+								Type:     output.HTMLFormat}, "/tags/hugo/index.html"},
+							{"HTML taxonomy term", targetPathDescriptor{
+								Kind:     KindTaxonomy,
+								Sections: []string{"tags"},
+								BaseName: "_index",
+								Type:     output.HTMLFormat}, "/tags/index.html"},
+							{
+								"HTML page", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/a/b",
+									BaseName: "mypage",
+									Sections: []string{"a"},
+									Type:     output.HTMLFormat}, "/a/b/mypage/index.html"},
 
-					for i, test := range tests {
-						test.d.PathSpec = pathSpec
-						test.d.UglyURLs = uglyURLs
-						test.d.LangPrefix = langPrefix
-						test.d.Dir = filepath.FromSlash(test.d.Dir)
-						isUgly := uglyURLs && !test.d.Type.NoUgly
+							{
+								// Issue #3396
+								"HTML page with index as base", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/a/b",
+									BaseName: "index",
+									Sections: []string{"a"},
+									Type:     output.HTMLFormat}, "/a/b/index.html"},
 
-						expected := test.expected
+							{
+								"HTML page with special chars", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/a/b",
+									BaseName: "My Page!",
+									Type:     output.HTMLFormat}, "/a/b/My-Page/index.html"},
+							{"RSS home", targetPathDescriptor{Kind: kindRSS, Type: output.RSSFormat}, "/index.xml"},
+							{"RSS section list", targetPathDescriptor{
+								Kind:     kindRSS,
+								Sections: []string{"sect1"},
+								Type:     output.RSSFormat}, "/sect1/index.xml"},
+							{
+								"AMP page", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/a/b/c",
+									BaseName: "myamp",
+									Type:     output.AMPFormat}, "/amp/a/b/c/myamp/index.html"},
+							{
+								"AMP page with URL with suffix", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/sect/",
+									BaseName: "mypage",
+									URL:      "/some/other/url.xhtml",
+									Type:     output.HTMLFormat}, "/some/other/url.xhtml"},
+							{
+								"JSON page with URL without suffix", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/sect/",
+									BaseName: "mypage",
+									URL:      "/some/other/path/",
+									Type:     output.JSONFormat}, "/some/other/path/index.json"},
+							{
+								"JSON page with URL without suffix and no trailing slash", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/sect/",
+									BaseName: "mypage",
+									URL:      "/some/other/path",
+									Type:     output.JSONFormat}, "/some/other/path/index.json"},
+							{
+								"HTML page with expanded permalink", targetPathDescriptor{
+									Kind:              KindPage,
+									Dir:               "/a/b",
+									BaseName:          "mypage",
+									ExpandedPermalink: "/2017/10/my-title",
+									Type:              output.HTMLFormat}, "/2017/10/my-title/index.html"},
+							{
+								"Paginated HTML home", targetPathDescriptor{
+									Kind:     KindHome,
+									BaseName: "_index",
+									Type:     output.HTMLFormat,
+									Addends:  "page/3"}, "/page/3/index.html"},
+							{
+								"Paginated Taxonomy list", targetPathDescriptor{
+									Kind:     KindTaxonomy,
+									BaseName: "_index",
+									Sections: []string{"tags", "hugo"},
+									Type:     output.HTMLFormat,
+									Addends:  "page/3"}, "/tags/hugo/page/3/index.html"},
+							{
+								"Regular page with addend", targetPathDescriptor{
+									Kind:     KindPage,
+									Dir:      "/a/b",
+									BaseName: "mypage",
+									Addends:  "c/d/e",
+									Type:     output.HTMLFormat}, "/a/b/mypage/c/d/e/index.html"},
+						}
 
-						// TODO(bep) simplify
-						if test.d.Kind == KindPage && test.d.BaseName == test.d.Type.BaseName {
+						for i, test := range tests {
+							test.d.PathSpec = pathSpec
+							test.d.UglyURLs = uglyURLs
+							test.d.LangPrefix = langPrefix
+							test.d.IsMultihost = multiHost
+							test.d.Dir = filepath.FromSlash(test.d.Dir)
+							isUgly := uglyURLs && !test.d.Type.NoUgly
 
-						} else if test.d.Kind == KindHome && test.d.Type.Path != "" {
-						} else if (!strings.HasPrefix(expected, "/index") || test.d.Addends != "") && test.d.URL == "" && isUgly {
-							expected = strings.Replace(expected,
-								"/"+test.d.Type.BaseName+"."+test.d.Type.MediaType.Suffix,
-								"."+test.d.Type.MediaType.Suffix, -1)
-						}
+							expected := test.expected
 
-						if test.d.LangPrefix != "" && !(test.d.Kind == KindPage && test.d.URL != "") {
-							expected = "/" + test.d.LangPrefix + expected
-						}
+							// TODO(bep) simplify
+							if test.d.Kind == KindPage && test.d.BaseName == test.d.Type.BaseName {
 
-						expected = filepath.FromSlash(expected)
+							} else if test.d.Kind == KindHome && test.d.Type.Path != "" {
+							} else if (!strings.HasPrefix(expected, "/index") || test.d.Addends != "") && test.d.URL == "" && isUgly {
+								expected = strings.Replace(expected,
+									"/"+test.d.Type.BaseName+"."+test.d.Type.MediaType.Suffix,
+									"."+test.d.Type.MediaType.Suffix, -1)
+							}
 
-						pagePath := createTargetPath(test.d)
+							if test.d.LangPrefix != "" && !(test.d.Kind == KindPage && test.d.URL != "") {
+								expected = "/" + test.d.LangPrefix + expected
+							} else if multiHost && test.d.LangPrefix != "" && test.d.URL != "" {
+								expected = "/" + test.d.LangPrefix + expected
+							}
 
-						if pagePath != expected {
-							t.Fatalf("[%d] [%s] targetPath expected %q, got: %q", i, test.name, expected, pagePath)
+							expected = filepath.FromSlash(expected)
+
+							pagePath := createTargetPath(test.d)
+
+							if pagePath != expected {
+								t.Fatalf("[%d] [%s] targetPath expected %q, got: %q", i, test.name, expected, pagePath)
+							}
 						}
-					}
-				})
+					})
+			}
 		}
 	}
 }
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -16,6 +16,7 @@
 import (
 	"fmt"
 	"path"
+	"strings"
 	"sync"
 
 	"github.com/gohugoio/hugo/helpers"
@@ -378,6 +379,13 @@
 				if f.Path != "" {
 					// Make sure AMP and similar doesn't clash with regular aliases.
 					a = path.Join(a, f.Path)
+				}
+
+				lang := p.Lang()
+
+				if s.owner.multihost && !strings.HasPrefix(a, "/"+lang) {
+					// These need to be in its language root.
+					a = path.Join(lang, a)
 				}
 
 				if err := s.writeDestAlias(a, plink, p); err != nil {