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.
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

View 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 {
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 {

View 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)
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)
}
}

View 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

View 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 -}}

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 {
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) {