summaryrefslogtreecommitdiffstats
path: root/hugolib/page.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-03-09 19:19:29 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-03-27 15:43:56 +0200
commit6bf010fed432e5574e19fd2946ee6397d895950e (patch)
tree75282ccbd526adc8dba62f9392db282b3bcec49f /hugolib/page.go
parentc8fff9501d424882a42f750800d9982ec47df640 (diff)
hugolib: Refactor/-work the permalink/target path logic
This is a pretty fundamental change in Hugo, but absolutely needed if we should have any hope of getting "multiple outputs" done. This commit's goal is to say: * Every file target path is created by `createTargetPath`, i.e. one function for all. * That function takes every page and site parameter into account, to avoid fragile string parsing to uglify etc. later on. * The path creation logic has full test coverage. * All permalinks, paginator URLs etc. are then built on top of that same logic. Fixes #1252 Fixes #2110 Closes #2374 Fixes #1885 Fixes #3102 Fixes #3179 Fixes #1641 Fixes #1989
Diffstat (limited to 'hugolib/page.go')
-rw-r--r--hugolib/page.go182
1 files changed, 21 insertions, 161 deletions
diff --git a/hugolib/page.go b/hugolib/page.go
index 8efe78225..8d8bd2be9 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -28,7 +28,6 @@ import (
"html/template"
"io"
- "net/url"
"path"
"path/filepath"
"regexp"
@@ -188,11 +187,9 @@ type Page struct {
RSSLink template.URL
URLPath
- permalink *url.URL
+ permalink string
relPermalink string
- paginator *Pager
-
scratch *Scratch
// It would be tempting to use the language set on the Site, but in they way we do
@@ -204,6 +201,10 @@ type Page struct {
// The output types this page will be rendered to.
outputTypes output.Types
+ // This is the PageOutput that represents the first item in outputTypes.
+ // Use with care, as there are potential for inifinite loops.
+ mainPageOutput *PageOutput
+
// Used to pick the correct template(s)
layoutIdentifier pageLayoutIdentifier
}
@@ -248,12 +249,10 @@ type pageInit struct {
languageInit sync.Once
pageMenusInit sync.Once
pageMetaInit sync.Once
- paginatorInit sync.Once
plainInit sync.Once
plainWordsInit sync.Once
renderingConfigInit sync.Once
pageURLInit sync.Once
- relPermalinkInit sync.Once
}
// IsNode returns whether this is an item of one of the list types in Hugo,
@@ -787,68 +786,6 @@ func (p *Page) analyzePage() {
})
}
-func (p *Page) getPermalink() *url.URL {
- p.pageURLInit.Do(func() {
- u, err := p.createPermalink()
- if err != nil {
- p.s.Log.ERROR.Printf("Failed to create permalink for page %q: %s", p.FullFilePath(), err)
- p.permalink = new(url.URL)
- return
- }
-
- p.permalink = u
- })
-
- // The link may be modified by the receiver, so create a copy.
- l := *p.permalink
-
- return &l
-}
-
-func (p *Page) createPermalink() (*url.URL, error) {
- // TODO(bep) this should probably be set once during build. Maybe.
- // And simplified.
- baseURL := string(p.Site.BaseURL)
-
- if p.IsNode() {
- // No permalink config for nodes (currently)
- pURL := strings.TrimSpace(p.s.PathSpec.URLize(p.URLPath.URL))
- pURL = p.addLangPathPrefix(pURL)
- pURL = p.s.PathSpec.URLPrep(pURL)
- url := helpers.MakePermalink(baseURL, pURL)
- return url, nil
- }
-
- dir := strings.TrimSpace(p.s.PathSpec.MakePath(filepath.ToSlash(strings.ToLower(p.Source.Dir()))))
- pSlug := strings.TrimSpace(p.s.PathSpec.URLize(p.Slug))
- pURL := strings.TrimSpace(p.s.PathSpec.URLize(p.URLPath.URL))
- var permalink string
- var err error
-
- if len(pURL) > 0 {
- return helpers.MakePermalink(baseURL, pURL), nil
- }
-
- if override, ok := p.Site.Permalinks[p.Section()]; ok {
- permalink, err = override.Expand(p)
-
- if err != nil {
- return nil, err
- }
- } else {
- if len(pSlug) > 0 {
- permalink = p.s.PathSpec.URLPrep(path.Join(dir, p.Slug+"."+p.Extension()))
- } else {
- t := p.Source.TranslationBaseName()
- permalink = p.s.PathSpec.URLPrep(path.Join(dir, (strings.TrimSpace(t) + "." + p.Extension())))
- }
- }
-
- permalink = p.addLangPathPrefix(permalink)
-
- return helpers.MakePermalink(baseURL, permalink), nil
-}
-
func (p *Page) Extension() string {
if p.extension != "" {
// TODO(bep) output remove/deprecate this
@@ -927,10 +864,6 @@ func (p *Page) IsExpired() bool {
return p.ExpiryDate.Before(time.Now())
}
-func (p *Page) Permalink() string {
- return p.getPermalink().String()
-}
-
func (p *Page) URL() string {
if p.IsPage() && p.URLPath.URL != "" {
@@ -942,39 +875,25 @@ func (p *Page) URL() string {
return u
}
-func (p *Page) RelPermalink() string {
- p.relPermalinkInit.Do(func() {
- link := p.getPermalink()
-
- if p.s.Info.canonifyURLs { // replacements for relpermalink with baseURL on the form http://myhost.com/sub/ will fail later on
- // have to return the URL relative from baseURL
- relpath, err := helpers.GetRelativePath(link.String(), string(p.Site.BaseURL))
- if err != nil {
- return
- }
-
- relpath = filepath.ToSlash(relpath)
-
- if relpath[0] == '.' {
- relpath = relpath[1:]
- }
-
- if !strings.HasPrefix(relpath, "/") {
- relpath = "/" + relpath
- }
+// Permalink returns the absolute URL to this Page.
+func (p *Page) Permalink() string {
+ p.initURLs()
+ return p.permalink
+}
- p.relPermalink = relpath
- return
- }
+// RelPermalink gets a URL to the resource relative to the host.
+func (p *Page) RelPermalink() string {
+ p.initURLs()
+ return p.relPermalink
+}
- link.Scheme = ""
- link.Host = ""
- link.User = nil
- link.Opaque = ""
- p.relPermalink = link.String()
+func (p *Page) initURLs() {
+ p.pageURLInit.Do(func() {
+ rel := p.createRelativePermalink()
+ p.permalink = p.s.permalink(rel)
+ rel = p.s.PathSpec.PrependBasePath(rel)
+ p.relPermalink = rel
})
-
- return p.relPermalink
}
var ErrHasDraftAndPublished = errors.New("both draft and published parameters were found in page's frontmatter")
@@ -1507,56 +1426,6 @@ func (p *Page) FullFilePath() string {
return filepath.Join(p.Dir(), p.LogicalName())
}
-func (p *Page) TargetPath() (outfile string) {
-
- switch p.Kind {
- case KindHome:
- return p.addLangFilepathPrefix(helpers.FilePathSeparator)
- case KindSection:
- return p.addLangFilepathPrefix(p.sections[0])
- case KindTaxonomy:
- return p.addLangFilepathPrefix(filepath.Join(p.sections...))
- case KindTaxonomyTerm:
- return p.addLangFilepathPrefix(filepath.Join(p.sections...))
- }
-
- // Always use URL if it's specified
- if len(strings.TrimSpace(p.URLPath.URL)) > 2 {
- outfile = strings.TrimSpace(p.URLPath.URL)
-
- if strings.HasSuffix(outfile, "/") {
- outfile = outfile + "index.html"
- }
- outfile = filepath.FromSlash(outfile)
- return
- }
-
- // If there's a Permalink specification, we use that
- if override, ok := p.Site.Permalinks[p.Section()]; ok {
- var err error
- outfile, err = override.Expand(p)
- if err == nil {
- outfile, _ = url.QueryUnescape(outfile)
- if strings.HasSuffix(outfile, "/") {
- outfile += "index.html"
- }
- outfile = filepath.FromSlash(outfile)
- outfile = p.addLangFilepathPrefix(outfile)
- return
- }
- }
-
- if len(strings.TrimSpace(p.Slug)) > 0 {
- outfile = strings.TrimSpace(p.Slug) + "." + p.Extension()
- } else {
- // Fall back to filename
- outfile = (p.Source.TranslationBaseName() + "." + p.Extension())
- }
-
- return p.addLangFilepathPrefix(filepath.Join(strings.ToLower(
- p.s.PathSpec.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
-}
-
// Pre render prepare steps
func (p *Page) prepareLayouts() error {
@@ -1682,9 +1551,6 @@ func (p *Page) updatePageDates() {
// copy creates a copy of this page with the lazy sync.Once vars reset
// so they will be evaluated again, for word count calculations etc.
func (p *Page) copy() *Page {
- // This is a temporary workaround for the data race in #3129
- p.getPermalink()
-
c := *p
c.pageInit = &pageInit{}
return &c
@@ -1895,12 +1761,6 @@ func kindFromFilename(filename string) string {
return kindUnknown
}
-// TODO(bep) output
-var (
- outputTypesWithRSS = output.Types{output.HTMLType, output.RSSType}
- outputTypesHTML = output.Types{output.HTMLType}
-)
-
func (p *Page) setValuesForKind(s *Site) {
if p.Kind == kindUnknown {
// This is either a taxonomy list, taxonomy term or a section