Add optional lang as argument to rel/relref
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 17 Jul 2018 19:44:08 +0000 (21:44 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 17 Jul 2018 22:07:20 +0000 (00:07 +0200)
Fixes #4956

hugolib/language_content_dir_test.go
hugolib/page.go
hugolib/page_collections.go
hugolib/shortcode.go
tpl/tplimpl/embedded/templates.autogen.go
tpl/tplimpl/embedded/templates/shortcodes/ref.html
tpl/tplimpl/embedded/templates/shortcodes/relref.html
tpl/urls/urls.go

index 3c3642bf3d420e4ab1124f2aaeec8177e5b800a9..577fdfaeb77abc01ee8439199ea0a22e9d0f8060 100644 (file)
@@ -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
index 0b820b7796b93d3d9b6a32d464298515216e604f..353e546d34fb3d8d719a6a0158e93ad364ead70b 100644 (file)
@@ -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 {
+type refArgs struct {
+       Path         string
+       Lang         string
+       OutputFormat string
+}
+
+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.Ref(refs[0], p, refs[1])
+
+       if args.OutputFormat != "" {
+               return s.Ref(args.Path, p, args.OutputFormat)
        }
-       return p.Site.Ref(refs[0], p)
+       return s.Ref(args.Path, p)
 }
 
-func (p *Page) RelRef(refs ...string) (string, error) {
-       if len(refs) == 0 {
+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 len(refs) > 1 {
-               return p.Site.RelRef(refs[0], p, refs[1])
+
+       if args.OutputFormat != "" {
+               return s.RelRef(args.Path, p, args.OutputFormat)
        }
-       return p.Site.RelRef(refs[0], p)
+       return s.RelRef(args.Path, p)
 }
 
 func (p *Page) String() string {
index 7ec03c1f16f80c5958e2994f0dd9b6fedaa1bbb0..e364d2ef25138495c8195ccd2362417fe37065c3 100644 (file)
@@ -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)
+                               if sourceRef != "" {
+                                       // index the canonical ref
+                                       // e.g. /section/article.md
+                                       add(sourceRef, p)
+                               }
 
-                                       translationBaseName := p.Source.TranslationBaseName()
+                               // Ref/Relref supports this potentially ambiguous lookup.
+                               add(p.Source.LogicalName(), p)
 
-                                       dir, _ := path.Split(sourceRef)
-                                       dir = strings.TrimSuffix(dir, "/")
+                               translationBaseName := p.Source.TranslationBaseName()
 
-                                       if translationBaseName == "index" {
-                                               add(dir, p)
-                                               add(path.Base(dir), p)
-                                       } else {
-                                               add(translationBaseName, p)
-                                       }
+                               dir, _ := path.Split(sourceRef)
+                               dir = strings.TrimSuffix(dir, "/")
 
-                                       // We need a way to get to the current language version.
-                                       pathWithNoExtensions := path.Join(dir, translationBaseName)
-                                       add(pathWithNoExtensions, p)
+                               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)
                        }
                }
 
index 2752937715457987f521142c9f18ebd9ef383470..8ad53993577a30f397586dfc4b965a551aeb39b9 100644 (file)
@@ -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
index fc8ce5cc6b88b841c848d904dec6142b87feb197..35559b8c36acfe76c230d882ff70c89c83d0bbf5 100644 (file)
@@ -380,8 +380,8 @@ if (!doNotTrack) {
 </style>
 {{ 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 -}}
index 84e3e3820c42e1a1486ed2d5d3a18aaac7d9f90d..c4d9f1d16ec221673ddb47f8101260978cd4113a 100644 (file)
@@ -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
index c61423bf1a02712f72da47e3e3ae490f8a932494..d3a31ea4449defc8f794a9a473a71e9358bb868e 100644 (file)
@@ -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
index b93c7cada54eac350aeaa33f61ccad1a5a2fe61b..54c94ec17cff791c6c9def0db605bc9d528963b0 100644 (file)
@@ -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) {