urls.go (4825B)
1 // Copyright 2017 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 // Package urls provides template functions to deal with URLs.
15 package urls
16
17 import (
18 "errors"
19 "fmt"
20 "html/template"
21 "net/url"
22
23 "github.com/gohugoio/hugo/common/urls"
24 "github.com/gohugoio/hugo/deps"
25 "github.com/spf13/cast"
26 )
27
28 // New returns a new instance of the urls-namespaced template functions.
29 func New(deps *deps.Deps) *Namespace {
30 return &Namespace{
31 deps: deps,
32 multihost: deps.Cfg.GetBool("multihost"),
33 }
34 }
35
36 // Namespace provides template functions for the "urls" namespace.
37 type Namespace struct {
38 deps *deps.Deps
39 multihost bool
40 }
41
42 // AbsURL takes the string s and converts it to an absolute URL.
43 func (ns *Namespace) AbsURL(s any) (template.HTML, error) {
44 ss, err := cast.ToStringE(s)
45 if err != nil {
46 return "", nil
47 }
48
49 return template.HTML(ns.deps.PathSpec.AbsURL(ss, false)), nil
50 }
51
52 // Parse parses rawurl into a URL structure. The rawurl may be relative or
53 // absolute.
54 func (ns *Namespace) Parse(rawurl any) (*url.URL, error) {
55 s, err := cast.ToStringE(rawurl)
56 if err != nil {
57 return nil, fmt.Errorf("Error in Parse: %w", err)
58 }
59
60 return url.Parse(s)
61 }
62
63 // RelURL takes the string s and prepends the relative path according to a
64 // page's position in the project directory structure.
65 func (ns *Namespace) RelURL(s any) (template.HTML, error) {
66 ss, err := cast.ToStringE(s)
67 if err != nil {
68 return "", nil
69 }
70
71 return template.HTML(ns.deps.PathSpec.RelURL(ss, false)), nil
72 }
73
74 // URLize returns the the strings s formatted as an URL.
75 func (ns *Namespace) URLize(s any) (string, error) {
76 ss, err := cast.ToStringE(s)
77 if err != nil {
78 return "", nil
79 }
80 return ns.deps.PathSpec.URLize(ss), nil
81 }
82
83 // Anchorize creates sanitized anchor name version of the string s that is compatible
84 // with how your configured markdown renderer does it.
85 func (ns *Namespace) Anchorize(s any) (string, error) {
86 ss, err := cast.ToStringE(s)
87 if err != nil {
88 return "", nil
89 }
90 return ns.deps.ContentSpec.SanitizeAnchorName(ss), nil
91 }
92
93 // Ref returns the absolute URL path to a given content item from Page p.
94 func (ns *Namespace) Ref(p any, args any) (template.HTML, error) {
95 pp, ok := p.(urls.RefLinker)
96 if !ok {
97 return "", errors.New("invalid Page received in Ref")
98 }
99 argsm, err := ns.refArgsToMap(args)
100 if err != nil {
101 return "", err
102 }
103 s, err := pp.Ref(argsm)
104 return template.HTML(s), err
105 }
106
107 // RelRef returns the relative URL path to a given content item from Page p.
108 func (ns *Namespace) RelRef(p any, args any) (template.HTML, error) {
109 pp, ok := p.(urls.RefLinker)
110 if !ok {
111 return "", errors.New("invalid Page received in RelRef")
112 }
113 argsm, err := ns.refArgsToMap(args)
114 if err != nil {
115 return "", err
116 }
117
118 s, err := pp.RelRef(argsm)
119 return template.HTML(s), err
120 }
121
122 func (ns *Namespace) refArgsToMap(args any) (map[string]any, error) {
123 var (
124 s string
125 of string
126 )
127
128 v := args
129 if _, ok := v.([]any); ok {
130 v = cast.ToStringSlice(v)
131 }
132
133 switch v := v.(type) {
134 case map[string]any:
135 return v, nil
136 case map[string]string:
137 m := make(map[string]any)
138 for k, v := range v {
139 m[k] = v
140 }
141 return m, nil
142 case []string:
143 if len(v) == 0 || len(v) > 2 {
144 return nil, fmt.Errorf("invalid number of arguments to ref")
145 }
146 // These where the options before we introduced the map type:
147 s = v[0]
148 if len(v) == 2 {
149 of = v[1]
150 }
151 default:
152 var err error
153 s, err = cast.ToStringE(args)
154 if err != nil {
155 return nil, err
156 }
157
158 }
159
160 return map[string]any{
161 "path": s,
162 "outputFormat": of,
163 }, nil
164 }
165
166 // RelLangURL takes the string s and prepends the relative path according to a
167 // page's position in the project directory structure and the current language.
168 func (ns *Namespace) RelLangURL(s any) (template.HTML, error) {
169 ss, err := cast.ToStringE(s)
170 if err != nil {
171 return "", err
172 }
173
174 return template.HTML(ns.deps.PathSpec.RelURL(ss, !ns.multihost)), nil
175 }
176
177 // AbsLangURL the string s and converts it to an absolute URL according
178 // to a page's position in the project directory structure and the current
179 // language.
180 func (ns *Namespace) AbsLangURL(s any) (template.HTML, error) {
181 ss, err := cast.ToStringE(s)
182 if err != nil {
183 return "", err
184 }
185
186 return template.HTML(ns.deps.PathSpec.AbsURL(ss, !ns.multihost)), nil
187 }