diff --git a/hugolib/page.go b/hugolib/page.go index a5fe6c8a3b3..900c05d1530 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -1115,6 +1115,9 @@ func (p *Page) update(frontmatter map[string]interface{}) error { // Needed for case insensitive fetching of params values helpers.ToLowerMap(frontmatter) + // Handle the date separately + p.s.frontmatterConfig.handleDate(frontmatter, p) + var modified time.Time var err error @@ -1151,11 +1154,7 @@ func (p *Page) update(frontmatter map[string]interface{}) error { p.Keywords = cast.ToStringSlice(v) p.params[loki] = p.Keywords case "date": - p.Date, err = cast.ToTimeE(v) - if err != nil { - p.s.Log.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path()) - } - p.params[loki] = p.Date + // Handled separately. case "headless": // For now, only the leaf bundles ("index.md") can be headless (i.e. produce no output). // We may expand on this in the future, but that gets more complex pretty fast. diff --git a/hugolib/page_frontmatter.go b/hugolib/page_frontmatter.go index 72e085b742b..db571bfe819 100644 --- a/hugolib/page_frontmatter.go +++ b/hugolib/page_frontmatter.go @@ -15,23 +15,46 @@ package hugolib import ( "fmt" + "io/ioutil" + "log" + "os" "strings" "github.com/gohugoio/hugo/config" "github.com/spf13/cast" + jww "github.com/spf13/jwalterweatherman" ) type frontmatterConfig struct { // Ordered chain. dateHandlers []frontmatterFieldHandler + + logger *jww.Notepad +} + +func (f frontmatterConfig) handleField(handlers []frontmatterFieldHandler, frontmatter map[string]interface{}, p *Page) { + for _, h := range handlers { + if handled := h(frontmatter, p); handled { + return + } + } +} + +func (f frontmatterConfig) handleDate(frontmatter map[string]interface{}, p *Page) { + f.handleField(f.dateHandlers, frontmatter, p) } type frontmatterFieldHandler func(frontmatter map[string]interface{}, p *Page) bool -func newFrontmatterConfig(cfg config.Provider) (frontmatterConfig, error) { - var f frontmatterConfig +func newFrontmatterConfig(logger *jww.Notepad, cfg config.Provider) (frontmatterConfig, error) { + + if logger == nil { + logger = jww.NewNotepad(jww.LevelWarn, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) + } - handlers := new(frontmatterFieldHandlers) + f := frontmatterConfig{logger: logger} + + handlers := &frontmatterFieldHandlers{logger: logger} f.dateHandlers = []frontmatterFieldHandler{handlers.defaultDateHandler} @@ -59,9 +82,28 @@ func newFrontmatterConfig(cfg config.Provider) (frontmatterConfig, error) { return f, nil } -type frontmatterFieldHandlers int +type frontmatterFieldHandlers struct { + logger *jww.Notepad +} + +// TODO(bep) modtime func (f *frontmatterFieldHandlers) defaultDateHandler(frontmatter map[string]interface{}, p *Page) bool { + loki := "date" + v, found := frontmatter[loki] + if !found { + return false + } + + var err error + p.Date, err = cast.ToTimeE(v) + if err != nil { + f.logger.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path()) + return true + } + + p.params[loki] = p.Date + return true } diff --git a/hugolib/page_frontmatter_test.go b/hugolib/page_frontmatter_test.go index ac7d35de6ef..a4b74400c23 100644 --- a/hugolib/page_frontmatter_test.go +++ b/hugolib/page_frontmatter_test.go @@ -31,7 +31,7 @@ func TestNewFrontmatterConfig(t *testing.T) { assert := require.New(t) - fc, err := newFrontmatterConfig(v) + fc, err := newFrontmatterConfig(newWarningLogger(), v) assert.NoError(err) assert.Equal(2, len(fc.dateHandlers)) diff --git a/hugolib/site.go b/hugolib/site.go index 95cd0a23edf..6fdde99336b 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -121,6 +121,9 @@ type Site struct { outputFormatsConfig output.Formats mediaTypesConfig media.Types + // How to handle page front matter. + frontmatterConfig frontmatterConfig + // We render each site for all the relevant output formats in serial with // this rendering context pointing to the current one. rc *siteRenderingContext @@ -177,6 +180,7 @@ func (s *Site) reset() *Site { relatedDocsHandler: newSearchIndexHandler(s.relatedDocsHandler.cfg), outputFormats: s.outputFormats, outputFormatsConfig: s.outputFormatsConfig, + frontmatterConfig: s.frontmatterConfig, mediaTypesConfig: s.mediaTypesConfig, resourceSpec: s.resourceSpec, Language: s.Language, @@ -248,6 +252,11 @@ func newSite(cfg deps.DepsCfg) (*Site, error) { titleFunc := helpers.GetTitleFunc(cfg.Language.GetString("titleCaseStyle")) + fmConfig, err := newFrontmatterConfig(cfg.Logger, cfg.Cfg) + if err != nil { + return nil, err + } + s := &Site{ PageCollections: c, layoutHandler: output.NewLayoutHandler(cfg.Cfg.GetString("themesDir") != ""), @@ -258,6 +267,7 @@ func newSite(cfg deps.DepsCfg) (*Site, error) { outputFormats: outputFormats, outputFormatsConfig: siteOutputFormatsConfig, mediaTypesConfig: siteMediaTypesConfig, + frontmatterConfig: fmConfig, } s.Info = newSiteInfo(siteBuilderCfg{s: s, pageCollections: c, language: s.Language})