output: Fix base theme vs project base template logic

Fixes #3323
This commit is contained in:
Bjørn Erik Pedersen 2017-04-12 20:40:36 +02:00
parent efc0e05c4e
commit 077005e514
3 changed files with 63 additions and 38 deletions

View file

@ -38,7 +38,11 @@ type TemplateNames struct {
} }
type TemplateLookupDescriptor struct { type TemplateLookupDescriptor struct {
// The full path to the site or theme root. // TemplateDir is the project or theme root of the current template.
// This will be the same as WorkingDir for non-theme templates.
TemplateDir string
// The full path to the site root.
WorkingDir string WorkingDir string
// Main project layout dir, defaults to "layouts" // Main project layout dir, defaults to "layouts"
@ -51,8 +55,8 @@ type TemplateLookupDescriptor struct {
// The template name prefix to look for, i.e. "theme". // The template name prefix to look for, i.e. "theme".
Prefix string Prefix string
// The theme name if active. // The theme dir if theme active.
Theme string ThemeDir string
// All the output formats in play. This is used to decide if text/template or // All the output formats in play. This is used to decide if text/template or
// html/template. // html/template.
@ -64,16 +68,29 @@ type TemplateLookupDescriptor struct {
func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) { func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) {
var id TemplateNames
name := filepath.ToSlash(d.RelPath) name := filepath.ToSlash(d.RelPath)
if d.Prefix != "" { if d.Prefix != "" {
name = strings.Trim(d.Prefix, "/") + "/" + name name = strings.Trim(d.Prefix, "/") + "/" + name
} }
baseLayoutDir := filepath.Join(d.WorkingDir, d.LayoutDir) var (
fullPath := filepath.Join(baseLayoutDir, d.RelPath) id TemplateNames
// This is the path to the actual template in process. This may
// be in the theme's or the project's /layouts.
baseLayoutDir = filepath.Join(d.TemplateDir, d.LayoutDir)
fullPath = filepath.Join(baseLayoutDir, d.RelPath)
// This is always the project's layout dir.
baseWorkLayoutDir = filepath.Join(d.WorkingDir, d.LayoutDir)
baseThemeLayoutDir string
)
if d.ThemeDir != "" {
baseThemeLayoutDir = filepath.Join(d.ThemeDir, "layouts")
}
// The filename will have a suffix with an optional type indicator. // The filename will have a suffix with an optional type indicator.
// Examples: // Examples:
@ -140,8 +157,8 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) {
currBaseFilename := fmt.Sprintf("%s-%s", filenameNoSuffix, baseFilename) currBaseFilename := fmt.Sprintf("%s-%s", filenameNoSuffix, baseFilename)
templateDir := filepath.Dir(fullPath) templateDir := filepath.Dir(fullPath)
themeDir := filepath.Join(d.WorkingDir, d.Theme)
// Find the base, e.g. "_default".
baseTemplatedDir := strings.TrimPrefix(templateDir, baseLayoutDir) baseTemplatedDir := strings.TrimPrefix(templateDir, baseLayoutDir)
baseTemplatedDir = strings.TrimPrefix(baseTemplatedDir, helpers.FilePathSeparator) baseTemplatedDir = strings.TrimPrefix(baseTemplatedDir, helpers.FilePathSeparator)
@ -162,7 +179,7 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) {
Loop: Loop:
for _, pair := range pairsToCheck { for _, pair := range pairsToCheck {
pathsToCheck := basePathsToCheck(pair, baseLayoutDir, themeDir) pathsToCheck := basePathsToCheck(pair, baseLayoutDir, baseWorkLayoutDir, baseThemeLayoutDir)
for _, pathToCheck := range pathsToCheck { for _, pathToCheck := range pathsToCheck {
if ok, err := d.FileExists(pathToCheck); err == nil && ok { if ok, err := d.FileExists(pathToCheck); err == nil && ok {
@ -177,13 +194,18 @@ func CreateTemplateNames(d TemplateLookupDescriptor) (TemplateNames, error) {
} }
func basePathsToCheck(path []string, layoutDir, themeDir string) []string { func basePathsToCheck(path []string, layoutDir, workLayoutDir, themeLayoutDir string) []string {
// Always look in the project. // workLayoutDir will always be the most specific, so start there.
pathsToCheck := []string{filepath.Join((append([]string{layoutDir}, path...))...)} pathsToCheck := []string{filepath.Join((append([]string{workLayoutDir}, path...))...)}
if layoutDir != "" && layoutDir != workLayoutDir {
pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{layoutDir}, path...))...))
}
// May have a theme // May have a theme
if themeDir != "" { if themeLayoutDir != "" && themeLayoutDir != layoutDir {
pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeDir, "layouts"}, path...))...)) pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeLayoutDir}, path...))...))
} }
return pathsToCheck return pathsToCheck

View file

