summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2018-07-18 19:58:39 +0200
committerBjørn Erik Pedersen <[email protected]>2018-07-19 14:32:43 +0200
commite25aa655f4227ac064be5fe770d517a80acd46b2 (patch)
treed5826566794ba8acba57fda155d56ba3d844fbad
parent12679b408362a93a3c6159588d6291a3b7ed5548 (diff)
downloadhugo-e25aa655f4227ac064be5fe770d517a80acd46b2.tar.gz
hugo-e25aa655f4227ac064be5fe770d517a80acd46b2.zip
Add configurable ref/relref error handling and notFoundURL
Two new settings: * refLinksErrorLevel: ERROR (default) or WARNING. ERROR will fail the build. * refLinksNotFoundURL: Used as a placeholder when page references cannot be found. Fixes #4964
-rw-r--r--hugolib/config_test.go2
-rw-r--r--hugolib/hugo_sites.go19
-rw-r--r--hugolib/page.go15
-rw-r--r--hugolib/shortcode_test.go18
-rw-r--r--hugolib/site.go146
-rw-r--r--hugolib/site_test.go2
-rw-r--r--hugolib/testhelpers_test.go2
7 files changed, 112 insertions, 92 deletions
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index 0fe692805..16d07d1af 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -392,6 +392,6 @@ privacyEnhanced = true
b.WithConfigFile("toml", tomlConfig)
b.Build(BuildCfg{SkipRender: true})
- assert.True(b.H.Sites[0].Info.Config.Privacy.YouTube.PrivacyEnhanced)
+ assert.True(b.H.Sites[0].Info.Config().Privacy.YouTube.PrivacyEnhanced)
}
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 8cb3cf2fd..859e0da7c 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -129,7 +129,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
s.owner = h
}
- if err := applyDepsIfNeeded(cfg, sites...); err != nil {
+ if err := applyDeps(cfg, sites...); err != nil {
return nil, err
}
@@ -161,7 +161,7 @@ func (h *HugoSites) initGitInfo() error {
return nil
}
-func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
+func applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
if cfg.TemplateProvider == nil {
cfg.TemplateProvider = tplimpl.DefaultTemplateProvider
}
@@ -208,6 +208,19 @@ func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
s.Deps = d
}
+ if err := s.initializeSiteInfo(); err != nil {
+ return err
+ }
+
+ siteConfig, err := loadSiteConfig(s.Language)
+ if err != nil {
+ return err
+ }
+ s.siteConfig = siteConfig
+ s.siteRefLinker, err = newSiteRefLinker(s.Language, s)
+ if err != nil {
+ return err
+ }
}
return nil
@@ -308,7 +321,7 @@ func (h *HugoSites) createSitesFromConfig() error {
s.owner = h
}
- if err := applyDepsIfNeeded(depsCfg, sites...); err != nil {
+ if err := applyDeps(depsCfg, sites...); err != nil {
return err
}
diff --git a/hugolib/page.go b/hugolib/page.go
index 353e546d3..838791ab8 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -2063,7 +2063,8 @@ func (p *Page) decodeRefArgs(args map[string]interface{}) (refArgs, *SiteInfo, e
}
if !found {
- return ra, nil, fmt.Errorf("no site found with lang %q", ra.Lang)
+ p.s.siteRefLinker.logNotFound(ra.Path, fmt.Sprintf("no site found with lang %q", ra.Lang), p)
+ return ra, nil, nil
}
}
@@ -2076,6 +2077,10 @@ func (p *Page) Ref(argsm map[string]interface{}) (string, error) {
return "", fmt.Errorf("invalid arguments to Ref: %s", err)
}
+ if s == nil {
+ return p.s.siteRefLinker.notFoundURL, nil
+ }
+
if args.Path == "" {
return "", nil
}
@@ -2092,6 +2097,10 @@ func (p *Page) RelRef(argsm map[string]interface{}) (string, error) {
return "", fmt.Errorf("invalid arguments to Ref: %s", err)
}
+ if s == nil {
+ return p.s.siteRefLinker.notFoundURL, nil
+ }
+
if args.Path == "" {
return "", nil
}
@@ -2178,7 +2187,7 @@ func (p *Page) shouldAddLanguagePrefix() bool {
return false
}
- if !p.Site.defaultContentLanguageInSubdir && p.Lang() == p.Site.multilingual.DefaultLang.Lang {
+ if !p.Site.defaultContentLanguageInSubdir && p.Lang() == p.s.multilingual().DefaultLang.Lang {
return false
}
@@ -2191,7 +2200,7 @@ func (p *Page) initLanguage() {
return
}
- ml := p.Site.multilingual
+ ml := p.s.multilingual()
if ml == nil {
panic("Multilanguage not set")
}
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
index 1437ae0cf..f4935c6e9 100644
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -34,7 +34,6 @@ import (
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl"
"github.com/stretchr/testify/require"
@@ -42,19 +41,16 @@ import (
// TODO(bep) remove
func pageFromString(in, filename string, withTemplate ...func(templ tpl.TemplateHandler) error) (*Page, error) {
- s := newTestSite(nil)
- if len(withTemplate) > 0 {
- // Have to create a new site
- var err error
- cfg, fs := newTestCfg()
+ var err error
+ cfg, fs := newTestCfg()
- d := deps.DepsCfg{Language: langs.NewLanguage("en", cfg), Cfg: cfg, Fs: fs, WithTemplate: withTemplate[0]}
+ d := deps.DepsCfg{Cfg: cfg, Fs: fs, WithTemplate: withTemplate[0]}
- s, err = NewSiteForCfg(d)
- if err != nil {
- return nil, err
- }
+ s, err := NewSiteForCfg(d)
+ if err != nil {
+ return nil, err
}
+
return s.NewPageFrom(strings.NewReader(in), filename)
}
diff --git a/hugolib/site.go b/hugolib/site.go
index 08cdd2652..b460a0f81 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -18,6 +18,7 @@ import (
"fmt"
"html/template"
"io"
+ "log"
"mime"
"net/url"
"os"
@@ -127,6 +128,8 @@ type Site struct {
outputFormatsConfig output.Formats
mediaTypesConfig media.Types
+ siteConfig SiteConfig
+
// How to handle page front matter.
frontmatterHandler pagemeta.FrontMatterHandler
@@ -147,6 +150,7 @@ type Site struct {
titleFunc func(s string) string
relatedDocsHandler *relatedDocsHandler
+ siteRefLinker
}
type siteRenderingContext struct {
@@ -183,6 +187,7 @@ func (s *Site) reset() *Site {
disabledKinds: s.disabledKinds,
titleFunc: s.titleFunc,
relatedDocsHandler: newSearchIndexHandler(s.relatedDocsHandler.cfg),
+ siteRefLinker: s.siteRefLinker,
outputFormats: s.outputFormats,
rc: s.rc,
outputFormatsConfig: s.outputFormatsConfig,
@@ -190,7 +195,9 @@ func (s *Site) reset() *Site {
mediaTypesConfig: s.mediaTypesConfig,
Language: s.Language,
owner: s.owner,
+ siteConfig: s.siteConfig,
PageCollections: newPageCollections()}
+
}
// newSite creates a new site with the given configuration.
@@ -276,8 +283,6 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
frontmatterHandler: frontMatterHandler,
}
- s.Info = newSiteInfo(siteBuilderCfg{s: s, pageCollections: c, language: s.Language})
-
return s, nil
}
@@ -291,7 +296,7 @@ func NewSite(cfg deps.DepsCfg) (*Site, error) {
return nil, err
}
- if err = applyDepsIfNeeded(cfg, s); err != nil {
+ if err = applyDeps(cfg, s); err != nil {
return nil, err
}
@@ -333,7 +338,7 @@ func newSiteForLang(lang *langs.Language, withTemplate ...func(templ tpl.Templat
return nil
}
- cfg := deps.DepsCfg{WithTemplate: withTemplates, Language: lang, Cfg: lang}
+ cfg := deps.DepsCfg{WithTemplate: withTemplates, Cfg: lang}
return NewSiteForCfg(cfg)
@@ -343,16 +348,12 @@ func newSiteForLang(lang *langs.Language, withTemplate ...func(templ tpl.Templat
// The site will have a template system loaded and ready to use.
// Note: This is mainly used in single site tests.
func NewSiteForCfg(cfg deps.DepsCfg) (*Site, error) {
- s, err := newSite(cfg)
-
+ h, err := NewHugoSites(cfg)
if err != nil {
return nil, err
}
+ return h.Sites[0], nil
- if err := applyDepsIfNeeded(cfg, s); err != nil {
- return nil, err
- }
- return s, nil
}
type SiteInfos []*SiteInfo
@@ -370,28 +371,24 @@ type SiteInfo struct {
Authors AuthorList
Social SiteSocial
*PageCollections
- Menus *Menus
- Hugo *HugoInfo
- Title string
- RSSLink string
- Author map[string]interface{}
- LanguageCode string
- Copyright string
- LastChange time.Time
- Permalinks PermalinkOverrides
- Params map[string]interface{}
- BuildDrafts bool
- canonifyURLs bool
- relativeURLs bool
- uglyURLs func(p *Page) bool
- preserveTaxonomyNames bool
- Data *map[string]interface{}
-
- Config SiteConfig
-
+ Menus *Menus
+ Hugo *HugoInfo
+ Title string
+ RSSLink string
+ Author map[string]interface{}
+ LanguageCode string
+ Copyright string
+ LastChange time.Time
+ Permalinks PermalinkOverrides
+ Params map[string]interface{}
+ BuildDrafts bool
+ canonifyURLs bool
+ relativeURLs bool
+ uglyURLs func(p *Page) bool
+ preserveTaxonomyNames bool
+ Data *map[string]interface{}
owner *HugoSites
s *Site
- multilingual *Multilingual
Language *langs.Language
LanguagePrefix string
Languages langs.Languages
@@ -399,6 +396,10 @@ type SiteInfo struct {
sectionPagesMenu string
}
+func (s *SiteInfo) Config() SiteConfig {
+ return s.s.siteConfig
+}
+
func (s *SiteInfo) String() string {
return fmt.Sprintf("Site(%q)", s.Title)
}
@@ -422,34 +423,13 @@ func (s *SiteInfo) ServerPort() int {
// GoogleAnalytics is kept here for historic reasons.
func (s *SiteInfo) GoogleAnalytics() string {
- return s.Config.Services.GoogleAnalytics.ID
+ return s.Config().Services.GoogleAnalytics.ID
}
// DisqusShortname is kept here for historic reasons.
func (s *SiteInfo) DisqusShortname() string {
- return s.Config.Services.Disqus.Shortname
-}
-
-// Used in tests.
-
-type siteBuilderCfg struct {
- language *langs.Language
- s *Site
- pageCollections *PageCollections
-}
-
-// TODO(bep) get rid of this
-func newSiteInfo(cfg siteBuilderCfg) SiteInfo {
- return SiteInfo{
- s: cfg.s,
- multilingual: newMultiLingualForLanguage(cfg.language),
- PageCollections: cfg.pageCollections,
- Params: make(map[string]interface{}),
- uglyURLs: func(p *Page) bool {
- return false
- },
- }
+ return s.Config().Services.Disqus.Shortname
}
// SiteSocial is a place to put social details on a site level. These are the
@@ -487,7 +467,35 @@ func (s *SiteInfo) IsServer() bool {
return s.owner.running
}
-func (s *SiteInfo) refLink(ref string, page *Page, relative bool, outputFormat string) (string, error) {
+type siteRefLinker struct {
+ s *Site
+
+ errorLogger *log.Logger
+ notFoundURL string
+}
+
+func newSiteRefLinker(cfg config.Provider, s *Site) (siteRefLinker, error) {
+ logger := s.Log.ERROR
+
+ notFoundURL := cfg.GetString("refLinksNotFoundURL")
+ errLevel := cfg.GetString("refLinksErrorLevel")
+ if strings.EqualFold(errLevel, "warning") {
+ logger = s.Log.WARN
+ }
+ return siteRefLinker{s: s, errorLogger: logger, notFoundURL: notFoundURL}, nil
+}
+
+func (s siteRefLinker) logNotFound(ref, what string, p *Page) {
+ if p != nil {
+ s.errorLogger.Printf("REF_NOT_FOUND: Ref %q: %s", ref, what)
+ } else {
+ s.errorLogger.Printf("REF_NOT_FOUND: Ref %q from page %q: %s", ref, p.absoluteSourceRef(), what)
+ }
+
+}
+
+func (s *siteRefLinker) refLink(ref string, page *Page, relative bool, outputFormat string) (string, error) {
+
var refURL *url.URL
var err error
@@ -496,21 +504,22 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool, outputFormat s
refURL, err = url.Parse(ref)
if err != nil {
- return "", err
+ return s.notFoundURL, err
}
var target *Page
var link string
if refURL.Path != "" {
- target, err := s.getPageNew(page, refURL.Path)
+ target, err := s.s.getPageNew(page, refURL.Path)
if err != nil {
return "", err
}
if target == nil {
- return "", fmt.Errorf("No page found with path or logical name \"%s\".\n", refURL.Path)
+ s.logNotFound(refURL.Path, "page not found", page)
+ return s.notFoundURL, nil
}
var permalinker Permalinker = target
@@ -519,7 +528,8 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool, outputFormat s
o := target.OutputFormats().Get(outputFormat)
if o == nil {
- return "", fmt.Errorf("Output format %q not found for page %q", outputFormat, refURL.Path)
+ s.logNotFound(refURL.Path, fmt.Sprintf("output format %q", outputFormat), page)
+ return s.notFoundURL, nil
}
permalinker = o
}
@@ -551,7 +561,7 @@ func (s *SiteInfo) Ref(ref string, page *Page, options ...string) (string, error
outputFormat = options[0]
}
- return s.refLink(ref, page, false, outputFormat)
+ return s.s.refLink(ref, page, false, outputFormat)
}
// RelRef will give an relative URL to ref in the given Page.
@@ -561,13 +571,17 @@ func (s *SiteInfo) RelRef(ref string, page *Page, options ...string) (string, er
outputFormat = options[0]
}
- return s.refLink(ref, page, true, outputFormat)
+ return s.s.refLink(ref, page, true, outputFormat)
}
func (s *Site) running() bool {
return s.owner != nil && s.owner.running
}
+func (s *Site) multilingual() *Multilingual {
+ return s.owner.multilingual
+}
+
func init() {
defaultTimer = nitro.Initalize()
}
@@ -1102,11 +1116,6 @@ func (s *Site) initializeSiteInfo() error {
languagePrefix = "/" + lang.Lang
}
- var multilingual *Multilingual
- if s.owner != nil {
- multilingual = s.owner.multilingual
- }
-
var uglyURLs = func(p *Page) bool {
return false
}
@@ -1132,18 +1141,12 @@ func (s *Site) initializeSiteInfo() error {
}
}
- siteConfig, err := loadSiteConfig(lang)
- if err != nil {
- return err
- }
-
s.Info = SiteInfo{
Title: lang.GetString("title"),
Author: lang.GetStringMap("author"),
Social: lang.GetStringMapString("social"),
LanguageCode: lang.GetString("languageCode"),
Copyright: lang.GetString("copyright"),
- multilingual: multilingual,
Language: lang,
LanguagePrefix: languagePrefix,
Languages: languages,
@@ -1161,7 +1164,6 @@ func (s *Site) initializeSiteInfo() error {
Data: &s.Data,
owner: s.owner,
s: s,
- Config: siteConfig,
// TODO(bep) make this Menu and similar into delegate methods on SiteInfo
Taxonomies: s.Taxonomies,
}
diff --git a/hugolib/site_test.go b/hugolib/site_test.go
index 202c01986..7787ea2a4 100644
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -925,7 +925,7 @@ func TestRefLinking(t *testing.T) {
{"level2/common.md", "", true, "/level2/common/"},
{"3-root.md", "", true, "/level2/level3/3-root/"},
} {
- if out, err := site.Info.refLink(test.link, currentPage, test.relative, test.outputFormat); err != nil || out != test.expected {
+ if out, err := site.refLink(test.link, currentPage, test.relative, test.outputFormat); err != nil || out != test.expected {
t.Errorf("[%d] Expected %s to resolve to (%s), got (%s) - error: %s", i, test.link, test.expected, out, err)
}
}
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 9fe60c434..9a72bcbb8 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -566,7 +566,7 @@ func newTestSite(t testing.TB, configKeyValues ...interface{}) *Site {
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
}
- d := deps.DepsCfg{Language: langs.NewLanguage("en", cfg), Fs: fs, Cfg: cfg}
+ d := deps.DepsCfg{Fs: fs, Cfg: cfg}
s, err := NewSiteForCfg(d)