diff --git a/hugolib/page.go b/hugolib/page.go index 679f13ebf..1581091cb 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -24,35 +24,35 @@ import ( "strings" "time" - "github.com/BurntSushi/toml" "github.com/spf13/cast" "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/parser" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" "github.com/theplant/blackfriday" - "launchpad.net/goyaml" - json "launchpad.net/rjson" ) 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 Template - Markup string - renderable bool - layout string - linkTitle string + 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 Template + Markup string + renderable bool + layout string + linkTitle string + frontmatter []byte + sourceFrontmatter []byte + sourceContent []byte PageMeta File Position @@ -293,35 +293,6 @@ func (p *Page) RelPermalink() (string, error) { return link.String(), nil } -func (page *Page) handleTomlMetaData(datum []byte) (interface{}, error) { - m := map[string]interface{}{} - datum = removeTomlIdentifier(datum) - if _, err := toml.Decode(string(datum), &m); err != nil { - return m, fmt.Errorf("Invalid TOML in %s \nError parsing page meta data: %s", page.FileName, err) - } - return m, nil -} - -func removeTomlIdentifier(datum []byte) []byte { - return bytes.Replace(datum, []byte("+++"), []byte(""), -1) -} - -func (page *Page) handleYamlMetaData(datum []byte) (interface{}, error) { - m := map[string]interface{}{} - if err := goyaml.Unmarshal(datum, &m); err != nil { - return m, fmt.Errorf("Invalid YAML in %s \nError parsing page meta data: %s", page.FileName, err) - } - return m, nil -} - -func (page *Page) handleJsonMetaData(datum []byte) (interface{}, error) { - var f interface{} - if err := json.Unmarshal(datum, &f); err != nil { - return f, fmt.Errorf("Invalid JSON in %v \nError parsing page meta data: %s", page.FileName, err) - } - return f, nil -} - func (page *Page) update(f interface{}) error { m := f.(map[string]interface{}) @@ -499,28 +470,6 @@ func (page *Page) Menus() PageMenus { return nil } -type frontmatterType struct { - markstart, markend []byte - parse func([]byte) (interface{}, error) - includeMark bool -} - -const YAML_DELIM = "---" -const TOML_DELIM = "+++" - -func (page *Page) detectFrontMatter(mark rune) (f *frontmatterType) { - switch mark { - case '-': - return &frontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), page.handleYamlMetaData, false} - case '+': - return &frontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), page.handleTomlMetaData, false} - case '{': - return &frontmatterType{[]byte{'{'}, []byte{'}'}, page.handleJsonMetaData, true} - default: - return nil - } -} - func (p *Page) Render(layout ...string) template.HTML { curLayout := "" @@ -573,34 +522,49 @@ func guessType(in string) string { return "unknown" } +func (page *Page) detectFrontMatter() (f *parser.FrontmatterType) { + return parser.DetectFrontMatter(rune(page.frontmatter[0])) +} + func (page *Page) parse(reader io.Reader) error { - p, err := parser.ReadFrom(reader) + psr, err := parser.ReadFrom(reader) if err != nil { return err } - page.renderable = p.IsRenderable() - - front := p.FrontMatter() - - if len(front) != 0 { - fm := page.detectFrontMatter(rune(front[0])) - meta, err := fm.parse(front) - if err != nil { - return err - } - - if err = page.update(meta); err != nil { - return err - } - + page.renderable = psr.IsRenderable() + page.frontmatter = psr.FrontMatter() + meta, err := psr.Metadata() + if err != nil { + jww.ERROR.Printf("Error parsing page meta data for %s", page.FileName) + jww.ERROR.Println(err) + return err } - page.rawContent = p.Content() + if err = page.update(meta); err != nil { + return err + } + + page.rawContent = psr.Content() page.setSummary() return nil } +func (page *Page) SetSourceContent(content []byte) { + page.sourceContent = content +} + +func (page *Page) SetSourceMetaData(in interface{}, mark rune) (err error) { + by, err := parser.InterfaceToFrontMatter(in, mark) + if err != nil { + return err + } + + page.sourceFrontmatter = by + + return nil +} + func (p *Page) ProcessShortcodes(t Template) { p.rawContent = []byte(ShortcodesHandle(string(p.rawContent), p, t)) p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t)) diff --git a/parser/frontmatter.go b/parser/frontmatter.go new file mode 100644 index 000000000..6ace0031d --- /dev/null +++ b/parser/frontmatter.go @@ -0,0 +1,129 @@ +// Copyright © 2013 Steve Francia . +// +// 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 parser + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/BurntSushi/toml" + "launchpad.net/goyaml" +) + +type FrontmatterType struct { + markstart, markend []byte + Parse func([]byte) (interface{}, error) + includeMark bool +} + +func InterfaceToFrontMatter(in interface{}, mark rune) ([]byte, error) { + if in == nil { + return []byte{}, fmt.Errorf("input was nil") + } + + b := new(bytes.Buffer) + + switch mark { + case rune(YAML_LEAD[0]): + _, err := b.Write([]byte(YAML_DELIM_UNIX)) + if err != nil { + return nil, err + } + by, err := goyaml.Marshal(in) + if err != nil { + return nil, err + } + b.Write(by) + _, err = b.Write([]byte(YAML_DELIM_UNIX)) + if err != nil { + return nil, err + } + return b.Bytes(), nil + case rune(TOML_LEAD[0]): + _, err := b.Write([]byte(TOML_DELIM_UNIX)) + if err != nil { + return nil, err + } + + err = toml.NewEncoder(b).Encode(in) + if err != nil { + fmt.Println("toml encoder failed", in) + fmt.Println(err) + return nil, err + } + _, err = b.Write([]byte("\n" + TOML_DELIM_UNIX)) + if err != nil { + return nil, err + } + return b.Bytes(), nil + case rune(JSON_LEAD[0]): + by, err := json.MarshalIndent(in, "", " ") + if err != nil { + fmt.Println("json encoder failed", in) + fmt.Println(err) + return nil, err + } + b.Write(by) + _, err = b.Write([]byte("\n")) + if err != nil { + return nil, err + } + return b.Bytes(), nil + default: + return nil, fmt.Errorf("Unsupported Format provided") + } +} + +func DetectFrontMatter(mark rune) (f *FrontmatterType) { + switch mark { + case '-': + return &FrontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), HandleYamlMetaData, false} + case '+': + return &FrontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), HandleTomlMetaData, false} + case '{': + return &FrontmatterType{[]byte{'{'}, []byte{'}'}, HandleJsonMetaData, true} + default: + return nil + } +} + +func HandleTomlMetaData(datum []byte) (interface{}, error) { + m := map[string]interface{}{} + datum = removeTomlIdentifier(datum) + if _, err := toml.Decode(string(datum), &m); err != nil { + return m, err + } + return m, nil +} + +func removeTomlIdentifier(datum []byte) []byte { + return bytes.Replace(datum, []byte(TOML_DELIM), []byte(""), -1) +} + +func HandleYamlMetaData(datum []byte) (interface{}, error) { + m := map[string]interface{}{} + if err := goyaml.Unmarshal(datum, &m); err != nil { + return m, err + } + return m, nil +} + +func HandleJsonMetaData(datum []byte) (interface{}, error) { + var f interface{} + if err := json.Unmarshal(datum, &f); err != nil { + return f, err + } + return f, nil +} diff --git a/parser/page.go b/parser/page.go index c0e1eebc4..a3c0ec7d7 100644 --- a/parser/page.go +++ b/parser/page.go @@ -13,9 +13,11 @@ const ( YAML_LEAD = "-" YAML_DELIM_UNIX = "---\n" YAML_DELIM_DOS = "---\r\n" + YAML_DELIM = "---" TOML_LEAD = "+" TOML_DELIM_UNIX = "+++\n" TOML_DELIM_DOS = "+++\r\n" + TOML_DELIM = "+++" JSON_LEAD = "{" ) @@ -39,6 +41,7 @@ type Page interface { FrontMatter() FrontMatter Content() Content IsRenderable() bool + Metadata() (interface{}, error) } type page struct { @@ -59,6 +62,19 @@ func (p *page) IsRenderable() bool { return p.render } +func (p *page) Metadata() (meta interface{}, err error) { + frontmatter := p.FrontMatter() + + if len(frontmatter) != 0 { + fm := DetectFrontMatter(rune(frontmatter[0])) + meta, err = fm.Parse(frontmatter) + if err != nil { + return + } + } + return +} + // ReadFrom reads the content from an io.Reader and constructs a page. func ReadFrom(r io.Reader) (p Page, err error) { reader := bufio.NewReader(r)