mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-26 15:12:15 +00:00
a07293cf97
In a multi-language setup, before this commit the Node's Translations() method would return some "dummy nodes" that would point to the correct page (Permalink), but would not be the same as the node it points to -- it would not have the translated title etc. The node creation is, however, so mingled with rendering, whihc is too early to have any global state, so the nodes has to be split in a prepare and a render phase. This commits does that with as small a change as possible. This implementation is a temp solution until we fix #2297. Updates #2309
326 lines
7 KiB
Go
326 lines
7 KiB
Go
// Copyright 2015 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 (
|
|
"html/template"
|
|
"path"
|
|
"path/filepath"
|
|
//"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
jww "github.com/spf13/jwalterweatherman"
|
|
|
|
"github.com/spf13/hugo/helpers"
|
|
|
|
"github.com/spf13/cast"
|
|
)
|
|
|
|
type Node struct {
|
|
// a natural key that should be unique for this site
|
|
// for the home page this will typically be "home", but it can anything
|
|
// as long as it is the same for repeated builds.
|
|
nodeID string
|
|
|
|
RSSLink template.HTML
|
|
Site *SiteInfo `json:"-"`
|
|
// layout string
|
|
Data map[string]interface{}
|
|
Title string
|
|
Description string
|
|
Keywords []string
|
|
Params map[string]interface{}
|
|
Date time.Time
|
|
Lastmod time.Time
|
|
Sitemap Sitemap
|
|
URLPath
|
|
IsHome bool
|
|
paginator *Pager
|
|
paginatorInit sync.Once
|
|
scratch *Scratch
|
|
|
|
language *helpers.Language
|
|
languageInit sync.Once
|
|
lang string
|
|
|
|
translations Nodes
|
|
translationsInit sync.Once
|
|
}
|
|
|
|
// The Nodes type is temporary until we get https://github.com/spf13/hugo/issues/2297 fixed.
|
|
type Nodes []*Node
|
|
|
|
func (n Nodes) Len() int {
|
|
return len(n)
|
|
}
|
|
|
|
func (n Nodes) Less(i, j int) bool {
|
|
return n[i].language.Weight < n[j].language.Weight
|
|
}
|
|
|
|
func (n Nodes) Swap(i, j int) {
|
|
n[i], n[j] = n[j], n[i]
|
|
}
|
|
|
|
func (n *Node) Now() time.Time {
|
|
return time.Now()
|
|
}
|
|
|
|
func (n *Node) HasMenuCurrent(menuID string, inme *MenuEntry) bool {
|
|
if inme.HasChildren() {
|
|
me := MenuEntry{Name: n.Title, URL: n.URL()}
|
|
|
|
for _, child := range inme.Children {
|
|
if me.IsSameResource(child) {
|
|
return true
|
|
}
|
|
if n.HasMenuCurrent(menuID, child) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (n *Node) IsMenuCurrent(menuID string, inme *MenuEntry) bool {
|
|
|
|
me := MenuEntry{Name: n.Title, URL: n.Site.createNodeMenuEntryURL(n.URL())}
|
|
|
|
if !me.IsSameResource(inme) {
|
|
return false
|
|
}
|
|
|
|
// this resource may be included in several menus
|
|
// search for it to make sure that it is in the menu with the given menuId
|
|
if menu, ok := (*n.Site.Menus)[menuID]; ok {
|
|
for _, menuEntry := range *menu {
|
|
if menuEntry.IsSameResource(inme) {
|
|
return true
|
|
}
|
|
|
|
descendantFound := n.isSameAsDescendantMenu(inme, menuEntry)
|
|
if descendantFound {
|
|
return descendantFound
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Param is a convenience method to do lookups in Site's Params map.
|
|
//
|
|
// This method is also implemented on Page.
|
|
func (n *Node) Param(key interface{}) (interface{}, error) {
|
|
keyStr, err := cast.ToStringE(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return n.Site.Params[keyStr], err
|
|
}
|
|
|
|
func (n *Node) Hugo() *HugoInfo {
|
|
return hugoInfo
|
|
}
|
|
|
|
func (n *Node) isSameAsDescendantMenu(inme *MenuEntry, parent *MenuEntry) bool {
|
|
if parent.HasChildren() {
|
|
for _, child := range parent.Children {
|
|
if child.IsSameResource(inme) {
|
|
return true
|
|
}
|
|
descendantFound := n.isSameAsDescendantMenu(inme, child)
|
|
if descendantFound {
|
|
return descendantFound
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (n *Node) RSSlink() template.HTML {
|
|
return n.RSSLink
|
|
}
|
|
|
|
func (n *Node) IsNode() bool {
|
|
return true
|
|
}
|
|
|
|
func (n *Node) IsPage() bool {
|
|
return !n.IsNode()
|
|
}
|
|
|
|
func (n *Node) Ref(ref string) (string, error) {
|
|
return n.Site.Ref(ref, nil)
|
|
}
|
|
|
|
func (n *Node) RelRef(ref string) (string, error) {
|
|
return n.Site.RelRef(ref, nil)
|
|
}
|
|
|
|
type URLPath struct {
|
|
URL string
|
|
Permalink string
|
|
Slug string
|
|
Section string
|
|
}
|
|
|
|
func (n *Node) URL() string {
|
|
return n.addLangPathPrefix(n.URLPath.URL)
|
|
}
|
|
|
|
func (n *Node) Permalink() string {
|
|
return permalink(n.URL())
|
|
}
|
|
|
|
// Scratch returns the writable context associated with this Node.
|
|
func (n *Node) Scratch() *Scratch {
|
|
if n.scratch == nil {
|
|
n.scratch = newScratch()
|
|
}
|
|
return n.scratch
|
|
}
|
|
|
|
func (n *Node) Language() *helpers.Language {
|
|
n.initLanguage()
|
|
return n.language
|
|
}
|
|
|
|
func (n *Node) Lang() string {
|
|
// When set, Language can be different from lang in the case where there is a
|
|
// content file (doc.sv.md) with language indicator, but there is no language
|
|
// config for that language. Then the language will fall back on the site default.
|
|
if n.Language() != nil {
|
|
return n.Language().Lang
|
|
}
|
|
return n.lang
|
|
}
|
|
|
|
func (n *Node) shouldAddLanguagePrefix() bool {
|
|
if !n.Site.IsMultiLingual() {
|
|
return false
|
|
}
|
|
|
|
if n.Lang() == "" {
|
|
return false
|
|
}
|
|
|
|
if !n.Site.defaultContentLanguageInSubdir && n.Lang() == n.Site.multilingual.DefaultLang.Lang {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (n *Node) initLanguage() {
|
|
n.languageInit.Do(func() {
|
|
if n.language != nil {
|
|
return
|
|
}
|
|
pageLang := n.lang
|
|
ml := n.Site.multilingual
|
|
if ml == nil {
|
|
panic("Multilanguage not set")
|
|
}
|
|
if pageLang == "" {
|
|
n.language = ml.DefaultLang
|
|
return
|
|
}
|
|
|
|
language := ml.Language(pageLang)
|
|
|
|
if language == nil {
|
|
// It can be a file named stefano.chiodino.md.
|
|
jww.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
|
|
language = ml.DefaultLang
|
|
}
|
|
|
|
n.language = language
|
|
})
|
|
}
|
|
|
|
func (n *Node) LanguagePrefix() string {
|
|
return n.Site.LanguagePrefix
|
|
}
|
|
|
|
// AllTranslations returns all translations, including the current Node.
|
|
// Note that this and the one below is kind of a temporary hack before #2297 is solved.
|
|
func (n *Node) AllTranslations() Nodes {
|
|
n.initTranslations()
|
|
return n.translations
|
|
}
|
|
|
|
// Translations returns the translations excluding the current Node.
|
|
func (n *Node) Translations() Nodes {
|
|
n.initTranslations()
|
|
translations := make(Nodes, 0)
|
|
|
|
for _, t := range n.translations {
|
|
|
|
if t != n {
|
|
translations = append(translations, t)
|
|
}
|
|
}
|
|
return translations
|
|
}
|
|
|
|
// IsTranslated returns whether this node is translated to
|
|
// other language(s).
|
|
func (n *Node) IsTranslated() bool {
|
|
n.initTranslations()
|
|
return len(n.translations) > 1
|
|
}
|
|
|
|
func (n *Node) initTranslations() {
|
|
n.translationsInit.Do(func() {
|
|
n.translations = n.Site.owner.getNodes(n.nodeID)
|
|
//sort.Sort(n.translations)
|
|
})
|
|
}
|
|
|
|
func (n *Node) addLangPathPrefix(outfile string) string {
|
|
return n.addLangPathPrefixIfFlagSet(outfile, n.shouldAddLanguagePrefix())
|
|
}
|
|
|
|
func (n *Node) addLangPathPrefixIfFlagSet(outfile string, should bool) string {
|
|
if helpers.IsAbsURL(outfile) {
|
|
return outfile
|
|
}
|
|
|
|
if !should {
|
|
return outfile
|
|
}
|
|
|
|
hadSlashSuffix := strings.HasSuffix(outfile, "/")
|
|
|
|
outfile = "/" + path.Join(n.Lang(), outfile)
|
|
if hadSlashSuffix {
|
|
outfile += "/"
|
|
}
|
|
return outfile
|
|
}
|
|
|
|
func (n *Node) addLangFilepathPrefix(outfile string) string {
|
|
if outfile == "" {
|
|
outfile = helpers.FilePathSeparator
|
|
}
|
|
if !n.shouldAddLanguagePrefix() {
|
|
return outfile
|
|
}
|
|
return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
|
|
}
|