Repository: gohugoio/hugo
Pull request #12780 (2024-08-17 13:46:28)

Add config options page.nextPrevSortOrder/nextPrevInSectionSortOrder
See #12776



Patch:
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index afa72fb11..802000f97 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -182,6 +182,9 @@ type Config struct {
 	// Pagination configuration.
 	Pagination config.Pagination `mapstructure:"-"`
 
+	// Page configuration.
+	Page config.PageConfig `mapstructure:"-"`
+
 	// Privacy configuration.
 	Privacy privacy.Config `mapstructure:"-"`
 
diff --git a/config/allconfig/alldecoders.go b/config/allconfig/alldecoders.go
index c09aa0ff0..45cdd0de6 100644
--- a/config/allconfig/alldecoders.go
+++ b/config/allconfig/alldecoders.go
@@ -327,6 +327,25 @@ var allDecoderSetups = map[string]decodeWeight{
 			return err
 		},
 	},
+	"page": {
+		key: "page",
+		decode: func(d decodeWeight, p decodeConfig) error {
+			p.c.Page = config.PageConfig{
+				NextPrevSortOrder:          "desc",
+				NextPrevInSectionSortOrder: "desc",
+			}
+			if p.p.IsSet(d.key) {
+				if err := mapstructure.WeakDecode(p.p.Get(d.key), &p.c.Page); err != nil {
+					return err
+				}
+			}
+
+			return nil
+		},
+		getCompiler: func(c *Config) configCompiler {
+			return &c.Page
+		},
+	},
 	"pagination": {
 		key: "pagination",
 		decode: func(d decodeWeight, p decodeConfig) error {
diff --git a/config/commonConfig.go b/config/commonConfig.go
index a615c51a7..9dea4a2fc 100644
--- a/config/commonConfig.go
+++ b/config/commonConfig.go
@@ -422,3 +422,18 @@ type Pagination struct {
 	// Whether to disable generation of alias for the first pagination page.
 	DisableAliases bool
 }
+
+// PageConfig configures the behavior of pages.
+type PageConfig struct {
+	// Sort order for Page.Next and Page.Prev. Default "desc" (the default page sort order in Hugo).
+	NextPrevSortOrder string
+
+	// Sort order for Page.NextInSection and Page.PrevInSection. Default "desc".
+	NextPrevInSectionSortOrder string
+}
+
+func (c *PageConfig) CompileConfig(loggers.Logger) error {
+	c.NextPrevInSectionSortOrder = strings.ToLower(c.NextPrevInSectionSortOrder)
+	c.NextPrevSortOrder = strings.ToLower(c.NextPrevSortOrder)
+	return nil
+}
diff --git a/docs/data/docs.yaml b/docs/data/docs.yaml
index e399426b6..7ac76ee39 100644
--- a/docs/data/docs.yaml
+++ b/docs/data/docs.yaml
@@ -1600,6 +1600,9 @@ config:
     term:
     - html
     - rss
+  page:
+    nextPrevInSectionSortOrder: desc
+    nextPrevSortOrder: desc
   paginate: 0
   paginatePath: ""
   pagination:
@@ -1784,6 +1787,8 @@ config_helpers:
       _merge: shallow
     outputs:
       _merge: none
+    page:
+      _merge: none
     pagination:
       _merge: none
     params:
diff --git a/hugolib/site.go b/hugolib/site.go
index bbf110d1d..a93bbdbe6 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -117,6 +117,9 @@ func (s *Site) prepareInits() {
 
 	s.init.prevNext = init.Branch(func(context.Context) (any, error) {
 		regularPages := s.RegularPages()
+		if s.conf.Page.NextPrevSortOrder == "asc" {
+			regularPages = regularPages.Reverse()
+		}
 		for i, p := range regularPages {
 			np, ok := p.(nextPrevProvider)
 			if !ok {
@@ -181,7 +184,11 @@ func (s *Site) prepareInits() {
 		)
 
 		for _, section := range sections {
-			setNextPrev(section.RegularPages())
+			ps := section.RegularPages()
+			if s.conf.Page.NextPrevInSectionSortOrder == "asc" {
+				ps = ps.Reverse()
+			}
+			setNextPrev(ps)
 		}
 
 		return nil, nil


Test patch:
diff --git a/resources/page/pages_prev_next_integration_test.go b/resources/page/pages_prev_next_integration_test.go
new file mode 100644
index 000000000..d61d23cf0
--- /dev/null
+++ b/resources/page/pages_prev_next_integration_test.go
@@ -0,0 +1,82 @@
+// Copyright 2024 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 (
+	"strings"
+	"testing"
+
+	"github.com/gohugoio/hugo/hugolib"
+)
+
+func TestNextPrevConfig(t *testing.T) {
+	filesTemplate := `
+-- hugo.toml --
+-- content/mysection/_index.md --
+-- content/mysection/p1.md --
+---
+title: "Page 1"
+weight: 10
+---
+-- content/mysection/p2.md --
+---
+title: "Page 2"
+weight: 20
+---
+-- content/mysection/p3.md --
+---
+title: "Page 3"
+weight: 30
+---
+-- layouts/_default/single.html --
+{{ .Title }}|Next: {{ with .Next}}{{ .Title}}{{ end }}|Prev: {{ with .Prev}}{{ .Title}}{{ end }}|NextInSection: {{ with .NextInSection}}{{ .Title}}{{ end }}|PrevInSection: {{ with .PrevInSection}}{{ .Title}}{{ end }}|
+
+`
+	b := hugolib.Test(t, filesTemplate)
+
+	b.AssertFileContent("public/mysection/p1/index.html", "Page 1|Next: |Prev: Page 2|NextInSection: |PrevInSection: Page 2|")
+	b.AssertFileContent("public/mysection/p2/index.html", "Page 2|Next: Page 1|Prev: Page 3|NextInSection: Page 1|PrevInSection: Page 3|")
+	b.AssertFileContent("public/mysection/p3/index.html", "Page 3|Next: Page 2|Prev: |NextInSection: Page 2|PrevInSection: |")
+
+	files := strings.ReplaceAll(filesTemplate, "-- hugo.toml --", `-- hugo.toml --
+[page]
+nextPrevSortOrder="aSc"
+nextPrevInSectionSortOrder="asC"
+`)
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/mysection/p1/index.html", "Page 1|Next: Page 2|Prev: |NextInSection: Page 2|PrevInSection: |")
+	b.AssertFileContent("public/mysection/p2/index.html", "Page 2|Next: Page 3|Prev: Page 1|NextInSection: Page 3|PrevInSection: Page 1|")
+	b.AssertFileContent("public/mysection/p3/index.html", "Page 3|Next: |Prev: Page 2|NextInSection: |PrevInSection: Page 2|")
+
+	files = strings.ReplaceAll(filesTemplate, "-- hugo.toml --", `-- hugo.toml --
+[page]
+nextPrevSortOrder="aSc"
+`)
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/mysection/p1/index.html", "Page 1|Next: Page 2|Prev: |NextInSection: |PrevInSection: Page 2|")
+	b.AssertFileContent("public/mysection/p2/index.html", "Page 2|Next: Page 3|Prev: Page 1|NextInSection: Page 1|PrevInSection: Page 3|")
+	b.AssertFileContent("public/mysection/p3/index.html", "Page 3|Next: |Prev: Page 2|NextInSection: Page 2|PrevInSection: |")
+
+	files = strings.ReplaceAll(filesTemplate, "-- hugo.toml --", `-- hugo.toml --
+[page]
+nextPrevInSectionSortOrder="aSc"
+`)
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/mysection/p1/index.html", "Page 1|Next: |Prev: Page 2|NextInSection: Page 2|PrevInSection: |")
+}