mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-30 01:23:34 -05:00
4b7d3e57a4
This commit also pulls down the log level for a set of WARN statements to INFO. There should be no ERRORs or WARNINGs in a regular Hugo build. That is the story about the Boy Who Cried Wolf. Since the WARN log is now more visible, this commit also improves on some of them, most notable the "layout not found", which now would look something like this: ```bash WARN 2018/11/02 09:02:18 Found no layout for "home", language "en", output format "CSS": create a template below /layouts with one of these filenames: index.en.css.css, home.en.css.css, list.en.css.css, index.css.css, home.css.css, list.css.css, index.en.css, home.en.css, list.en.css, index.css, home.css, list.css, _default/index.en.css.css, _default/home.en.css.css, _default/list.en.css.css, _default/index.css.css, _default/home.css.css, _default/list.css.css, _default/index.en.css, _default/home.en.css, _default/list.en.css, _default/index.css, _default/home.css, _default/list.css ``` Fixes #5203
193 lines
5.9 KiB
Go
193 lines
5.9 KiB
Go
// Copyright 2017 The Hugo Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package hugolib
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/gohugoio/hugo/common/loggers"
|
|
|
|
"github.com/gohugoio/hugo/output"
|
|
"github.com/gohugoio/hugo/publisher"
|
|
"github.com/gohugoio/hugo/tpl"
|
|
|
|
"github.com/gohugoio/hugo/helpers"
|
|
)
|
|
|
|
const (
|
|
alias = "<!DOCTYPE html><html><head><title>{{ .Permalink }}</title><link rel=\"canonical\" href=\"{{ .Permalink }}\"/><meta name=\"robots\" content=\"noindex\"><meta charset=\"utf-8\" /><meta http-equiv=\"refresh\" content=\"0; url={{ .Permalink }}\" /></head></html>"
|
|
aliasXHtml = "<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>{{ .Permalink }}</title><link rel=\"canonical\" href=\"{{ .Permalink }}\"/><meta name=\"robots\" content=\"noindex\"><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" /><meta http-equiv=\"refresh\" content=\"0; url={{ .Permalink }}\" /></head></html>"
|
|
)
|
|
|
|
var defaultAliasTemplates *template.Template
|
|
|
|
func init() {
|
|
//TODO(bep) consolidate
|
|
defaultAliasTemplates = template.New("")
|
|
template.Must(defaultAliasTemplates.New("alias").Parse(alias))
|
|
template.Must(defaultAliasTemplates.New("alias-xhtml").Parse(aliasXHtml))
|
|
}
|
|
|
|
type aliasHandler struct {
|
|
t tpl.TemplateFinder
|
|
log *loggers.Logger
|
|
allowRoot bool
|
|
}
|
|
|
|
func newAliasHandler(t tpl.TemplateFinder, l *loggers.Logger, allowRoot bool) aliasHandler {
|
|
return aliasHandler{t, l, allowRoot}
|
|
}
|
|
|
|
func (a aliasHandler) renderAlias(isXHTML bool, permalink string, page *Page) (io.Reader, error) {
|
|
t := "alias"
|
|
if isXHTML {
|
|
t = "alias-xhtml"
|
|
}
|
|
|
|
var templ tpl.Template
|
|
var found bool
|
|
|
|
if a.t != nil {
|
|
templ, found = a.t.Lookup("alias.html")
|
|
}
|
|
|
|
if !found {
|
|
def := defaultAliasTemplates.Lookup(t)
|
|
if def != nil {
|
|
templ = &tpl.TemplateAdapter{Template: def}
|
|
}
|
|
|
|
}
|
|
data := struct {
|
|
Permalink string
|
|
Page *Page
|
|
}{
|
|
permalink,
|
|
page,
|
|
}
|
|
|
|
buffer := new(bytes.Buffer)
|
|
err := templ.Execute(buffer, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buffer, nil
|
|
}
|
|
|
|
func (s *Site) writeDestAlias(path, permalink string, outputFormat output.Format, p *Page) (err error) {
|
|
return s.publishDestAlias(false, path, permalink, outputFormat, p)
|
|
}
|
|
|
|
func (s *Site) publishDestAlias(allowRoot bool, path, permalink string, outputFormat output.Format, p *Page) (err error) {
|
|
handler := newAliasHandler(s.Tmpl, s.Log, allowRoot)
|
|
|
|
isXHTML := strings.HasSuffix(path, ".xhtml")
|
|
|
|
s.Log.DEBUG.Println("creating alias:", path, "redirecting to", permalink)
|
|
|
|
targetPath, err := handler.targetPathAlias(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
aliasContent, err := handler.renderAlias(isXHTML, permalink, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pd := publisher.Descriptor{
|
|
Src: aliasContent,
|
|
TargetPath: targetPath,
|
|
StatCounter: &s.PathSpec.ProcessingStats.Aliases,
|
|
OutputFormat: outputFormat,
|
|
}
|
|
|
|
return s.publisher.Publish(pd)
|
|
|
|
}
|
|
|
|
func (a aliasHandler) targetPathAlias(src string) (string, error) {
|
|
originalAlias := src
|
|
if len(src) <= 0 {
|
|
return "", fmt.Errorf("Alias \"\" is an empty string")
|
|
}
|
|
|
|
alias := filepath.Clean(src)
|
|
components := strings.Split(alias, helpers.FilePathSeparator)
|
|
|
|
if !a.allowRoot && alias == helpers.FilePathSeparator {
|
|
return "", fmt.Errorf("Alias \"%s\" resolves to website root directory", originalAlias)
|
|
}
|
|
|
|
// Validate against directory traversal
|
|
if components[0] == ".." {
|
|
return "", fmt.Errorf("Alias \"%s\" traverses outside the website root directory", originalAlias)
|
|
}
|
|
|
|
// Handle Windows file and directory naming restrictions
|
|
// See "Naming Files, Paths, and Namespaces" on MSDN
|
|
// https://msdn.microsoft.com/en-us/library/aa365247%28v=VS.85%29.aspx?f=255&MSPPError=-2147217396
|
|
msgs := []string{}
|
|
reservedNames := []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}
|
|
|
|
if strings.ContainsAny(alias, ":*?\"<>|") {
|
|
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains invalid characters on Windows: : * ? \" < > |", originalAlias))
|
|
}
|
|
for _, ch := range alias {
|
|
if ch < ' ' {
|
|
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains ASCII control code (0x00 to 0x1F), invalid on Windows: : * ? \" < > |", originalAlias))
|
|
continue
|
|
}
|
|
}
|
|
for _, comp := range components {
|
|
if strings.HasSuffix(comp, " ") || strings.HasSuffix(comp, ".") {
|
|
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with a trailing space or period, problematic on Windows", originalAlias))
|
|
}
|
|
for _, r := range reservedNames {
|
|
if comp == r {
|
|
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with reserved name \"%s\" on Windows", originalAlias, r))
|
|
}
|
|
}
|
|
}
|
|
if len(msgs) > 0 {
|
|
if runtime.GOOS == "windows" {
|
|
for _, m := range msgs {
|
|
a.log.ERROR.Println(m)
|
|
}
|
|
return "", fmt.Errorf("Cannot create \"%s\": Windows filename restriction", originalAlias)
|
|
}
|
|
for _, m := range msgs {
|
|
a.log.INFO.Println(m)
|
|
}
|
|
}
|
|
|
|
// Add the final touch
|
|
alias = strings.TrimPrefix(alias, helpers.FilePathSeparator)
|
|
if strings.HasSuffix(alias, helpers.FilePathSeparator) {
|
|
alias = alias + "index.html"
|
|
} else if !strings.HasSuffix(alias, ".html") {
|
|
alias = alias + helpers.FilePathSeparator + "index.html"
|
|
}
|
|
if originalAlias != alias {
|
|
a.log.INFO.Printf("Alias \"%s\" translated to \"%s\"\n", originalAlias, alias)
|
|
}
|
|
|
|
return alias, nil
|
|
}
|