diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-03-09 19:19:29 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-03-27 15:43:56 +0200 |
commit | 6bf010fed432e5574e19fd2946ee6397d895950e (patch) | |
tree | 75282ccbd526adc8dba62f9392db282b3bcec49f /hugolib/page.go | |
parent | c8fff9501d424882a42f750800d9982ec47df640 (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.go | 182 |
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 |