ref: 79d9f82e79014fffabaedd34a3997475967508f6
parent: 207d8fb7af6d1b7bcb9753a290fd60e90f8e5e0c
author: Noah Campbell <[email protected]>
date: Tue Sep 3 11:38:20 EDT 2013
Code reorg, helpers.go has been decomposed. It started with wanting to move templates in template bundles and the rest followed. I did my best to start grouping related functions together, but there are some that I missed. There is also the method Urlize that seems to be a special function used in both worlds. I'll need to revisit this method.
--- a/hugolib/helpers.go
+++ /dev/null
@@ -1,367 +1,0 @@
-// Copyright © 2013 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 (
- "bytes"
- "html/template"
- "errors"
- "fmt"
- "github.com/kr/pretty"
- "os"
- "os/exec"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "time"
-)
-
-var sanitizeRegexp = regexp.MustCompile("[^a-zA-Z0-9./_-]")
-var summaryLength = 70
-var summaryDivider = []byte("<!--more-->")
-
-// TODO: Make these wrappers private
-// Wrapper around Fprintf taking verbose flag in account.
-func Printvf(format string, a ...interface{}) {
- //if *verbose {
- fmt.Fprintf(os.Stderr, format, a...)
- //}
-}
-
-func Printer(x interface{}) {
- fmt.Printf("%#v", pretty.Formatter(x))
- fmt.Println("")
-}
-
-// Wrapper around Fprintln taking verbose flag in account.
-func Printvln(a ...interface{}) {
- //if *verbose {
- fmt.Fprintln(os.Stderr, a...)
- //}
-}
-
-func FatalErr(str string) {
- fmt.Println(str)
- os.Exit(1)
-}
-
-func PrintErr(str string, a ...interface{}) {
- fmt.Fprintln(os.Stderr, str, a)
-}
-
-func Error(str string, a ...interface{}) {
- fmt.Fprintln(os.Stderr, str, a)
-}
-
-func interfaceToStringToDate(i interface{}) time.Time {
- s := interfaceToString(i)
-
- if d, e := parseDateWith(s, []string{
- time.RFC3339,
- time.RFC1123Z,
- time.RFC1123,
- time.RFC822Z,
- time.RFC822,
- time.ANSIC,
- time.UnixDate,
- time.RubyDate,
- "2006-01-02 15:04:05Z07:00",
- "02 Jan 06 15:04 MST",
- "2006-01-02",
- "02 Jan 2006",
- }); e == nil {
- return d
- }
-
- return time.Unix(0, 0)
-}
-
-func parseDateWith(s string, dates []string) (d time.Time, e error) {
- for _, dateType := range dates {
- if d, e = time.Parse(dateType, s); e == nil {
- return
- }
- }
- return d, errors.New(fmt.Sprintf("Unable to parse date: %s", s))
-}
-
-func interfaceToBool(i interface{}) bool {
- switch b := i.(type) {
- case bool:
- return b
- default:
- Error("Only Boolean values are supported for this YAML key")
- }
-
- return false
-
-}
-
-func interfaceArrayToStringArray(i interface{}) []string {
- var a []string
-
- switch vv := i.(type) {
- case []interface{}:
- for _, u := range vv {
- a = append(a, interfaceToString(u))
- }
- }
-
- return a
-}
-
-func interfaceToString(i interface{}) string {
- switch s := i.(type) {
- case string:
- return s
- default:
- Error("Only Strings are supported for this YAML key")
- }
-
- return ""
-}
-
-// Check if Exists && is Directory
-func dirExists(path string) (bool, error) {
- fi, err := os.Stat(path)
- if err == nil && fi.IsDir() {
- return true, nil
- }
- if os.IsNotExist(err) {
- return false, nil
- }
- return false, err
-}
-
-// 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
-}
-
-func mkdirIf(path string) error {
- return os.MkdirAll(path, 0777)
-}
-
-func Urlize(url string) string {
- return Sanitize(strings.ToLower(strings.Replace(strings.TrimSpace(url), " ", "-", -1)))
-}
-
-func AbsUrl(url string, base string) template.HTML {
- if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
- return template.HTML(url)
- }
- return template.HTML(MakePermalink(base, url))
-}
-
-func Gt(a interface{}, b interface{}) bool {
- var left, right int64
- av := reflect.ValueOf(a)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- left = int64(av.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- left = av.Int()
- case reflect.String:
- left, _ = strconv.ParseInt(av.String(), 10, 64)
- }
-
- bv := reflect.ValueOf(b)
-
- switch bv.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- right = int64(bv.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- right = bv.Int()
- case reflect.String:
- right, _ = strconv.ParseInt(bv.String(), 10, 64)
- }
-
- return left > right
-}
-
-func IsSet(a interface{}, key interface{}) bool {
- av := reflect.ValueOf(a)
- kv := reflect.ValueOf(key)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Slice:
- if int64(av.Len()) > kv.Int() {
- return true
- }
- case reflect.Map:
- if kv.Type() == av.Type().Key() {
- return av.MapIndex(kv).IsValid()
- }
- }
-
- return false
-}
-
-func ReturnWhenSet(a interface{}, index int) interface{} {
- av := reflect.ValueOf(a)
-
- switch av.Kind() {
- case reflect.Array, reflect.Slice:
- if av.Len() > index {
-
- avv := av.Index(index)
- switch avv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return avv.Int()
- case reflect.String:
- return avv.String()
- }
- }
- }
-
- return ""
-}
-
-func Sanitize(s string) string {
- return sanitizeRegexp.ReplaceAllString(s, "")
-}
-
-func fileExt(path string) (file, ext string) {
- if strings.Contains(path, ".") {
- i := len(path) - 1
- for path[i] != '.' {
- i--
- }
- return path[:i], path[i+1:]
- }
- return path, ""
-}
-
-func replaceExtension(path string, newExt string) string {
- f, _ := fileExt(path)
- return f + "." + newExt
-}
-
-func TotalWords(s string) int {
- return len(strings.Fields(s))
-}
-
-func WordCount(s string) map[string]int {
- m := make(map[string]int)
- for _, f := range strings.Fields(s) {
- m[f] += 1
- }
-
- return m
-}
-
-func RemoveSummaryDivider(content []byte) []byte {
- return bytes.Replace(content, summaryDivider, []byte(""), -1)
-}
-
-func StripHTML(s string) string {
- output := ""
-
- // Shortcut strings with no tags in them
- if !strings.ContainsAny(s, "<>") {
- output = s
- } else {
- s = strings.Replace(s, "\n", " ", -1)
- s = strings.Replace(s, "</p>", " \n", -1)
- s = strings.Replace(s, "<br>", " \n", -1)
- s = strings.Replace(s, "</br>", " \n", -1)
-
- // Walk through the string removing all tags
- b := new(bytes.Buffer)
- inTag := false
- for _, r := range s {
- switch r {
- case '<':
- inTag = true
- case '>':
- inTag = false
- default:
- if !inTag {
- b.WriteRune(r)
- }
- }
- }
- output = b.String()
- }
- return output
-}
-
-func TruncateWords(s string, max int) string {
- words := strings.Fields(s)
- if max > len(words) {
- return strings.Join(words, " ")
- }
-
- return strings.Join(words[:max], " ")
-}
-
-func TruncateWordsToWholeSentence(s string, max int) string {
- words := strings.Fields(s)
- if max > len(words) {
- return strings.Join(words, " ")
- }
-
- for counter, word := range words[max:] {
- if strings.HasSuffix(word, ".") ||
- strings.HasSuffix(word, "?") ||
- strings.HasSuffix(word, ".\"") ||
- strings.HasSuffix(word, "!") {
- return strings.Join(words[:max+counter+1], " ")
- }
- }
-
- return strings.Join(words[:max], " ")
-}
-
-func MakePermalink(domain string, path string) string {
- return strings.TrimRight(domain, "/") + "/" + strings.TrimLeft(path, "/")
-}
-
-func getSummaryString(content []byte) ([]byte, bool) {
- if bytes.Contains(content, summaryDivider) {
- return bytes.Split(content, summaryDivider)[0], false
- } else {
- plainContent := StripHTML(StripShortcodes(string(content)))
- return []byte(TruncateWordsToWholeSentence(plainContent, summaryLength)), true
- }
-}
-
-func getRstContent(content []byte) string {
- cleanContent := bytes.Replace(content, summaryDivider, []byte(""), 1)
-
- cmd := exec.Command("rst2html.py", "--leave-comments")
- cmd.Stdin = bytes.NewReader(cleanContent)
- var out bytes.Buffer
- cmd.Stdout = &out
- if err := cmd.Run(); err != nil {
- fmt.Println(err)
- }
-
- rstLines := strings.Split(out.String(), "\n")
- for i, line := range rstLines {
- if strings.HasPrefix(line, "<body>") {
- rstLines = (rstLines[i+1 : len(rstLines)-3])
- }
- }
- return strings.Join(rstLines, "\n")
-}
--- a/hugolib/index.go
+++ b/hugolib/index.go
@@ -14,6 +14,7 @@
package hugolib
import (
+ "github.com/spf13/hugo/template"
"sort"
)
@@ -30,7 +31,7 @@
// KeyPrep... Indexes should be case insensitive. Can make it easily conditional later.
func kp(in string) string {
- return Urlize(in)
+ return template.Urlize(in)
}
func (i Index) Get(key string) Pages { return i[kp(key)] }
--- /dev/null
+++ b/hugolib/metadata.go
@@ -1,0 +1,81 @@
+package hugolib
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "time"
+)
+
+func interfaceToStringToDate(i interface{}) time.Time {
+ s := interfaceToString(i)
+
+ if d, e := parseDateWith(s, []string{
+ time.RFC3339,
+ time.RFC1123Z,
+ time.RFC1123,
+ time.RFC822Z,
+ time.RFC822,
+ time.ANSIC,
+ time.UnixDate,
+ time.RubyDate,
+ "2006-01-02 15:04:05Z07:00",
+ "02 Jan 06 15:04 MST",
+ "2006-01-02",
+ "02 Jan 2006",
+ }); e == nil {
+ return d
+ }
+
+ return time.Unix(0, 0)
+}
+
+// TODO remove this and return a proper error.
+func errorf(str string, a ...interface{}) {
+ fmt.Fprintln(os.Stderr, str, a)
+}
+
+func parseDateWith(s string, dates []string) (d time.Time, e error) {
+ for _, dateType := range dates {
+ if d, e = time.Parse(dateType, s); e == nil {
+ return
+ }
+ }
+ return d, errors.New(fmt.Sprintf("Unable to parse date: %s", s))
+}
+
+func interfaceToBool(i interface{}) bool {
+ switch b := i.(type) {
+ case bool:
+ return b
+ default:
+ errorf("Only Boolean values are supported for this YAML key")
+ }
+
+ return false
+
+}
+
+func interfaceArrayToStringArray(i interface{}) []string {
+ var a []string
+
+ switch vv := i.(type) {
+ case []interface{}:
+ for _, u := range vv {
+ a = append(a, interfaceToString(u))
+ }
+ }
+
+ return a
+}
+
+func interfaceToString(i interface{}) string {
+ switch s := i.(type) {
+ case string:
+ return s
+ default:
+ errorf("Only Strings are supported for this YAML key")
+ }
+
+ return ""
+}
--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -14,8 +14,8 @@
package hugolib
import (
- "time"
"html/template"
+ "time"
)
type Node struct {
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -20,9 +20,11 @@
"errors"
"fmt"
"github.com/BurntSushi/toml"
+ helper "github.com/spf13/hugo/template"
+ "github.com/spf13/hugo/template/bundle"
"github.com/theplant/blackfriday"
- "io"
"html/template"
+ "io"
"io/ioutil"
"launchpad.net/goyaml"
"os"
@@ -46,7 +48,7 @@
contentType string
Draft bool
Aliases []string
- Tmpl Template
+ Tmpl bundle.Template
Markup string
PageMeta
File
@@ -78,6 +80,15 @@
func (p Pages) Sort() { sort.Sort(p) }
func (p Pages) Limit(n int) Pages { return p[0:n] }
+func getSummaryString(content []byte) ([]byte, bool) {
+ if bytes.Contains(content, summaryDivider) {
+ return bytes.Split(content, summaryDivider)[0], false
+ } else {
+ plainContent := StripHTML(StripShortcodes(string(content)))
+ return []byte(TruncateWordsToWholeSentence(plainContent, summaryLength)), true
+ }
+}
+
// TODO abstract further to support loading from more
// than just files on disk. Should load reader (file, []byte)
func NewPage(filename string) *Page {
@@ -91,6 +102,38 @@
return &page
}
+func StripHTML(s string) string {
+ output := ""
+
+ // Shortcut strings with no tags in them
+ if !strings.ContainsAny(s, "<>") {
+ output = s
+ } else {
+ s = strings.Replace(s, "\n", " ", -1)
+ s = strings.Replace(s, "</p>", " \n", -1)
+ s = strings.Replace(s, "<br>", " \n", -1)
+ s = strings.Replace(s, "</br>", " \n", -1)
+
+ // Walk through the string removing all tags
+ b := new(bytes.Buffer)
+ inTag := false
+ for _, r := range s {
+ switch r {
+ case '<':
+ inTag = true
+ case '>':
+ inTag = false
+ default:
+ if !inTag {
+ b.WriteRune(r)
+ }
+ }
+ }
+ output = b.String()
+ }
+ return output
+}
+
func (page *Page) Initalize() error {
err := page.buildPageFromFile()
if err != nil {
@@ -246,12 +289,12 @@
case "description":
page.Description = interfaceToString(v)
case "slug":
- page.Slug = Urlize(interfaceToString(v))
+ page.Slug = helper.Urlize(interfaceToString(v))
case "url":
if url := interfaceToString(v); strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
return fmt.Errorf("Only relative urls are supported, %v provided", url)
}
- page.Url = Urlize(interfaceToString(v))
+ page.Url = helper.Urlize(interfaceToString(v))
case "type":
page.contentType = interfaceToString(v)
case "keywords":
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1,11 +1,11 @@
package hugolib
import (
+ "html/template"
"path/filepath"
- "time"
"strings"
"testing"
- "html/template"
+ "time"
)
var EMPTY_PAGE = ""
--- /dev/null
+++ b/hugolib/path.go
@@ -1,0 +1,34 @@
+package hugolib
+
+import (
+ "os"
+ "strings"
+)
+
+func fileExt(path string) (file, ext string) {
+ if strings.Contains(path, ".") {
+ i := len(path) - 1
+ for path[i] != '.' {
+ i--
+ }
+ return path[:i], path[i+1:]
+ }
+ return path, ""
+}
+
+func replaceExtension(path string, newExt string) string {
+ f, _ := fileExt(path)
+ return f + "." + newExt
+}
+
+// Check if Exists && is Directory
+func dirExists(path string) (bool, error) {
+ fi, err := os.Stat(path)
+ if err == nil && fi.IsDir() {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
--- a/hugolib/path_seperators_windows_test.go
+++ /dev/null
@@ -1,17 +1,0 @@
-package hugolib
-
-import (
- "testing"
-)
-
-const (
- win_base = "c:\\a\\windows\\path\\layout"
- win_path = "c:\\a\\windows\\path\\layout\\sub1\\index.html"
-)
-
-func TestTemplatePathSeperator(t *testing.T) {
- tmpl := new(GoHtmlTemplate)
- if name := tmpl.generateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
- t.Fatalf("Template name incorrect. Expected: %s, Got: %s", "sub1/index.html", name)
- }
-}
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -16,6 +16,7 @@
import (
"bytes"
"fmt"
+ "github.com/spf13/hugo/template/bundle"
"strings"
"unicode"
)
@@ -36,7 +37,7 @@
type Shortcodes map[string]ShortcodeFunc
-func ShortcodesHandle(stringToParse string, p *Page, t Template) string {
+func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
posStart := strings.Index(stringToParse, "{{%")
if posStart > 0 {
posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
@@ -123,7 +124,7 @@
return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
}
-func ShortcodeRender(name string, data *ShortcodeWithPage, t Template) string {
+func ShortcodeRender(name string, data *ShortcodeWithPage, t bundle.Template) string {
buffer := new(bytes.Buffer)
t.ExecuteTemplate(buffer, "shortcodes/"+name+".html", data)
return buffer.String()
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -15,11 +15,13 @@
import (
"bitbucket.org/pkg/inflect"
- "html/template"
"bytes"
"fmt"
"github.com/spf13/hugo/target"
+ helpers "github.com/spf13/hugo/template"
+ "github.com/spf13/hugo/template/bundle"
"github.com/spf13/nitro"
+ "html/template"
"os"
"path/filepath"
"strings"
@@ -28,10 +30,27 @@
var DefaultTimer = nitro.Initalize()
+func MakePermalink(domain string, path string) string {
+ return strings.TrimRight(domain, "/") + "/" + strings.TrimLeft(path, "/")
+}
+
+func mkdirIf(path string) error {
+ return os.MkdirAll(path, 0777)
+}
+
+func FatalErr(str string) {
+ fmt.Println(str)
+ os.Exit(1)
+}
+
+func PrintErr(str string, a ...interface{}) {
+ fmt.Fprintln(os.Stderr, str, a)
+}
+
type Site struct {
Config Config
Pages Pages
- Tmpl Template
+ Tmpl bundle.Template
Indexes IndexList
Files []string
Sections Index
@@ -83,7 +102,7 @@
}
func (s *Site) prepTemplates() {
- s.Tmpl = NewTemplate()
+ s.Tmpl = bundle.NewTemplate()
s.Tmpl.LoadTemplates(s.absLayoutDir())
}
@@ -150,7 +169,6 @@
walker := func(path string, fi os.FileInfo, err error) error {
if err != nil {
- PrintErr("Walker: ", err)
return nil
}
@@ -179,6 +197,18 @@
s.Shortcodes = make(map[string]ShortcodeFunc)
}
+// 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
+}
+
func ignoreDotFile(path string) bool {
return filepath.Base(path)[0] == '.'
}
@@ -420,7 +450,7 @@
for k, o := range s.Indexes[plural] {
n := s.NewNode()
n.Title = strings.Title(k)
- url := Urlize(plural + "/" + k)
+ url := helpers.Urlize(plural + "/" + k)
plink := url
if s.Config.UglyUrls {
n.Url = url + ".html"
@@ -455,9 +485,9 @@
// XML Feed
y := s.NewXMLBuffer()
if s.Config.UglyUrls {
- n.Url = Urlize(plural + "/" + k + ".xml")
+ n.Url = helpers.Urlize(plural + "/" + k + ".xml")
} else {
- n.Url = Urlize(plural + "/" + k + "/" + "index.xml")
+ n.Url = helpers.Urlize(plural + "/" + k + "/" + "index.xml")
}
n.Permalink = permalink(s, n.Url)
s.Tmpl.ExecuteTemplate(y, "rss.xml", n)
@@ -477,7 +507,7 @@
for singular, plural := range s.Config.Indexes {
n := s.NewNode()
n.Title = strings.Title(plural)
- url := Urlize(plural)
+ url := helpers.Urlize(plural)
n.Url = url + "/index.html"
n.Permalink = permalink(s, n.Url)
n.Data["Singular"] = singular
@@ -503,7 +533,7 @@
for section, data := range s.Sections {
n := s.NewNode()
n.Title = strings.Title(inflect.Pluralize(section))
- n.Url = Urlize(section + "/" + "index.html")
+ n.Url = helpers.Urlize(section + "/" + "index.html")
n.Permalink = permalink(s, n.Url)
n.RSSlink = permalink(s, section+".xml")
n.Date = data[0].Date
@@ -522,9 +552,9 @@
if a := s.Tmpl.Lookup("rss.xml"); a != nil {
// XML Feed
if s.Config.UglyUrls {
- n.Url = Urlize(section + ".xml")
+ n.Url = helpers.Urlize(section + ".xml")
} else {
- n.Url = Urlize(section + "/" + "index.xml")
+ n.Url = helpers.Urlize(section + "/" + "index.xml")
}
n.Permalink = template.HTML(string(n.Site.BaseUrl) + n.Url)
y := s.NewXMLBuffer()
@@ -539,7 +569,7 @@
func (s *Site) RenderHomePage() error {
n := s.NewNode()
n.Title = n.Site.Title
- n.Url = Urlize(string(n.Site.BaseUrl))
+ n.Url = helpers.Urlize(string(n.Site.BaseUrl))
n.RSSlink = permalink(s, "index.xml")
n.Permalink = permalink(s, "")
if len(s.Pages) > 0 {
@@ -561,7 +591,7 @@
if a := s.Tmpl.Lookup("rss.xml"); a != nil {
// XML Feed
- n.Url = Urlize("index.xml")
+ n.Url = helpers.Urlize("index.xml")
n.Title = "Recent Content"
n.Permalink = permalink(s, "index.xml")
y := s.NewXMLBuffer()
@@ -571,7 +601,7 @@
}
if a := s.Tmpl.Lookup("404.html"); a != nil {
- n.Url = Urlize("404.html")
+ n.Url = helpers.Urlize("404.html")
n.Title = "404 Page not found"
n.Permalink = permalink(s, "404.html")
x, err := s.RenderThing(n, "404.html")
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -3,9 +3,9 @@
import (
"bytes"
"fmt"
+ "html/template"
"strings"
"testing"
- "html/template"
)
var TEMPLATE_TITLE = "{{ .Title }}"
--- /dev/null
+++ b/hugolib/summary.go
@@ -1,0 +1,75 @@
+package hugolib
+
+import (
+ "bytes"
+ "fmt"
+ "os/exec"
+ "strings"
+)
+
+var summaryLength = 70
+var summaryDivider = []byte("<!--more-->")
+
+func TotalWords(s string) int {
+ return len(strings.Fields(s))
+}
+
+func WordCount(s string) map[string]int {
+ m := make(map[string]int)
+ for _, f := range strings.Fields(s) {
+ m[f] += 1
+ }
+
+ return m
+}
+
+func RemoveSummaryDivider(content []byte) []byte {
+ return bytes.Replace(content, summaryDivider, []byte(""), -1)
+}
+
+func TruncateWords(s string, max int) string {
+ words := strings.Fields(s)
+ if max > len(words) {
+ return strings.Join(words, " ")
+ }
+
+ return strings.Join(words[:max], " ")
+}
+
+func TruncateWordsToWholeSentence(s string, max int) string {
+ words := strings.Fields(s)
+ if max > len(words) {
+ return strings.Join(words, " ")
+ }
+
+ for counter, word := range words[max:] {
+ if strings.HasSuffix(word, ".") ||
+ strings.HasSuffix(word, "?") ||
+ strings.HasSuffix(word, ".\"") ||
+ strings.HasSuffix(word, "!") {
+ return strings.Join(words[:max+counter+1], " ")
+ }
+ }
+
+ return strings.Join(words[:max], " ")
+}
+
+func getRstContent(content []byte) string {
+ cleanContent := bytes.Replace(content, summaryDivider, []byte(""), 1)
+
+ cmd := exec.Command("rst2html.py", "--leave-comments")
+ cmd.Stdin = bytes.NewReader(cleanContent)
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ if err := cmd.Run(); err != nil {
+ fmt.Println(err)
+ }
+
+ rstLines := strings.Split(out.String(), "\n")
+ for i, line := range rstLines {
+ if strings.HasPrefix(line, "<body>") {
+ rstLines = (rstLines[i+1 : len(rstLines)-3])
+ }
+ }
+ return strings.Join(rstLines, "\n")
+}
--- a/hugolib/template.go
+++ /dev/null
@@ -1,123 +1,0 @@
-package hugolib
-
-import (
- "io/ioutil"
- "github.com/eknkc/amber"
- "html/template"
- "io"
- "os"
- "path/filepath"
- "strings"
-)
-
-// HTML encapsulates a known safe HTML document fragment.
-// It should not be used for HTML from a third-party, or HTML with
-// 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 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 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)
-}
--- /dev/null
+++ b/template/bundle/bundle_test.go
@@ -1,0 +1,12 @@
+package bundle
+
+import (
+ "testing"
+)
+
+func TestNothing(t *testing.T) {
+ b := NewTemplate()
+ if b.Lookup("alias") == nil {
+ t.Fatalf("Expecting alias to be initialized with new bundle")
+ }
+}
--- /dev/null
+++ b/template/bundle/path_seperators_windows_test.go
@@ -1,0 +1,17 @@
+package bundle
+
+import (
+ "testing"
+)
+
+const (
+ win_base = "c:\\a\\windows\\path\\layout"
+ win_path = "c:\\a\\windows\\path\\layout\\sub1\\index.html"
+)
+
+func TestTemplatePathSeperator(t *testing.T) {
+ tmpl := new(GoHtmlTemplate)
+ if name := tmpl.generateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
+ t.Fatalf("Template name incorrect. Expected: %s, Got: %s", "sub1/index.html", name)
+ }
+}
--- /dev/null
+++ b/template/bundle/template.go
@@ -1,0 +1,188 @@
+package bundle
+
+import (
+ "github.com/eknkc/amber"
+ helpers "github.com/spf13/hugo/template"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+func Gt(a interface{}, b interface{}) bool {
+ var left, right int64
+ av := reflect.ValueOf(a)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ left = int64(av.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ left = av.Int()
+ case reflect.String:
+ left, _ = strconv.ParseInt(av.String(), 10, 64)
+ }
+
+ bv := reflect.ValueOf(b)
+
+ switch bv.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ right = int64(bv.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ right = bv.Int()
+ case reflect.String:
+ right, _ = strconv.ParseInt(bv.String(), 10, 64)
+ }
+
+ return left > right
+}
+
+func IsSet(a interface{}, key interface{}) bool {
+ av := reflect.ValueOf(a)
+ kv := reflect.ValueOf(key)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Slice:
+ if int64(av.Len()) > kv.Int() {
+ return true
+ }
+ case reflect.Map:
+ if kv.Type() == av.Type().Key() {
+ return av.MapIndex(kv).IsValid()
+ }
+ }
+
+ return false
+}
+
+func ReturnWhenSet(a interface{}, index int) interface{} {
+ av := reflect.ValueOf(a)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Slice:
+ if av.Len() > index {
+
+ avv := av.Index(index)
+ switch avv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return avv.Int()
+ case reflect.String:
+ return avv.String()
+ }
+ }
+ }
+
+ return ""
+}
+
+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 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": helpers.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 ignoreDotFile(path string) bool {
+ return filepath.Base(path)[0] == '.'
+}
+
+func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
+ walker := func(path string, fi os.FileInfo, err error) error {
+ if err != nil {
+ 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 {
+ return err
+ }
+
+ } else {
+ t.AddTemplateFile(tplName, path)
+ }
+ }
+ return nil
+ }
+
+ filepath.Walk(absPath, walker)
+}
--- /dev/null
+++ b/template/helpers.go
@@ -1,0 +1,29 @@
+// Copyright © 2013 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 template
+
+import (
+ "regexp"
+ "strings"
+)
+
+var sanitizeRegexp = regexp.MustCompile("[^a-zA-Z0-9./_-]")
+
+func Urlize(url string) string {
+ return Sanitize(strings.ToLower(strings.Replace(strings.TrimSpace(url), " ", "-", -1)))
+}
+
+func Sanitize(s string) string {
+ return sanitizeRegexp.ReplaceAllString(s, "")
+}