ref: d0825a211a43db2d30220394cee677fef03e23a3
parent: f62e3e9940fafb89f88e0ee6b8987b89bbfd281e
author: spf13 <[email protected]>
date: Mon Jan 27 12:16:28 EST 2014
Big refactor of pages code. Changed TOC code to only parse when actually used
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -28,29 +28,27 @@
json "launchpad.net/rjson"
"net/url"
"path"
- "sort"
"strings"
"time"
)
type Page struct {
- Status string
- Images []string
- RawContent []byte
- Content template.HTML
- Summary template.HTML
- TableOfContents template.HTML
- Truncated bool
- plain string // TODO should be []byte
- Params map[string]interface{}
- contentType string
- Draft bool
- Aliases []string
- Tmpl bundle.Template
- Markup string
- renderable bool
- layout string
- linkTitle string
+ Status string
+ Images []string
+ rawContent []byte
+ Content template.HTML
+ Summary template.HTML
+ Truncated bool
+ plain string // TODO should be []byte
+ Params map[string]interface{}
+ contentType string
+ Draft bool
+ Aliases []string
+ Tmpl bundle.Template
+ Markup string
+ renderable bool
+ layout string
+ linkTitle string
PageMeta
File
Position
@@ -75,109 +73,41 @@
type Pages []*Page
-/*
- * Implementation of a custom sorter for Pages
- */
-
-// A type to implement the sort interface for Pages
-type PageSorter struct {
- pages Pages
- by PageBy
-}
-
-// Closure used in the Sort.Less method.
-type PageBy func(p1, p2 *Page) bool
-
-func (by PageBy) Sort(pages Pages) {
- ps := &PageSorter{
- pages: pages,
- by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
- }
- sort.Sort(ps)
-}
-
-var DefaultPageSort = func(p1, p2 *Page) bool {
- if p1.Weight == p2.Weight {
- return p1.Date.Unix() > p2.Date.Unix()
- } else {
- return p1.Weight < p2.Weight
- }
-}
-
-func (ps *PageSorter) Len() int { return len(ps.pages) }
-func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] }
-
-// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
-func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) }
-
-func (p Pages) Sort() {
- PageBy(DefaultPageSort).Sort(p)
-}
-
-func (p Pages) Limit(n int) Pages {
- if len(p) < n {
- return p[0:n]
- } else {
- return p
- }
-}
-
-func (p Pages) ByWeight() Pages {
- PageBy(DefaultPageSort).Sort(p)
- return p
-}
-
-func (p Pages) ByDate() Pages {
- date := func(p1, p2 *Page) bool {
- return p1.Date.Unix() < p2.Date.Unix()
- }
-
- PageBy(date).Sort(p)
- return p
-}
-
-func (p Pages) ByLength() Pages {
- length := func(p1, p2 *Page) bool {
- return len(p1.Content) < len(p2.Content)
- }
-
- PageBy(length).Sort(p)
- return p
-}
-
-func (p Pages) Reverse() Pages {
- for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
- p[i], p[j] = p[j], p[i]
- }
-
- return p
-}
-
-func (p Page) Plain() string {
+func (p *Page) Plain() string {
if len(p.plain) == 0 {
- p.plain = StripHTML(StripShortcodes(string(p.Content)))
+ p.plain = StripHTML(StripShortcodes(string(p.rawContent)))
}
return p.plain
}
-// nb: this is only called for recognised types; so while .html might work for
-// creating posts, it results in missing summaries.
-func getSummaryString(content []byte, pagefmt string) (summary []byte, truncates bool) {
- if bytes.Contains(content, summaryDivider) {
+func (p *Page) setSummary() {
+ if bytes.Contains(p.rawContent, summaryDivider) {
// If user defines split:
// Split then render
- truncates = true // by definition
- summary = renderBytes(bytes.Split(content, summaryDivider)[0], pagefmt)
+ p.Truncated = true // by definition
+ header := string(bytes.Split(p.rawContent, summaryDivider)[0])
+ p.Summary = bytesToHTML(p.renderBytes([]byte(ShortcodesHandle(header, p, p.Tmpl))))
} else {
// If hugo defines split:
// render, strip html, then split
- plain := strings.TrimSpace(StripHTML(StripShortcodes(string(renderBytes(content, pagefmt)))))
- summary = []byte(TruncateWordsToWholeSentence(plain, summaryLength))
- truncates = len(summary) != len(plain)
+ plain := strings.TrimSpace(p.Plain())
+ p.Summary = bytesToHTML([]byte(TruncateWordsToWholeSentence(plain, summaryLength)))
+ p.Truncated = len(p.Summary) != len(plain)
}
- return
}
+func bytesToHTML(b []byte) template.HTML {
+ return template.HTML(string(b))
+}
+
+func (p *Page) renderBytes(content []byte) []byte {
+ return renderBytes(content, p.guessMarkupType())
+}
+
+func (p *Page) renderString(content string) []byte {
+ return renderBytes([]byte(content), p.guessMarkupType())
+}
+
func renderBytes(content []byte, pagefmt string) []byte {
switch pagefmt {
default:
@@ -293,22 +223,20 @@
return nil, errors.New("Zero length page name")
}
+ // Create new page
p := newPage(name)
+ // Parse for metadata & body
if err = p.parse(buf); err != nil {
return
}
+ //analyze for raw stats
p.analyzePage()
return p, nil
}
-func (p *Page) ProcessShortcodes(t bundle.Template) {
- p.Content = template.HTML(ShortcodesHandle(string(p.Content), p, t))
- p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t))
-}
-
func (p *Page) analyzePage() {
p.WordCount = TotalWords(p.Plain())
p.FuzzyWordCount = int((p.WordCount+100)/100) * 100
@@ -543,7 +471,7 @@
curLayout = layout[0]
}
- return template.HTML(string(p.ExecuteTemplate(curLayout).Bytes()))
+ return bytesToHTML(p.ExecuteTemplate(curLayout).Bytes())
}
func (p *Page) ExecuteTemplate(layout string) *bytes.Buffer {
@@ -577,7 +505,7 @@
}
func guessType(in string) string {
- switch in {
+ switch strings.ToLower(in) {
case "md", "markdown", "mdown":
return "markdown"
case "rst":
@@ -610,22 +538,36 @@
}
}
- page.Content = template.HTML(p.Content())
+ page.rawContent = p.Content()
+ page.setSummary()
return nil
}
+func (p *Page) ProcessShortcodes(t bundle.Template) {
+ p.rawContent = []byte(ShortcodesHandle(string(p.rawContent), p, t))
+ p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t))
+}
+
func (page *Page) Convert() error {
- switch page.guessMarkupType() {
- case "markdown":
- page.convertMarkdown(bytes.NewReader([]byte(page.Content)))
- case "rst":
- page.convertRestructuredText(bytes.NewReader([]byte(page.Content)))
+ markupType := page.guessMarkupType()
+ switch markupType {
+ case "markdown", "rst":
+ page.Content = bytesToHTML(page.renderString(string(RemoveSummaryDivider(page.rawContent))))
+ case "html":
+ page.Content = bytesToHTML(page.rawContent)
+ default:
+ return errors.New("Error converting unsupported file type " + markupType)
}
return nil
}
-func getTableOfContents(content []byte) template.HTML {
+// Lazily generate the TOC
+func (page *Page) TableOfContents() template.HTML {
+ return tableOfContentsFromBytes([]byte(page.Content))
+}
+
+func tableOfContentsFromBytes(content []byte) template.HTML {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
htmlFlags |= blackfriday.HTML_TOC
@@ -632,28 +574,13 @@
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
- return template.HTML(string(blackfriday.Markdown(content, renderer, 0)))
+ return template.HTML(string(blackfriday.Markdown(RemoveSummaryDivider(content), renderer, 0)))
}
-func (page *Page) convertMarkdown(lines io.Reader) {
+func ReaderToBytes(lines io.Reader) []byte {
b := new(bytes.Buffer)
b.ReadFrom(lines)
- content := b.Bytes()
- page.Content = template.HTML(string(blackfriday.MarkdownCommon(RemoveSummaryDivider(content))))
- summary, truncated := getSummaryString(content, "markdown")
- page.Summary = template.HTML(string(summary))
- page.TableOfContents = getTableOfContents(RemoveSummaryDivider(content))
- page.Truncated = truncated
-}
-
-func (page *Page) convertRestructuredText(lines io.Reader) {
- b := new(bytes.Buffer)
- b.ReadFrom(lines)
- content := b.Bytes()
- page.Content = template.HTML(getRstContent(content))
- summary, truncated := getSummaryString(content, "rst")
- page.Summary = template.HTML(string(summary))
- page.Truncated = truncated
+ return b.Bytes()
}
func (p *Page) TargetPath() (outfile string) {
--- /dev/null
+++ b/hugolib/pageSort.go
@@ -1,0 +1,96 @@
+// Copyright © 2014 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.
+// You may obtain a copy of the License at
+// http://opensource.org/licenses/Simple-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+import (
+ "sort"
+)
+
+/*
+ * Implementation of a custom sorter for Pages
+ */
+
+// A type to implement the sort interface for Pages
+type PageSorter struct {
+ pages Pages
+ by PageBy
+}
+
+// Closure used in the Sort.Less method.
+type PageBy func(p1, p2 *Page) bool
+
+func (by PageBy) Sort(pages Pages) {
+ ps := &PageSorter{
+ pages: pages,
+ by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
+ }
+ sort.Sort(ps)
+}
+
+var DefaultPageSort = func(p1, p2 *Page) bool {
+ if p1.Weight == p2.Weight {
+ return p1.Date.Unix() > p2.Date.Unix()
+ } else {
+ return p1.Weight < p2.Weight
+ }
+}
+
+func (ps *PageSorter) Len() int { return len(ps.pages) }
+func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] }
+
+// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
+func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) }
+
+func (p Pages) Sort() {
+ PageBy(DefaultPageSort).Sort(p)
+}
+
+func (p Pages) Limit(n int) Pages {
+ if len(p) < n {
+ return p[0:n]
+ } else {
+ return p
+ }
+}
+
+func (p Pages) ByWeight() Pages {
+ PageBy(DefaultPageSort).Sort(p)
+ return p
+}
+
+func (p Pages) ByDate() Pages {
+ date := func(p1, p2 *Page) bool {
+ return p1.Date.Unix() < p2.Date.Unix()
+ }
+
+ PageBy(date).Sort(p)
+ return p
+}
+
+func (p Pages) ByLength() Pages {
+ length := func(p1, p2 *Page) bool {
+ return len(p1.Content) < len(p2.Content)
+ }
+
+ PageBy(length).Sort(p)
+ return p
+}
+
+func (p Pages) Reverse() Pages {
+ for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
+ p[i], p[j] = p[j], p[i]
+ }
+
+ return p
+}
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -296,6 +296,7 @@
func TestWordCount(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_LONG_CONTENT), "simple.md")
p.Convert()
+ p.analyzePage()
if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
}
--- a/hugolib/page_toc_test.go
+++ b/hugolib/page_toc_test.go
@@ -1,11 +1,11 @@
package hugolib
import (
- "testing"
+ "testing"
)
func TestTableOfContents(t *testing.T) {
- text := `
+ text := `
Blah blah blah blah blah.
## AA
@@ -25,10 +25,10 @@
Blah blah blah blah blah.
`
- markdown := RemoveSummaryDivider([]byte(text))
- toc := string(getTableOfContents(markdown))
+ markdown := RemoveSummaryDivider([]byte(text))
+ toc := string(tableOfContentsFromBytes(markdown))
- expected := `<nav>
+ expected := `<nav>
<ul>
<li>
<ul>
@@ -45,7 +45,7 @@
</nav>
`
- if toc != expected {
- t.Errorf("Expected table of contents: %s, got: %s", expected, toc)
- }
+ if toc != expected {
+ t.Errorf("Expected table of contents: %s, got: %s", expected, toc)
+ }
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -14,21 +14,21 @@
package hugolib
import (
- "bitbucket.org/pkg/inflect"
- "bytes"
- "fmt"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/source"
- "github.com/spf13/hugo/target"
- "github.com/spf13/hugo/template/bundle"
- "github.com/spf13/hugo/transform"
- "github.com/spf13/nitro"
- "html/template"
- "io"
- "net/url"
- "os"
- "strings"
- "time"
+ "bitbucket.org/pkg/inflect"
+ "bytes"
+ "fmt"
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/target"
+ "github.com/spf13/hugo/template/bundle"
+ "github.com/spf13/hugo/transform"
+ "github.com/spf13/nitro"
+ "html/template"
+ "io"
+ "net/url"
+ "os"
+ "strings"
+ "time"
)
var _ = transform.AbsURL
@@ -36,7 +36,7 @@
var DefaultTimer *nitro.B
func MakePermalink(base *url.URL, path *url.URL) *url.URL {
- return base.ResolveReference(path)
+ return base.ResolveReference(path)
}
// Site contains all the information relevant for constructing a static
@@ -57,621 +57,621 @@
//
// 5. The entire collection of files is written to disk.
type Site struct {
- Config Config
- Pages Pages
- Tmpl bundle.Template
- Indexes IndexList
- Source source.Input
- Sections Index
- Info SiteInfo
- Shortcodes map[string]ShortcodeFunc
- timer *nitro.B
- Target target.Output
- Alias target.AliasPublisher
- Completed chan bool
- RunMode runmode
- params map[string]interface{}
+ Config Config
+ Pages Pages
+ Tmpl bundle.Template
+ Indexes IndexList
+ Source source.Input
+ Sections Index
+ Info SiteInfo
+ Shortcodes map[string]ShortcodeFunc
+ timer *nitro.B
+ Target target.Output
+ Alias target.AliasPublisher
+ Completed chan bool
+ RunMode runmode
+ params map[string]interface{}
}
type SiteInfo struct {
- BaseUrl template.URL
- Indexes IndexList
- Recent *Pages
- LastChange time.Time
- Title string
- Config *Config
- Permalinks PermalinkOverrides
- Params map[string]interface{}
+ BaseUrl template.URL
+ Indexes IndexList
+ Recent *Pages
+ LastChange time.Time
+ Title string
+ Config *Config
+ Permalinks PermalinkOverrides
+ Params map[string]interface{}
}
type runmode struct {
- Watching bool
+ Watching bool
}
func (s *Site) Running() bool {
- return s.RunMode.Watching
+ return s.RunMode.Watching
}
func init() {
- DefaultTimer = nitro.Initalize()
+ DefaultTimer = nitro.Initalize()
}
func (s *Site) timerStep(step string) {
- if s.timer == nil {
- s.timer = DefaultTimer
- }
- s.timer.Step(step)
+ if s.timer == nil {
+ s.timer = DefaultTimer
+ }
+ s.timer.Step(step)
}
func (s *Site) Build() (err error) {
- if err = s.Process(); err != nil {
- return
- }
- if err = s.Render(); err != nil {
- fmt.Printf("Error rendering site: %s\nAvailable templates:\n", err)
- for _, template := range s.Tmpl.Templates() {
- fmt.Printf("\t%s\n", template.Name())
- }
- return
- }
- return nil
+ if err = s.Process(); err != nil {
+ return
+ }
+ if err = s.Render(); err != nil {
+ fmt.Printf("Error rendering site: %s\nAvailable templates:\n", err)
+ for _, template := range s.Tmpl.Templates() {
+ fmt.Printf("\t%s\n", template.Name())
+ }
+ return
+ }
+ return nil
}
func (s *Site) Analyze() {
- s.Process()
- s.initTarget()
- s.Alias = &target.HTMLRedirectAlias{
- PublishDir: s.absPublishDir(),
- }
- s.ShowPlan(os.Stdout)
+ s.Process()
+ s.initTarget()
+ s.Alias = &target.HTMLRedirectAlias{
+ PublishDir: s.absPublishDir(),
+ }
+ s.ShowPlan(os.Stdout)
}
func (s *Site) prepTemplates() {
- s.Tmpl = bundle.NewTemplate()
- s.Tmpl.LoadTemplates(s.absLayoutDir())
+ s.Tmpl = bundle.NewTemplate()
+ s.Tmpl.LoadTemplates(s.absLayoutDir())
}
func (s *Site) addTemplate(name, data string) error {
- return s.Tmpl.AddTemplate(name, data)
+ return s.Tmpl.AddTemplate(name, data)
}
func (s *Site) Process() (err error) {
- if err = s.initialize(); err != nil {
- return
- }
- s.prepTemplates()
- s.timerStep("initialize & template prep")
- if err = s.CreatePages(); err != nil {
- return
- }
- s.setupPrevNext()
- s.timerStep("import pages")
- if err = s.BuildSiteMeta(); err != nil {
- return
- }
- s.timerStep("build indexes")
- return
+ if err = s.initialize(); err != nil {
+ return
+ }
+ s.prepTemplates()
+ s.timerStep("initialize & template prep")
+ if err = s.CreatePages(); err != nil {
+ return
+ }
+ s.setupPrevNext()
+ s.timerStep("import pages")
+ if err = s.BuildSiteMeta(); err != nil {
+ return
+ }
+ s.timerStep("build indexes")
+ return
}
func (s *Site) setupPrevNext() {
- for i, page := range s.Pages {
- if i < len(s.Pages)-1 {
- page.Next = s.Pages[i+1]
- }
+ for i, page := range s.Pages {
+ if i < len(s.Pages)-1 {
+ page.Next = s.Pages[i+1]
+ }
- if i > 0 {
- page.Prev = s.Pages[i-1]
- }
- }
+ if i > 0 {
+ page.Prev = s.Pages[i-1]
+ }
+ }
}
func (s *Site) Render() (err error) {
- if err = s.RenderAliases(); err != nil {
- return
- }
- s.timerStep("render and write aliases")
- if err = s.RenderIndexes(); err != nil {
- return
- }
- s.timerStep("render and write indexes")
- s.RenderIndexesIndexes()
- s.timerStep("render & write index indexes")
- if err = s.RenderLists(); err != nil {
- return
- }
- s.timerStep("render and write lists")
- if err = s.RenderPages(); err != nil {
- return
- }
- s.timerStep("render and write pages")
- if err = s.RenderHomePage(); err != nil {
- return
- }
- s.timerStep("render and write homepage")
- return
+ if err = s.RenderAliases(); err != nil {
+ return
+ }
+ s.timerStep("render and write aliases")
+ if err = s.RenderIndexes(); err != nil {
+ return
+ }
+ s.timerStep("render and write indexes")
+ s.RenderIndexesIndexes()
+ s.timerStep("render & write index indexes")
+ if err = s.RenderLists(); err != nil {
+ return
+ }
+ s.timerStep("render and write lists")
+ if err = s.RenderPages(); err != nil {
+ return
+ }
+ s.timerStep("render and write pages")
+ if err = s.RenderHomePage(); err != nil {
+ return
+ }
+ s.timerStep("render and write homepage")
+ return
}
func (s *Site) checkDescriptions() {
- for _, p := range s.Pages {
- if len(p.Description) < 60 {
- fmt.Println(p.FileName + " ")
- }
- }
+ for _, p := range s.Pages {
+ if len(p.Description) < 60 {
+ fmt.Println(p.FileName + " ")
+ }
+ }
}
func (s *Site) initialize() (err error) {
- if err = s.checkDirectories(); err != nil {
- return err
- }
+ if err = s.checkDirectories(); err != nil {
+ return err
+ }
- staticDir := s.Config.GetAbsPath(s.Config.StaticDir + "/")
+ staticDir := s.Config.GetAbsPath(s.Config.StaticDir + "/")
- s.Source = &source.Filesystem{
- AvoidPaths: []string{staticDir},
- Base: s.absContentDir(),
- }
+ s.Source = &source.Filesystem{
+ AvoidPaths: []string{staticDir},
+ Base: s.absContentDir(),
+ }
- s.initializeSiteInfo()
+ s.initializeSiteInfo()
- s.Shortcodes = make(map[string]ShortcodeFunc)
- return
+ s.Shortcodes = make(map[string]ShortcodeFunc)
+ return
}
func (s *Site) initializeSiteInfo() {
- s.Info = SiteInfo{
- BaseUrl: template.URL(s.Config.BaseUrl),
- Title: s.Config.Title,
- Recent: &s.Pages,
- Config: &s.Config,
- Params: s.Config.Params,
- Permalinks: s.Config.Permalinks,
- }
+ s.Info = SiteInfo{
+ BaseUrl: template.URL(s.Config.BaseUrl),
+ Title: s.Config.Title,
+ Recent: &s.Pages,
+ Config: &s.Config,
+ Params: s.Config.Params,
+ Permalinks: s.Config.Permalinks,
+ }
}
// Check if File / Directory Exists
func exists(path string) (bool, error) {
- _, err := os.Stat(path)
- if err == nil {
- return true, nil
- }
- if os.IsNotExist(err) {
- return false, nil
- }
- return false, err
+ _, err := os.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
}
func (s *Site) absLayoutDir() string {
- return s.Config.GetAbsPath(s.Config.LayoutDir)
+ return s.Config.GetAbsPath(s.Config.LayoutDir)
}
func (s *Site) absContentDir() string {
- return s.Config.GetAbsPath(s.Config.ContentDir)
+ return s.Config.GetAbsPath(s.Config.ContentDir)
}
func (s *Site) absPublishDir() string {
- return s.Config.GetAbsPath(s.Config.PublishDir)
+ return s.Config.GetAbsPath(s.Config.PublishDir)
}
func (s *Site) checkDirectories() (err error) {
- /*
- if b, _ := dirExists(s.absLayoutDir()); !b {
- return fmt.Errorf("No layout directory found, expecting to find it at " + s.absLayoutDir())
- }
- */
- if b, _ := dirExists(s.absContentDir()); !b {
- return fmt.Errorf("No source directory found, expecting to find it at " + s.absContentDir())
- }
- return
+ /*
+ if b, _ := dirExists(s.absLayoutDir()); !b {
+ return fmt.Errorf("No layout directory found, expecting to find it at " + s.absLayoutDir())
+ }
+ */
+ if b, _ := dirExists(s.absContentDir()); !b {
+ return fmt.Errorf("No source directory found, expecting to find it at " + s.absContentDir())
+ }
+ return
}
func (s *Site) CreatePages() (err error) {
- if s.Source == nil {
- panic(fmt.Sprintf("s.Source not set %s", s.absContentDir()))
- }
- if len(s.Source.Files()) < 1 {
- return fmt.Errorf("No source files found in %s", s.absContentDir())
- }
- for _, file := range s.Source.Files() {
- page, err := ReadFrom(file.Contents, file.LogicalName)
- if err != nil {
- return err
- }
- page.Site = s.Info
- page.Tmpl = s.Tmpl
- page.Section = file.Section
- page.Dir = file.Dir
+ if s.Source == nil {
+ panic(fmt.Sprintf("s.Source not set %s", s.absContentDir()))
+ }
+ if len(s.Source.Files()) < 1 {
+ return fmt.Errorf("No source files found in %s", s.absContentDir())
+ }
+ for _, file := range s.Source.Files() {
+ page, err := ReadFrom(file.Contents, file.LogicalName)
+ if err != nil {
+ return err
+ }
+ page.Site = s.Info
+ page.Tmpl = s.Tmpl
+ page.Section = file.Section
+ page.Dir = file.Dir
- //Handling short codes prior to Conversion to HTML
- page.ProcessShortcodes(s.Tmpl)
+ //Handling short codes prior to Conversion to HTML
+ page.ProcessShortcodes(s.Tmpl)
- err = page.Convert()
- if err != nil {
- return err
- }
+ err = page.Convert()
+ if err != nil {
+ return err
+ }
- if s.Config.BuildDrafts || !page.Draft {
- s.Pages = append(s.Pages, page)
- }
- }
+ if s.Config.BuildDrafts || !page.Draft {
+ s.Pages = append(s.Pages, page)
+ }
+ }
- s.Pages.Sort()
- return
+ s.Pages.Sort()
+ return
}
func (s *Site) BuildSiteMeta() (err error) {
- s.Indexes = make(IndexList)
- s.Sections = make(Index)
+ s.Indexes = make(IndexList)
+ s.Sections = make(Index)
- for _, plural := range s.Config.Indexes {
- s.Indexes[plural] = make(Index)
- for _, p := range s.Pages {
- vals := p.GetParam(plural)
- weight := p.GetParam(plural + "_weight")
- if weight == nil {
- weight = 0
- }
+ for _, plural := range s.Config.Indexes {
+ s.Indexes[plural] = make(Index)
+ for _, p := range s.Pages {
+ vals := p.GetParam(plural)
+ weight := p.GetParam(plural + "_weight")
+ if weight == nil {
+ weight = 0
+ }
- if vals != nil {
- v, ok := vals.([]string)
- if ok {
- for _, idx := range v {
- x := WeightedPage{weight.(int), p}
+ if vals != nil {
+ v, ok := vals.([]string)
+ if ok {
+ for _, idx := range v {
+ x := WeightedPage{weight.(int), p}
- s.Indexes[plural].Add(idx, x)
- }
- } else {
- if s.Config.Verbose {
- fmt.Fprintf(os.Stderr, "Invalid %s in %s\n", plural, p.File.FileName)
- }
- }
- }
- }
- for k := range s.Indexes[plural] {
- s.Indexes[plural][k].Sort()
- }
- }
+ s.Indexes[plural].Add(idx, x)
+ }
+ } else {
+ if s.Config.Verbose {
+ fmt.Fprintf(os.Stderr, "Invalid %s in %s\n", plural, p.File.FileName)
+ }
+ }
+ }
+ }
+ for k := range s.Indexes[plural] {
+ s.Indexes[plural][k].Sort()
+ }
+ }
- for i, p := range s.Pages {
- s.Sections.Add(p.Section, WeightedPage{s.Pages[i].Weight, s.Pages[i]})
- }
+ for i, p := range s.Pages {
+ s.Sections.Add(p.Section, WeightedPage{s.Pages[i].Weight, s.Pages[i]})
+ }
- for k := range s.Sections {
- s.Sections[k].Sort()
- }
+ for k := range s.Sections {
+ s.Sections[k].Sort()
+ }
- s.Info.Indexes = s.Indexes
+ s.Info.Indexes = s.Indexes
- if len(s.Pages) == 0 {
- return
- }
- s.Info.LastChange = s.Pages[0].Date
+ if len(s.Pages) == 0 {
+ return
+ }
+ s.Info.LastChange = s.Pages[0].Date
- // populate pages with site metadata
- for _, p := range s.Pages {
- p.Site = s.Info
- }
+ // populate pages with site metadata
+ for _, p := range s.Pages {
+ p.Site = s.Info
+ }
- return
+ return
}
func (s *Site) possibleIndexes() (indexes []string) {
- for _, p := range s.Pages {
- for k := range p.Params {
- if !inStringArray(indexes, k) {
- indexes = append(indexes, k)
- }
- }
- }
- return
+ for _, p := range s.Pages {
+ for k := range p.Params {
+ if !inStringArray(indexes, k) {
+ indexes = append(indexes, k)
+ }
+ }
+ }
+ return
}
func inStringArray(arr []string, el string) bool {
- for _, v := range arr {
- if v == el {
- return true
- }
- }
- return false
+ for _, v := range arr {
+ if v == el {
+ return true
+ }
+ }
+ return false
}
func (s *Site) RenderAliases() error {
- for _, p := range s.Pages {
- for _, a := range p.Aliases {
- plink, err := p.Permalink()
- if err != nil {
- return err
- }
- if err := s.WriteAlias(a, template.HTML(plink)); err != nil {
- return err
- }
- }
- }
- return nil
+ for _, p := range s.Pages {
+ for _, a := range p.Aliases {
+ plink, err := p.Permalink()
+ if err != nil {
+ return err
+ }
+ if err := s.WriteAlias(a, template.HTML(plink)); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
}
func (s *Site) RenderPages() (err error) {
- for _, p := range s.Pages {
- var layout []string
+ for _, p := range s.Pages {
+ var layout []string
- if !p.IsRenderable() {
- self := "__" + p.TargetPath()
- _, err := s.Tmpl.New(self).Parse(string(p.Content))
- if err != nil {
- return err
- }
- layout = append(layout, self)
- } else {
- layout = append(layout, p.Layout()...)
- layout = append(layout, "_default/single.html")
- }
+ if !p.IsRenderable() {
+ self := "__" + p.TargetPath()
+ _, err := s.Tmpl.New(self).Parse(string(p.Content))
+ if err != nil {
+ return err
+ }
+ layout = append(layout, self)
+ } else {
+ layout = append(layout, p.Layout()...)
+ layout = append(layout, "_default/single.html")
+ }
- err := s.render(p, p.TargetPath(), layout...)
- if err != nil {
- return err
- }
- }
- return nil
+ err := s.render(p, p.TargetPath(), layout...)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
}
func (s *Site) RenderIndexes() error {
- for singular, plural := range s.Config.Indexes {
- for k, o := range s.Indexes[plural] {
- n := s.NewNode()
- n.Title = strings.Title(k)
- url := helpers.Urlize(plural + "/" + k)
- n.Url = url + ".html"
- plink := n.Url
- n.Permalink = permalink(s, plink)
- n.RSSLink = permalink(s, url+".xml")
- n.Date = o[0].Page.Date
- n.Data[singular] = o
- n.Data["Pages"] = o.Pages()
- layout := "indexes/" + singular + ".html"
+ for singular, plural := range s.Config.Indexes {
+ for k, o := range s.Indexes[plural] {
+ n := s.NewNode()
+ n.Title = strings.Title(k)
+ url := helpers.Urlize(plural + "/" + k)
+ n.Url = url + ".html"
+ plink := n.Url
+ n.Permalink = permalink(s, plink)
+ n.RSSLink = permalink(s, url+".xml")
+ n.Date = o[0].Page.Date
+ n.Data[singular] = o
+ n.Data["Pages"] = o.Pages()
+ layout := "indexes/" + singular + ".html"
- var base string
- base = plural + "/" + k
- err := s.render(n, base+".html", layout)
- if err != nil {
- return err
- }
+ var base string
+ base = plural + "/" + k
+ err := s.render(n, base+".html", layout)
+ if err != nil {
+ return err
+ }
- if a := s.Tmpl.Lookup("rss.xml"); a != nil {
- // XML Feed
- n.Url = helpers.Urlize(plural + "/" + k + ".xml")
- n.Permalink = permalink(s, n.Url)
- err := s.render(n, base+".xml", "rss.xml")
- if err != nil {
- return err
- }
- }
- }
- }
- return nil
-}
+ if a := s.Tmpl.Lookup("rss.xml"); a != nil {
+ // XML Feed
+ n.Url = helpers.Urlize(plural + "/" + k + ".xml")
+ n.Permalink = permalink(s, n.Url)
+ err := s.render(n, base+".xml", "rss.xml")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
func (s *Site) RenderIndexesIndexes() (err error) {
- layout := "indexes/indexes.html"
- if s.Tmpl.Lookup(layout) != nil {
- for singular, plural := range s.Config.Indexes {
- n := s.NewNode()
- n.Title = strings.Title(plural)
- url := helpers.Urlize(plural)
- n.Url = url + "/index.html"
- n.Permalink = permalink(s, n.Url)
- n.Data["Singular"] = singular
- n.Data["Plural"] = plural
- n.Data["Index"] = s.Indexes[plural]
- // keep the following just for legacy reasons
- n.Data["OrderedIndex"] = s.Indexes[plural]
+ layout := "indexes/indexes.html"
+ if s.Tmpl.Lookup(layout) != nil {
+ for singular, plural := range s.Config.Indexes {
+ n := s.NewNode()
+ n.Title = strings.Title(plural)
+ url := helpers.Urlize(plural)
+ n.Url = url + "/index.html"
+ n.Permalink = permalink(s, n.Url)
+ n.Data["Singular"] = singular
+ n.Data["Plural"] = plural
+ n.Data["Index"] = s.Indexes[plural]
+ // keep the following just for legacy reasons
+ n.Data["OrderedIndex"] = s.Indexes[plural]
- err := s.render(n, plural+"/index.html", layout)
- if err != nil {
- return err
- }
- }
- }
- return
+ err := s.render(n, plural+"/index.html", layout)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return
}
func (s *Site) RenderLists() error {
- for section, data := range s.Sections {
- n := s.NewNode()
- n.Title = strings.Title(inflect.Pluralize(section))
- n.Url = helpers.Urlize(section + "/" + "index.html")
- n.Permalink = permalink(s, n.Url)
- n.RSSLink = permalink(s, section+".xml")
- n.Date = data[0].Page.Date
- n.Data["Pages"] = data.Pages()
- layout := "indexes/" + section + ".html"
+ for section, data := range s.Sections {
+ n := s.NewNode()
+ n.Title = strings.Title(inflect.Pluralize(section))
+ n.Url = helpers.Urlize(section + "/" + "index.html")
+ n.Permalink = permalink(s, n.Url)
+ n.RSSLink = permalink(s, section+".xml")
+ n.Date = data[0].Page.Date
+ n.Data["Pages"] = data.Pages()
+ layout := "indexes/" + section + ".html"
- err := s.render(n, section, layout, "_default/indexes.html")
- if err != nil {
- return err
- }
+ err := s.render(n, section, layout, "_default/indexes.html")
+ if err != nil {
+ return err
+ }
- if a := s.Tmpl.Lookup("rss.xml"); a != nil {
- // XML Feed
- n.Url = helpers.Urlize(section + ".xml")
- n.Permalink = template.HTML(string(n.Site.BaseUrl) + n.Url)
- err = s.render(n, section+".xml", "rss.xml")
- if err != nil {
- return err
- }
- }
- }
- return nil
+ if a := s.Tmpl.Lookup("rss.xml"); a != nil {
+ // XML Feed
+ n.Url = helpers.Urlize(section + ".xml")
+ n.Permalink = template.HTML(string(n.Site.BaseUrl) + n.Url)
+ err = s.render(n, section+".xml", "rss.xml")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
}
func (s *Site) RenderHomePage() error {
- n := s.NewNode()
- n.Title = n.Site.Title
- n.Url = helpers.Urlize(string(n.Site.BaseUrl))
- n.RSSLink = permalink(s, "index.xml")
- n.Permalink = permalink(s, "")
- n.Data["Pages"] = s.Pages
- err := s.render(n, "/", "index.html")
- if err != nil {
- return err
- }
+ n := s.NewNode()
+ n.Title = n.Site.Title
+ n.Url = helpers.Urlize(string(n.Site.BaseUrl))
+ n.RSSLink = permalink(s, "index.xml")
+ n.Permalink = permalink(s, "")
+ n.Data["Pages"] = s.Pages
+ err := s.render(n, "/", "index.html")
+ if err != nil {
+ return err
+ }
- if a := s.Tmpl.Lookup("rss.xml"); a != nil {
- // XML Feed
- n.Url = helpers.Urlize("index.xml")
- n.Title = "Recent Content"
- n.Permalink = permalink(s, "index.xml")
- high := 50
- if len(s.Pages) < high {
- high = len(s.Pages)
- }
- n.Data["Pages"] = s.Pages[:high]
- if len(s.Pages) > 0 {
- n.Date = s.Pages[0].Date
- }
- err := s.render(n, ".xml", "rss.xml")
- if err != nil {
- return err
- }
- }
+ if a := s.Tmpl.Lookup("rss.xml"); a != nil {
+ // XML Feed
+ n.Url = helpers.Urlize("index.xml")
+ n.Title = "Recent Content"
+ n.Permalink = permalink(s, "index.xml")
+ high := 50
+ if len(s.Pages) < high {
+ high = len(s.Pages)
+ }
+ n.Data["Pages"] = s.Pages[:high]
+ if len(s.Pages) > 0 {
+ n.Date = s.Pages[0].Date
+ }
+ err := s.render(n, ".xml", "rss.xml")
+ if err != nil {
+ return err
+ }
+ }
- if a := s.Tmpl.Lookup("404.html"); a != nil {
- n.Url = helpers.Urlize("404.html")
- n.Title = "404 Page not found"
- n.Permalink = permalink(s, "404.html")
- return s.render(n, "404.html", "404.html")
- }
+ if a := s.Tmpl.Lookup("404.html"); a != nil {
+ n.Url = helpers.Urlize("404.html")
+ n.Title = "404 Page not found"
+ n.Permalink = permalink(s, "404.html")
+ return s.render(n, "404.html", "404.html")
+ }
- return nil
+ return nil
}
func (s *Site) Stats() {
- fmt.Printf("%d pages created \n", len(s.Pages))
- for _, pl := range s.Config.Indexes {
- fmt.Printf("%d %s index created\n", len(s.Indexes[pl]), pl)
- }
+ fmt.Printf("%d pages created \n", len(s.Pages))
+ for _, pl := range s.Config.Indexes {
+ fmt.Printf("%d %s index created\n", len(s.Indexes[pl]), pl)
+ }
}
func permalink(s *Site, plink string) template.HTML {
- base, err := url.Parse(string(s.Config.BaseUrl))
- if err != nil {
- panic(err)
- }
+ base, err := url.Parse(string(s.Config.BaseUrl))
+ if err != nil {
+ panic(err)
+ }
- path, err := url.Parse(plink)
- if err != nil {
- panic(err)
- }
+ path, err := url.Parse(plink)
+ if err != nil {
+ panic(err)
+ }
- return template.HTML(MakePermalink(base, path).String())
+ return template.HTML(MakePermalink(base, path).String())
}
func (s *Site) NewNode() *Node {
- return &Node{
- Data: make(map[string]interface{}),
- Site: s.Info,
- }
+ return &Node{
+ Data: make(map[string]interface{}),
+ Site: s.Info,
+ }
}
func (s *Site) render(d interface{}, out string, layouts ...string) (err error) {
- layout := s.findFirstLayout(layouts...)
- if layout == "" {
- if s.Config.Verbose {
- fmt.Printf("Unable to locate layout: %s\n", layouts)
- }
- return
- }
+ layout := s.findFirstLayout(layouts...)
+ if layout == "" {
+ if s.Config.Verbose {
+ fmt.Printf("Unable to locate layout: %s\n", layouts)
+ }
+ return
+ }
- transformLinks := transform.NewEmptyTransforms()
+ transformLinks := transform.NewEmptyTransforms()
- if s.Config.CanonifyUrls {
- absURL, err := transform.AbsURL(s.Config.BaseUrl)
- if err != nil {
- return err
- }
- transformLinks = append(transformLinks, absURL...)
- }
+ if s.Config.CanonifyUrls {
+ absURL, err := transform.AbsURL(s.Config.BaseUrl)
+ if err != nil {
+ return err
+ }
+ transformLinks = append(transformLinks, absURL...)
+ }
- transformer := transform.NewChain(transformLinks...)
+ transformer := transform.NewChain(transformLinks...)
- var renderBuffer *bytes.Buffer
+ var renderBuffer *bytes.Buffer
- if strings.HasSuffix(out, ".xml") {
- renderBuffer = s.NewXMLBuffer()
- } else {
- renderBuffer = new(bytes.Buffer)
- }
+ if strings.HasSuffix(out, ".xml") {
+ renderBuffer = s.NewXMLBuffer()
+ } else {
+ renderBuffer = new(bytes.Buffer)
+ }
- err = s.renderThing(d, layout, renderBuffer)
- if err != nil {
- // Behavior here should be dependent on if running in server or watch mode.
- fmt.Println(fmt.Errorf("Rendering error: %v", err))
- if !s.Running() {
- os.Exit(-1)
- }
- }
+ err = s.renderThing(d, layout, renderBuffer)
+ if err != nil {
+ // Behavior here should be dependent on if running in server or watch mode.
+ fmt.Println(fmt.Errorf("Rendering error: %v", err))
+ if !s.Running() {
+ os.Exit(-1)
+ }
+ }
- var outBuffer = new(bytes.Buffer)
- if strings.HasSuffix(out, ".xml") {
- outBuffer = renderBuffer
- } else {
- transformer.Apply(outBuffer, renderBuffer)
- }
+ var outBuffer = new(bytes.Buffer)
+ if strings.HasSuffix(out, ".xml") {
+ outBuffer = renderBuffer
+ } else {
+ transformer.Apply(outBuffer, renderBuffer)
+ }
- return s.WritePublic(out, outBuffer)
+ return s.WritePublic(out, outBuffer)
}
func (s *Site) findFirstLayout(layouts ...string) (layout string) {
- for _, layout = range layouts {
- if s.Tmpl.Lookup(layout) != nil {
- return
- }
- }
- return ""
+ for _, layout = range layouts {
+ if s.Tmpl.Lookup(layout) != nil {
+ return
+ }
+ }
+ return ""
}
func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {
- // If the template doesn't exist, then return, but leave the Writer open
- if s.Tmpl.Lookup(layout) == nil {
- return fmt.Errorf("Layout not found: %s", layout)
- }
- //defer w.Close()
- return s.Tmpl.ExecuteTemplate(w, layout, d)
+ // If the template doesn't exist, then return, but leave the Writer open
+ if s.Tmpl.Lookup(layout) == nil {
+ return fmt.Errorf("Layout not found: %s", layout)
+ }
+ //defer w.Close()
+ return s.Tmpl.ExecuteTemplate(w, layout, d)
}
func (s *Site) NewXMLBuffer() *bytes.Buffer {
- header := "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"
- return bytes.NewBufferString(header)
+ header := "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"
+ return bytes.NewBufferString(header)
}
func (s *Site) initTarget() {
- if s.Target == nil {
- s.Target = &target.Filesystem{
- PublishDir: s.absPublishDir(),
- UglyUrls: s.Config.UglyUrls,
- }
- }
+ if s.Target == nil {
+ s.Target = &target.Filesystem{
+ PublishDir: s.absPublishDir(),
+ UglyUrls: s.Config.UglyUrls,
+ }
+ }
}
func (s *Site) WritePublic(path string, reader io.Reader) (err error) {
- s.initTarget()
+ s.initTarget()
- if s.Config.Verbose {
- fmt.Println(path)
- }
- return s.Target.Publish(path, reader)
+ if s.Config.Verbose {
+ fmt.Println(path)
+ }
+ return s.Target.Publish(path, reader)
}
func (s *Site) WriteAlias(path string, permalink template.HTML) (err error) {
- if s.Alias == nil {
- s.initTarget()
- s.Alias = &target.HTMLRedirectAlias{
- PublishDir: s.absPublishDir(),
- }
- }
+ if s.Alias == nil {
+ s.initTarget()
+ s.Alias = &target.HTMLRedirectAlias{
+ PublishDir: s.absPublishDir(),
+ }
+ }
- if s.Config.Verbose {
- fmt.Println(path)
- }
+ if s.Config.Verbose {
+ fmt.Println(path)
+ }
- return s.Alias.Publish(path, permalink)
+ return s.Alias.Publish(path, permalink)
}