Add custom protocol support in Permalink

This commit is contained in:
Bjørn Erik Pedersen 2017-03-23 20:05:10 +01:00
parent 8bcc08e3b0
commit d851d6b98f
9 changed files with 171 additions and 37 deletions

74
helpers/baseURL.go Normal file
View file

@ -0,0 +1,74 @@
// Copyright 2017-present 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 helpers
import (
"fmt"
"net/url"
"strings"
)
// A BaseURL in Hugo is normally on the form scheme://path, but the
// form scheme: is also valid (mailto:hugo@rules.com).
type BaseURL struct {
url *url.URL
urlStr string
}
func (b BaseURL) String() string {
return b.urlStr
}
// Protocol is normaly on the form "scheme://", i.e. "webcal://".
func (b BaseURL) WithProtocol(protocol string) (string, error) {
u := b.URL()
scheme := protocol
isFullProtocol := strings.HasSuffix(scheme, "://")
isOpaqueProtocol := strings.HasSuffix(scheme, ":")
if isFullProtocol {
scheme = strings.TrimSuffix(scheme, "://")
} else if isOpaqueProtocol {
scheme = strings.TrimSuffix(scheme, ":")
}
u.Scheme = scheme
if isFullProtocol && u.Opaque != "" {
u.Opaque = "//" + u.Opaque
} else if isOpaqueProtocol && u.Opaque == "" {
return "", fmt.Errorf("Cannot determine BaseURL for protocol %q", protocol)
}
return u.String(), nil
}
func (b BaseURL) URL() *url.URL {
// create a copy as it will be modified.
c := *b.url
return &c
}
func newBaseURLFromString(b string) (BaseURL, error) {
var result BaseURL
base, err := url.Parse(b)
if err != nil {
return result, err
}
// TODO(bep) output consider saving original URL?
return BaseURL{url: base, urlStr: base.String()}, nil
}

51
helpers/baseURL_test.go Normal file
View file

@ -0,0 +1,51 @@
// Copyright 2017-present 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 helpers
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestBaseURL(t *testing.T) {
b, err := newBaseURLFromString("http://example.com")
require.NoError(t, err)
require.Equal(t, "http://example.com", b.String())
p, err := b.WithProtocol("webcal://")
require.NoError(t, err)
require.Equal(t, "webcal://example.com", p)
p, err = b.WithProtocol("webcal")
require.NoError(t, err)
require.Equal(t, "webcal://example.com", p)
_, err = b.WithProtocol("mailto:")
require.Error(t, err)
b, err = newBaseURLFromString("mailto:hugo@rules.com")
require.NoError(t, err)
require.Equal(t, "mailto:hugo@rules.com", b.String())
// These are pretty constructed
p, err = b.WithProtocol("webcal")
require.NoError(t, err)
require.Equal(t, "webcal:hugo@rules.com", p)
p, err = b.WithProtocol("webcal://")
require.NoError(t, err)
require.Equal(t, "webcal://hugo@rules.com", p)
}

View file

@ -23,33 +23,6 @@ import (
"github.com/PuerkitoBio/purell"
)
type BaseURL struct {
url *url.URL
urlStr string
}
func (b BaseURL) String() string {
return b.urlStr
}
func (b BaseURL) URL() *url.URL {
// create a copy as it will be modified.
c := *b.url
return &c
}
func newBaseURLFromString(b string) (BaseURL, error) {
var result BaseURL
base, err := url.Parse(b)
if err != nil {
return result, err
}
// TODO(bep) output consider saving original URL?
return BaseURL{url: base, urlStr: base.String()}, nil
}
type pathBridge struct {
}

View file

@ -862,7 +862,12 @@ func (p *Page) initURLs() error {
p.outputFormats = p.s.outputFormats[p.Kind]
}
rel := p.createRelativePermalink()
p.permalink = p.s.permalink(rel)
var err error
p.permalink, err = p.s.permalinkForOutputFormat(rel, p.outputFormats[0])
if err != nil {
return err
}
rel = p.s.PathSpec.PrependBasePath(rel)
p.relPermalink = rel
p.layoutDescriptor = p.createLayoutDescriptor()

