diff --git a/hugolib/language_content_dir_test.go b/hugolib/language_content_dir_test.go index 3c3642bf3..577fdfaeb 100644 --- a/hugolib/language_content_dir_test.go +++ b/hugolib/language_content_dir_test.go @@ -81,6 +81,9 @@ weight: %d Content. +SVP3-REF: {{< ref path="/sect/page3.md" lang="sv" >}} +SVP3-RELREF: {{< relref path="/sect/page3.md" lang="sv" >}} + ` pageBundleTemplate := ` @@ -211,24 +214,32 @@ Content. assert.NoError(err) nnP2, err := nnSite.getPageNew(nil, "/sect/page2.md") assert.NoError(err) - nnP2_2, err := svSite.getPageNew(nil, "/nn/sect/page2.md") - assert.NoError(err) - enP2_2, err := nnSite.getPageNew(nil, "/en/sect/page2.md") - assert.NoError(err) - svP2_2, err := enSite.getPageNew(nil, "/sv/sect/page2.md") - assert.NoError(err) enP2, err := enSite.getPageNew(nil, "/sect/page2.md") assert.NoError(err) - assert.NotNil(enP2) - assert.NotNil(svP2) - assert.NotNil(nnP2) + assert.Equal("en", enP2.Lang()) assert.Equal("sv", svP2.Lang()) assert.Equal("nn", nnP2.Lang()) - assert.Equal("en", enP2.Lang()) - assert.Equal(nnP2, nnP2_2) - assert.Equal(enP2, enP2_2) - assert.Equal(svP2, svP2_2) + + content, _ := nnP2.Content() + assert.Contains(content, "SVP3-REF: https://example.org/sv/sect/p-sv-3/") + assert.Contains(content, "SVP3-RELREF: /sv/sect/p-sv-3/") + + // Test RelRef with and without language indicator. + nn3RefArgs := map[string]interface{}{ + "path": "/sect/page3.md", + "lang": "nn", + } + nnP3RelRef, err := svP2.RelRef( + nn3RefArgs, + ) + assert.NoError(err) + assert.Equal("/nn/sect/p-nn-3/", nnP3RelRef) + nnP3Ref, err := svP2.Ref( + nn3RefArgs, + ) + assert.NoError(err) + assert.Equal("https://example.org/nn/sect/p-nn-3/", nnP3Ref) for i, p := range enSite.RegularPages { j := i + 1 diff --git a/hugolib/page.go b/hugolib/page.go index 0b820b779..353e546d3 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -2038,24 +2038,68 @@ func (p *Page) GetPage(ref string) (*Page, error) { return p.s.getPageNew(p, ref) } -func (p *Page) Ref(refs ...string) (string, error) { - if len(refs) == 0 { - return "", nil - } - if len(refs) > 1 { - return p.Site.Ref(refs[0], p, refs[1]) - } - return p.Site.Ref(refs[0], p) +type refArgs struct { + Path string + Lang string + OutputFormat string } -func (p *Page) RelRef(refs ...string) (string, error) { - if len(refs) == 0 { +func (p *Page) decodeRefArgs(args map[string]interface{}) (refArgs, *SiteInfo, error) { + var ra refArgs + err := mapstructure.WeakDecode(args, &ra) + if err != nil { + return ra, nil, nil + } + s := p.Site + + if ra.Lang != "" && ra.Lang != p.Lang() { + // Find correct site + found := false + for _, ss := range p.s.owner.Sites { + if ss.Lang() == ra.Lang { + found = true + s = &ss.Info + } + } + + if !found { + return ra, nil, fmt.Errorf("no site found with lang %q", ra.Lang) + } + } + + return ra, s, nil +} + +func (p *Page) Ref(argsm map[string]interface{}) (string, error) { + args, s, err := p.decodeRefArgs(argsm) + if err != nil { + return "", fmt.Errorf("invalid arguments to Ref: %s", err) + } + + if args.Path == "" { return "", nil } - if len(refs) > 1 { - return p.Site.RelRef(refs[0], p, refs[1]) + + if args.OutputFormat != "" { + return s.Ref(args.Path, p, args.OutputFormat) } - return p.Site.RelRef(refs[0], p) + return s.Ref(args.Path, p) +} + +func (p *Page) RelRef(argsm map[string]interface{}) (string, error) { + args, s, err := p.decodeRefArgs(argsm) + if err != nil { + return "", fmt.Errorf("invalid arguments to Ref: %s", err) + } + + if args.Path == "" { + return "", nil + } + + if args.OutputFormat != "" { + return s.RelRef(args.Path, p, args.OutputFormat) + } + return s.RelRef(args.Path, p) } func (p *Page) String() string { diff --git a/hugolib/page_collections.go b/hugolib/page_collections.go index 7ec03c1f1..e364d2ef2 100644 --- a/hugolib/page_collections.go +++ b/hugolib/page_collections.go @@ -76,12 +76,6 @@ func (c *PageCollections) refreshPageCaches() { c.RegularPages = c.findPagesByKindIn(KindPage, c.Pages) c.AllRegularPages = c.findPagesByKindIn(KindPage, c.AllPages) - var s *Site - - if len(c.Pages) > 0 { - s = c.Pages[0].s - } - indexLoader := func() (map[string]interface{}, error) { index := make(map[string]interface{}) @@ -94,44 +88,34 @@ func (c *PageCollections) refreshPageCaches() { } } - // Note that we deliberately use the pages from all sites - // in this index, as we intend to use this in the ref and relref - // shortcodes. - for _, pageCollection := range []Pages{c.AllRegularPages, c.headlessPages} { + for _, pageCollection := range []Pages{c.RegularPages, c.headlessPages} { for _, p := range pageCollection { sourceRef := p.absoluteSourceRef() - // Allow cross language references by - // adding the language code as prefix. - add(path.Join("/"+p.Lang(), sourceRef), p) - - // For pages in the current language. - if s != nil && p.s == s { - if sourceRef != "" { - // index the canonical ref - // e.g. /section/article.md - add(sourceRef, p) - } - - // Ref/Relref supports this potentially ambiguous lookup. - add(p.Source.LogicalName(), p) - - translationBaseName := p.Source.TranslationBaseName() - - dir, _ := path.Split(sourceRef) - dir = strings.TrimSuffix(dir, "/") - - if translationBaseName == "index" { - add(dir, p) - add(path.Base(dir), p) - } else { - add(translationBaseName, p) - } - - // We need a way to get to the current language version. - pathWithNoExtensions := path.Join(dir, translationBaseName) - add(pathWithNoExtensions, p) + if sourceRef != "" { + // index the canonical ref + // e.g. /section/article.md + add(sourceRef, p) } + + // Ref/Relref supports this potentially ambiguous lookup. + add(p.Source.LogicalName(), p) + + translationBaseName := p.Source.TranslationBaseName() + + dir, _ := path.Split(sourceRef) + dir = strings.TrimSuffix(dir, "/") + + if translationBaseName == "index" { + add(dir, p) + add(path.Base(dir), p) + } else { + add(translationBaseName, p) + } + + // We need a way to get to the current language version. + pathWithNoExtensions := path.Join(dir, translationBaseName) + add(pathWithNoExtensions, p) } } diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 275293771..8ad539935 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -55,13 +55,13 @@ func (scp *ShortcodeWithPage) Site() *SiteInfo { } // Ref is a shortcut to the Ref method on Page. -func (scp *ShortcodeWithPage) Ref(ref string) (string, error) { - return scp.Page.Ref(ref) +func (scp *ShortcodeWithPage) Ref(args map[string]interface{}) (string, error) { + return scp.Page.Ref(args) } // RelRef is a shortcut to the RelRef method on Page. -func (scp *ShortcodeWithPage) RelRef(ref string) (string, error) { - return scp.Page.RelRef(ref) +func (scp *ShortcodeWithPage) RelRef(args map[string]interface{}) (string, error) { + return scp.Page.RelRef(args) } // Scratch returns a scratch-pad scoped for this shortcode. This can be used diff --git a/tpl/tplimpl/embedded/templates.autogen.go b/tpl/tplimpl/embedded/templates.autogen.go index fc8ce5cc6..35559b8c3 100644 --- a/tpl/tplimpl/embedded/templates.autogen.go +++ b/tpl/tplimpl/embedded/templates.autogen.go @@ -380,8 +380,8 @@ if (!doNotTrack) { {{ end }} {{ end }}`}, - {`shortcodes/ref.html`, `{{ if len .Params | eq 2 }}{{ ref .Page (.Get 0) (.Get 1) }}{{ else }}{{ ref .Page (.Get 0) }}{{ end }}`}, - {`shortcodes/relref.html`, `{{ if len .Params | eq 2 }}{{ relref .Page (.Get 0) (.Get 1) }}{{ else }}{{ relref .Page (.Get 0) }}{{ end }}`}, + {`shortcodes/ref.html`, `{{ ref .Page .Params }}`}, + {`shortcodes/relref.html`, `{{ relref .Page .Params }}`}, {`shortcodes/twitter.html`, `{{- $pc := .Page.Site.Config.Privacy.Twitter -}} {{- if not $pc.Disable -}} {{- if $pc.Simple -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/ref.html b/tpl/tplimpl/embedded/templates/shortcodes/ref.html index 84e3e3820..c4d9f1d16 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/ref.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/ref.html @@ -1 +1 @@ -{{ if len .Params | eq 2 }}{{ ref .Page (.Get 0) (.Get 1) }}{{ else }}{{ ref .Page (.Get 0) }}{{ end }} \ No newline at end of file +{{ ref .Page .Params }} \ No newline at end of file diff --git a/tpl/tplimpl/embedded/templates/shortcodes/relref.html b/tpl/tplimpl/embedded/templates/shortcodes/relref.html index c61423bf1..d3a31ea44 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/relref.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/relref.html @@ -1 +1 @@ -{{ if len .Params | eq 2 }}{{ relref .Page (.Get 0) (.Get 1) }}{{ else }}{{ relref .Page (.Get 0) }}{{ end }} \ No newline at end of file +{{ relref .Page .Params }} \ No newline at end of file diff --git a/tpl/urls/urls.go b/tpl/urls/urls.go index b93c7cada..54c94ec17 100644 --- a/tpl/urls/urls.go +++ b/tpl/urls/urls.go @@ -91,30 +91,76 @@ func (ns *Namespace) Anchorize(a interface{}) (string, error) { } type reflinker interface { - Ref(refs ...string) (string, error) - RelRef(refs ...string) (string, error) + Ref(args map[string]interface{}) (string, error) + RelRef(args map[string]interface{}) (string, error) } // Ref returns the absolute URL path to a given content item. -func (ns *Namespace) Ref(in interface{}, refs ...string) (template.HTML, error) { +func (ns *Namespace) Ref(in interface{}, args interface{}) (template.HTML, error) { p, ok := in.(reflinker) if !ok { return "", errors.New("invalid Page received in Ref") } - s, err := p.Ref(refs...) + argsm, err := ns.refArgsToMap(args) + if err != nil { + return "", err + } + s, err := p.Ref(argsm) return template.HTML(s), err } // RelRef returns the relative URL path to a given content item. -func (ns *Namespace) RelRef(in interface{}, refs ...string) (template.HTML, error) { +func (ns *Namespace) RelRef(in interface{}, args interface{}) (template.HTML, error) { p, ok := in.(reflinker) if !ok { return "", errors.New("invalid Page received in RelRef") } - s, err := p.RelRef(refs...) + argsm, err := ns.refArgsToMap(args) + if err != nil { + return "", err + } + + s, err := p.RelRef(argsm) return template.HTML(s), err } +func (ns *Namespace) refArgsToMap(args interface{}) (map[string]interface{}, error) { + var ( + s string + of string + ) + switch v := args.(type) { + case map[string]interface{}: + return v, nil + case map[string]string: + m := make(map[string]interface{}) + for k, v := range v { + m[k] = v + } + return m, nil + case []string: + if len(v) == 0 || len(v) > 2 { + return nil, fmt.Errorf("invalid numer of arguments to ref") + } + // These where the options before we introduced the map type: + s = v[0] + if len(v) == 2 { + of = v[1] + } + default: + var err error + s, err = cast.ToStringE(args) + if err != nil { + return nil, err + } + + } + return map[string]interface{}{ + "path": s, + "outputFormat": of, + }, nil +} + // RelLangURL takes a given string and prepends the relative path according to a // page's position in the project directory structure and the current language. func (ns *Namespace) RelLangURL(a interface{}) (template.HTML, error) {