From 142558719324aa1628541d556ef1fa2d123f1e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 4 Apr 2017 18:14:41 +0200 Subject: [PATCH] hugolib: Add optional outputFormat to Ref/RelRef Fixes #3224 --- hugolib/page.go | 20 ++++++++++--- hugolib/page_output.go | 3 +- hugolib/permalinker.go | 25 +++++++++++++++++ hugolib/site.go | 35 ++++++++++++++++++----- hugolib/site_test.go | 32 +++++++++++---------- tpl/tplimpl/template_embedded.go | 4 +-- tpl/tplimpl/template_funcs.go | 48 ++++++++++++-------------------- 7 files changed, 108 insertions(+), 59 deletions(-) create mode 100644 hugolib/permalinker.go diff --git a/hugolib/page.go b/hugolib/page.go index 7b7d8d655..00646d27d 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -1522,12 +1522,24 @@ func (p *Page) RSSlink() template.URL { return p.RSSLink() } -func (p *Page) Ref(ref string) (string, error) { - return p.Site.Ref(ref, nil) +func (p *Page) Ref(refs ...string) (string, error) { + if len(refs) == 0 { + return "", nil + } + if len(refs) > 1 { + return p.Site.Ref(refs[0], nil, refs[1]) + } + return p.Site.Ref(refs[0], nil) } -func (p *Page) RelRef(ref string) (string, error) { - return p.Site.RelRef(ref, nil) +func (p *Page) RelRef(refs ...string) (string, error) { + if len(refs) == 0 { + return "", nil + } + if len(refs) > 1 { + return p.Site.RelRef(refs[0], nil, refs[1]) + } + return p.Site.RelRef(refs[0], nil) } func (p *Page) String() string { diff --git a/hugolib/page_output.go b/hugolib/page_output.go index 58d09d688..c6a72d324 100644 --- a/hugolib/page_output.go +++ b/hugolib/page_output.go @@ -243,9 +243,8 @@ func (p *Page) AlternativeOutputFormats() (OutputFormats, error) { // Get gets a OutputFormat given its name, i.e. json, html etc. // It returns nil if not found. func (o OutputFormats) Get(name string) *OutputFormat { - name = strings.ToLower(name) for _, f := range o { - if strings.ToLower(f.f.Name) == name { + if strings.EqualFold(f.f.Name, name) { return f } } diff --git a/hugolib/permalinker.go b/hugolib/permalinker.go new file mode 100644 index 000000000..5e7a13a02 --- /dev/null +++ b/hugolib/permalinker.go @@ -0,0 +1,25 @@ +// Copyright 2017-present 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 + +var ( + _ Permalinker = (*Page)(nil) + _ Permalinker = (*OutputFormat)(nil) +) + +// Permalinker provides permalinks of both the relative and absolute kind. +type Permalinker interface { + Permalink() string + RelPermalink() string +} diff --git a/hugolib/site.go b/hugolib/site.go index aefc8c940..7aec4d4d3 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -407,7 +407,7 @@ func (s *SiteInfo) IsMultiLingual() bool { return len(s.Languages) > 1 } -func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error) { +func (s *SiteInfo) refLink(ref string, page *Page, relative bool, outputFormat string) (string, error) { var refURL *url.URL var err error @@ -433,10 +433,21 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error return "", fmt.Errorf("No page found with path or logical name \"%s\".\n", refURL.Path) } + var permalinker Permalinker = target + + if outputFormat != "" { + o := target.OutputFormats().Get(outputFormat) + + if o == nil { + return "", fmt.Errorf("Output format %q not found for page %q", outputFormat, refURL.Path) + } + permalinker = o + } + if relative { - link = target.RelPermalink() + link = permalinker.RelPermalink() } else { - link = target.Permalink() + link = permalinker.Permalink() } } @@ -454,13 +465,23 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error } // Ref will give an absolute URL to ref in the given Page. -func (s *SiteInfo) Ref(ref string, page *Page) (string, error) { - return s.refLink(ref, page, false) +func (s *SiteInfo) Ref(ref string, page *Page, options ...string) (string, error) { + outputFormat := "" + if len(options) > 0 { + outputFormat = options[0] + } + + return s.refLink(ref, page, false, outputFormat) } // RelRef will give an relative URL to ref in the given Page. -func (s *SiteInfo) RelRef(ref string, page *Page) (string, error) { - return s.refLink(ref, page, true) +func (s *SiteInfo) RelRef(ref string, page *Page, options ...string) (string, error) { + outputFormat := "" + if len(options) > 0 { + outputFormat = options[0] + } + + return s.refLink(ref, page, true, outputFormat) } // SourceRelativeLink attempts to convert any source page relative links (like [../another.md]) into absolute links diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 5f66b153c..e00e8b230 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -894,10 +894,6 @@ func setupLinkingMockSite(t *testing.T) *Site { {Name: filepath.FromSlash("level2/index.md"), Content: []byte("")}, {Name: filepath.FromSlash("level2/common.md"), Content: []byte("")}, - // {Name: filepath.FromSlash("level2b/2b-root.md"), Content: []byte("")}, - // {Name: filepath.FromSlash("level2b/index.md"), Content: []byte("")}, - // {Name: filepath.FromSlash("level2b/common.md"), Content: []byte("")}, - {Name: filepath.FromSlash("level2/2-image.png"), Content: []byte("")}, {Name: filepath.FromSlash("level2/common.png"), Content: []byte("")}, @@ -912,12 +908,14 @@ func setupLinkingMockSite(t *testing.T) *Site { cfg.Set("baseURL", "http://auth/") cfg.Set("uglyURLs", false) + cfg.Set("outputs", map[string]interface{}{ + "page": []string{"HTML", "AMP"}, + }) cfg.Set("pluralizeListTitles", false) cfg.Set("canonifyURLs", false) cfg.Set("blackfriday", map[string]interface{}{ "sourceRelativeLinksProjectFolder": "/docs"}) - writeSourcesToSource(t, "content", fs, sources...) return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) @@ -932,19 +930,25 @@ func TestRefLinking(t *testing.T) { t.Fatalf("failed to find current page in site") } - // refLink doesn't use the location of the current page to work out reflinks - okresults := map[string]string{ + for i, test := range []struct { + link string + outputFormat string + relative bool + expected string + }{ // Note: There are no magic in the index.md name. This was fixed in Hugo 0.20. // Before that, index.md would wrongly resolve to "/". - "index.md": "/index/", - "common.md": "/level2/common/", - "3-root.md": "/level2/level3/3-root/", - } - for link, url := range okresults { - if out, err := site.Info.refLink(link, currentPage, true); err != nil || out != url { - t.Errorf("Expected %s to resolve to (%s), got (%s) - error: %s", link, url, out, err) + {"index.md", "", true, "/index/"}, + {"common.md", "", true, "/level2/common/"}, + {"3-root.md", "", true, "/level2/level3/3-root/"}, + {"index.md", "amp", true, "/amp/index/"}, + {"index.md", "amp", false, "http://auth/amp/index/"}, + } { + if out, err := site.Info.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) } } + // TODO: and then the failure cases. } diff --git a/tpl/tplimpl/template_embedded.go b/tpl/tplimpl/template_embedded.go index b1562a0e7..2d4769f78 100644 --- a/tpl/tplimpl/template_embedded.go +++ b/tpl/tplimpl/template_embedded.go @@ -14,8 +14,8 @@ package tplimpl func (t *templateHandler) embedShortcodes() { - t.addInternalShortcode("ref.html", `{{ .Get 0 | ref .Page }}`) - t.addInternalShortcode("relref.html", `{{ .Get 0 | relref .Page }}`) + t.addInternalShortcode("ref.html", `{{ if len .Params | eq 2 }}{{ ref .Page (.Get 0) (.Get 1) }}{{ else }}{{ ref .Page (.Get 0) }}{{ end }}`) + t.addInternalShortcode("relref.html", `{{ if len .Params | eq 2 }}{{ relref .Page (.Get 0) (.Get 1) }}{{ else }}{{ relref .Page (.Get 0) }}{{ end }}`) t.addInternalShortcode("highlight.html", `{{ if len .Params | eq 2 }}{{ highlight .Inner (.Get 0) (.Get 1) }}{{ else }}{{ highlight .Inner (.Get 0) "" }}{{ end }}`) t.addInternalShortcode("test.html", `This is a simple Test`) t.addInternalShortcode("figure.html", ` diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index 9703f6cff..11ab4511c 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -46,7 +46,6 @@ import ( "github.com/spf13/afero" "github.com/spf13/cast" "github.com/spf13/hugo/helpers" - jww "github.com/spf13/jwalterweatherman" // Importing image codecs for image.DecodeConfig _ "image/gif" @@ -1432,41 +1431,30 @@ func plainify(in interface{}) (string, error) { return helpers.StripHTML(s), nil } -func refPage(page interface{}, ref, methodName string) template.HTML { - value := reflect.ValueOf(page) - - method := value.MethodByName(methodName) - - if method.IsValid() && method.Type().NumIn() == 1 && method.Type().NumOut() == 2 { - result := method.Call([]reflect.Value{reflect.ValueOf(ref)}) - - url, err := result[0], result[1] - - if !err.IsNil() { - jww.ERROR.Printf("%s", err.Interface()) - return template.HTML(fmt.Sprintf("%s", err.Interface())) - } - - if url.String() == "" { - jww.ERROR.Printf("ref %s could not be found\n", ref) - return template.HTML(ref) - } - - return template.HTML(url.String()) - } - - jww.ERROR.Printf("Can only create references from Page and Node objects.") - return template.HTML(ref) +type reflinker interface { + Ref(refs ...string) (string, error) + RelRef(refs ...string) (string, error) } // ref returns the absolute URL path to a given content item. -func ref(page interface{}, ref string) template.HTML { - return refPage(page, ref, "Ref") +func ref(in interface{}, refs ...string) (template.HTML, error) { + p, ok := in.(reflinker) + if !ok { + return "", errors.New("invalid Page received in ref") + } + s, err := p.Ref(refs...) + return template.HTML(s), err } // relRef returns the relative URL path to a given content item. -func relRef(page interface{}, ref string) template.HTML { - return refPage(page, ref, "RelRef") +func relRef(in interface{}, refs ...string) (template.HTML, error) { + p, ok := in.(reflinker) + if !ok { + return "", errors.New("invalid Page received in relref") + } + + s, err := p.RelRef(refs...) + return template.HTML(s), err } // chomp removes trailing newline characters from a string.