View file

@ -197,7 +197,8 @@ func (o OutputFormats) Get(name string) *OutputFormat {
// Permalink returns the absolute permalink to this output format.
func (o *OutputFormat) Permalink() string {
rel := o.p.createRelativePermalinkForOutputFormat(o.f)
return o.p.s.permalink(rel)
perm, _ := o.p.s.permalinkForOutputFormat(rel, o.f)
return perm
}
// Permalink returns the relative permalink to this output format.

View file

@ -1766,9 +1766,29 @@ func (s *SiteInfo) GetPage(typ string, path ...string) *Page {
return s.getPage(typ, path...)
}
func (s *Site) permalink(link string) string {
baseURL := s.PathSpec.BaseURL.String()
func (s *Site) permalinkForOutputFormat(link string, f output.Format) (string, error) {
var (
baseURL string
err error
)
if f.Protocol != "" {
baseURL, err = s.PathSpec.BaseURL.WithProtocol(f.Protocol)
if err != nil {
return "", err
}
} else {
baseURL = s.PathSpec.BaseURL.String()
}
return s.permalinkForBaseURL(link, baseURL), nil
}
func (s *Site) permalink(link string) string {
return s.permalinkForBaseURL(link, s.PathSpec.BaseURL.String())
}
func (s *Site) permalinkForBaseURL(link, baseURL string) string {
link = strings.TrimPrefix(link, "/")
if !strings.HasSuffix(baseURL, "/") {
baseURL += "/"

View file

@ -52,7 +52,7 @@ func TestDefaultOutputFormats(t *testing.T) {
}
func TestSiteWithPageOutputs(t *testing.T) {
for _, outputs := range [][]string{{"html", "json"}, {"json"}} {
for _, outputs := range [][]string{{"html", "json", "calendar"}, {"json"}} {
t.Run(fmt.Sprintf("%v", outputs), func(t *testing.T) {
doTestSiteWithPageOutputs(t, outputs)
})
@ -146,4 +146,12 @@ Output/Rel: {{ .Name -}}/{{ .Rel }}|
require.Equal(t, "/blog/index.json", json.RelPermalink())
require.Equal(t, "http://example.com/blog/index.json", json.Permalink())
if helpers.InStringArray(outputs, "cal") {
// TODO(bep) output have do some protocil handling for the default too if set.
cal := of.Get("calendar")
require.NotNil(t, cal)
require.Equal(t, "/blog/index.ics", cal.RelPermalink())
require.Equal(t, "webcal://example.com/blog/index.ics", cal.Permalink())
}
}

View file

@ -72,11 +72,12 @@ var (
)
var builtInTypes = map[string]Format{
strings.ToLower(AMPType.Name): AMPType,
strings.ToLower(CSSType.Name): CSSType,
strings.ToLower(HTMLType.Name): HTMLType,
strings.ToLower(JSONType.Name): JSONType,
strings.ToLower(RSSType.Name): RSSType,
strings.ToLower(AMPType.Name): AMPType,
strings.ToLower(CalendarType.Name): CalendarType,
strings.ToLower(CSSType.Name): CSSType,
strings.ToLower(HTMLType.Name): HTMLType,
strings.ToLower(JSONType.Name): JSONType,
strings.ToLower(RSSType.Name): RSSType,
}
type Formats []Format

View file

@ -30,6 +30,7 @@ func TestDefaultTypes(t *testing.T) {
require.Equal(t, "HTML", HTMLType.Name)
require.Equal(t, media.HTMLType, HTMLType.MediaType)
require.Empty(t, HTMLType.Path)
require.Empty(t, HTMLType.Protocol) // Will inherit the BaseURL protocol.
require.False(t, HTMLType.IsPlainText)
require.Equal(t, "RSS", RSSType.Name)