shithub: hugo

Download patch

ref: d851d6b98fefbe7de91c8c58b883cca4da17eea9
parent: 8bcc08e3b0ddd5762101bb2f061e0be04ecd8d57
author: Bjørn Erik Pedersen <[email protected]>
date: Thu Mar 23 16:05:10 EDT 2017

Add custom protocol support in Permalink

--- /dev/null
+++ b/helpers/baseURL.go
@@ -1,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:[email protected]).
+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
+}
--- /dev/null
+++ b/helpers/baseURL_test.go
@@ -1,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:[email protected]")
+	require.NoError(t, err)
+	require.Equal(t, "mailto:[email protected]", b.String())
+
+	// These are pretty constructed
+	p, err = b.WithProtocol("webcal")
+	require.NoError(t, err)
+	require.Equal(t, "webcal:[email protected]", p)
+
+	p, err = b.WithProtocol("webcal://")
+	require.NoError(t, err)
+	require.Equal(t, "webcal://[email protected]", p)
+
+}
--- a/helpers/url.go
+++ b/helpers/url.go
@@ -23,33 +23,6 @@
 	"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 {
 }
 
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -862,7 +862,12 @@
 		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()
--- a/hugolib/page_output.go
+++ b/hugolib/page_output.go
@@ -197,7 +197,8 @@
 // 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.
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1766,9 +1766,29 @@
 	return s.getPage(typ, path...)
 }
 
+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 {
-	baseURL := s.PathSpec.BaseURL.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 += "/"
--- a/hugolib/site_output_test.go
+++ b/hugolib/site_output_test.go
@@ -52,7 +52,7 @@
 }
 
 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)
 		})
@@ -145,5 +145,13 @@
 	require.NotNil(t, json)
 	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())
+	}
 
 }
--- a/output/outputFormat.go
+++ b/output/outputFormat.go
@@ -72,11 +72,12 @@
 )
 
 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
--- a/output/outputFormat_test.go
+++ b/output/outputFormat_test.go
@@ -30,6 +30,7 @@
 	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)