1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-12-03 14:47:50 +01:00
gojekyll/pages/page.go

175 lines
3.9 KiB
Go
Raw Normal View History

2017-06-22 17:02:32 +02:00
package pages
import (
"bytes"
2017-07-03 15:37:14 +02:00
"fmt"
"io"
"io/ioutil"
"path/filepath"
2017-07-03 15:37:14 +02:00
"time"
2017-06-17 02:11:52 +02:00
"github.com/osteele/gojekyll/helpers"
"github.com/osteele/gojekyll/templates"
2017-07-03 15:37:14 +02:00
"github.com/osteele/liquid/generics"
)
// Page is a post or collection page.
2017-07-02 19:46:05 +02:00
type Page interface {
Document
Content(rc RenderingContext) ([]byte, error)
2017-07-03 15:37:14 +02:00
PostDate() time.Time
2017-07-02 19:46:05 +02:00
}
type page struct {
file
2017-07-01 15:35:54 +02:00
raw []byte
content *[]byte
}
// Static is in the File interface.
2017-07-02 19:46:05 +02:00
func (p *page) Static() bool { return false }
2017-07-02 19:46:05 +02:00
func newPage(filename string, f file) (*page, error) {
b, err := ioutil.ReadFile(filename)
2017-06-17 06:16:59 +02:00
if err != nil {
2017-06-22 16:37:31 +02:00
return nil, err
2017-06-17 06:16:59 +02:00
}
frontMatter, err := templates.ReadFrontMatter(&b)
2017-06-15 15:07:06 +02:00
if err != nil {
2017-06-22 16:37:31 +02:00
return nil, err
2017-06-15 15:07:06 +02:00
}
f.frontMatter = templates.MergeVariableMaps(f.frontMatter, frontMatter)
2017-07-02 19:46:05 +02:00
return &page{
file: f,
raw: b,
}, nil
}
2017-07-03 15:37:14 +02:00
// ToLiquid is in the liquid.Drop interface.
func (p *page) ToLiquid() interface{} {
var (
relpath = p.relpath
ext = filepath.Ext(relpath)
root = helpers.TrimExt(p.relpath)
base = filepath.Base(root)
)
2017-07-01 05:56:29 +02:00
data := map[string]interface{}{
"path": relpath,
"url": p.Permalink(),
// TODO output
2017-06-15 15:07:06 +02:00
// not documented, but present in both collection and non-collection pages
"permalink": p.Permalink(),
// TODO only in non-collection pages:
// TODO dir
// TODO name
// TODO next previous
// TODO Documented as present in all pages, but de facto only defined for collection pages
"id": base,
"title": base, // TODO capitalize
// TODO excerpt category? categories tags
// TODO slug
2017-07-03 15:37:14 +02:00
"categories": p.Categories(),
"tags": p.Tags(),
// TODO Only present in collection pages https://jekyllrb.com/docs/collections/#documents
"relative_path": p.Path(),
// TODO collection(name)
// TODO undocumented; only present in collection pages:
"ext": ext,
}
for k, v := range p.frontMatter {
switch k {
// doc implies these aren't present, but they appear to be present in a collection page:
// case "layout", "published":
case "permalink":
// omit this, in order to use the value above
default:
data[k] = v
}
}
2017-07-03 15:37:14 +02:00
if p.content != nil {
data["content"] = string(*p.content)
// TODO excerpt
}
return data
2017-06-15 15:07:06 +02:00
}
2017-07-03 15:37:14 +02:00
// MarshalYAML is part of the yaml.Marshaler interface
// The variables subcommand uses this.
func (p *page) MarshalYAML() (interface{}, error) {
return p.ToLiquid(), nil
}
// TemplateContext returns the local variables for template evaluation
2017-07-02 19:46:05 +02:00
func (p *page) TemplateContext(rc RenderingContext) map[string]interface{} {
2017-07-01 05:56:29 +02:00
return map[string]interface{}{
2017-07-03 15:37:14 +02:00
"page": p,
"site": rc.SiteVariables(),
}
}
2017-06-15 15:07:06 +02:00
2017-07-03 15:37:14 +02:00
// // Categories is part of the Page interface.
// func (p *page) Categories() []string {
// return []string{}
// }
// Tags is part of the Page interface.
func (p *page) Tags() []string {
return []string{}
}
// PostDate is part of the Page interface.
func (p *page) PostDate() time.Time {
switch value := p.frontMatter["date"].(type) {
case time.Time:
return value
case string:
t, err := generics.ParseTime(value)
if err == nil {
return t
}
default:
panic(fmt.Sprintf("expected a date %v", value))
}
panic("read posts should have set this")
}
// Write applies Liquid and Markdown, as appropriate.
2017-07-02 19:46:05 +02:00
func (p *page) Write(rc RenderingContext, w io.Writer) error {
rp := rc.RenderingPipeline()
b, err := rp.Render(w, p.raw, p.filename, p.TemplateContext(rc))
if err != nil {
return err
}
2017-07-01 05:56:29 +02:00
layout := templates.VariableMap(p.frontMatter).String("layout", "")
if layout != "" {
b, err = rp.ApplyLayout(layout, b, p.TemplateContext(rc))
if err != nil {
return err
}
}
_, err = w.Write(b)
return err
2017-06-15 15:07:06 +02:00
}
2017-06-22 18:56:08 +02:00
2017-07-01 15:35:54 +02:00
// Content computes the page content.
2017-07-02 19:46:05 +02:00
func (p *page) Content(rc RenderingContext) ([]byte, error) {
2017-07-01 15:35:54 +02:00
if p.content == nil {
// TODO DRY w/ Page.Write
rp := rc.RenderingPipeline()
buf := new(bytes.Buffer)
b, err := rp.Render(buf, p.raw, p.filename, p.TemplateContext(rc))
if err != nil {
return nil, err
2017-06-22 18:56:08 +02:00
}
2017-07-01 15:35:54 +02:00
p.content = &b
2017-06-22 18:56:08 +02:00
}
2017-07-01 15:35:54 +02:00
return *p.content, nil
2017-06-22 18:56:08 +02:00
}