diff options
Diffstat (limited to 'hugolib/pagemeta/page_frontmatter.go')
-rw-r--r-- | hugolib/pagemeta/page_frontmatter.go | 426 |
1 files changed, 0 insertions, 426 deletions
diff --git a/hugolib/pagemeta/page_frontmatter.go b/hugolib/pagemeta/page_frontmatter.go deleted file mode 100644 index b67ffbc05..000000000 --- a/hugolib/pagemeta/page_frontmatter.go +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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 pagemeta - -import ( - "strings" - "time" - - "github.com/gohugoio/hugo/common/loggers" - "github.com/gohugoio/hugo/helpers" - - "github.com/gohugoio/hugo/config" - "github.com/spf13/cast" -) - -// FrontMatterHandler maps front matter into Page fields and .Params. -// Note that we currently have only extracted the date logic. -type FrontMatterHandler struct { - fmConfig frontmatterConfig - - dateHandler frontMatterFieldHandler - lastModHandler frontMatterFieldHandler - publishDateHandler frontMatterFieldHandler - expiryDateHandler frontMatterFieldHandler - - // A map of all date keys configured, including any custom. - allDateKeys map[string]bool - - logger *loggers.Logger -} - -// FrontMatterDescriptor describes how to handle front matter for a given Page. -// It has pointers to values in the receiving page which gets updated. -type FrontMatterDescriptor struct { - - // This the Page's front matter. - Frontmatter map[string]interface{} - - // This is the Page's base filename (BaseFilename), e.g. page.md., or - // if page is a leaf bundle, the bundle folder name (ContentBaseName). - BaseFilename string - - // The content file's mod time. - ModTime time.Time - - // May be set from the author date in Git. - GitAuthorDate time.Time - - // The below are pointers to values on Page and will be modified. - - // This is the Page's params. - Params map[string]interface{} - - // This is the Page's dates. - Dates *PageDates - - // This is the Page's Slug etc. - PageURLs *URLPath -} - -var ( - dateFieldAliases = map[string][]string{ - fmDate: {}, - fmLastmod: {"modified"}, - fmPubDate: {"pubdate", "published"}, - fmExpiryDate: {"unpublishdate"}, - } -) - -// HandleDates updates all the dates given the current configuration and the -// supplied front matter params. Note that this requires all lower-case keys -// in the params map. -func (f FrontMatterHandler) HandleDates(d *FrontMatterDescriptor) error { - if d.Dates == nil { - panic("missing dates") - } - - if f.dateHandler == nil { - panic("missing date handler") - } - - if _, err := f.dateHandler(d); err != nil { - return err - } - - if _, err := f.lastModHandler(d); err != nil { - return err - } - - if _, err := f.publishDateHandler(d); err != nil { - return err - } - - if _, err := f.expiryDateHandler(d); err != nil { - return err - } - - return nil -} - -// IsDateKey returns whether the given front matter key is considered a date by the current -// configuration. -func (f FrontMatterHandler) IsDateKey(key string) bool { - return f.allDateKeys[key] -} - -// A Zero date is a signal that the name can not be parsed. -// This follows the format as outlined in Jekyll, https://jekyllrb.com/docs/posts/: -// "Where YEAR is a four-digit number, MONTH and DAY are both two-digit numbers" -func dateAndSlugFromBaseFilename(name string) (time.Time, string) { - withoutExt, _ := helpers.FileAndExt(name) - - if len(withoutExt) < 10 { - // This can not be a date. - return time.Time{}, "" - } - - // Note: Hugo currently have no custom timezone support. - // We will have to revisit this when that is in place. - d, err := time.Parse("2006-01-02", withoutExt[:10]) - if err != nil { - return time.Time{}, "" - } - - // Be a little lenient with the format here. - slug := strings.Trim(withoutExt[10:], " -_") - - return d, slug -} - -type frontMatterFieldHandler func(d *FrontMatterDescriptor) (bool, error) - -func (f FrontMatterHandler) newChainedFrontMatterFieldHandler(handlers ...frontMatterFieldHandler) frontMatterFieldHandler { - return func(d *FrontMatterDescriptor) (bool, error) { - for _, h := range handlers { - // First successful handler wins. - success, err := h(d) - if err != nil { - f.logger.ERROR.Println(err) - } else if success { - return true, nil - } - } - return false, nil - } -} - -type frontmatterConfig struct { - date []string - lastmod []string - publishDate []string - expiryDate []string -} - -const ( - // These are all the date handler identifiers - // All identifiers not starting with a ":" maps to a front matter parameter. - fmDate = "date" - fmPubDate = "publishdate" - fmLastmod = "lastmod" - fmExpiryDate = "expirydate" - - // Gets date from filename, e.g 218-02-22-mypage.md - fmFilename = ":filename" - - // Gets date from file OS mod time. - fmModTime = ":filemodtime" - - // Gets date from Git - fmGitAuthorDate = ":git" -) - -// This is the config you get when doing nothing. -func newDefaultFrontmatterConfig() frontmatterConfig { - return frontmatterConfig{ - date: []string{fmDate, fmPubDate, fmLastmod}, - lastmod: []string{fmGitAuthorDate, fmLastmod, fmDate, fmPubDate}, - publishDate: []string{fmPubDate, fmDate}, - expiryDate: []string{fmExpiryDate}, - } -} - -func newFrontmatterConfig(cfg config.Provider) (frontmatterConfig, error) { - c := newDefaultFrontmatterConfig() - defaultConfig := c - - if cfg.IsSet("frontmatter") { - fm := cfg.GetStringMap("frontmatter") - for k, v := range fm { - loki := strings.ToLower(k) - switch loki { - case fmDate: - c.date = toLowerSlice(v) - case fmPubDate: - c.publishDate = toLowerSlice(v) - case fmLastmod: - c.lastmod = toLowerSlice(v) - case fmExpiryDate: - c.expiryDate = toLowerSlice(v) - } - } - } - - expander := func(c, d []string) []string { - out := expandDefaultValues(c, d) - out = addDateFieldAliases(out) - return out - } - - c.date = expander(c.date, defaultConfig.date) - c.publishDate = expander(c.publishDate, defaultConfig.publishDate) - c.lastmod = expander(c.lastmod, defaultConfig.lastmod) - c.expiryDate = expander(c.expiryDate, defaultConfig.expiryDate) - - return c, nil -} - -func addDateFieldAliases(values []string) []string { - var complete []string - - for _, v := range values { - complete = append(complete, v) - if aliases, found := dateFieldAliases[v]; found { - complete = append(complete, aliases...) - } - } - return helpers.UniqueStrings(complete) -} - -func expandDefaultValues(values []string, defaults []string) []string { - var out []string - for _, v := range values { - if v == ":default" { - out = append(out, defaults...) - } else { - out = append(out, v) - } - } - return out -} - -func toLowerSlice(in interface{}) []string { - out := cast.ToStringSlice(in) - for i := 0; i < len(out); i++ { - out[i] = strings.ToLower(out[i]) - } - - return out -} - -// NewFrontmatterHandler creates a new FrontMatterHandler with the given logger and configuration. -// If no logger is provided, one will be created. -func NewFrontmatterHandler(logger *loggers.Logger, cfg config.Provider) (FrontMatterHandler, error) { - - if logger == nil { - logger = loggers.NewWarningLogger() - } - - frontMatterConfig, err := newFrontmatterConfig(cfg) - if err != nil { - return FrontMatterHandler{}, err - } - - allDateKeys := make(map[string]bool) - addKeys := func(vals []string) { - for _, k := range vals { - if !strings.HasPrefix(k, ":") { - allDateKeys[k] = true - } - } - } - - addKeys(frontMatterConfig.date) - addKeys(frontMatterConfig.expiryDate) - addKeys(frontMatterConfig.lastmod) - addKeys(frontMatterConfig.publishDate) - - f := FrontMatterHandler{logger: logger, fmConfig: frontMatterConfig, allDateKeys: allDateKeys} - - if err := f.createHandlers(); err != nil { - return f, err - } - - return f, nil -} - -func (f *FrontMatterHandler) createHandlers() error { - var err error - - if f.dateHandler, err = f.createDateHandler(f.fmConfig.date, - func(d *FrontMatterDescriptor, t time.Time) { - d.Dates.Date = t - setParamIfNotSet(fmDate, t, d) - }); err != nil { - return err - } - - if f.lastModHandler, err = f.createDateHandler(f.fmConfig.lastmod, - func(d *FrontMatterDescriptor, t time.Time) { - setParamIfNotSet(fmLastmod, t, d) - d.Dates.Lastmod = t - }); err != nil { - return err - } - - if f.publishDateHandler, err = f.createDateHandler(f.fmConfig.publishDate, - func(d *FrontMatterDescriptor, t time.Time) { - setParamIfNotSet(fmPubDate, t, d) - d.Dates.PublishDate = t - }); err != nil { - return err - } - - if f.expiryDateHandler, err = f.createDateHandler(f.fmConfig.expiryDate, - func(d *FrontMatterDescriptor, t time.Time) { - setParamIfNotSet(fmExpiryDate, t, d) - d.Dates.ExpiryDate = t - }); err != nil { - return err - } - - return nil -} - -func setParamIfNotSet(key string, value interface{}, d *FrontMatterDescriptor) { - if _, found := d.Params[key]; found { - return - } - d.Params[key] = value -} - -func (f FrontMatterHandler) createDateHandler(identifiers []string, setter func(d *FrontMatterDescriptor, t time.Time)) (frontMatterFieldHandler, error) { - var h *frontmatterFieldHandlers - var handlers []frontMatterFieldHandler - - for _, identifier := range identifiers { - switch identifier { - case fmFilename: - handlers = append(handlers, h.newDateFilenameHandler(setter)) - case fmModTime: - handlers = append(handlers, h.newDateModTimeHandler(setter)) - case fmGitAuthorDate: - handlers = append(handlers, h.newDateGitAuthorDateHandler(setter)) - default: - handlers = append(handlers, h.newDateFieldHandler(identifier, setter)) - } - } - - return f.newChainedFrontMatterFieldHandler(handlers...), nil - -} - -type frontmatterFieldHandlers int - -func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d *FrontMatterDescriptor, t time.Time)) frontMatterFieldHandler { - return func(d *FrontMatterDescriptor) (bool, error) { - v, found := d.Frontmatter[key] - - if !found { - return false, nil - } - - date, err := cast.ToTimeE(v) - if err != nil { - return false, nil - } - - // We map several date keys to one, so, for example, - // "expirydate", "unpublishdate" will all set .ExpiryDate (first found). - setter(d, date) - - // This is the params key as set in front matter. - d.Params[key] = date - - return true, nil - } -} - -func (f *frontmatterFieldHandlers) newDateFilenameHandler(setter func(d *FrontMatterDescriptor, t time.Time)) frontMatterFieldHandler { - return func(d *FrontMatterDescriptor) (bool, error) { - date, slug := dateAndSlugFromBaseFilename(d.BaseFilename) - if date.IsZero() { - return false, nil - } - - setter(d, date) - - if _, found := d.Frontmatter["slug"]; !found { - // Use slug from filename - d.PageURLs.Slug = slug - } - - return true, nil - } -} - -func (f *frontmatterFieldHandlers) newDateModTimeHandler(setter func(d *FrontMatterDescriptor, t time.Time)) frontMatterFieldHandler { - return func(d *FrontMatterDescriptor) (bool, error) { - if d.ModTime.IsZero() { - return false, nil - } - setter(d, d.ModTime) - return true, nil - } -} - -func (f *frontmatterFieldHandlers) newDateGitAuthorDateHandler(setter func(d *FrontMatterDescriptor, t time.Time)) frontMatterFieldHandler { - return func(d *FrontMatterDescriptor) (bool, error) { - if d.GitAuthorDate.IsZero() { - return false, nil - } - setter(d, d.GitAuthorDate) - return true, nil - } -} |