hugo

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

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

paths.go (7600B)

    1 // Copyright 2018 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 	"path/filepath"
   19 	"strings"
   20 
   21 	hpaths "github.com/gohugoio/hugo/common/paths"
   22 
   23 	"github.com/gohugoio/hugo/config"
   24 	"github.com/gohugoio/hugo/langs"
   25 	"github.com/gohugoio/hugo/modules"
   26 
   27 	"github.com/gohugoio/hugo/hugofs"
   28 )
   29 
   30 var FilePathSeparator = string(filepath.Separator)
   31 
   32 type Paths struct {
   33 	Fs  *hugofs.Fs
   34 	Cfg config.Provider
   35 
   36 	BaseURL
   37 	BaseURLString       string
   38 	BaseURLNoPathString string
   39 
   40 	// If the baseURL contains a base path, e.g. https://example.com/docs, then "/docs" will be the BasePath.
   41 	BasePath string
   42 
   43 	// Directories
   44 	// TODO(bep) when we have trimmed down most of the dirs usage outside of this package, make
   45 	// these into an interface.
   46 	ThemesDir  string
   47 	WorkingDir string
   48 
   49 	// Directories to store Resource related artifacts.
   50 	AbsResourcesDir string
   51 
   52 	AbsPublishDir string
   53 
   54 	// pagination path handling
   55 	PaginatePath string
   56 
   57 	// When in multihost mode, this returns a list of base paths below PublishDir
   58 	// for each language.
   59 	MultihostTargetBasePaths []string
   60 
   61 	DisablePathToLower bool
   62 	RemovePathAccents  bool
   63 	UglyURLs           bool
   64 	CanonifyURLs       bool
   65 
   66 	Language              *langs.Language
   67 	Languages             langs.Languages
   68 	LanguagesDefaultFirst langs.Languages
   69 
   70 	// The PathSpec looks up its config settings in both the current language
   71 	// and then in the global Viper config.
   72 	// Some settings, the settings listed below, does not make sense to be set
   73 	// on per-language-basis. We have no good way of protecting against this
   74 	// other than a "white-list". See language.go.
   75 	defaultContentLanguageInSubdir bool
   76 	DefaultContentLanguage         string
   77 	multilingual                   bool
   78 
   79 	AllModules    modules.Modules
   80 	ModulesClient *modules.Client
   81 }
   82 
   83 func New(fs *hugofs.Fs, cfg config.Provider) (*Paths, error) {
   84 	baseURLstr := cfg.GetString("baseURL")
   85 	baseURL, err := newBaseURLFromString(baseURLstr)
   86 	if err != nil {
   87 		return nil, fmt.Errorf("Failed to create baseURL from %q:: %w", baseURLstr, err)
   88 	}
   89 
   90 	contentDir := filepath.Clean(cfg.GetString("contentDir"))
   91 	workingDir := filepath.Clean(cfg.GetString("workingDir"))
   92 	resourceDir := filepath.Clean(cfg.GetString("resourceDir"))
   93 	publishDir := filepath.Clean(cfg.GetString("publishDir"))
   94 
   95 	if publishDir == "" {
   96 		return nil, fmt.Errorf("publishDir not set")
   97 	}
   98 
   99 	defaultContentLanguage := cfg.GetString("defaultContentLanguage")
  100 
  101 	var (
  102 		language              *langs.Language
  103 		languages             langs.Languages
  104 		languagesDefaultFirst langs.Languages
  105 	)
  106 
  107 	if l, ok := cfg.(*langs.Language); ok {
  108 		language = l
  109 	}
  110 
  111 	if l, ok := cfg.Get("languagesSorted").(langs.Languages); ok {
  112 		languages = l
  113 	}
  114 
  115 	if l, ok := cfg.Get("languagesSortedDefaultFirst").(langs.Languages); ok {
  116 		languagesDefaultFirst = l
  117 	}
  118 
  119 	//
  120 
  121 	if len(languages) == 0 {
  122 		// We have some old tests that does not test the entire chain, hence
  123 		// they have no languages. So create one so we get the proper filesystem.
  124 		languages = langs.Languages{&langs.Language{Lang: "en", Cfg: cfg, ContentDir: contentDir}}
  125 	}
  126 
  127 	absPublishDir := hpaths.AbsPathify(workingDir, publishDir)
  128 	if !strings.HasSuffix(absPublishDir, FilePathSeparator) {
  129 		absPublishDir += FilePathSeparator
  130 	}
  131 	// If root, remove the second '/'
  132 	if absPublishDir == "//" {
  133 		absPublishDir = FilePathSeparator
  134 	}
  135 	absResourcesDir := hpaths.AbsPathify(workingDir, resourceDir)
  136 	if !strings.HasSuffix(absResourcesDir, FilePathSeparator) {
  137 		absResourcesDir += FilePathSeparator
  138 	}
  139 	if absResourcesDir == "//" {
  140 		absResourcesDir = FilePathSeparator
  141 	}
  142 
  143 	var multihostTargetBasePaths []string
  144 	if languages.IsMultihost() {
  145 		for _, l := range languages {
  146 			multihostTargetBasePaths = append(multihostTargetBasePaths, l.Lang)
  147 		}
  148 	}
  149 
  150 	var baseURLString = baseURL.String()
  151 	var baseURLNoPath = baseURL.URL()
  152 	baseURLNoPath.Path = ""
  153 	var baseURLNoPathString = baseURLNoPath.String()
  154 
  155 	p := &Paths{
  156 		Fs:                  fs,
  157 		Cfg:                 cfg,
  158 		BaseURL:             baseURL,
  159 		BaseURLString:       baseURLString,
  160 		BaseURLNoPathString: baseURLNoPathString,
  161 
  162 		DisablePathToLower: cfg.GetBool("disablePathToLower"),
  163 		RemovePathAccents:  cfg.GetBool("removePathAccents"),
  164 		UglyURLs:           cfg.GetBool("uglyURLs"),
  165 		CanonifyURLs:       cfg.GetBool("canonifyURLs"),
  166 
  167 		ThemesDir:  cfg.GetString("themesDir"),
  168 		WorkingDir: workingDir,
  169 
  170 		AbsResourcesDir: absResourcesDir,
  171 		AbsPublishDir:   absPublishDir,
  172 
  173 		multilingual:                   cfg.GetBool("multilingual"),
  174 		defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"),
  175 		DefaultContentLanguage:         defaultContentLanguage,
  176 
  177 		Language:                 language,
  178 		Languages:                languages,
  179 		LanguagesDefaultFirst:    languagesDefaultFirst,
  180 		MultihostTargetBasePaths: multihostTargetBasePaths,
  181 
  182 		PaginatePath: cfg.GetString("paginatePath"),
  183 	}
  184 
  185 	if cfg.IsSet("allModules") {
  186 		p.AllModules = cfg.Get("allModules").(modules.Modules)
  187 	}
  188 
  189 	if cfg.IsSet("modulesClient") {
  190 		p.ModulesClient = cfg.Get("modulesClient").(*modules.Client)
  191 	}
  192 
  193 	return p, nil
  194 }
  195 
  196 // GetBasePath returns any path element in baseURL if needed.
  197 func (p *Paths) GetBasePath(isRelativeURL bool) string {
  198 	if isRelativeURL && p.CanonifyURLs {
  199 		// The baseURL will be prepended later.
  200 		return ""
  201 	}
  202 	return p.BasePath
  203 }
  204 
  205 func (p *Paths) Lang() string {
  206 	if p == nil || p.Language == nil {
  207 		return ""
  208 	}
  209 	return p.Language.Lang
  210 }
  211 
  212 func (p *Paths) GetTargetLanguageBasePath() string {
  213 	if p.Languages.IsMultihost() {
  214 		// In a multihost configuration all assets will be published below the language code.
  215 		return p.Lang()
  216 	}
  217 	return p.GetLanguagePrefix()
  218 }
  219 
  220 func (p *Paths) GetURLLanguageBasePath() string {
  221 	if p.Languages.IsMultihost() {
  222 		return ""
  223 	}
  224 	return p.GetLanguagePrefix()
  225 }
  226 
  227 func (p *Paths) GetLanguagePrefix() string {
  228 	if !p.multilingual {
  229 		return ""
  230 	}
  231 
  232 	defaultLang := p.DefaultContentLanguage
  233 	defaultInSubDir := p.defaultContentLanguageInSubdir
  234 
  235 	currentLang := p.Language.Lang
  236 	if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
  237 		return ""
  238 	}
  239 	return currentLang
  240 }
  241 
  242 // GetLangSubDir returns the given language's subdir if needed.
  243 func (p *Paths) GetLangSubDir(lang string) string {
  244 	if !p.multilingual {
  245 		return ""
  246 	}
  247 
  248 	if p.Languages.IsMultihost() {
  249 		return ""
  250 	}
  251 
  252 	if lang == "" || (lang == p.DefaultContentLanguage && !p.defaultContentLanguageInSubdir) {
  253 		return ""
  254 	}
  255 
  256 	return lang
  257 }
  258 
  259 // AbsPathify creates an absolute path if given a relative path. If already
  260 // absolute, the path is just cleaned.
  261 func (p *Paths) AbsPathify(inPath string) string {
  262 	return hpaths.AbsPathify(p.WorkingDir, inPath)
  263 }
  264 
  265 // RelPathify trims any WorkingDir prefix from the given filename. If
  266 // the filename is not considered to be absolute, the path is just cleaned.
  267 func (p *Paths) RelPathify(filename string) string {
  268 	filename = filepath.Clean(filename)
  269 	if !filepath.IsAbs(filename) {
  270 		return filename
  271 	}
  272 
  273 	return strings.TrimPrefix(strings.TrimPrefix(filename, p.WorkingDir), FilePathSeparator)
  274 }