diff --git a/hugolib/page.go b/hugolib/page.go index fd6278bb443..a5fe6c8a3b3 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Hugo Authors. All rights reserved. +// 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. @@ -1373,6 +1373,30 @@ func (p *Page) update(frontmatter map[string]interface{}) error { return nil } +// 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 +} + func (p *Page) GetParam(key string) interface{} { return p.getParam(key, false) } diff --git a/hugolib/page_frontmatter.go b/hugolib/page_frontmatter.go new file mode 100644 index 00000000000..72e085b742b --- /dev/null +++ b/hugolib/page_frontmatter.go @@ -0,0 +1,70 @@ +// 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 hugolib + +import ( + "fmt" + "strings" + + "github.com/gohugoio/hugo/config" + "github.com/spf13/cast" +) + +type frontmatterConfig struct { + // Ordered chain. + dateHandlers []frontmatterFieldHandler +} + +type frontmatterFieldHandler func(frontmatter map[string]interface{}, p *Page) bool + +func newFrontmatterConfig(cfg config.Provider) (frontmatterConfig, error) { + var f frontmatterConfig + + handlers := new(frontmatterFieldHandlers) + + f.dateHandlers = []frontmatterFieldHandler{handlers.defaultDateHandler} + + if cfg.IsSet("frontmatter") { + fm := cfg.GetStringMap("frontmatter") + if fm != nil { + dateFallbacks, found := fm["datefallbacks"] + if found { + slice, err := cast.ToStringSliceE(dateFallbacks) + if err != nil { + return f, fmt.Errorf("invalid value for dataCallbacks, expeced a string slice, got %T", dateFallbacks) + } + + for _, v := range slice { + if strings.EqualFold(v, "filename") { + f.dateHandlers = append(f.dateHandlers, handlers.fileanameFallbackDateHandler) + // No more for now. + break + } + } + } + } + } + + return f, nil +} + +type frontmatterFieldHandlers int + +func (f *frontmatterFieldHandlers) defaultDateHandler(frontmatter map[string]interface{}, p *Page) bool { + return true +} + +func (f *frontmatterFieldHandlers) fileanameFallbackDateHandler(frontmatter map[string]interface{}, p *Page) bool { + return true +} diff --git a/hugolib/page_frontmatter_test.go b/hugolib/page_frontmatter_test.go new file mode 100644 index 00000000000..ac7d35de6ef --- /dev/null +++ b/hugolib/page_frontmatter_test.go @@ -0,0 +1,39 @@ +// 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 hugolib + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func TestNewFrontmatterConfig(t *testing.T) { + t.Parallel() + + v := viper.New() + + v.Set("frontmatter", map[string]interface{}{ + "dateFallbacks": []string{"filename"}, + }) + + assert := require.New(t) + + fc, err := newFrontmatterConfig(v) + + assert.NoError(err) + assert.Equal(2, len(fc.dateHandlers)) + +} diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 814556c6c59..b5f97caac80 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Hugo Authors. All rights reserved. +// 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. @@ -985,6 +985,25 @@ Page With empty front matter` zero_FM = "Page With empty front matter" ) +/*func TestPageWithFilenameDateAsFallback(t *testing.T) { + t.Parallel() + cfg, fs := newTestCfg() + + var tests = []struct { + }{} + + writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageRFC3339Date) + + s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) + + require.Len(t, s.RegularPages, 1) + + p := s.RegularPages[0] + d, _ := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z") + + checkPageDate(t, p, d) +} +*/ func TestMetadataDates(t *testing.T) { t.Parallel() var tests = []struct { @@ -1873,6 +1892,43 @@ tags: } } +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 { + expectedDate, err := time.Parse("2006-01-02", test.date) + assert.NoError(err) + + errMsg := fmt.Sprintf("Test %d", i) + gotDate, gotSlug := dateAndSlugFromBaseFilename(test.name) + + assert.Equal(expectedDate, gotDate, errMsg) + assert.Equal(test.slug, gotSlug, errMsg) + + } +} + func BenchmarkParsePage(b *testing.B) { s := newTestSite(b) f, _ := os.Open("testdata/redis.cn.md")