summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Francl <look@recursion.org>2019-11-09 21:58:38 -0800
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-11-10 11:00:02 +0100
commit70a1aa345b95bcf325f19c6e7184bcd6f885e454 (patch)
treefee67032d577cabd8eaf2da981e444be2de7fa4e
parentcafecca440e495ec915cc6290fe09d2a343e9c95 (diff)
Support Go time format strings in permalinks
In the vein of an ancient TODO about supporting custom date formatting with strftime, this allows `:`-prefixed Go time format strings in permalink segments. This allows users to customize date-based permalinks any way they need to. For example, with a date of 2019-11-09, the permalink `/:06/:1/:2` will render as `/19/11/9`. See: https://github.com/gohugoio/hugo/commit/07978e4a4922bc21c230fee65052232b829bd1ab#diff-0688a3b65c7f5d01aa216f8d9b57fd00R111-R112 https://discourse.gohugo.io/t/implementing-additional-date-formats-for-permalinks/17860 https://github.com/gohugoio/hugo/pull/6488
-rw-r--r--docs/content/en/content-management/urls.md9
-rw-r--r--resources/page/permalinks.go30
-rw-r--r--resources/page/permalinks_test.go15
3 files changed, 43 insertions, 11 deletions
diff --git a/docs/content/en/content-management/urls.md b/docs/content/en/content-management/urls.md
index 9cb16f52d..14971af43 100644
--- a/docs/content/en/content-management/urls.md
+++ b/docs/content/en/content-management/urls.md
@@ -38,6 +38,13 @@ permalinks:
Only the content under `posts/` will have the new URL structure. For example, the file `content/posts/sample-entry.md` with `date: 2017-02-27T19:20:00-05:00` in its front matter will render to `public/2017/02/sample-entry/index.html` at build time and therefore be reachable at `https://example.com/2017/02/sample-entry/`.
+If the standard date-based permalink configuration does not meet your needs, you can also format URL segments using [Go time formatting directives](https://golang.org/pkg/time/#Time.Format). For example, a URL structure with two digit years and month and day digits without zero padding can be accomplished with:
+
+{{< code-toggle file="config" copy="false" >}}
+permalinks:
+ posts: /:06/:1/:2/:title/
+{{< /code-toggle >}}
+
You can also configure permalinks of taxonomies with the same syntax, by using the plural form of the taxonomy instead of the section. You will probably only want to use the configuration values `:slug` or `:title`.
### Permalink Configuration Values
@@ -80,6 +87,8 @@ The following is a list of values that can be used in a `permalink` definition i
`:filename`
: the content's filename (without extension)
+Additionally, a Go time format string prefixed with `:` may be used.
+
## Aliases
Aliases can be used to create redirects to your page from other URLs.
diff --git a/resources/page/permalinks.go b/resources/page/permalinks.go
index e82ee1a4f..0e9b9e212 100644
--- a/resources/page/permalinks.go
+++ b/resources/page/permalinks.go
@@ -20,6 +20,7 @@ import (
"regexp"
"strconv"
"strings"
+ "time"
"github.com/pkg/errors"
@@ -38,6 +39,24 @@ type PermalinkExpander struct {
ps *helpers.PathSpec
}
+// Time for checking date formats. Every field is different than the
+// Go reference time for date formatting. This ensures that formatting this date
+// with a Go time format always has a different output than the format itself.
+var referenceTime = time.Date(2019, time.November, 9, 23, 1, 42, 1, time.UTC)
+
+// Return the callback for the given permalink attribute and a boolean indicating if the attribute is valid or not.
+func (p PermalinkExpander) callback(attr string) (pageToPermaAttribute, bool) {
+ if callback, ok := p.knownPermalinkAttributes[attr]; ok {
+ return callback, true
+ }
+
+ if referenceTime.Format(attr) != attr {
+ return p.pageToPermalinkDate, true
+ }
+
+ return nil, false
+}
+
// NewPermalinkExpander creates a new PermalinkExpander configured by the given
// PathSpec.
func NewPermalinkExpander(ps *helpers.PathSpec) (PermalinkExpander, error) {
@@ -109,7 +128,7 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa
replacement := m[0]
attr := replacement[1:]
replacements[i] = replacement
- callback, ok := l.knownPermalinkAttributes[attr]
+ callback, ok := l.callback(attr)
if !ok {
return nil, &permalinkExpandError{pattern: pattern, err: errPermalinkAttributeUnknown}
@@ -173,8 +192,8 @@ func (l PermalinkExpander) validate(pp string) bool {
}
for _, match := range matches {
- k := strings.ToLower(match[0][1:])
- if _, ok := l.knownPermalinkAttributes[k]; !ok {
+ k := match[0][1:]
+ if _, ok := l.callback(k); !ok {
return false
}
}
@@ -214,9 +233,8 @@ func (l PermalinkExpander) pageToPermalinkDate(p Page, dateField string) (string
case "yearday":
return strconv.Itoa(p.Date().YearDay()), nil
}
- //TODO: support classic strftime escapes too
- // (and pass those through despite not being in the map)
- panic("coding error: should not be here")
+
+ return p.Date().Format(dateField), nil
}
// pageToPermalinkTitle returns the URL-safe form of the title
diff --git a/resources/page/permalinks_test.go b/resources/page/permalinks_test.go
index c9e55e7bd..e4eeda748 100644
--- a/resources/page/permalinks_test.go
+++ b/resources/page/permalinks_test.go
@@ -32,16 +32,21 @@ var testdataPermalinks = []struct {
{":title", true, "spf13-vim-3.0-release-and-new-website"},
{"/:year-:month-:title", true, "/2012-04-spf13-vim-3.0-release-and-new-website"},
{"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, "/2012/97/04/April/06/5/Friday/"}, // Dates
- {"/:section/", true, "/blue/"}, // Section
- {"/:title/", true, "/spf13-vim-3.0-release-and-new-website/"}, // Title
- {"/:slug/", true, "/the-slug/"}, // Slug
- {"/:filename/", true, "/test-page/"}, // Filename
+ {"/:section/", true, "/blue/"}, // Section
+ {"/:title/", true, "/spf13-vim-3.0-release-and-new-website/"}, // Title
+ {"/:slug/", true, "/the-slug/"}, // Slug
+ {"/:filename/", true, "/test-page/"}, // Filename
+ {"/:06-:1-:2-:Monday", true, "/12-4-6-Friday"}, // Dates with Go formatting
+ {"/:2006_01_02_15_04_05.000", true, "/2012_04_06_03_01_59.000"}, // Complicated custom date format
// TODO(moorereason): need test scaffolding for this.
//{"/:sections/", false, "/blue/"}, // Sections
// Failures
{"/blog/:fred", false, ""},
{"/:year//:title", false, ""},
+ {"/:TITLE", false, ""}, // case is not normalized
+ {"/:2017", false, ""}, // invalid date format
+ {"/:2006-01-02", false, ""}, // valid date format but invalid attribute name
}
func TestPermalinkExpansion(t *testing.T) {
@@ -51,7 +56,7 @@ func TestPermalinkExpansion(t *testing.T) {
page := newTestPageWithFile("/test-page/index.md")
page.title = "Spf13 Vim 3.0 Release and new website"
- d, _ := time.Parse("2006-01-02", "2012-04-06")
+ d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59")
page.date = d
page.section = "blue"
page.slug = "The Slug"