github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/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  	"github.com/PuerkitoBio/purell"
    23  )
    24  
    25  type pathBridge struct {
    26  }
    27  
    28  func (pathBridge) Base(in string) string {
    29  	return path.Base(in)
    30  }
    31  
    32  func (pathBridge) Clean(in string) string {
    33  	return path.Clean(in)
    34  }
    35  
    36  func (pathBridge) Dir(in string) string {
    37  	return path.Dir(in)
    38  }
    39  
    40  func (pathBridge) Ext(in string) string {
    41  	return path.Ext(in)
    42  }
    43  
    44  func (pathBridge) Join(elem ...string) string {
    45  	return path.Join(elem...)
    46  }
    47  
    48  func (pathBridge) Separator() string {
    49  	return "/"
    50  }
    51  
    52  var pb pathBridge
    53  
    54  func sanitizeURLWithFlags(in string, f purell.NormalizationFlags) string {
    55  	s, err := purell.NormalizeURLString(in, f)
    56  	if err != nil {
    57  		return in
    58  	}
    59  
    60  	// Temporary workaround for the bug fix and resulting
    61  	// behavioral change in purell.NormalizeURLString():
    62  	// a leading '/' was inadvertently added to relative links,
    63  	// but no longer, see #878.
    64  	//
    65  	// I think the real solution is to allow Hugo to
    66  	// make relative URL with relative path,
    67  	// e.g. "../../post/hello-again/", as wished by users
    68  	// in issues #157, #622, etc., without forcing
    69  	// relative URLs to begin with '/'.
    70  	// Once the fixes are in, let's remove this kludge
    71  	// and restore SanitizeURL() to the way it was.
    72  	//                         -- @anthonyfok, 2015-02-16
    73  	//
    74  	// Begin temporary kludge
    75  	u, err := url.Parse(s)
    76  	if err != nil {
    77  		panic(err)
    78  	}
    79  	if len(u.Path) > 0 && !strings.HasPrefix(u.Path, "/") {
    80  		u.Path = "/" + u.Path
    81  	}
    82  	return u.String()
    83  	// End temporary kludge
    84  
    85  	// return s
    86  
    87  }
    88  
    89  // SanitizeURL sanitizes the input URL string.
    90  func SanitizeURL(in string) string {
    91  	return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveTrailingSlash|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
    92  }
    93  
    94  // SanitizeURLKeepTrailingSlash is the same as SanitizeURL, but will keep any trailing slash.
    95  func SanitizeURLKeepTrailingSlash(in string) string {
    96  	return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
    97  }
    98  
    99  // MakePermalink combines base URL with content path to create full URL paths.
   100  // Example
   101  //    base:   http://spf13.com/
   102  //    path:   post/how-i-blog
   103  //    result: http://spf13.com/post/how-i-blog
   104  func MakePermalink(host, plink string) *url.URL {
   105  	base, err := url.Parse(host)
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  
   110  	p, err := url.Parse(plink)
   111  	if err != nil {
   112  		panic(err)
   113  	}
   114  
   115  	if p.Host != "" {
   116  		panic(fmt.Errorf("can't make permalink from absolute link %q", plink))
   117  	}
   118  
   119  	base.Path = path.Join(base.Path, p.Path)
   120  
   121  	// path.Join will strip off the last /, so put it back if it was there.
   122  	hadTrailingSlash := (plink == "" && strings.HasSuffix(host, "/")) || strings.HasSuffix(p.Path, "/")
   123  	if hadTrailingSlash && !strings.HasSuffix(base.Path, "/") {
   124  		base.Path = base.Path + "/"
   125  	}
   126  
   127  	return base
   128  }
   129  
   130  // IsAbsURL determines whether the given path points to an absolute URL.
   131  func IsAbsURL(path string) bool {
   132  	url, err := url.Parse(path)
   133  	if err != nil {
   134  		return false
   135  	}
   136  
   137  	return url.IsAbs() || strings.HasPrefix(path, "//")
   138  }
   139  
   140  // AddContextRoot adds the context root to an URL if it's not already set.
   141  // For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite),
   142  // relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set.
   143  func AddContextRoot(baseURL, relativePath string) string {
   144  	url, err := url.Parse(baseURL)
   145  	if err != nil {
   146  		panic(err)
   147  	}
   148  
   149  	newPath := path.Join(url.Path, relativePath)
   150  
   151  	// path strips trailing slash, ignore root path.
   152  	if newPath != "/" && strings.HasSuffix(relativePath, "/") {
   153  		newPath += "/"
   154  	}
   155  	return newPath
   156  }
   157  
   158  // URLizeAn
   159  
   160  // PrettifyURL takes a URL string and returns a semantic, clean URL.
   161  func PrettifyURL(in string) string {
   162  	x := PrettifyURLPath(in)
   163  
   164  	if path.Base(x) == "index.html" {
   165  		return path.Dir(x)
   166  	}
   167  
   168  	if in == "" {
   169  		return "/"
   170  	}
   171  
   172  	return x
   173  }
   174  
   175  // PrettifyURLPath takes a URL path to a content and converts it
   176  // to enable pretty URLs.
   177  //     /section/name.html       becomes /section/name/index.html
   178  //     /section/name/           becomes /section/name/index.html
   179  //     /section/name/index.html becomes /section/name/index.html
   180  func PrettifyURLPath(in string) string {
   181  	return prettifyPath(in, pb)
   182  }
   183  
   184  // Uglify does the opposite of PrettifyURLPath().
   185  //     /section/name/index.html becomes /section/name.html
   186  //     /section/name/           becomes /section/name.html
   187  //     /section/name.html       becomes /section/name.html
   188  func Uglify(in string) string {
   189  	if path.Ext(in) == "" {
   190  		if len(in) < 2 {
   191  			return "/"
   192  		}
   193  		// /section/name/  -> /section/name.html
   194  		return path.Clean(in) + ".html"
   195  	}
   196  
   197  	name, ext := fileAndExt(in, pb)
   198  	if name == "index" {
   199  		// /section/name/index.html -> /section/name.html
   200  		d := path.Dir(in)
   201  		if len(d) > 1 {
   202  			return d + ext
   203  		}
   204  		return in
   205  	}
   206  	// /.xml -> /index.xml
   207  	if name == "" {
   208  		return path.Dir(in) + "index" + ext
   209  	}
   210  	// /section/name.html -> /section/name.html
   211  	return path.Clean(in)
   212  }