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 }