github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/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  	"path/filepath"
    21  	"strings"
    22  )
    23  
    24  type pathBridge struct{}
    25  
    26  func (pathBridge) Base(in string) string {
    27  	return path.Base(in)
    28  }
    29  
    30  func (pathBridge) Clean(in string) string {
    31  	return path.Clean(in)
    32  }
    33  
    34  func (pathBridge) Dir(in string) string {
    35  	return path.Dir(in)
    36  }
    37  
    38  func (pathBridge) Ext(in string) string {
    39  	return path.Ext(in)
    40  }
    41  
    42  func (pathBridge) Join(elem ...string) string {
    43  	return path.Join(elem...)
    44  }
    45  
    46  func (pathBridge) Separator() string {
    47  	return "/"
    48  }
    49  
    50  var pb pathBridge
    51  
    52  // MakePermalink combines base URL with content path to create full URL paths.
    53  // Example
    54  //    base:   http://spf13.com/
    55  //    path:   post/how-i-blog
    56  //    result: http://spf13.com/post/how-i-blog
    57  func MakePermalink(host, plink string) *url.URL {
    58  	base, err := url.Parse(host)
    59  	if err != nil {
    60  		panic(err)
    61  	}
    62  
    63  	p, err := url.Parse(plink)
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  
    68  	if p.Host != "" {
    69  		panic(fmt.Errorf("can't make permalink from absolute link %q", plink))
    70  	}
    71  
    72  	base.Path = path.Join(base.Path, p.Path)
    73  
    74  	// path.Join will strip off the last /, so put it back if it was there.
    75  	hadTrailingSlash := (plink == "" && strings.HasSuffix(host, "/")) || strings.HasSuffix(p.Path, "/")
    76  	if hadTrailingSlash && !strings.HasSuffix(base.Path, "/") {
    77  		base.Path = base.Path + "/"
    78  	}
    79  
    80  	return base
    81  }
    82  
    83  // AddContextRoot adds the context root to an URL if it's not already set.
    84  // For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite),
    85  // relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set.
    86  func AddContextRoot(baseURL, relativePath string) string {
    87  	url, err := url.Parse(baseURL)
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  
    92  	newPath := path.Join(url.Path, relativePath)
    93  
    94  	// path strips trailing slash, ignore root path.
    95  	if newPath != "/" && strings.HasSuffix(relativePath, "/") {
    96  		newPath += "/"
    97  	}
    98  	return newPath
    99  }
   100  
   101  // URLizeAn
   102  
   103  // PrettifyURL takes a URL string and returns a semantic, clean URL.
   104  func PrettifyURL(in string) string {
   105  	x := PrettifyURLPath(in)
   106  
   107  	if path.Base(x) == "index.html" {
   108  		return path.Dir(x)
   109  	}
   110  
   111  	if in == "" {
   112  		return "/"
   113  	}
   114  
   115  	return x
   116  }
   117  
   118  // PrettifyURLPath takes a URL path to a content and converts it
   119  // to enable pretty URLs.
   120  //     /section/name.html       becomes /section/name/index.html
   121  //     /section/name/           becomes /section/name/index.html
   122  //     /section/name/index.html becomes /section/name/index.html
   123  func PrettifyURLPath(in string) string {
   124  	return prettifyPath(in, pb)
   125  }
   126  
   127  // Uglify does the opposite of PrettifyURLPath().
   128  //     /section/name/index.html becomes /section/name.html
   129  //     /section/name/           becomes /section/name.html
   130  //     /section/name.html       becomes /section/name.html
   131  func Uglify(in string) string {
   132  	if path.Ext(in) == "" {
   133  		if len(in) < 2 {
   134  			return "/"
   135  		}
   136  		// /section/name/  -> /section/name.html
   137  		return path.Clean(in) + ".html"
   138  	}
   139  
   140  	name, ext := fileAndExt(in, pb)
   141  	if name == "index" {
   142  		// /section/name/index.html -> /section/name.html
   143  		d := path.Dir(in)
   144  		if len(d) > 1 {
   145  			return d + ext
   146  		}
   147  		return in
   148  	}
   149  	// /.xml -> /index.xml
   150  	if name == "" {
   151  		return path.Dir(in) + "index" + ext
   152  	}
   153  	// /section/name.html -> /section/name.html
   154  	return path.Clean(in)
   155  }
   156  
   157  // UrlToFilename converts the URL s to a filename.
   158  // If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
   159  func UrlToFilename(s string) (string, bool) {
   160  	u, err := url.ParseRequestURI(s)
   161  
   162  	if err != nil {
   163  		return filepath.FromSlash(s), false
   164  	}
   165  
   166  	p := u.Path
   167  
   168  	if p == "" {
   169  		p, _ = url.QueryUnescape(u.Opaque)
   170  		return filepath.FromSlash(p), true
   171  	}
   172  
   173  	p = filepath.FromSlash(p)
   174  
   175  	if u.Host != "" {
   176  		// C:\data\file.txt
   177  		p = strings.ToUpper(u.Host) + ":" + p
   178  	}
   179  
   180  	return p, true
   181  }