From 0665d0c65974533fbd313f4e0b062b5103057aeb Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 18 May 2024 18:29:19 +0200 Subject: Replace all URL rendering within templates by a URLConstructor --- src/render/methods.go | 14 +++-- src/render/url.go | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/render/url.go (limited to 'src/render') diff --git a/src/render/methods.go b/src/render/methods.go index 9b5a41e..ee22dfd 100644 --- a/src/render/methods.go +++ b/src/render/methods.go @@ -56,6 +56,8 @@ type Methods struct { ctx context.Context url *url.URL publicURL *url.URL + httpURL *url.URL + geminiURL *url.URL geminiGatewayURL *url.URL postStore post.Store postAssetStore asset.Store @@ -71,6 +73,8 @@ func NewMethods( ctx context.Context, url *url.URL, publicURL *url.URL, + httpURL *url.URL, + geminiURL *url.URL, geminiGatewayURL *url.URL, postStore post.Store, postAssetStore asset.Store, @@ -81,6 +85,8 @@ func NewMethods( ctx, url, publicURL, + httpURL, + geminiURL, geminiGatewayURL, postStore, postAssetStore, @@ -91,6 +97,10 @@ func NewMethods( } } +func (m *Methods) RootURL() URLBuilder { + return NewURLBuilder(m.publicURL, m.httpURL, m.geminiURL) +} + func (m *Methods) GetTags() ([]string, error) { return m.postStore.GetTags() } @@ -263,7 +273,3 @@ func (m *Methods) GetPath() (string, error) { } func (m *Methods) Add(a, b int) int { return a + b } - -func (m *Methods) URLIsSafe(s string) template.URL { - return template.URL(s) -} diff --git a/src/render/url.go b/src/render/url.go new file mode 100644 index 0000000..eb9756b --- /dev/null +++ b/src/render/url.go @@ -0,0 +1,168 @@ +package render + +import ( + "html/template" + "net/url" + "path" + "strconv" + "strings" +) + +const ( + urlBasePublic = iota + urlBaseHTTP + urlBaseGemini +) + +// URLBuilder is used to construct a URL for the site. The API of URLBuilder is +// designed to be convenient to used from a go template. +type URLBuilder struct { + publicURL, httpURL, geminiURL *url.URL + + abs bool + base int + path, method string + page int +} + +func NewURLBuilder(publicURL, httpURL, geminiURL *url.URL) URLBuilder { + return URLBuilder{ + publicURL: publicURL, + httpURL: httpURL, + geminiURL: geminiURL, + } +} + +func (b URLBuilder) String() string { + var u *url.URL + switch b.base { + case urlBasePublic: + u = b.publicURL + case urlBaseHTTP: + u = b.httpURL + case urlBaseGemini: + u = b.geminiURL + } + + u = u.JoinPath(b.path) + + if u.Scheme == "gemini" && + u.Host == b.geminiURL.Host && + !strings.HasSuffix(u.Path, "/") && + path.Ext(u.Path) == "" { + u.Path += ".gmi" + } + + { + query := url.Values{} + if b.method != "" { + query.Set("method", b.method) + } + if b.page > 0 { + query.Set("page", strconv.Itoa(b.page)) + } + if len(query) > 0 { + u.RawQuery = query.Encode() + } + } + + if abs := b.abs || u.Scheme != b.publicURL.Scheme; !abs { + u.Scheme = "" + u.Host = "" + } + + return u.String() +} + +func (b URLBuilder) HTMLSafe() template.URL { + return template.URL(b.String()) +} + +// Absolute returns a URLBuilder which will always construct a URL containing +// scheme and host. +func (b URLBuilder) Absolute() URLBuilder { + b.abs = true + return b +} + +func (b URLBuilder) Gemini() URLBuilder { + b.base = urlBaseGemini + return b +} + +func (b URLBuilder) HTTP() URLBuilder { + b.base = urlBaseHTTP + return b +} + +func (b URLBuilder) Assets() URLBuilder { + b.path = "assets/" + return b +} + +func (b URLBuilder) Asset(id string) URLBuilder { + b.path = "assets/" + id + return b +} + +func (b URLBuilder) Posts() URLBuilder { + b.path = "posts/" + return b +} + +func (b URLBuilder) Post(id string) URLBuilder { + b.path = "posts/" + id + return b +} + +func (b URLBuilder) Drafts() URLBuilder { + b.base = urlBaseHTTP + b.path = "drafts/" + return b +} + +func (b URLBuilder) Draft(id string) URLBuilder { + b.base = urlBaseHTTP + b.path = "drafts/" + id + return b +} + +func (b URLBuilder) Static(staticPath string) URLBuilder { + b.base = urlBaseHTTP + b.path = path.Join("static", staticPath) + return b +} + +func (b URLBuilder) Path(p string) URLBuilder { + b.path = p + return b +} + +func (b URLBuilder) Page(page int) URLBuilder { + b.page = page + return b +} + +func (b URLBuilder) MethodEdit() URLBuilder { + b.base = urlBaseHTTP + b.method = "edit" + return b +} + +func (b URLBuilder) MethodManage() URLBuilder { + b.base = urlBaseHTTP + b.method = "manage" + return b +} + +func (b URLBuilder) MethodDelete() URLBuilder { + b.base = urlBaseHTTP + b.method = "delete" + return b +} + +func (b URLBuilder) MethodPreview() URLBuilder { + b.base = urlBaseHTTP + b.method = "preview" + return b +} -- cgit v1.2.3