mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-30 22:39:58 +02:00
Make Page an interface
The main motivation of this commit is to add a `page.Page` interface to replace the very file-oriented `hugolib.Page` struct. This is all a preparation step for issue #5074, "pages from other data sources". But this also fixes a set of annoying limitations, especially related to custom output formats, and shortcodes. Most notable changes: * The inner content of shortcodes using the `{{%` as the outer-most delimiter will now be sent to the content renderer, e.g. Blackfriday. This means that any markdown will partake in the global ToC and footnote context etc. * The Custom Output formats are now "fully virtualized". This removes many of the current limitations. * The taxonomy list type now has a reference to the `Page` object. This improves the taxonomy template `.Title` situation and make common template constructs much simpler. See #5074 Fixes #5763 Fixes #5758 Fixes #5090 Fixes #5204 Fixes #4695 Fixes #5607 Fixes #5707 Fixes #5719 Fixes #3113 Fixes #5706 Fixes #5767 Fixes #5723 Fixes #5769 Fixes #5770 Fixes #5771 Fixes #5759 Fixes #5776 Fixes #5777 Fixes #5778
This commit is contained in:
427
resources/page/pagemeta/page_frontmatter.go
Normal file
427
resources/page/pagemeta/page_frontmatter.go
Normal file
@@ -0,0 +1,427 @@
|
||||
// Copyright 2019 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/resources/resource"
|
||||
|
||||
"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 *resource.Dates
|
||||
|
||||
// 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.NewErrorLogger()
|
||||
}
|
||||
|
||||
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.FDate = 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.FLastmod = 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.FPublishDate = 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.FExpiryDate = 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
|
||||
}
|
||||
}
|
262
resources/page/pagemeta/page_frontmatter_test.go
Normal file
262
resources/page/pagemeta/page_frontmatter_test.go
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright 2019 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDateAndSlugFromBaseFilename(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
date string
|
||||
slug string
|
||||
}{
|
||||
{"page.md", "0001-01-01", ""},
|
||||
{"2012-09-12-page.md", "2012-09-12", "page"},
|
||||
{"2018-02-28-page.md", "2018-02-28", "page"},
|
||||
{"2018-02-28_page.md", "2018-02-28", "page"},
|
||||
{"2018-02-28 page.md", "2018-02-28", "page"},
|
||||
{"2018-02-28page.md", "2018-02-28", "page"},
|
||||
{"2018-02-28-.md", "2018-02-28", ""},
|
||||
{"2018-02-28-.md", "2018-02-28", ""},
|
||||
{"2018-02-28.md", "2018-02-28", ""},
|
||||
{"2018-02-28-page", "2018-02-28", "page"},
|
||||
{"2012-9-12-page.md", "0001-01-01", ""},
|
||||
{"asdfasdf.md", "0001-01-01", ""},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
expecteFDate, err := time.Parse("2006-01-02", test.date)
|
||||
assert.NoError(err)
|
||||
|
||||
errMsg := fmt.Sprintf("Test %d", i)
|
||||
gotDate, gotSlug := dateAndSlugFromBaseFilename(test.name)
|
||||
|
||||
assert.Equal(expecteFDate, gotDate, errMsg)
|
||||
assert.Equal(test.slug, gotSlug, errMsg)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func newTestFd() *FrontMatterDescriptor {
|
||||
return &FrontMatterDescriptor{
|
||||
Frontmatter: make(map[string]interface{}),
|
||||
Params: make(map[string]interface{}),
|
||||
Dates: &resource.Dates{},
|
||||
PageURLs: &URLPath{},
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrontMatterNewConfig(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"publishDate", "LastMod"},
|
||||
"Lastmod": []string{"publishDate"},
|
||||
"expiryDate": []string{"lastMod"},
|
||||
"publishDate": []string{"date"},
|
||||
})
|
||||
|
||||
fc, err := newFrontmatterConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.Equal([]string{"publishdate", "pubdate", "published", "lastmod", "modified"}, fc.date)
|
||||
assert.Equal([]string{"publishdate", "pubdate", "published"}, fc.lastmod)
|
||||
assert.Equal([]string{"lastmod", "modified"}, fc.expiryDate)
|
||||
assert.Equal([]string{"date"}, fc.publishDate)
|
||||
|
||||
// Default
|
||||
cfg = viper.New()
|
||||
fc, err = newFrontmatterConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.Equal([]string{"date", "publishdate", "pubdate", "published", "lastmod", "modified"}, fc.date)
|
||||
assert.Equal([]string{":git", "lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
|
||||
assert.Equal([]string{"expirydate", "unpublishdate"}, fc.expiryDate)
|
||||
assert.Equal([]string{"publishdate", "pubdate", "published", "date"}, fc.publishDate)
|
||||
|
||||
// :default keyword
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"d1", ":default"},
|
||||
"lastmod": []string{"d2", ":default"},
|
||||
"expiryDate": []string{"d3", ":default"},
|
||||
"publishDate": []string{"d4", ":default"},
|
||||
})
|
||||
fc, err = newFrontmatterConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.Equal([]string{"d1", "date", "publishdate", "pubdate", "published", "lastmod", "modified"}, fc.date)
|
||||
assert.Equal([]string{"d2", ":git", "lastmod", "modified", "date", "publishdate", "pubdate", "published"}, fc.lastmod)
|
||||
assert.Equal([]string{"d3", "expirydate", "unpublishdate"}, fc.expiryDate)
|
||||
assert.Equal([]string{"d4", "publishdate", "pubdate", "published", "date"}, fc.publishDate)
|
||||
|
||||
}
|
||||
|
||||
func TestFrontMatterDatesHandlers(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
for _, handlerID := range []string{":filename", ":fileModTime", ":git"} {
|
||||
|
||||
cfg := viper.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{handlerID, "date"},
|
||||
})
|
||||
|
||||
handler, err := NewFrontmatterHandler(nil, cfg)
|
||||
assert.NoError(err)
|
||||
|
||||
d1, _ := time.Parse("2006-01-02", "2018-02-01")
|
||||
d2, _ := time.Parse("2006-01-02", "2018-02-02")
|
||||
|
||||
d := newTestFd()
|
||||
switch strings.ToLower(handlerID) {
|
||||
case ":filename":
|
||||
d.BaseFilename = "2018-02-01-page.md"
|
||||
case ":filemodtime":
|
||||
d.ModTime = d1
|
||||
case ":git":
|
||||
d.GitAuthorDate = d1
|
||||
}
|
||||
d.Frontmatter["date"] = d2
|
||||
assert.NoError(handler.HandleDates(d))
|
||||
assert.Equal(d1, d.Dates.FDate)
|
||||
assert.Equal(d2, d.Params["date"])
|
||||
|
||||
d = newTestFd()
|
||||
d.Frontmatter["date"] = d2
|
||||
assert.NoError(handler.HandleDates(d))
|
||||
assert.Equal(d2, d.Dates.FDate)
|
||||
assert.Equal(d2, d.Params["date"])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrontMatterDatesCustomConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"mydate"},
|
||||
"lastmod": []string{"publishdate"},
|
||||
"publishdate": []string{"publishdate"},
|
||||
})
|
||||
|
||||
handler, err := NewFrontmatterHandler(nil, cfg)
|
||||
assert.NoError(err)
|
||||
|
||||
testDate, err := time.Parse("2006-01-02", "2018-02-01")
|
||||
assert.NoError(err)
|
||||
|
||||
d := newTestFd()
|
||||
d.Frontmatter["mydate"] = testDate
|
||||
testDate = testDate.Add(24 * time.Hour)
|
||||
d.Frontmatter["date"] = testDate
|
||||
testDate = testDate.Add(24 * time.Hour)
|
||||
d.Frontmatter["lastmod"] = testDate
|
||||
testDate = testDate.Add(24 * time.Hour)
|
||||
d.Frontmatter["publishdate"] = testDate
|
||||
testDate = testDate.Add(24 * time.Hour)
|
||||
d.Frontmatter["expirydate"] = testDate
|
||||
|
||||
assert.NoError(handler.HandleDates(d))
|
||||
|
||||
assert.Equal(1, d.Dates.FDate.Day())
|
||||
assert.Equal(4, d.Dates.FLastmod.Day())
|
||||
assert.Equal(4, d.Dates.FPublishDate.Day())
|
||||
assert.Equal(5, d.Dates.FExpiryDate.Day())
|
||||
|
||||
assert.Equal(d.Dates.FDate, d.Params["date"])
|
||||
assert.Equal(d.Dates.FDate, d.Params["mydate"])
|
||||
assert.Equal(d.Dates.FPublishDate, d.Params["publishdate"])
|
||||
assert.Equal(d.Dates.FExpiryDate, d.Params["expirydate"])
|
||||
|
||||
assert.False(handler.IsDateKey("date")) // This looks odd, but is configured like this.
|
||||
assert.True(handler.IsDateKey("mydate"))
|
||||
assert.True(handler.IsDateKey("publishdate"))
|
||||
assert.True(handler.IsDateKey("pubdate"))
|
||||
|
||||
}
|
||||
|
||||
func TestFrontMatterDatesDefaultKeyword(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"mydate", ":default"},
|
||||
"publishdate": []string{":default", "mypubdate"},
|
||||
})
|
||||
|
||||
handler, err := NewFrontmatterHandler(nil, cfg)
|
||||
assert.NoError(err)
|
||||
|
||||
testDate, _ := time.Parse("2006-01-02", "2018-02-01")
|
||||
d := newTestFd()
|
||||
d.Frontmatter["mydate"] = testDate
|
||||
d.Frontmatter["date"] = testDate.Add(1 * 24 * time.Hour)
|
||||
d.Frontmatter["mypubdate"] = testDate.Add(2 * 24 * time.Hour)
|
||||
d.Frontmatter["publishdate"] = testDate.Add(3 * 24 * time.Hour)
|
||||
|
||||
assert.NoError(handler.HandleDates(d))
|
||||
|
||||
assert.Equal(1, d.Dates.FDate.Day())
|
||||
assert.Equal(2, d.Dates.FLastmod.Day())
|
||||
assert.Equal(4, d.Dates.FPublishDate.Day())
|
||||
assert.True(d.Dates.FExpiryDate.IsZero())
|
||||
|
||||
}
|
||||
|
||||
func TestExpandDefaultValues(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
assert.Equal([]string{"a", "b", "c", "d"}, expandDefaultValues([]string{"a", ":default", "d"}, []string{"b", "c"}))
|
||||
assert.Equal([]string{"a", "b", "c"}, expandDefaultValues([]string{"a", "b", "c"}, []string{"a", "b", "c"}))
|
||||
assert.Equal([]string{"b", "c", "a", "b", "c", "d"}, expandDefaultValues([]string{":default", "a", ":default", "d"}, []string{"b", "c"}))
|
||||
|
||||
}
|
||||
|
||||
func TestFrontMatterDateFieldHandler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
handlers := new(frontmatterFieldHandlers)
|
||||
|
||||
fd := newTestFd()
|
||||
d, _ := time.Parse("2006-01-02", "2018-02-01")
|
||||
fd.Frontmatter["date"] = d
|
||||
h := handlers.newDateFieldHandler("date", func(d *FrontMatterDescriptor, t time.Time) { d.Dates.FDate = t })
|
||||
|
||||
handled, err := h(fd)
|
||||
assert.True(handled)
|
||||
assert.NoError(err)
|
||||
assert.Equal(d, fd.Dates.FDate)
|
||||
}
|
21
resources/page/pagemeta/pagemeta.go
Normal file
21
resources/page/pagemeta/pagemeta.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 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
|
||||
|
||||
type URLPath struct {
|
||||
URL string
|
||||
Permalink string
|
||||
Slug string
|
||||
Section string
|
||||
}
|
Reference in New Issue
Block a user