hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

url.go (6309B)

    1 // Copyright 2015 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 helpers
   15 
   16 import (
   17 	"net/url"
   18 	"path"
   19 	"path/filepath"
   20 	"strings"
   21 
   22 	"github.com/gohugoio/hugo/common/paths"
   23 
   24 	"github.com/PuerkitoBio/purell"
   25 )
   26 
   27 func sanitizeURLWithFlags(in string, f purell.NormalizationFlags) string {
   28 	s, err := purell.NormalizeURLString(in, f)
   29 	if err != nil {
   30 		return in
   31 	}
   32 
   33 	// Temporary workaround for the bug fix and resulting
   34 	// behavioral change in purell.NormalizeURLString():
   35 	// a leading '/' was inadvertently added to relative links,
   36 	// but no longer, see #878.
   37 	//
   38 	// I think the real solution is to allow Hugo to
   39 	// make relative URL with relative path,
   40 	// e.g. "../../post/hello-again/", as wished by users
   41 	// in issues #157, #622, etc., without forcing
   42 	// relative URLs to begin with '/'.
   43 	// Once the fixes are in, let's remove this kludge
   44 	// and restore SanitizeURL() to the way it was.
   45 	//                         -- @anthonyfok, 2015-02-16
   46 	//
   47 	// Begin temporary kludge
   48 	u, err := url.Parse(s)
   49 	if err != nil {
   50 		panic(err)
   51 	}
   52 	if len(u.Path) > 0 && !strings.HasPrefix(u.Path, "/") {
   53 		u.Path = "/" + u.Path
   54 	}
   55 	return u.String()
   56 	// End temporary kludge
   57 
   58 	// return s
   59 
   60 }
   61 
   62 // SanitizeURL sanitizes the input URL string.
   63 func SanitizeURL(in string) string {
   64 	return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveTrailingSlash|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
   65 }
   66 
   67 // SanitizeURLKeepTrailingSlash is the same as SanitizeURL, but will keep any trailing slash.
   68 func SanitizeURLKeepTrailingSlash(in string) string {
   69 	return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
   70 }
   71 
   72 // URLize is similar to MakePath, but with Unicode handling
   73 // Example:
   74 //     uri: Vim (text editor)
   75 //     urlize: vim-text-editor
   76 func (p *PathSpec) URLize(uri string) string {
   77 	return p.URLEscape(p.MakePathSanitized(uri))
   78 }
   79 
   80 // URLizeFilename creates an URL from a filename by escaping unicode letters
   81 // and turn any filepath separator into forward slashes.
   82 func (p *PathSpec) URLizeFilename(filename string) string {
   83 	return p.URLEscape(filepath.ToSlash(filename))
   84 }
   85 
   86 // URLEscape escapes unicode letters.
   87 func (p *PathSpec) URLEscape(uri string) string {
   88 	// escape unicode letters
   89 	parsedURI, err := url.Parse(uri)
   90 	if err != nil {
   91 		// if net/url can not parse URL it means Sanitize works incorrectly
   92 		panic(err)
   93 	}
   94 	x := parsedURI.String()
   95 	return x
   96 }
   97 
   98 // AbsURL creates an absolute URL from the relative path given and the BaseURL set in config.
   99 func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
  100 	url, err := url.Parse(in)
  101 	if err != nil {
  102 		return in
  103 	}
  104 
  105 	if url.IsAbs() || strings.HasPrefix(in, "//") {
  106 		// It  is already  absolute, return it as is.
  107 		return in
  108 	}
  109 
  110 	baseURL := p.getBaseURLRoot(in)
  111 
  112 	if addLanguage {
  113 		prefix := p.GetLanguagePrefix()
  114 		if prefix != "" {
  115 			hasPrefix := false
  116 			// avoid adding language prefix if already present
  117 			in2 := in
  118 			if strings.HasPrefix(in, "/") {
  119 				in2 = in[1:]
  120 			}
  121 			if in2 == prefix {
  122 				hasPrefix = true
  123 			} else {
  124 				hasPrefix = strings.HasPrefix(in2, prefix+"/")
  125 			}
  126 
  127 			if !hasPrefix {
  128 				addSlash := in == "" || strings.HasSuffix(in, "/")
  129 				in = path.Join(prefix, in)
  130 
  131 				if addSlash {
  132 					in += "/"
  133 				}
  134 			}
  135 		}
  136 	}
  137 
  138 	return paths.MakePermalink(baseURL, in).String()
  139 }
  140 
  141 func (p *PathSpec) getBaseURLRoot(path string) string {
  142 	if strings.HasPrefix(path, "/") {
  143 		// Treat it as relative to the server root.
  144 		return p.BaseURLNoPathString
  145 	} else {
  146 		// Treat it as relative to the baseURL.
  147 		return p.BaseURLString
  148 	}
  149 }
  150 
  151 func (p *PathSpec) RelURL(in string, addLanguage bool) string {
  152 	baseURL := p.getBaseURLRoot(in)
  153 	canonifyURLs := p.CanonifyURLs
  154 	if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
  155 		return in
  156 	}
  157 
  158 	u := in
  159 
  160 	if strings.HasPrefix(in, baseURL) {
  161 		u = strings.TrimPrefix(u, baseURL)
  162 	}
  163 
  164 	if addLanguage {
  165 		prefix := p.GetLanguagePrefix()
  166 		if prefix != "" {
  167 			hasPrefix := false
  168 			// avoid adding language prefix if already present
  169 			in2 := in
  170 			if strings.HasPrefix(in, "/") {
  171 				in2 = in[1:]
  172 			}
  173 			if in2 == prefix {
  174 				hasPrefix = true
  175 			} else {
  176 				hasPrefix = strings.HasPrefix(in2, prefix+"/")
  177 			}
  178 
  179 			if !hasPrefix {
  180 				hadSlash := strings.HasSuffix(u, "/")
  181 
  182 				u = path.Join(prefix, u)
  183 
  184 				if hadSlash {
  185 					u += "/"
  186 				}
  187 			}
  188 		}
  189 	}
  190 
  191 	if !canonifyURLs {
  192 		u = paths.AddContextRoot(baseURL, u)
  193 	}
  194 
  195 	if in == "" && !strings.HasSuffix(u, "/") && strings.HasSuffix(baseURL, "/") {
  196 		u += "/"
  197 	}
  198 
  199 	if !strings.HasPrefix(u, "/") {
  200 		u = "/" + u
  201 	}
  202 
  203 	return u
  204 }
  205 
  206 // PrependBasePath prepends any baseURL sub-folder to the given resource
  207 func (p *PathSpec) PrependBasePath(rel string, isAbs bool) string {
  208 	basePath := p.GetBasePath(!isAbs)
  209 	if basePath != "" {
  210 		rel = filepath.ToSlash(rel)
  211 		// Need to prepend any path from the baseURL
  212 		hadSlash := strings.HasSuffix(rel, "/")
  213 		rel = path.Join(basePath, rel)
  214 		if hadSlash {
  215 			rel += "/"
  216 		}
  217 	}
  218 	return rel
  219 }
  220 
  221 // URLizeAndPrep applies misc sanitation to the given URL to get it in line
  222 // with the Hugo standard.
  223 func (p *PathSpec) URLizeAndPrep(in string) string {
  224 	return p.URLPrep(p.URLize(in))
  225 }
  226 
  227 // URLPrep applies misc sanitation to the given URL.
  228 func (p *PathSpec) URLPrep(in string) string {
  229 	if p.UglyURLs {
  230 		return paths.Uglify(SanitizeURL(in))
  231 	}
  232 	pretty := paths.PrettifyURL(SanitizeURL(in))
  233 	if path.Ext(pretty) == ".xml" {
  234 		return pretty
  235 	}
  236 	url, err := purell.NormalizeURLString(pretty, purell.FlagAddTrailingSlash)
  237 	if err != nil {
  238 		return pretty
  239 	}
  240 	return url
  241 }