Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing path-segment sanitization rules #4091

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ee4b0cd
vendor: Lock mage to v1
bep Oct 23, 2017
26d2d5c
Update toml dep to fetch its latest master branch instead of v0.3.0
kaushalmodi Oct 23, 2017
7b0e0e7
Update toml dependency revision in Gopkg.lock too
kaushalmodi Oct 23, 2017
921c5e4
Add linebreak to README.md for better readability
scrowten Oct 28, 2017
31ac5b3
Add support for height argument to figure shortcode
kaushalmodi Oct 27, 2017
777f3d8
tpl/math: Refactor Mod with cast
artem-sidorenko Oct 30, 2017
5ded80c
i18n: Allow custom language codes
KevinGimbel Oct 15, 2017
975efb9
i18n: Prevent data race in lang code handling
bep Nov 4, 2017
badab72
Revert "i18n: Prevent data race in lang code handling"
bep Nov 5, 2017
60364ba
Revert "i18n: Allow custom language codes"
bep Nov 5, 2017
25f32d7
gopkg: Bump go-i18n version
bep Nov 6, 2017
7103043
i18n: Support unknown language codes
bep Nov 6, 2017
378555b
Handle Taxonomy permalinks
betaveros Nov 7, 2017
3390581
hugolib: Add some more taxonomy permalinks test cases
bep Nov 7, 2017
a7c583d
Update Travis and snapcraft to Go 1.9.2
bep Nov 7, 2017
dfefa88
Update Chroma to get the latest SASS lexer
bep Nov 9, 2017
7d67be0
hugolib: Pre-allocate some slices
bep Nov 11, 2017
9687d71
circleci: Bump to Go 1.9.2
bep Nov 11, 2017
a5cce85
Use ms precision for static change logging
bep Nov 15, 2017
721d555
tplimpl: Make partial benchmarks use RunParallel
bep Nov 16, 2017
bdaf319
tpl/partials: Fix cache locking
bep Nov 16, 2017
74712b0
gopkg: Update Chroma to get SVG support
bep Nov 16, 2017
cca97fb
tpl/partials: Fix the lock contention in cached partial
bep Nov 16, 2017
436ceee
Implementing path-segment sanitization rules, for things like taxonom…
tsuereth Nov 16, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /go/src/github.com/gohugoio
docker:
- image: bepsays/ci-goreleaser:0.34.2-5
- image: bepsays/ci-goreleaser:0.34.2-6

version: 2
jobs:
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ language: go
sudo: required
dist: precise
go:
- 1.8.4
- 1.9.1
- 1.8.5
- 1.9.2
- tip
os:
- linux
Expand Down
16 changes: 8 additions & 8 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[[constraint]]
name = "github.com/BurntSushi/toml"
version = "0.3.0"
branch = "master"

[[constraint]]
name = "github.com/PuerkitoBio/purell"
Expand All @@ -24,6 +24,10 @@
branch = "master"
name = "github.com/dchest/cssmin"

[[constraint]]
name = "github.com/magefile/mage"
version = "v1"

[[constraint]]
branch = "master"
name = "github.com/eknkc/amber"
Expand Down Expand Up @@ -70,7 +74,7 @@

[[constraint]]
name = "github.com/nicksnyder/go-i18n"
version = "1.9.0"
version = "1.10.0"

