summaryrefslogtreecommitdiffstats
path: root/hugolib
diff options
context:
space:
mode:
authorFabrizio (Misto) Milo <mistobaan@gmail.com>2013-08-31 17:47:21 -0700
committerNoah Campbell <noahcampbell@gmail.com>2013-09-01 19:51:32 -0700
commitba82a20321b7c093077c787ced76e6fa45b5bd67 (patch)
tree135f791098a7cb69c3abc687e8138d22468455b4 /hugolib
parentee5865f239786f354c6d3f14bf14bcb5890a6c22 (diff)
Add support for amber files
If a layout file ends with .amber it will interpreted as a Amber file Signed-off-by: Noah Campbell <noahcampbell@gmail.com>
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/page.go3
-rw-r--r--hugolib/page_test.go1
-rw-r--r--hugolib/shortcode.go5
-rw-r--r--hugolib/site.go152
-rw-r--r--hugolib/site_test.go3
-rw-r--r--hugolib/template.go115
6 files changed, 171 insertions, 108 deletions
diff --git a/hugolib/page.go b/hugolib/page.go
index 24b1bc860..5b2b29704 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -21,7 +21,6 @@ import (
"fmt"
"github.com/BurntSushi/toml"
"github.com/theplant/blackfriday"
- "html/template"
"io"
"io/ioutil"
"launchpad.net/goyaml"
@@ -46,7 +45,7 @@ type Page struct {
contentType string
Draft bool
Aliases []string
- Tmpl *template.Template
+ Tmpl Template
Markup string
PageMeta
File
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 0c61a703f..99f7c3a1a 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1,7 +1,6 @@
package hugolib
import (
- "html/template"
"path/filepath"
"strings"
"testing"
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index fac6f5a2e..eede060cd 100644
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -16,7 +16,6 @@ package hugolib
import (
"bytes"
"fmt"
- "html/template"
"strings"
"unicode"
)
@@ -37,7 +36,7 @@ type ShortcodeWithPage struct {
type Shortcodes map[string]ShortcodeFunc
-func ShortcodesHandle(stringToParse string, p *Page, t *template.Template) string {
+func ShortcodesHandle(stringToParse string, p *Page, t Template) string {
posStart := strings.Index(stringToParse, "{{%")
if posStart > 0 {
posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
@@ -124,7 +123,7 @@ func SplitParams(in string) (name string, par2 string) {
return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
}
-func ShortcodeRender(name string, data *ShortcodeWithPage, t *template.Template) string {
+func ShortcodeRender(name string, data *ShortcodeWithPage, t Template) string {
buffer := new(bytes.Buffer)
t.ExecuteTemplate(buffer, "shortcodes/"+name+".html", data)
return buffer.String()
diff --git a/hugolib/site.go b/hugolib/site.go
index 8f51026f3..2c6c610bf 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -16,12 +16,9 @@ package hugolib
import (
"bitbucket.org/pkg/inflect"
"bytes"
- "errors"
"fmt"
"github.com/spf13/hugo/target"
"github.com/spf13/nitro"
- "html/template"
- "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -33,7 +30,7 @@ var DefaultTimer = nitro.Initalize()
type Site struct {
Config Config
Pages Pages
- Tmpl *template.Template
+ Tmpl Template
Indexes IndexList
Files []string
Sections Index
@@ -44,7 +41,7 @@ type Site struct {
}
type SiteInfo struct {
- BaseUrl template.URL
+ BaseUrl URL
Indexes OrderedIndexList
Recent *Pages
LastChange time.Time
@@ -70,8 +67,8 @@ func (s *Site) Build() (err error) {
if err = s.Render(); err != nil {
fmt.Printf("Error rendering site: %s\n", err)
fmt.Printf("Available templates:")
- for _, template := range s.Tmpl.Templates() {
- fmt.Printf("\t%s\n", template.Name())
+ for _, tpl := range s.Tmpl.Templates() {
+ fmt.Printf("\t%s\n", tpl.Name())
}
return
}
@@ -84,6 +81,15 @@ func (s *Site) Analyze() {
s.checkDescriptions()
}
+func (s *Site) prepTemplates() {
+ s.Tmpl = NewTemplate()
+ s.Tmpl.LoadTemplates(s.absLayoutDir())
+}
+
+func (s *Site) addTemplate(name, data string) error {
+ return s.Tmpl.AddTemplate(name, data)
+}
+
func (s *Site) Process() (err error) {
s.initialize()
s.prepTemplates()
@@ -136,65 +142,6 @@ func (s *Site) checkDescriptions() {
}
}
-func (s *Site) prepTemplates() {
- var templates = template.New("")
-
- funcMap := template.FuncMap{
- "urlize": Urlize,
- "gt": Gt,
- "isset": IsSet,
- "echoParam": ReturnWhenSet,
- }
-
- templates.Funcs(funcMap)
-
- s.Tmpl = templates
- s.primeTemplates()
- s.loadTemplates()
-}
-
-func (s *Site) loadTemplates() {
- walker := func(path string, fi os.FileInfo, err error) error {
- if err != nil {
- PrintErr("Walker: ", err)
- return nil
- }
-
- if !fi.IsDir() {
- if ignoreDotFile(path) {
- return nil
- }
- filetext, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- s.addTemplate(s.generateTemplateNameFrom(path), string(filetext))
- }
- return nil
- }
-
- filepath.Walk(s.absLayoutDir(), walker)
-}
-
-func (s *Site) addTemplate(name, tmpl string) (err error) {
- _, err = s.Tmpl.New(name).Parse(tmpl)
- return
-}
-
-func (s *Site) generateTemplateNameFrom(path string) (name string) {
- name = filepath.ToSlash(path[len(s.absLayoutDir())+1:])
- return
-}
-
-func (s *Site) primeTemplates() {
- alias := "<!DOCTYPE html>\n <html>\n <head>\n <link rel=\"canonical\" href=\"{{ .Permalink }}\"/>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0;url={{ .Permalink }}\" />\n </head>\n </html>"
- alias_xhtml := "<!DOCTYPE html>\n <html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <link rel=\"canonical\" href=\"{{ .Permalink }}\"/>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0;url={{ .Permalink }}\" />\n </head>\n </html>"
-
- s.addTemplate("alias", alias)
- s.addTemplate("alias-xhtml", alias_xhtml)
-
-}
-
func (s *Site) initialize() {
s.checkDirectories()
@@ -222,7 +169,7 @@ func (s *Site) initialize() {
filepath.Walk(s.absContentDir(), walker)
s.Info = SiteInfo{
- BaseUrl: template.URL(s.Config.BaseUrl),
+ BaseUrl: URL(s.Config.BaseUrl),
Title: s.Config.Title,
Recent: &s.Pages,
Config: &s.Config,
@@ -258,22 +205,22 @@ func (s *Site) checkDirectories() {
}
func (s *Site) ProcessShortcodes() {
- for i, _ := range s.Pages {
- s.Pages[i].Content = HTML(ShortcodesHandle(string(s.Pages[i].Content), s.Pages[i], s.Tmpl))
+ for _, page := range s.Pages {
+ page.Content = HTML(ShortcodesHandle(string(page.Content), page, s.Tmpl))
}
}
func (s *Site) AbsUrlify() {
baseWithoutTrailingSlash := strings.TrimRight(s.Config.BaseUrl, "/")
baseWithSlash := baseWithoutTrailingSlash + "/"
- for i, _ := range s.Pages {
- content := string(s.Pages[i].Content)
+ for _, page := range s.Pages {
+ content := string(page.Content)
content = strings.Replace(content, " src=\"/", " src=\""+baseWithSlash, -1)
content = strings.Replace(content, " src='/", " src='"+baseWithSlash, -1)
content = strings.Replace(content, " href='/", " href='"+baseWithSlash, -1)
content = strings.Replace(content, " href=\"/", " href=\""+baseWithSlash, -1)
content = strings.Replace(content, baseWithoutTrailingSlash+"//", baseWithSlash, -1)
- s.Pages[i].Content = HTML(content)
+ page.Content = HTML(content)
}
}
@@ -294,13 +241,13 @@ func (s *Site) CreatePages() {
}
func (s *Site) setupPrevNext() {
- for i, _ := range s.Pages {
+ for i, page := range s.Pages {
if i < len(s.Pages)-1 {
- s.Pages[i].Next = s.Pages[i+1]
+ page.Next = s.Pages[i+1]
}
if i > 0 {
- s.Pages[i].Prev = s.Pages[i-1]
+ page.Prev = s.Pages[i-1]
}
}
}
@@ -310,7 +257,7 @@ func (s *Site) setUrlPath(p *Page) error {
x := strings.Split(y, string(os.PathSeparator))
if len(x) <= 1 {
- return errors.New("Zero length page name")
+ return fmt.Errorf("Zero length page name")
}
p.Section = strings.Trim(x[1], "/\\")
@@ -361,14 +308,14 @@ func (s *Site) BuildSiteMeta() (err error) {
for _, plural := range s.Config.Indexes {
s.Indexes[plural] = make(Index)
- for i, p := range s.Pages {
+ for _, p := range s.Pages {
vals := p.GetParam(plural)
if vals != nil {
v, ok := vals.([]string)
if ok {
for _, idx := range v {
- s.Indexes[plural].Add(idx, s.Pages[i])
+ s.Indexes[plural].Add(idx, p)
}
} else {
PrintErr("Invalid " + plural + " in " + p.File.FileName)
@@ -380,8 +327,8 @@ func (s *Site) BuildSiteMeta() (err error) {
}
}
- for i, p := range s.Pages {
- s.Sections.Add(p.Section, s.Pages[i])
+ for _, p := range s.Pages {
+ s.Sections.Add(p.Section, p)
}
for k, _ := range s.Sections {
@@ -424,13 +371,13 @@ func inStringArray(arr []string, el string) bool {
}
func (s *Site) RenderAliases() error {
- for i, p := range s.Pages {
+ for _, p := range s.Pages {
for _, a := range p.Aliases {
t := "alias"
if strings.HasSuffix(a, ".xhtml") {
t = "alias-xhtml"
}
- content, err := s.RenderThing(s.Pages[i], t)
+ content, err := s.RenderThing(p, t)
if strings.HasSuffix(a, "/") {
a = a + "index.html"
}
@@ -447,12 +394,12 @@ func (s *Site) RenderAliases() error {
}
func (s *Site) RenderPages() error {
- for i, _ := range s.Pages {
- content, err := s.RenderThingOrDefault(s.Pages[i], s.Pages[i].Layout(), "_default/single.html")
+ for _, p := range s.Pages {
+ content, err := s.RenderThingOrDefault(p, p.Layout(), "_default/single.html")
if err != nil {
return err
}
- s.Pages[i].RenderedContent = content
+ p.RenderedContent = content
}
return nil
}
@@ -480,8 +427,8 @@ func (s *Site) RenderIndexes() error {
} else {
n.Url = url + "/index.html"
}
- n.Permalink = HTML(MakePermalink(string(n.Site.BaseUrl), string(plink)))
- n.RSSlink = HTML(MakePermalink(string(n.Site.BaseUrl), string(url+".xml")))
+ n.Permalink = permalink(s, plink)
+ n.RSSlink = permalink(s, url+".xml")
n.Date = o[0].Date
n.Data[singular] = o
n.Data["Pages"] = o
@@ -511,7 +458,7 @@ func (s *Site) RenderIndexes() error {
} else {
n.Url = Urlize(plural + "/" + k + "/" + "index.xml")
}
- n.Permalink = HTML(string(n.Site.BaseUrl) + n.Url)
+ n.Permalink = permalink(s, n.Url)
s.Tmpl.ExecuteTemplate(y, "rss.xml", n)
err = s.WritePublic(base+".xml", y.Bytes())
if err != nil {
@@ -531,7 +478,7 @@ func (s *Site) RenderIndexesIndexes() (err error) {
n.Title = strings.Title(plural)
url := Urlize(plural)
n.Url = url + "/index.html"
- n.Permalink = HTML(MakePermalink(string(n.Site.BaseUrl), string(n.Url)))
+ n.Permalink = permalink(s, n.Url)
n.Data["Singular"] = singular
n.Data["Plural"] = plural
n.Data["Index"] = s.Indexes[plural]
@@ -556,8 +503,8 @@ func (s *Site) RenderLists() error {
n := s.NewNode()
n.Title = strings.Title(inflect.Pluralize(section))
n.Url = Urlize(section + "/" + "index.html")
- n.Permalink = HTML(MakePermalink(string(n.Site.BaseUrl), string(n.Url)))
- n.RSSlink = HTML(MakePermalink(string(n.Site.BaseUrl), string(section+".xml")))
+ n.Permalink = permalink(s, n.Url)
+ n.RSSlink = permalink(s, section+".xml")
n.Date = data[0].Date
n.Data["Pages"] = data
layout := "indexes/" + section + ".html"
@@ -592,8 +539,8 @@ func (s *Site) RenderHomePage() error {
n := s.NewNode()
n.Title = n.Site.Title
n.Url = Urlize(string(n.Site.BaseUrl))
- n.RSSlink = HTML(MakePermalink(string(n.Site.BaseUrl), string("index.xml")))
- n.Permalink = HTML(string(n.Site.BaseUrl))
+ n.RSSlink = permalink(s, "index.xml")
+ n.Permalink = permalink(s, "")
if len(s.Pages) > 0 {
n.Date = s.Pages[0].Date
if len(s.Pages) < 9 {
@@ -615,7 +562,7 @@ func (s *Site) RenderHomePage() error {
// XML Feed
n.Url = Urlize("index.xml")
n.Title = "Recent Content"
- n.Permalink = HTML(string(n.Site.BaseUrl) + "index.xml")
+ n.Permalink = permalink(s, "index.xml")
y := s.NewXMLBuffer()
s.Tmpl.ExecuteTemplate(y, "rss.xml", n)
err = s.WritePublic("index.xml", y.Bytes())
@@ -625,7 +572,7 @@ func (s *Site) RenderHomePage() error {
if a := s.Tmpl.Lookup("404.html"); a != nil {
n.Url = Urlize("404.html")
n.Title = "404 Page not found"
- n.Permalink = HTML(string(n.Site.BaseUrl) + "404.html")
+ n.Permalink = permalink(s, "404.html")
x, err := s.RenderThing(n, "404.html")
if err != nil {
return err
@@ -644,15 +591,20 @@ func (s *Site) Stats() {
}
}
-func (s *Site) NewNode() (y Node) {
- y.Data = make(map[string]interface{})
- y.Site = s.Info
- return y
+func permalink(s *Site, plink string) HTML {
+ return HTML(MakePermalink(string(s.Info.BaseUrl), plink))
+}
+
+func (s *Site) NewNode() *Node {
+ return &Node{
+ Data: make(map[string]interface{}),
+ Site: s.Info,
+ }
}
func (s *Site) RenderThing(d interface{}, layout string) (*bytes.Buffer, error) {
if s.Tmpl.Lookup(layout) == nil {
- return nil, errors.New(fmt.Sprintf("Layout not found: %s", layout))
+ return nil, fmt.Errorf("Layout not found: %s", layout)
}
buffer := new(bytes.Buffer)
err := s.Tmpl.ExecuteTemplate(buffer, layout, d)
diff --git a/hugolib/site_test.go b/hugolib/site_test.go
index 4df0bd604..0f614146f 100644
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -39,10 +39,9 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
}
}
-func TestPrimeTempaltes(t *testing.T) {
+func TestPrimeTemplates(t *testing.T) {
s := new(Site)
s.prepTemplates()
- s.primeTemplates()
if s.Tmpl.Lookup("alias") == nil {
t.Fatalf("alias template not created.")
}
diff --git a/hugolib/template.go b/hugolib/template.go
index 2d2074c07..f275c0cb9 100644
--- a/hugolib/template.go
+++ b/hugolib/template.go
@@ -1,7 +1,13 @@
package hugolib
import (
+ "io/ioutil"
+ "github.com/eknkc/amber"
"html/template"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
)
// HTML encapsulates a known safe HTML document fragment.
@@ -9,3 +15,112 @@ import (
// unclosed tags or comments. The outputs of a sound HTML sanitizer
// and a template escaped by this package are fine for use with HTML.
type HTML template.HTML
+
+type Template interface {
+ ExecuteTemplate(wr io.Writer, name string, data interface{}) error
+ Lookup(name string) *template.Template
+ Templates() []*template.Template
+ New(name string) *template.Template
+ LoadTemplates(absPath string)
+ AddTemplate(name, tpl string) error
+}
+
+type URL template.URL
+
+type templateErr struct {
+ name string
+ err error
+}
+
+type GoHtmlTemplate struct {
+ template.Template
+ errors []*templateErr
+}
+
+func NewTemplate() Template {
+ var templates = &GoHtmlTemplate{
+ Template: *template.New(""),
+ errors: make([]*templateErr, 0),
+ }
+
+ funcMap := template.FuncMap{
+ "urlize": Urlize,
+ "gt": Gt,
+ "isset": IsSet,
+ "echoParam": ReturnWhenSet,
+ }
+
+ templates.Funcs(funcMap)
+ templates.primeTemplates()
+ return templates
+}
+
+func (t *GoHtmlTemplate) AddTemplate(name, tpl string) error {
+ _, err := t.New(name).Parse(tpl)
+ if err != nil {
+ t.errors = append(t.errors, &templateErr{name: name, err: err})
+ }
+ return err
+}
+
+func (t *GoHtmlTemplate) AddTemplateFile(name, path string) error {
+ b, err := ioutil.ReadFile(path)
+ 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
+}
+
+func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
+ return filepath.ToSlash(path[len(base)+1:])
+}
+
+func (t *GoHtmlTemplate) primeTemplates() {
+ alias := "<!DOCTYPE html>\n <html>\n <head>\n <link rel=\"canonical\" href=\"{{ .Permalink }}\"/>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0;url={{ .Permalink }}\" />\n </head>\n </html>"
+ alias_xhtml := "<!DOCTYPE html>\n <html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <link rel=\"canonical\" href=\"{{ .Permalink }}\"/>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0;url={{ .Permalink }}\" />\n </head>\n </html>"
+
+ t.AddTemplate("alias", alias)
+ t.AddTemplate("alias-xhtml", alias_xhtml)
+}
+
+func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
+ walker := func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ PrintErr("Walker: ", err)
+ return nil
+ }
+
+ if !fi.IsDir() {
+ if ignoreDotFile(path) {
+ return nil
+ }
+
+ tplName := t.generateTemplateNameFrom(absPath, path)
+
+ if strings.HasSuffix(path, ".amber") {
+ compiler := amber.New()
+ // Parse the input file
+ if err := compiler.ParseFile(path); err != nil {
+ return nil
+ }
+
+ // note t.New(tplName)
+ if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil {
+ PrintErr("Could not compile amber file: "+path, err)
+ return err
+ }
+
+ } else {
+ t.AddTemplateFile(tplName, path)
+ }
+ }
+ return nil
+ }
+
+ filepath.Walk(absPath, walker)
+}