mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-28 23:52:04 -05:00
parent
e0a882fd3b
commit
658e11ebaa
13 changed files with 144 additions and 42 deletions
|
@ -73,6 +73,9 @@ type HugoSites struct {
|
||||||
// Render output formats for all sites.
|
// Render output formats for all sites.
|
||||||
renderFormats output.Formats
|
renderFormats output.Formats
|
||||||
|
|
||||||
|
// The currently rendered Site.
|
||||||
|
currentSite *Site
|
||||||
|
|
||||||
*deps.Deps
|
*deps.Deps
|
||||||
|
|
||||||
gitInfo *gitInfo
|
gitInfo *gitInfo
|
||||||
|
|
|
@ -289,6 +289,7 @@ func (h *HugoSites) render(config *BuildCfg) error {
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, s := range h.Sites {
|
for _, s := range h.Sites {
|
||||||
|
h.currentSite = s
|
||||||
for siteOutIdx, renderFormat := range s.renderFormats {
|
for siteOutIdx, renderFormat := range s.renderFormats {
|
||||||
siteRenderContext.outIdx = siteOutIdx
|
siteRenderContext.outIdx = siteOutIdx
|
||||||
siteRenderContext.sitesOutIdx = i
|
siteRenderContext.sitesOutIdx = i
|
||||||
|
|
|
@ -738,6 +738,11 @@ func (s *SiteInfo) Sites() page.Sites {
|
||||||
return s.s.h.siteInfos()
|
return s.s.h.siteInfos()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Current returns the currently rendered Site.
|
||||||
|
func (s *SiteInfo) Current() page.Site {
|
||||||
|
return s.s.h.currentSite.Info
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SiteInfo) String() string {
|
func (s *SiteInfo) String() string {
|
||||||
return fmt.Sprintf("Site(%q)", s.title)
|
return fmt.Sprintf("Site(%q)", s.title)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/htime"
|
||||||
"github.com/gohugoio/hugo/common/maps"
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
"github.com/gohugoio/locales"
|
"github.com/gohugoio/locales"
|
||||||
|
@ -78,6 +79,7 @@ type Language struct {
|
||||||
// templates.
|
// templates.
|
||||||
// TODO(bep) do the same for some of the others.
|
// TODO(bep) do the same for some of the others.
|
||||||
translator locales.Translator
|
translator locales.Translator
|
||||||
|
timeFormatter htime.TimeFormatter
|
||||||
|
|
||||||
location *time.Location
|
location *time.Location
|
||||||
|
|
||||||
|
@ -116,6 +118,7 @@ func NewLanguage(lang string, cfg config.Provider) *Language {
|
||||||
Provider: compositeConfig,
|
Provider: compositeConfig,
|
||||||
params: params,
|
params: params,
|
||||||
translator: translator,
|
translator: translator,
|
||||||
|
timeFormatter: htime.NewTimeFormatter(translator),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := l.loadLocation(cfg.GetString("timeZone")); err != nil {
|
if err := l.loadLocation(cfg.GetString("timeZone")); err != nil {
|
||||||
|
@ -260,6 +263,10 @@ func (l *Language) IsSet(key string) bool {
|
||||||
// Internal access to unexported Language fields.
|
// Internal access to unexported Language fields.
|
||||||
// This construct is to prevent them from leaking to the templates.
|
// This construct is to prevent them from leaking to the templates.
|
||||||
|
|
||||||
|
func GetTimeFormatter(l *Language) htime.TimeFormatter {
|
||||||
|
return l.timeFormatter
|
||||||
|
}
|
||||||
|
|
||||||
func GetTranslator(l *Language) locales.Translator {
|
func GetTranslator(l *Language) locales.Translator {
|
||||||
return l.translator
|
return l.translator
|
||||||
}
|
}
|
||||||
|
|
72
resources/page/integration_test.go
Normal file
72
resources/page/integration_test.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2021 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 page_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGroupByLocalizedDate(t *testing.T) {
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
defaultContentLanguage = 'en'
|
||||||
|
defaultContentLanguageInSubdir = true
|
||||||
|
[languages]
|
||||||
|
[languages.en]
|
||||||
|
title = 'My blog'
|
||||||
|
weight = 1
|
||||||
|
[languages.fr]
|
||||||
|
title = 'Mon blogue'
|
||||||
|
weight = 2
|
||||||
|
[languages.nn]
|
||||||
|
title = 'Bloggen min'
|
||||||
|
weight = 3
|
||||||
|
-- content/p1.md --
|
||||||
|
---
|
||||||
|
title: "Post 1"
|
||||||
|
date: "2020-01-01"
|
||||||
|
---
|
||||||
|
-- content/p2.md --
|
||||||
|
---
|
||||||
|
title: "Post 2"
|
||||||
|
date: "2020-02-01"
|
||||||
|
---
|
||||||
|
-- content/p1.fr.md --
|
||||||
|
---
|
||||||
|
title: "Post 1"
|
||||||
|
date: "2020-01-01"
|
||||||
|
---
|
||||||
|
-- content/p2.fr.md --
|
||||||
|
---
|
||||||
|
title: "Post 2"
|
||||||
|
date: "2020-02-01"
|
||||||
|
---
|
||||||
|
-- layouts/index.html --
|
||||||
|
{{ range $k, $v := site.RegularPages.GroupByDate "January, 2006" }}{{ $k }}|{{ $v.Key }}|{{ $v.Pages }}{{ end }}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
NeedsOsFS: true,
|
||||||
|
}).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/en/index.html", "0|February, 2020|Pages(1)1|January, 2020|Pages(1)")
|
||||||
|
b.AssertFileContent("public/fr/index.html", "0|février, 2020|Pages(1)1|janvier, 2020|Pages(1)")
|
||||||
|
}
|
|
@ -14,10 +14,11 @@
|
||||||
package page
|
package page
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gohugoio/hugo/common/hugo"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/hugo"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/gohugoio/hugo/common/collections"
|
"github.com/gohugoio/hugo/common/collections"
|
||||||
"github.com/gohugoio/hugo/common/hreflect"
|
"github.com/gohugoio/hugo/common/hreflect"
|
||||||
"github.com/gohugoio/hugo/compare"
|
"github.com/gohugoio/hugo/compare"
|
||||||
|
"github.com/gohugoio/hugo/langs"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
)
|
)
|
||||||
|
@ -219,7 +220,7 @@ func (p Pages) GroupByParam(key string, order ...string) (PagesGroup, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pages) groupByDateField(sorter func(p Pages) Pages, formatter func(p Page) string, order ...string) (PagesGroup, error) {
|
func (p Pages) groupByDateField(format string, sorter func(p Pages) Pages, getDate func(p Page) time.Time, order ...string) (PagesGroup, error) {
|
||||||
if len(p) < 1 {
|
if len(p) < 1 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -234,16 +235,24 @@ func (p Pages) groupByDateField(sorter func(p Pages) Pages, formatter func(p Pag
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
date := formatter(sp[0].(Page))
|
firstPage := sp[0].(Page)
|
||||||
|
date := getDate(firstPage)
|
||||||
|
|
||||||
|
// Pages may be a mix of multiple languages, so we need to use the language
|
||||||
|
// for the currently rendered Site.
|
||||||
|
currentSite := firstPage.Site().Current()
|
||||||
|
formatter := langs.GetTimeFormatter(currentSite.Language())
|
||||||
|
formatted := formatter.Format(date, format)
|
||||||
var r []PageGroup
|
var r []PageGroup
|
||||||
r = append(r, PageGroup{Key: date, Pages: make(Pages, 0)})
|
r = append(r, PageGroup{Key: formatted, Pages: make(Pages, 0)})
|
||||||
r[0].Pages = append(r[0].Pages, sp[0])
|
r[0].Pages = append(r[0].Pages, sp[0])
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, e := range sp[1:] {
|
for _, e := range sp[1:] {
|
||||||
date = formatter(e.(Page))
|
date = getDate(e.(Page))
|
||||||
if r[i].Key.(string) != date {
|
formatted := formatter.Format(date, format)
|
||||||
r = append(r, PageGroup{Key: date})
|
if r[i].Key.(string) != formatted {
|
||||||
|
r = append(r, PageGroup{Key: formatted})
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
r[i].Pages = append(r[i].Pages, e)
|
r[i].Pages = append(r[i].Pages, e)
|
||||||
|
@ -259,10 +268,10 @@ func (p Pages) GroupByDate(format string, order ...string) (PagesGroup, error) {
|
||||||
sorter := func(p Pages) Pages {
|
sorter := func(p Pages) Pages {
|
||||||
return p.ByDate()
|
return p.ByDate()
|
||||||
}
|
}
|
||||||
formatter := func(p Page) string {
|
getDate := func(p Page) time.Time {
|
||||||
return p.Date().Format(format)
|
return p.Date()
|
||||||
}
|
}
|
||||||
return p.groupByDateField(sorter, formatter, order...)
|
return p.groupByDateField(format, sorter, getDate, order...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupByPublishDate groups by the given page's PublishDate value in
|
// GroupByPublishDate groups by the given page's PublishDate value in
|
||||||
|
@ -273,10 +282,10 @@ func (p Pages) GroupByPublishDate(format string, order ...string) (PagesGroup, e
|
||||||
sorter := func(p Pages) Pages {
|
sorter := func(p Pages) Pages {
|
||||||
return p.ByPublishDate()
|
return p.ByPublishDate()
|
||||||
}
|
}
|
||||||
formatter := func(p Page) string {
|
getDate := func(p Page) time.Time {
|
||||||
return p.PublishDate().Format(format)
|
return p.PublishDate()
|
||||||
}
|
}
|
||||||
return p.groupByDateField(sorter, formatter, order...)
|
return p.groupByDateField(format, sorter, getDate, order...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupByExpiryDate groups by the given page's ExpireDate value in
|
// GroupByExpiryDate groups by the given page's ExpireDate value in
|
||||||
|
@ -287,10 +296,10 @@ func (p Pages) GroupByExpiryDate(format string, order ...string) (PagesGroup, er
|
||||||
sorter := func(p Pages) Pages {
|
sorter := func(p Pages) Pages {
|
||||||
return p.ByExpiryDate()
|
return p.ByExpiryDate()
|
||||||
}
|
}
|
||||||
formatter := func(p Page) string {
|
getDate := func(p Page) time.Time {
|
||||||
return p.ExpiryDate().Format(format)
|
return p.ExpiryDate()
|
||||||
}
|
}
|
||||||
return p.groupByDateField(sorter, formatter, order...)
|
return p.groupByDateField(format, sorter, getDate, order...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupByLastmod groups by the given page's Lastmod value in
|
// GroupByLastmod groups by the given page's Lastmod value in
|
||||||
|
@ -301,10 +310,10 @@ func (p Pages) GroupByLastmod(format string, order ...string) (PagesGroup, error
|
||||||
sorter := func(p Pages) Pages {
|
sorter := func(p Pages) Pages {
|
||||||
return p.ByLastmod()
|
return p.ByLastmod()
|
||||||
}
|
}
|
||||||
formatter := func(p Page) string {
|
getDate := func(p Page) time.Time {
|
||||||
return p.Lastmod().Format(format)
|
return p.Lastmod()
|
||||||
}
|
}
|
||||||
return p.groupByDateField(sorter, formatter, order...)
|
return p.groupByDateField(format, sorter, getDate, order...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupByParamDate groups by a date set as a param on the page in
|
// GroupByParamDate groups by a date set as a param on the page in
|
||||||
|
@ -340,10 +349,10 @@ func (p Pages) GroupByParamDate(key string, format string, order ...string) (Pag
|
||||||
pageBy(pdate).Sort(r)
|
pageBy(pdate).Sort(r)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
formatter := func(p Page) string {
|
getDate := func(p Page) time.Time {
|
||||||
return dates[p].Format(format)
|
return dates[p]
|
||||||
}
|
}
|
||||||
return p.groupByDateField(sorter, formatter, order...)
|
return p.groupByDateField(format, sorter, getDate, order...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProbablyEq wraps compare.ProbablyEqer
|
// ProbablyEq wraps compare.ProbablyEqer
|
||||||
|
|
|
@ -18,18 +18,17 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/htesting/hqt"
|
|
||||||
"github.com/gohugoio/hugo/source"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var eq = qt.CmpEquals(hqt.DeepAllowUnexported(
|
var eq = qt.CmpEquals(
|
||||||
&testPage{},
|
cmp.Comparer(func(p1, p2 testPage) bool {
|
||||||
&source.FileInfo{},
|
return p1.path == p2.path && p1.weight == p2.weight
|
||||||
))
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
func TestDefaultSort(t *testing.T) {
|
func TestDefaultSort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
|
@ -37,6 +37,7 @@ type Site interface {
|
||||||
ServerPort() int
|
ServerPort() int
|
||||||
Title() string
|
Title() string
|
||||||
Sites() Sites
|
Sites() Sites
|
||||||
|
Current() Site
|
||||||
Hugo() hugo.Info
|
Hugo() hugo.Info
|
||||||
BaseURL() template.URL
|
BaseURL() template.URL
|
||||||
Taxonomies() any
|
Taxonomies() any
|
||||||
|
@ -82,6 +83,10 @@ func (t testSite) Sites() Sites {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t testSite) Current() Site {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
func (t testSite) IsServer() bool {
|
func (t testSite) IsServer() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ func newTestPageWithFile(filename string) *testPage {
|
||||||
currentSection: &testPage{
|
currentSection: &testPage{
|
||||||
sectionEntries: []string{"a", "b", "c"},
|
sectionEntries: []string{"a", "b", "c"},
|
||||||
},
|
},
|
||||||
|
site: testSite{l: langs.NewDefaultLanguage(config.New())},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ func init() {
|
||||||
if d.Language == nil {
|
if d.Language == nil {
|
||||||
panic("Language must be set")
|
panic("Language must be set")
|
||||||
}
|
}
|
||||||
ctx := New(langs.GetTranslator(d.Language), langs.GetLocation(d.Language))
|
ctx := New(langs.GetTimeFormatter(d.Language), langs.GetLocation(d.Language))
|
||||||
|
|
||||||
ns := &internal.TemplateFuncsNamespace{
|
ns := &internal.TemplateFuncsNamespace{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
|
|
@ -21,15 +21,13 @@ import (
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/htime"
|
"github.com/gohugoio/hugo/common/htime"
|
||||||
|
|
||||||
"github.com/gohugoio/locales"
|
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New returns a new instance of the time-namespaced template functions.
|
// New returns a new instance of the time-namespaced template functions.
|
||||||
func New(translator locales.Translator, location *time.Location) *Namespace {
|
func New(timeFormatter htime.TimeFormatter, location *time.Location) *Namespace {
|
||||||
return &Namespace{
|
return &Namespace{
|
||||||
timeFormatter: htime.NewTimeFormatter(translator),
|
timeFormatter: timeFormatter,
|
||||||
location: location,
|
location: location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/htime"
|
||||||
translators "github.com/gohugoio/localescompressed"
|
translators "github.com/gohugoio/localescompressed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ func TestTimeLocation(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
loc, _ := time.LoadLocation("America/Antigua")
|
loc, _ := time.LoadLocation("America/Antigua")
|
||||||
ns := New(translators.GetTranslator("en"), loc)
|
ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), loc)
|
||||||
|
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -86,7 +87,7 @@ func TestFormat(t *testing.T) {
|
||||||
|
|
||||||
c.Run("UTC", func(c *qt.C) {
|
c.Run("UTC", func(c *qt.C) {
|
||||||
c.Parallel()
|
c.Parallel()
|
||||||
ns := New(translators.GetTranslator("en"), time.UTC)
|
ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), time.UTC)
|
||||||
|
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
layout string
|
layout string
|
||||||
|
@ -129,7 +130,7 @@ func TestFormat(t *testing.T) {
|
||||||
|
|
||||||
loc, err := time.LoadLocation("America/Los_Angeles")
|
loc, err := time.LoadLocation("America/Los_Angeles")
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
ns := New(translators.GetTranslator("en"), loc)
|
ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), loc)
|
||||||
|
|
||||||
d, err := ns.Format(":time_full", "2020-03-09T11:00:00")
|
d, err := ns.Format(":time_full", "2020-03-09T11:00:00")
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ func TestFormat(t *testing.T) {
|
||||||
func TestDuration(t *testing.T) {
|
func TestDuration(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ns := New(translators.GetTranslator("en"), time.UTC)
|
ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), time.UTC)
|
||||||
|
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
unit any
|
unit any
|
||||||
|
|
Loading…
Reference in a new issue