[[constraint]]
name = "github.com/russross/blackfriday"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ When reporting the issue, please provide the version of Hugo in use (`hugo versi
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
If you are interested in helping with the project, we will help you with your contribution.
Hugo is a very active project with many contributions happening daily.

Because we want to create the best possible product for our users and the best contribution experience for our developers,
we have a set of guidelines which ensure that all contributions are acceptable.
The guidelines are not intended as a filter or barrier to participation.
Expand Down
2 changes: 1 addition & 1 deletion commands/hugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ func (c *commandeer) newWatcher(port int) error {
}

c.Logger.FEEDBACK.Println("\nStatic file changes detected")
const layout = "2006-01-02 15:04 -0700"
const layout = "2006-01-02 15:04:05.000 -0700"
c.Logger.FEEDBACK.Println(time.Now().Format(layout))

if c.Cfg.GetBool("forceSyncStatic") {
Expand Down
1 change: 1 addition & 0 deletions docs/content/content-management/shortcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ The `figure` shortcode can use the following named parameters:
* `attr` (i.e., attribution)
* `attrlink`
* `alt`
* `height`

#### Example `figure` Input

Expand Down
2 changes: 1 addition & 1 deletion docs/content/content-management/taxonomies.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Note that if you use `preserveTaxonomyNames` and intend to manually construct UR
{{% note %}}
You can add content and front matter to your taxonomy list and taxonomy terms pages. See [Content Organization](/content-management/organization/) for more information on how to add an `_index.md` for this purpose.

Note also that taxonomy [permalinks](/content-management/urls/) are *not* configurable.
Much like regular pages, taxonomy list [permalinks](/content-management/urls/) are configurable, but taxonomy term page permalinks are not.
{{% /note %}}

## Add Taxonomies to Content
Expand Down
2 changes: 2 additions & 0 deletions docs/content/content-management/urls.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ permalinks:

Only the content under `post/` will have the new URL structure. For example, the file `content/post/sample-entry.md` with `date: 2017-02-27T19:20:00-05:00` in its front matter will render to `public/2017/02/sample-entry/index.html` at build time and therefore be reachable at `https://example.com/2013/11/sample-entry/`.

You can also configure permalinks of taxonomies with the same syntax, by using the plural form of the taxonomy instead of the section. You will probably only want to use the configuration values `:slug` or `:title`.

### Permalink Configuration Values

The following is a list of values that can be used in a `permalink` definition in your site `config` file. All references to time are dependent on the content's date.
Expand Down
1 change: 1 addition & 0 deletions docs/content/functions/findRe.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ If you are just learning RegEx, or at least Golang's flavor, you can practice pa
[`plainify`]: /functions/plainify/
[toc]: /content-management/toc/
[`urlize`]: /functions/urlize
[`urlizeSegment`]: /functions/urlizeSegment
2 changes: 1 addition & 1 deletion docs/content/functions/urlize.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ signature: ["urlize INPUT"]
hugoversion:
deprecated: false
workson: []
relatedfuncs: []
relatedfuncs: [urlizeSegment]
---

The following examples pull from a content file with the following front matter:
Expand Down
76 changes: 76 additions & 0 deletions docs/content/functions/urlizeSegment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: urlizeSegment
# linktitle: urlizeSegment
description: Takes a string, sanitizes it for usage in URLs, and converts spaces, slashes, and pound signs to hyphens.
date: 2017-10-21
publishdate: 2017-10-21
lastmod: 2017-10-21
categories: [functions]
menu:
docs:
parent: "functions"
keywords: [urls,strings]
godocref:
signature: ["urlizeSegment INPUT"]
hugoversion:
deprecated: false
workson: []
relatedfuncs: [urlize]
---

The following examples pull from a content file with the following front matter:

{{< code file="content/blog/greatest-city.md" copy="false">}}
+++
title = "The World's Greatest City"
location = "Chicago IL/USA"
tags = ["food/pizza","drink/beer","food/hot dogs","we're #1"]
+++
{{< /code >}}

The following might be used as a partial within a [single page template][singletemplate]:

{{< code file="layouts/partials/content-header.html" download="content-header.html" >}}
<header>
<h1>{{.Title}}</h1>
{{ with .Params.location }}
<div><a href="/locations/{{ . | urlizeSegment}}">{{.}}</a></div>
{{ end }}
<!-- Creates a list of tags for the content and links to each of their pages -->
{{ with .Params.tags }}
<ul>
{{range .}}
<li>
<a href="/tags/{{ . | urlizeSegment }}">{{ . }}</a>
</li>
{{end}}
</ul>
{{ end }}
</header>
{{< /code >}}

The preceding partial would then output to the rendered page as follows, assuming the page is being built with Hugo's default pretty URLs.

{{< output file="/blog/greatest-city/index.html" >}}
<header>
<h1>The World's Greatest City</h1>
<div><a href="/locations/chicago-il-usa/">Chicago IL/USA</a></div>
<ul>
<li>
<a href="/tags/food-pizza">food/pizza</a>
</li>
<li>
<a href="/tags/drink-beer">drink/beer</a>
</li>
<li>
<a href="/tags/food-hot-dogs">food/hot dogs</a>
</li>
<li>
<a href="/tags/were--1">we're #1</a>
</li>
</ul>
</header>
{{< /output >}}


[singletemplate]: /templates/single-page-templates/
9 changes: 9 additions & 0 deletions helpers/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ var (

// ErrWalkRootTooShort is returned when the root specified for a file walk is shorter than 4 characters.
ErrWalkRootTooShort = errors.New("Path too short. Stop walking.")

// This replacer is used in calls to MakePathSegmentSanitized.
SegmentReplacer = strings.NewReplacer("/", "-", "#", "-")
)

// filepathPathBridge is a bridge for common functionality in filepath vs path
Expand Down Expand Up @@ -92,6 +95,12 @@ func (p *PathSpec) MakePathSanitized(s string) string {
return strings.ToLower(p.MakePath(s))
}

// MakePathSegmentSanitized, in addition to MakePathSanitized,
// replaces slashes and pound signs with hyphens.
func (p *PathSpec) MakePathSegmentSanitized(s string) string {
return p.MakePathSanitized(SegmentReplacer.Replace(s))
}

// MakeTitle converts the path given to a suitable title, trimming whitespace
// and replacing hyphens with whitespace.
func MakeTitle(inpath string) string {
Expand Down
27 changes: 27 additions & 0 deletions helpers/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func TestMakePathSanitized(t *testing.T) {
{"Foo.Bar/fOO_bAr-Foo", "foo.bar/foo_bar-foo"},
{"FOO,bar:FooBar", "foobarfoobar"},
{"foo/BAR.HTML", "foo/bar.html"},
{"foo#BAR", "foo#bar"},
{"трям/трям", "трям/трям"},
{"은행", "은행"},
}
Expand All @@ -93,6 +94,32 @@ func TestMakePathSanitized(t *testing.T) {
}
}

func TestMakePathSegmentSanitized(t *testing.T) {
v := viper.New()
l := NewDefaultLanguage(v)
p, _ := NewPathSpec(hugofs.NewMem(v), l)

tests := []struct {
input string
expected string
}{
{" FOO bar ", "foo-bar"},
{"Foo.Bar/fOO_bAr-Foo", "foo.bar-foo_bar-foo"},
{"FOO,bar:FooBar", "foobarfoobar"},
{"foo/BAR.HTML", "foo-bar.html"},
{"foo#BAR", "foo-bar"},
{"трям/трям", "трям-трям"},
{"은행", "은행"},
}

for _, test := range tests {
output := p.MakePathSegmentSanitized(test.input)
if output != test.expected {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
}
}
}

func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
v := viper.New()

Expand Down
7 changes: 7 additions & 0 deletions helpers/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ func SanitizeURLKeepTrailingSlash(in string) string {
// urlize: vim-text-editor
func (p *PathSpec) URLize(uri string) string {
return p.URLEscape(p.MakePathSanitized(uri))
}

// URLizeSegment is URLize + slashes and pound signs become hyphens
// Example:
// uri: Vi/Vim (text editor)
// urlizeSegment: vi-vim-text-editor
func (p *PathSpec) URLizeSegment(uri string) string {
return p.URLEscape(p.MakePathSegmentSanitized(uri))
}

// URLizeFilename creates an URL from a filename by esacaping unicode letters
Expand Down
28 changes: 28 additions & 0 deletions helpers/url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func TestURLize(t *testing.T) {
{"foo.bar/foo_bar-foo", "foo.bar/foo_bar-foo"},
{"foo,bar:foobar", "foobarfoobar"},
{"foo/bar.html", "foo/bar.html"},
{"foo#bar", "foo#bar"},
{"трям/трям", "%D1%82%D1%80%D1%8F%D0%BC/%D1%82%D1%80%D1%8F%D0%BC"},
{"100%-google", "100-google"},
}
Expand All @@ -50,6 +51,33 @@ func TestURLize(t *testing.T) {
}
}

func TestURLizeSegment(t *testing.T) {

v := viper.New()
l := NewDefaultLanguage(v)
p, _ := NewPathSpec(hugofs.NewMem(v), l)

tests := []struct {
input string
expected string
}{
{" foo bar ", "foo-bar"},
{"foo.bar/foo_bar-foo", "foo.bar-foo_bar-foo"},
{"foo,bar:foobar", "foobarfoobar"},
{"foo/bar.html", "foo-bar.html"},
{"foo#bar", "foo-bar"},
{"трям/трям", "%D1%82%D1%80%D1%8F%D0%BC-%D1%82%D1%80%D1%8F%D0%BC"},
{"100%-google", "100-google"},
}

for _, test := range tests {
output := p.URLizeSegment(test.input)
if output != test.expected {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
}
}
}

func TestAbsURL(t *testing.T) {
for _, defaultInSubDir := range []bool{true, false} {
for _, addLanguage := range []bool{true, false} {
Expand Down
2 changes: 1 addition & 1 deletion hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ func (h *HugoSites) createMissingPages() error {
origKey := key

if s.Info.preserveTaxonomyNames {
key = s.PathSpec.MakePathSanitized(key)
key = s.PathSpec.MakePathSegmentSanitized(key)
}
for _, p := range taxonomyPages {
if p.sections[0] == plural && p.sections[1] == key {
Expand Down
2 changes: 1 addition & 1 deletion hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ func (p *Page) Type() string {
// since Hugo 0.22 we support nested sections, but this will always be the first
// element of any nested path.
func (p *Page) Section() string {
if p.Kind == KindSection {
if p.Kind == KindSection || p.Kind == KindTaxonomy || p.Kind == KindTaxonomyTerm {
return p.sections[0]
}
return p.Source.Section()
Expand Down
7 changes: 4 additions & 3 deletions hugolib/pageGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,10 @@ func (p Pages) GroupBy(key string, order ...string) (PagesGroup, error) {
tmp.SetMapIndex(fv, reflect.Append(tmp.MapIndex(fv), ppv))
}

var r []PageGroup
for _, k := range sortKeys(tmp.MapKeys(), direction) {
r = append(r, PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().([]*Page)})
sortedKeys := sortKeys(tmp.MapKeys(), direction)
r := make([]PageGroup, len(sortedKeys))
for i, k := range sortedKeys {
r[i] = PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().([]*Page)}
}

return r, nil
Expand Down
Loading