Add optional lang as argument to rel/relref

Fixes #4956
This commit is contained in:
Bjørn Erik Pedersen 2018-07-17 21:44:08 +02:00
parent 3eb313fef4
commit d741064beb
8 changed files with 165 additions and 80 deletions

View file

@ -81,6 +81,9 @@ weight: %d
Content. Content.
SVP3-REF: {{< ref path="/sect/page3.md" lang="sv" >}}
SVP3-RELREF: {{< relref path="/sect/page3.md" lang="sv" >}}
` `
pageBundleTemplate := ` pageBundleTemplate := `
@ -211,24 +214,32 @@ Content.
assert.NoError(err) assert.NoError(err)
nnP2, err := nnSite.getPageNew(nil, "/sect/page2.md") nnP2, err := nnSite.getPageNew(nil, "/sect/page2.md")
assert.NoError(err) 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") enP2, err := enSite.getPageNew(nil, "/sect/page2.md")
assert.NoError(err) assert.NoError(err)
assert.NotNil(enP2) assert.Equal("en", enP2.Lang())
assert.NotNil(svP2)
assert.NotNil(nnP2)
assert.Equal("sv", svP2.Lang()) assert.Equal("sv", svP2.Lang())
assert.Equal("nn", nnP2.Lang()) assert.Equal("nn", nnP2.Lang())
assert.Equal("en", enP2.Lang())
assert.Equal(nnP2, nnP2_2) content, _ := nnP2.Content()
assert.Equal(enP2, enP2_2) assert.Contains(content, "SVP3-REF: https://example.org/sv/sect/p-sv-3/")
assert.Equal(svP2, svP2_2) 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 { for i, p := range enSite.RegularPages {
j := i + 1 j := i + 1

View file

@ -2038,24 +2038,68 @@ func (p *Page) GetPage(ref string) (*Page, error) {
return p.s.getPageNew(p, ref) return p.s.getPageNew(p, ref)
} }
func (p *Page) Ref(refs ...string) (string, error) { type refArgs struct {
if len(refs) == 0 { Path string
return "", nil Lang string
} OutputFormat string
if len(refs) > 1 {
return p.Site.Ref(refs[0], p, refs[1])
}
return p.Site.Ref(refs[0], p)
} }
func (p *Page) RelRef(refs ...string) (string, error) { func (p *Page) decodeRefArgs(args map[string]interface{}) (refArgs, *SiteInfo, error) {
if len(refs) == 0 { 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 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 { func (p *Page) String() string {

View file

@ -76,12 +76,6 @@ func (c *PageCollections) refreshPageCaches() {
c.RegularPages = c.findPagesByKindIn(KindPage, c.Pages) c.RegularPages = c.findPagesByKindIn(KindPage, c.Pages)
c.AllRegularPages = c.findPagesByKindIn(KindPage, c.AllPages) 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) { indexLoader := func() (map[string]interface{}, error) {
index := make(map[string]interface{}) index := make(map[string]interface{})
@ -94,19 +88,10 @@ func (c *PageCollections) refreshPageCaches() {
} }
} }
// Note that we deliberately use the pages from all sites for _, pageCollection := range []Pages{c.RegularPages, c.headlessPages} {
// in this index, as we intend to use this in the ref and relref
// shortcodes.
for _, pageCollection := range []Pages{c.AllRegularPages, c.headlessPages} {
for _, p := range pageCollection { for _, p := range pageCollection {
sourceRef := p.absoluteSourceRef() 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 != "" { if sourceRef != "" {
// index the canonical ref // index the canonical ref
// e.g. /section/article.md // e.g. /section/article.md
@ -133,7 +118,6 @@ func (c *PageCollections) refreshPageCaches() {
add(pathWithNoExtensions, p) add(pathWithNoExtensions, p)
} }
} }
}
for _, p := range c.indexPages { for _, p := range c.indexPages {
// index the canonical, unambiguous ref for any backing file // index the canonical, unambiguous ref for any backing file

View file

@ -55,13 +55,13 @@ func (scp *ShortcodeWithPage) Site() *SiteInfo {
} }
// Ref is a shortcut to the Ref method on Page. // Ref is a shortcut to the Ref method on Page.
func (scp *ShortcodeWithPage) Ref(ref string) (string, error) { func (scp *ShortcodeWithPage) Ref(args map[string]interface{}) (string, error) {
return scp.Page.Ref(ref) return scp.Page.Ref(args)
} }
// RelRef is a shortcut to the RelRef method on Page. // RelRef is a shortcut to the RelRef method on Page.
func (scp *ShortcodeWithPage) RelRef(ref string) (string, error) { func (scp *ShortcodeWithPage) RelRef(args map[string]interface{}) (string, error) {
return scp.Page.RelRef(ref) return scp.Page.RelRef(args)
} }
// Scratch returns a scratch-pad scoped for this shortcode. This can be used // Scratch returns a scratch-pad scoped for this shortcode. This can be used

View file

@ -380,8 +380,8 @@ if (!doNotTrack) {
</style> </style>
{{ end }} {{ end }}
{{ end }}`}, {{ end }}`},
{`shortcodes/ref.html`, `{{ if len .Params | eq 2 }}{{ ref .Page (.Get 0) (.Get 1) }}{{ else }}{{ ref .Page (.Get 0) }}{{ end }}`}, {`shortcodes/ref.html`, `{{ ref .Page .Params }}`},
{`shortcodes/relref.html`, `{{ if len .Params | eq 2 }}{{ relref .Page (.Get 0) (.Get 1) }}{{ else }}{{ relref .Page (.Get 0) }}{{ end }}`}, {`shortcodes/relref.html`, `{{ relref .Page .Params }}`},
{`shortcodes/twitter.html`, `{{- $pc := .Page.Site.Config.Privacy.Twitter -}} {`shortcodes/twitter.html`, `{{- $pc := .Page.Site.Config.Privacy.Twitter -}}
{{- if not $pc.Disable -}} {{- if not $pc.Disable -}}
{{- if $pc.Simple -}} {{- if $pc.Simple -}}

View file

@ -1 +1 @@
{{ if len .Params | eq 2 }}{{ ref .Page (.Get 0) (.Get 1) }}{{ else }}{{ ref .Page (.Get 0) }}{{ end }} {{ ref .Page .Params }}

View file

@ -1 +1 @@
{{ if len .Params | eq 2 }}{{ relref .Page (.Get 0) (.Get 1) }}{{ else }}{{ relref .Page (.Get 0) }}{{ end }} {{ relref .Page .Params }}

View file

@ -91,30 +91,76 @@ func (ns *Namespace) Anchorize(a interface{}) (string, error) {
} }
type reflinker interface { type reflinker interface {
Ref(refs ...string) (string, error) Ref(args map[string]interface{}) (string, error)
RelRef(refs ...string) (string, error) RelRef(args map[string]interface{}) (string, error)
} }
// Ref returns the absolute URL path to a given content item. // 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) p, ok := in.(reflinker)
if !ok { if !ok {
return "", errors.New("invalid Page received in Ref") 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 return template.HTML(s), err
} }
// RelRef returns the relative URL path to a given content item. // 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) p, ok := in.(reflinker)
if !ok { if !ok {
return "", errors.New("invalid Page received in RelRef") 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 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 // 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. // page's position in the project directory structure and the current language.
func (ns *Namespace) RelLangURL(a interface{}) (template.HTML, error) { func (ns *Namespace) RelLangURL(a interface{}) (template.HTML, error) {