From 574d7a84386f4804860b8dbec132c4dc9daf8416 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Wed, 7 Feb 2018 21:52:23 -0600 Subject: [PATCH] helpers: Add PathSpec.MakePathSegment MakePathSegment properly sanitizes path segments (like taxonomy terms) with slashes (and pound signs). Updates #4090 --- helpers/path.go | 29 +++++++++++++++++++++++++++++ helpers/path_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/helpers/path.go b/helpers/path.go index 3586c574424..9d563a61867 100644 --- a/helpers/path.go +++ b/helpers/path.go @@ -74,6 +74,35 @@ func (filepathBridge) Separator() string { var fpb filepathBridge +// segmentReplacer replaces some URI-reserved characters in a path segments. +var segmentReplacer = strings.NewReplacer("/", "-", "#", "-") + +// MakeSegment returns a copy of string s that is appropriate for a path +// segment. MakeSegment is similar to MakePath but disallows the '/' and +// '#' characters because of their reserved meaning in URIs. +func (p *PathSpec) MakeSegment(s string) string { + s = p.MakePathSanitized(strings.Trim(segmentReplacer.Replace(s), "- ")) + + var pos int + var last byte + b := make([]byte, len(s)) + + for i := 0; i < len(s); i++ { + // consolidate dashes + if s[i] == '-' && last == '-' { + continue + } + + b[pos], last = s[i], s[i] + pos++ + } + + if p.disablePathToLower { + return string(b[:pos]) + } + return strings.ToLower(string(b[:pos])) +} + // MakePath takes a string with any characters and replace it // so the string could be used in a path. // It does so by creating a Unicode-sanitized string, with the spaces replaced, diff --git a/helpers/path_test.go b/helpers/path_test.go index c249a519dfe..c87a8ee710a 100644 --- a/helpers/path_test.go +++ b/helpers/path_test.go @@ -36,6 +36,34 @@ import ( "github.com/spf13/viper" ) +func TestMakeSegment(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"}, + {"трям/трям", "трям-трям"}, + {"은행", "은행"}, + {"Say What??", "say-what"}, + {"Your #1 Fan", "your-1-fan"}, + {"Red & Blue", "red-blue"}, + } + + for _, test := range tests { + output := p.MakeSegment(test.input) + if output != test.expected { + t.Errorf("Expected %#v, got %#v\n", test.expected, output) + } + } +} + func TestMakePath(t *testing.T) { tests := []struct { input string