ref: 8afff8c7c43d3e593313832dfa371461a0cb133b
parent: c0a046cbfb24d64847e6b8a5cd8be8a7e8a0fc80
author: spf13 <[email protected]>
date: Thu Apr 10 04:10:12 EDT 2014
Preliminary Theme Support
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -49,7 +49,7 @@
var hugoCmdV *cobra.Command
var BuildWatch, Draft, UglyUrls, Verbose, Logging, VerboseLog, DisableRSS bool
-var Source, Destination, BaseUrl, CfgFile, LogFile string
+var Source, Destination, Theme, BaseUrl, CfgFile, LogFile string
func Execute() {
AddCommands()
@@ -68,6 +68,7 @@
HugoCmd.PersistentFlags().BoolVar(&DisableRSS, "disableRSS", false, "Do not build RSS files")
HugoCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
HugoCmd.PersistentFlags().StringVarP(&Destination, "destination", "d", "", "filesystem path to write files to")
+ HugoCmd.PersistentFlags().StringVarP(&Theme, "theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
HugoCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
HugoCmd.PersistentFlags().BoolVar(&UglyUrls, "uglyurls", false, "if true, use /filename.html instead of /filename/")
HugoCmd.PersistentFlags().StringVarP(&BaseUrl, "base-url", "b", "", "hostname (and path) to the root eg. http://spf13.com/")
@@ -126,6 +127,11 @@
}
viper.Set("BaseUrl", BaseUrl)
}
+
+ if Theme != "" {
+ viper.Set("theme", Theme)
+ }
+
if Destination != "" {
viper.Set("PublishDir", Destination)
}
@@ -176,10 +182,24 @@
func copyStatic() error {
staticDir := helpers.AbsPathify(viper.GetString("StaticDir")) + "/"
if _, err := os.Stat(staticDir); os.IsNotExist(err) {
+ jww.ERROR.Println("Unable to find Static Directory:", viper.GetString("theme"), "in", staticDir)
return nil
}
publishDir := helpers.AbsPathify(viper.GetString("PublishDir")) + "/"
+
+ if themeSet() {
+ themeDir := helpers.AbsPathify("themes/"+viper.GetString("theme")) + "/static/"
+ if _, err := os.Stat(themeDir); os.IsNotExist(err) {
+ jww.ERROR.Println("Unable to find static directory for theme :", viper.GetString("theme"), "in", themeDir)
+ return nil
+ }
+
+ // Copy Static to Destination
+ jww.INFO.Println("syncing from", themeDir, "to", publishDir)
+ return fsync.Sync(publishDir, themeDir)
+ }
+
// Copy Static to Destination
jww.INFO.Println("syncing from", staticDir, "to", publishDir)
return fsync.Sync(publishDir, staticDir)
@@ -202,8 +222,15 @@
filepath.Walk(helpers.AbsPathify(viper.GetString("ContentDir")), walker)
filepath.Walk(helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
filepath.Walk(helpers.AbsPathify(viper.GetString("StaticDir")), walker)
+ if themeSet() {
+ filepath.Walk(helpers.AbsPathify("themes/"+viper.GetString("theme")), walker)
+ }
return a
+}
+
+func themeSet() bool {
+ return viper.GetString("theme") != ""
}
func buildSite(watching ...bool) (err error) {
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1,4 +1,4 @@
-// Copyright © 2013 Steve Francia <[email protected]>.
+// Copyright © 2013-14 Steve Francia <[email protected]>.
//
// Licensed under the Simple Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -132,6 +132,9 @@
func (s *Site) prepTemplates() {
s.Tmpl = bundle.NewTemplate()
s.Tmpl.LoadTemplates(s.absLayoutDir())
+ if s.hasTheme() {
+ s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
+ }
}
func (s *Site) addTemplate(name, data string) error {
@@ -244,6 +247,14 @@
}
}
+func (s *Site) hasTheme() bool {
+ return viper.GetString("theme") != ""
+}
+
+func (s *Site) absThemeDir() string {
+ return helpers.AbsPathify("themes/" + viper.GetString("theme"))
+}
+
func (s *Site) absLayoutDir() string {
return helpers.AbsPathify(viper.GetString("LayoutDir"))
}
@@ -422,7 +433,7 @@
layouts = append(layouts, "_default/single.html")
}
- return s.render(p, p.TargetPath(), layouts...)
+ return s.render(p, p.TargetPath(), s.appendThemeTemplates(layouts)...)
}(page)
}
wg.Wait()
@@ -433,6 +444,34 @@
return nil
}
+func (s *Site) appendThemeTemplates(in []string) []string {
+ if s.hasTheme() {
+ out := []string{}
+ // First place all non internal templates
+ for _, t := range in {
+ if !strings.HasPrefix("_internal/", t) {
+ out = append(out, t)
+ }
+ }
+
+ // Then place theme templates with the same names
+ for _, t := range in {
+ if !strings.HasPrefix("_internal/", t) {
+ out = append(out, "theme/"+t)
+ }
+ }
+ // Lastly place internal templates
+ for _, t := range in {
+ if strings.HasPrefix("_internal/", t) {
+ out = append(out, "theme/"+t)
+ }
+ }
+ return out
+ } else {
+ return in
+ }
+}
+
// Render the listing pages based on the meta data
// each unique term within a taxonomy will have a page created
func (s *Site) RenderTaxonomiesLists() (err error) {
@@ -451,8 +490,8 @@
n.Date = o[0].Page.Date
n.Data[singular] = o
n.Data["Pages"] = o.Pages()
- err = s.render(n, base+".html", "taxonomies/"+singular+".html", "indexes/"+singular+".html")
- //TODO add , "_default/taxonomy.html", "_default/list.html"
+ layouts := []string{"taxonomy/" + singular + ".html", "indexes/" + singular + ".html", "_default/taxonomy.html", "_default/list.html"}
+ err = s.render(n, base+".html", s.appendThemeTemplates(layouts)...)
if err != nil {
return err
}
@@ -460,7 +499,8 @@
if !viper.GetBool("DisableRSS") {
// XML Feed
s.setUrls(n, base+".xml")
- err := s.render(n, base+".xml", "rss.xml", "_internal/_default/rss.xml")
+ rssLayouts := []string{"taxonomy/" + singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
+ err := s.render(n, base+".xml", s.appendThemeTemplates(rssLayouts)...)
if err != nil {
return err
}
@@ -475,22 +515,20 @@
// Render a page per taxonomy that lists the terms for that taxonomy
func (s *Site) RenderListsOfTaxonomyTerms() (err error) {
- layouts := []string{"taxonomies/termslist.html", "indexes/indexes.html"}
- // TODO add "_default/termsList.html", "_default/termslist.html"
- // TODO add support for unique taxonomy terms list (`single`terms.html)
- if s.layoutExists(layouts...) {
- taxonomies := viper.GetStringMapString("Taxonomies")
- for singular, plural := range taxonomies {
- n := s.NewNode()
- n.Title = strings.Title(plural)
- s.setUrls(n, plural)
- n.Data["Singular"] = singular
- n.Data["Plural"] = plural
- n.Data["Terms"] = s.Taxonomies[plural]
- // keep the following just for legacy reasons
- n.Data["OrderedIndex"] = n.Data["Terms"]
- n.Data["Index"] = n.Data["Terms"]
-
+ taxonomies := viper.GetStringMapString("Taxonomies")
+ for singular, plural := range taxonomies {
+ n := s.NewNode()
+ n.Title = strings.Title(plural)
+ s.setUrls(n, plural)
+ n.Data["Singular"] = singular
+ n.Data["Plural"] = plural
+ n.Data["Terms"] = s.Taxonomies[plural]
+ // keep the following just for legacy reasons
+ n.Data["OrderedIndex"] = n.Data["Terms"]
+ n.Data["Index"] = n.Data["Terms"]
+ layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}
+ layouts = s.appendThemeTemplates(layouts)
+ if s.layoutExists(layouts...) {
err := s.render(n, plural+"/index.html", layouts...)
if err != nil {
return err
@@ -497,6 +535,7 @@
}
}
}
+
return
}
@@ -508,8 +547,9 @@
s.setUrls(n, section)
n.Date = data[0].Page.Date
n.Data["Pages"] = data.Pages()
+ layouts := []string{"section/" + section + ".html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"}
- err := s.render(n, section, "section/"+section+".html", "indexes/"+section+".html", "_default/section.html", "_default/list.html", "_default/indexes.html")
+ err := s.render(n, section, s.appendThemeTemplates(layouts)...)
if err != nil {
return err
}
@@ -516,9 +556,9 @@
if !viper.GetBool("DisableRSS") {
// XML Feed
+ rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
s.setUrls(n, section+".xml")
- err = s.render(n, section+".xml", "rss.xml", "_internal/_default/rss.xml")
- //TODO add section specific rss
+ err = s.render(n, section+".xml", s.appendThemeTemplates(rssLayouts)...)
if err != nil {
return err
}
@@ -532,7 +572,8 @@
n.Title = n.Site.Title
s.setUrls(n, "/")
n.Data["Pages"] = s.Pages
- err := s.render(n, "/", "index.html")
+ layouts := []string{"index.html"}
+ err := s.render(n, "/", s.appendThemeTemplates(layouts)...)
if err != nil {
return err
}
@@ -550,9 +591,13 @@
if len(s.Pages) > 0 {
n.Date = s.Pages[0].Date
}
- err := s.render(n, ".xml", "rss.xml", "_internal/_default/rss.xml")
- if err != nil {
- return err
+
+ if !viper.GetBool("DisableRSS") {
+ rssLayouts := []string{"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml"}
+ err := s.render(n, ".xml", s.appendThemeTemplates(rssLayouts)...)
+ if err != nil {
+ return err
+ }
}
}
@@ -560,7 +605,9 @@
n.Url = helpers.Urlize("404.html")
n.Title = "404 Page not found"
n.Permalink = s.permalink("404.html")
- return s.render(n, "404.html", "404.html")
+
+ layouts := []string{"404.html"}
+ return s.render(n, "404.html", s.appendThemeTemplates(layouts)...)
}
return nil
--- a/template/bundle/template.go
+++ b/template/bundle/template.go
@@ -138,6 +138,7 @@
Templates() []*template.Template
New(name string) *template.Template
LoadTemplates(absPath string)
+ LoadTemplatesWithPrefix(absPath, prefix string)
AddTemplate(name, tpl string) error
AddInternalTemplate(prefix, name, tpl string) error
AddInternalShortcode(name, tpl string) error
@@ -211,12 +212,7 @@
if err != nil {
return err
}
- s := string(b)
- _, err = t.New(name).Parse(s)
- if err != nil {
- t.errors = append(t.errors, &templateErr{name: name, err: err})
- }
- return err
+ return t.AddTemplate(name, string(b))
}
func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
@@ -227,7 +223,7 @@
return filepath.Base(path)[0] == '.'
}
-func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
+func (t *GoHtmlTemplate) loadTemplates(absPath string, prefix string) {
walker := func(path string, fi os.FileInfo, err error) error {
if err != nil {
return nil
@@ -240,6 +236,11 @@
tplName := t.generateTemplateNameFrom(absPath, path)
+ if prefix != "" {
+ tplName = strings.Trim(prefix, "/") + "/" + tplName
+ }
+
+ // TODO move this into the AddTemplateFile function
if strings.HasSuffix(path, ".amber") {
compiler := amber.New()
// Parse the input file
@@ -247,7 +248,6 @@
return nil
}
- // note t.New(tplName)
if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil {
return err
}
@@ -260,4 +260,12 @@
}
filepath.Walk(absPath, walker)
+}
+
+func (t *GoHtmlTemplate) LoadTemplatesWithPrefix(absPath string, prefix string) {
+ t.loadTemplates(absPath, prefix)
+}
+
+func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
+ t.loadTemplates(absPath, "")
}