mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-15 20:44:01 +02:00
295
hugolib/site_sections.go
Normal file
295
hugolib/site_sections.go
Normal file
@@ -0,0 +1,295 @@
|
||||
// Copyright 2017 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"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
radix "github.com/hashicorp/go-immutable-radix"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
)
|
||||
|
||||
// Deprecated: Use .Site.Home.Sections.
|
||||
// TODO(bep) Hugo 0.23 = Reuse as an alias for home's sections.
|
||||
func (s *SiteInfo) Sections() Taxonomy {
|
||||
|
||||
helpText := `In Hugo 0.22 we introduced nested sections, so this method now returns an empty taxonomy.
|
||||
|
||||
To list sections with its pages, you can do something like this:
|
||||
|
||||
{{ range $.Site.Home.Sections }}
|
||||
Section: {{ .Title }}
|
||||
{{ range .Pages }}
|
||||
Section Page: {{ .Title }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
To get a specific section, you can do this:
|
||||
|
||||
{{ $section := $.Site.GetPage "section" "blog" }}
|
||||
`
|
||||
|
||||
helpers.Deprecated("Site", "Sections", helpText, true)
|
||||
|
||||
return Taxonomy{}
|
||||
}
|
||||
|
||||
// Home is a shortcut to the home page, equivalent to .Site.GetPage "home".
|
||||
func (s *SiteInfo) Home() (*Page, error) {
|
||||
return s.GetPage(KindHome)
|
||||
}
|
||||
|
||||
// Parent returns a section's parent section or a page's section.
|
||||
// To get a section's subsections, see Page's Sections method.
|
||||
func (p *Page) Parent() *Page {
|
||||
return p.parent
|
||||
}
|
||||
|
||||
// current returns the page's current section.
|
||||
// Note that this will return nil for pages that is not regular, home or section pages.
|
||||
// Note that for paginated sections and home pages, this will return the original page pointer.
|
||||
func (p *Page) current() *Page {
|
||||
v := p
|
||||
if v.origOnCopy != nil {
|
||||
v = v.origOnCopy
|
||||
}
|
||||
if v.IsHome() || v.IsSection() {
|
||||
return v
|
||||
}
|
||||
|
||||
return v.parent
|
||||
}
|
||||
|
||||
// InSection returns whether the given page is in the current section.
|
||||
// Note that this will always return false for pages that are
|
||||
// not either regular, home or section pages.
|
||||
func (p *Page) InSection(other interface{}) (bool, error) {
|
||||
if p == nil || other == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if po, ok := other.(*PageOutput); ok {
|
||||
other = po.Page
|
||||
}
|
||||
|
||||
pp, ok := other.(*Page)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("%T not supported in InSection", other)
|
||||
}
|
||||
|
||||
if pp == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return pp.current() == p.current(), nil
|
||||
}
|
||||
|
||||
// Sections returns this section's subsections, if any.
|
||||
// Note that for non-sections, this method will always return an empty list.
|
||||
func (p *Page) Sections() Pages {
|
||||
return p.subSections
|
||||
}
|
||||
|
||||
func (s *Site) assembleSections() Pages {
|
||||
var newPages Pages
|
||||
|
||||
if !s.isEnabled(KindSection) {
|
||||
return newPages
|
||||
}
|
||||
|
||||
// Maps section kind pages to their path, i.e. "my/section"
|
||||
sectionPages := make(map[string]*Page)
|
||||
|
||||
// The sections with content files will already have been created.
|
||||
for _, sect := range s.findPagesByKind(KindSection) {
|
||||
sectionPages[path.Join(sect.sections...)] = sect
|
||||
}
|
||||
|
||||
const (
|
||||
sectKey = "__hs"
|
||||
sectSectKey = "_a" + sectKey
|
||||
sectPageKey = "_b" + sectKey
|
||||
)
|
||||
|
||||
var (
|
||||
home *Page
|
||||
inPages = radix.New().Txn()
|
||||
inSections = radix.New().Txn()
|
||||
undecided Pages
|
||||
)
|
||||
|
||||
for i, p := range s.Pages {
|
||||
if p.Kind != KindPage {
|
||||
if p.Kind == KindHome {
|
||||
home = p
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(p.sections) == 0 {
|
||||
// Root level pages. These will have the home page as their Parent.
|
||||
p.parent = home
|
||||
continue
|
||||
}
|
||||
|
||||
sectionKey := path.Join(p.sections...)
|
||||
sect, found := sectionPages[sectionKey]
|
||||
|
||||
if !found && len(p.sections) == 1 {
|
||||
// We only create content-file-less sections for the root sections.
|
||||
sect = s.newSectionPage(p.sections[0])
|
||||
sectionPages[sectionKey] = sect
|
||||
newPages = append(newPages, sect)
|
||||
} else if !found {
|
||||
// We don't know what to do with this section yet.
|
||||
undecided = append(undecided, p)
|
||||
}
|
||||
|
||||
pagePath := path.Join(sectionKey, sectPageKey, strconv.Itoa(i))
|
||||
inPages.Insert([]byte(pagePath), p)
|
||||
}
|
||||
|
||||
// Create any missing sections in the tree.
|
||||
// A sub-section needs a content file, but to create a navigational tree,
|
||||
// given a content file in /content/a/b/c/_index.md, we cannot create just
|
||||
// the c section.
|
||||
for _, sect := range sectionPages {
|
||||
for i := len(sect.sections); i > 0; i-- {
|
||||
sectionPath := sect.sections[:i]
|
||||
sectionKey := path.Join(sectionPath...)
|
||||
sect, found := sectionPages[sectionKey]
|
||||
if !found {
|
||||
sect = s.newSectionPage(sectionPath[len(sectionPath)-1])
|
||||
sect.sections = sectionPath
|
||||
sectionPages[sectionKey] = sect
|
||||
newPages = append(newPages, sect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create any missing root sections.
|
||||
for _, p := range undecided {
|
||||
sectionKey := p.sections[0]
|
||||
sect, found := sectionPages[sectionKey]
|
||||
if !found {
|
||||
sect = s.newSectionPage(sectionKey)
|
||||
sectionPages[sectionKey] = sect
|
||||
newPages = append(newPages, sect)
|
||||
}
|
||||
}
|
||||
|
||||
for k, sect := range sectionPages {
|
||||
inPages.Insert([]byte(path.Join(k, sectSectKey)), sect)
|
||||
inSections.Insert([]byte(k), sect)
|
||||
}
|
||||
|
||||
var (
|
||||
currentSection *Page
|
||||
children Pages
|
||||
rootPages = inPages.Commit().Root()
|
||||
rootSections = inSections.Commit().Root()
|
||||
)
|
||||
|
||||
rootPages.Walk(func(path []byte, v interface{}) bool {
|
||||
p := v.(*Page)
|
||||
|
||||
if p.Kind == KindSection {
|
||||
if currentSection != nil {
|
||||
// A new section
|
||||
currentSection.setPagePages(children)
|
||||
}
|
||||
|
||||
currentSection = p
|
||||
children = make(Pages, 0)
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// Regular page
|
||||
p.parent = currentSection
|
||||
children = append(children, p)
|
||||
return false
|
||||
})
|
||||
|
||||
if currentSection != nil {
|
||||
currentSection.setPagePages(children)
|
||||
}
|
||||
|
||||
// Build the sections hierarchy
|
||||
for _, sect := range sectionPages {
|
||||
if len(sect.sections) == 1 {
|
||||
sect.parent = home
|
||||
} else {
|
||||
parentSearchKey := path.Join(sect.sections[:len(sect.sections)-1]...)
|
||||
_, v, _ := rootSections.LongestPrefix([]byte(parentSearchKey))
|
||||
p := v.(*Page)
|
||||
sect.parent = p
|
||||
}
|
||||
|
||||
if sect.parent != nil {
|
||||
sect.parent.subSections = append(sect.parent.subSections, sect)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
sectionsParamId = "mainSections"
|
||||
sectionsParamIdLower = strings.ToLower(sectionsParamId)
|
||||
mainSections interface{}
|
||||
mainSectionsFound bool
|
||||
maxSectionWeight int
|
||||
)
|
||||
|
||||
mainSections, mainSectionsFound = s.Info.Params[sectionsParamIdLower]
|
||||
|
||||
for _, sect := range sectionPages {
|
||||
if sect.parent != nil {
|
||||
sect.parent.subSections.Sort()
|
||||
}
|
||||
|
||||
for i, p := range sect.Pages {
|
||||
if i > 0 {
|
||||
p.NextInSection = sect.Pages[i-1]
|
||||
}
|
||||
if i < len(sect.Pages)-1 {
|
||||
p.PrevInSection = sect.Pages[i+1]
|
||||
}
|
||||
}
|
||||
|
||||
if !mainSectionsFound {
|
||||
weight := len(sect.Pages) + (len(sect.Sections()) * 5)
|
||||
if weight >= maxSectionWeight {
|
||||
mainSections = []string{sect.Section()}
|
||||
maxSectionWeight = weight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to make this as backwards compatible as possible.
|
||||
s.Info.Params[sectionsParamId] = mainSections
|
||||
s.Info.Params[sectionsParamIdLower] = mainSections
|
||||
|
||||
return newPages
|
||||
|
||||
}
|
||||
|
||||
func (p *Page) setPagePages(pages Pages) {
|
||||
pages.Sort()
|
||||
p.Pages = pages
|
||||
p.Data = make(map[string]interface{})
|
||||
p.Data["Pages"] = pages
|
||||
}
|
Reference in New Issue
Block a user