github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/common/paths/url.go (about) 1 // Copyright 2021 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 paths 15 16 import ( 17 "fmt" 18 "net/url" 19 "path" 20 "strings" 21 ) 22 23 type pathBridge struct{} 24 25 func (pathBridge) Base(in string) string { 26 return path.Base(in) 27 } 28 29 func (pathBridge) Clean(in string) string { 30 return path.Clean(in) 31 } 32 33 func (pathBridge) Dir(in string) string { 34 return path.Dir(in) 35 } 36 37 func (pathBridge) Ext(in string) string { 38 return path.Ext(in) 39 } 40 41 func (pathBridge) Join(elem ...string) string { 42 return path.Join(elem...) 43 } 44 45 func (pathBridge) Separator() string { 46 return "/" 47 } 48 49 var pb pathBridge 50 51 // MakePermalink combines base URL with content path to create full URL paths. 52 // Example 53 // base: http://spf13.com/ 54 // path: post/how-i-blog 55 // result: http://spf13.com/post/how-i-blog 56 func MakePermalink(host, plink string) *url.URL { 57 base, err := url.Parse(host) 58 if err != nil { 59 panic(err) 60 } 61 62 p, err := url.Parse(plink) 63 if err != nil { 64 panic(err) 65 } 66 67 if p.Host != "" { 68 panic(fmt.Errorf("can't make permalink from absolute link %q", plink)) 69 } 70 71 base.Path = path.Join(base.Path, p.Path) 72 73 // path.Join will strip off the last /, so put it back if it was there. 74 hadTrailingSlash := (plink == "" && strings.HasSuffix(host, "/")) || strings.HasSuffix(p.Path, "/") 75 if hadTrailingSlash && !strings.HasSuffix(base.Path, "/") { 76 base.Path = base.Path + "/" 77 } 78 79 return base 80 } 81 82 // AddContextRoot adds the context root to an URL if it's not already set. 83 // For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite), 84 // relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set. 85 func AddContextRoot(baseURL, relativePath string) string { 86 url, err := url.Parse(baseURL) 87 if err != nil { 88 panic(err) 89 } 90 91 newPath := path.Join(url.Path, relativePath) 92 93 // path strips trailing slash, ignore root path. 94 if newPath != "/" && strings.HasSuffix(relativePath, "/") { 95 newPath += "/" 96 } 97 return newPath 98 } 99 100 // URLizeAn 101 102 // PrettifyURL takes a URL string and returns a semantic, clean URL. 103 func PrettifyURL(in string) string { 104 x := PrettifyURLPath(in) 105 106 if path.Base(x) == "index.html" { 107 return path.Dir(x) 108 } 109 110 if in == "" { 111 return "/" 112 } 113 114 return x 115 } 116 117 // PrettifyURLPath takes a URL path to a content and converts it 118 // to enable pretty URLs. 119 // /section/name.html becomes /section/name/index.html 120 // /section/name/ becomes /section/name/index.html 121 // /section/name/index.html becomes /section/name/index.html 122 func PrettifyURLPath(in string) string { 123 return prettifyPath(in, pb) 124 } 125 126 // Uglify does the opposite of PrettifyURLPath(). 127 // /section/name/index.html becomes /section/name.html 128 // /section/name/ becomes /section/name.html 129 // /section/name.html becomes /section/name.html 130 func Uglify(in string) string { 131 if path.Ext(in) == "" { 132 if len(in) < 2 { 133 return "/" 134 } 135 // /section/name/ -> /section/name.html 136 return path.Clean(in) + ".html" 137 } 138 139 name, ext := fileAndExt(in, pb) 140 if name == "index" { 141 // /section/name/index.html -> /section/name.html 142 d := path.Dir(in) 143 if len(d) > 1 { 144 return d + ext 145 } 146 return in 147 } 148 // /.xml -> /index.xml 149 if name == "" { 150 return path.Dir(in) + "index" + ext 151 } 152 // /section/name.html -> /section/name.html 153 return path.Clean(in) 154 }