@ -25,6 +25,7 @@ func TestLayoutBase(t *testing.T) {
var ( var (
workingDir = "/sites/mysite/" workingDir = "/sites/mysite/"
themeDir = "/themes/mytheme/"
layoutBase1 = "layouts" layoutBase1 = "layouts"
layoutPath1 = "_default/single.html" layoutPath1 = "_default/single.html"
layoutPathAmp = "_default/single.amp.html" layoutPathAmp = "_default/single.amp.html"
@ -38,76 +39,76 @@ func TestLayoutBase(t *testing.T) {
basePathMatchStrings string basePathMatchStrings string
expect TemplateNames expect TemplateNames
}{ }{
{"No base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, false, "", {"No base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, false, "",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html",
}}, }},
{"Base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, true, "", {"Base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, true, "",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/layouts/_default/single-baseof.html", MasterFilename: "/sites/mysite/layouts/_default/single-baseof.html",
}}, }},
{"Base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, {"Base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true,
"mytheme/layouts/_default/baseof.html", "mytheme/layouts/_default/baseof.html",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", MasterFilename: "/themes/mytheme/layouts/_default/baseof.html",
}}, }},
{"Template in theme, base in theme", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, {"Template in theme, base in theme", TemplateLookupDescriptor{TemplateDir: themeDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true,
"mytheme/layouts/_default/baseof.html", "mytheme/layouts/_default/baseof.html",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html", OverlayFilename: "/themes/mytheme/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", MasterFilename: "/themes/mytheme/layouts/_default/baseof.html",
}}, }},
{"Template in theme, base in site", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, {"Template in theme, base in site", TemplateLookupDescriptor{TemplateDir: themeDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true,
"mytheme/layouts/_default/baseof.html", "/sites/mysite/layouts/_default/baseof.html",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html", OverlayFilename: "/themes/mytheme/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", MasterFilename: "/sites/mysite/layouts/_default/baseof.html",
}}, }},
{"Template in site, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true, {"Template in site, base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, ThemeDir: themeDir}, true,
"/sites/mysite/mytheme/layouts/_default/baseof.html", "/themes/mytheme",
TemplateNames{ TemplateNames{
Name: "_default/single.html", Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", MasterFilename: "/themes/mytheme/layouts/_default/single-baseof.html",
}}, }},
{"With prefix, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, {"With prefix, base in theme", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1,
Theme: "mytheme", Prefix: "someprefix"}, true, ThemeDir: themeDir, Prefix: "someprefix"}, true,
"mytheme/layouts/_default/baseof.html", "mytheme/layouts/_default/baseof.html",
TemplateNames{ TemplateNames{
Name: "someprefix/_default/single.html", Name: "someprefix/_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html", OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html", MasterFilename: "/themes/mytheme/layouts/_default/baseof.html",
}}, }},
{"Partial", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: "partials/menu.html"}, true, {"Partial", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: "partials/menu.html"}, true,
"mytheme/layouts/_default/baseof.html", "mytheme/layouts/_default/baseof.html",
TemplateNames{ TemplateNames{
Name: "partials/menu.html", Name: "partials/menu.html",
OverlayFilename: "/sites/mysite/layouts/partials/menu.html", OverlayFilename: "/sites/mysite/layouts/partials/menu.html",
}}, }},
{"AMP, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, false, "", {"AMP, no base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, false, "",
TemplateNames{ TemplateNames{
Name: "_default/single.amp.html", Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
}}, }},
{"JSON, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, false, "", {"JSON, no base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, false, "",
TemplateNames{ TemplateNames{
Name: "_default/single.json", Name: "_default/single.json",
OverlayFilename: "/sites/mysite/layouts/_default/single.json", OverlayFilename: "/sites/mysite/layouts/_default/single.json",
}}, }},
{"AMP with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html", {"AMP with base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html",
TemplateNames{ TemplateNames{
Name: "_default/single.amp.html", Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
MasterFilename: "/sites/mysite/layouts/_default/single-baseof.amp.html", MasterFilename: "/sites/mysite/layouts/_default/single-baseof.amp.html",
}}, }},
{"AMP with no match in base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html", {"AMP with no match in base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html",
TemplateNames{ TemplateNames{
Name: "_default/single.amp.html", Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html", OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
@ -115,7 +116,7 @@ func TestLayoutBase(t *testing.T) {
MasterFilename: "", MasterFilename: "",
}}, }},
{"JSON with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, true, "single-baseof.json", {"JSON with base", TemplateLookupDescriptor{TemplateDir: workingDir, WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, true, "single-baseof.json",
TemplateNames{ TemplateNames{
Name: "_default/single.json", Name: "_default/single.json",
OverlayFilename: "/sites/mysite/layouts/_default/single.json", OverlayFilename: "/sites/mysite/layouts/_default/single.json",

View file

@ -420,13 +420,15 @@ func (t *templateHandler) loadTemplates(absPath string, prefix string) {
li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1 li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1
relPath := path[li:] relPath := path[li:]
templateDir := path[:li-len(layoutDir)-1]
descriptor := output.TemplateLookupDescriptor{ descriptor := output.TemplateLookupDescriptor{
TemplateDir: templateDir,
WorkingDir: workingDir, WorkingDir: workingDir,
LayoutDir: layoutDir, LayoutDir: layoutDir,
RelPath: relPath, RelPath: relPath,
Prefix: prefix, Prefix: prefix,
Theme: t.PathSpec.Theme(), ThemeDir: themeDir,
OutputFormats: t.OutputFormatsConfig, OutputFormats: t.OutputFormatsConfig,
FileExists: func(filename string) (bool, error) { FileExists: func(filename string) (bool, error) {
return helpers.Exists(filename, t.Fs.Source) return helpers.Exists(filename, t.Fs.Source)