summaryrefslogtreecommitdiff
path: root/src/http
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2024-05-17 23:37:43 +0200
committerBrian Picciano <mediocregopher@gmail.com>2024-05-18 14:47:09 +0200
commit8d7e708d98a3a46ba3ba08f9c8deeb4838bb8ca5 (patch)
tree6662c3e4c6c3baaea058a3deaba0d9cfc8e9cc40 /src/http
parentfac06df97a47cda6e8989bfc5f40f2a627279b92 (diff)
Render posts completely using common rendering methods
The aim is to reduce reliance on custom logic in the handlers for every protocol, eventually outsourcing all of it into `render.Methods`, leaving each protocol to simply direct calls to the correct template.
Diffstat (limited to 'src/http')
-rw-r--r--src/http/assets.go2
-rw-r--r--src/http/drafts.go2
-rw-r--r--src/http/http.go2
-rw-r--r--src/http/index.go2
-rw-r--r--src/http/posts.go129
-rw-r--r--src/http/tpl.go84
-rw-r--r--src/http/tpl/post.html30
7 files changed, 77 insertions, 174 deletions
diff --git a/src/http/assets.go b/src/http/assets.go
index 79ab98c..5a47152 100644
--- a/src/http/assets.go
+++ b/src/http/assets.go
@@ -34,7 +34,7 @@ func (a *api) managePostAssetsHandler() http.Handler {
IDs: ids,
}
- executeTemplate(rw, r, tpl, tplPayload)
+ a.executeTemplate(rw, r, tpl, tplPayload)
})
}
diff --git a/src/http/drafts.go b/src/http/drafts.go
index f8e4c8a..b0550ce 100644
--- a/src/http/drafts.go
+++ b/src/http/drafts.go
@@ -48,7 +48,7 @@ func (a *api) manageDraftPostsHandler() http.Handler {
tplPayload.NextPage = page + 1
}
- executeTemplate(rw, r, tpl, tplPayload)
+ a.executeTemplate(rw, r, tpl, tplPayload)
})
}
diff --git a/src/http/http.go b/src/http/http.go
index fd0ea16..9bfed59 100644
--- a/src/http/http.go
+++ b/src/http/http.go
@@ -146,7 +146,7 @@ func New(params Params) (API, error) {
auther: NewAuther(params.AuthUsers, params.AuthRatelimit),
}
- a.redirectTpl = a.mustParseTpl("redirect.html")
+ a.redirectTpl = mustParseTpl(a.emptyTpl(), "redirect.html")
a.srv = &http.Server{Handler: a.handler()}
diff --git a/src/http/index.go b/src/http/index.go
index 21c6c16..48d0d33 100644
--- a/src/http/index.go
+++ b/src/http/index.go
@@ -31,6 +31,6 @@ func (a *api) renderIndexHandler() http.Handler {
return
}
- executeTemplate(rw, r, tpl, nil)
+ a.executeTemplate(rw, r, tpl, nil)
})
}
diff --git a/src/http/posts.go b/src/http/posts.go
index ab3a18a..939b811 100644
--- a/src/http/posts.go
+++ b/src/http/posts.go
@@ -12,13 +12,10 @@ import (
txttpl "text/template"
"time"
- "github.com/gomarkdown/markdown"
- "github.com/gomarkdown/markdown/html"
- "github.com/gomarkdown/markdown/parser"
- "dev.mediocregopher.com/mediocre-blog.git/src/gmi"
"dev.mediocregopher.com/mediocre-blog.git/src/http/apiutil"
"dev.mediocregopher.com/mediocre-blog.git/src/post"
"dev.mediocregopher.com/mediocre-blog.git/src/post/asset"
+ "dev.mediocregopher.com/mediocre-blog.git/src/render"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
)
@@ -92,80 +89,6 @@ func (a *api) postPreprocessFuncs() post.PreprocessFunctions {
}
}
-func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) {
-
- preprocessFuncs := a.postPreprocessFuncs()
-
- bodyBuf := new(bytes.Buffer)
-
- if err := storedPost.PreprocessBody(bodyBuf, preprocessFuncs); err != nil {
- return postTplPayload{}, fmt.Errorf("preprocessing post body: %w", err)
- }
-
- if storedPost.Format == post.FormatGemtext {
-
- prevBodyBuf := bodyBuf
- bodyBuf = new(bytes.Buffer)
-
- err := gmi.GemtextToMarkdown(
- bodyBuf, prevBodyBuf, a.params.GeminiGatewayURL,
- )
-
- if err != nil {
- return postTplPayload{}, fmt.Errorf("converting gemtext to markdown: %w", err)
- }
- }
-
- // this helps the markdown renderer properly parse pages which end in a
- // `</script>` tag... I don't know why.
- _, _ = bodyBuf.WriteString("\n")
-
- parserExt := parser.CommonExtensions | parser.AutoHeadingIDs
- parser := parser.NewWithExtensions(parserExt)
-
- htmlFlags := html.HrefTargetBlank
- htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags})
-
- renderedBody := markdown.ToHTML(bodyBuf.Bytes(), parser, htmlRenderer)
-
- tplPayload := postTplPayload{
- StoredPost: storedPost,
- Body: template.HTML(renderedBody),
- }
-
- if series := storedPost.Series; series != "" {
-
- seriesPosts, err := a.params.PostStore.GetBySeries(series)
- if err != nil {
- return postTplPayload{}, fmt.Errorf(
- "fetching posts for series %q: %w", series, err,
- )
- }
-
- var foundThis bool
-
- for i := range seriesPosts {
-
- seriesPost := seriesPosts[i]
-
- if seriesPost.ID == storedPost.ID {
- foundThis = true
- continue
- }
-
- if !foundThis {
- tplPayload.SeriesNext = &seriesPost
- continue
- }
-
- tplPayload.SeriesPrevious = &seriesPost
- break
- }
- }
-
- return tplPayload, nil
-}
-
func (a *api) getPostsHandler() http.Handler {
tpl := a.mustParseBasedTpl("posts.html")
@@ -236,7 +159,7 @@ func (a *api) getPostsHandler() http.Handler {
tplPayload.NextPage = page + 1
}
- executeTemplate(rw, r, tpl, tplPayload)
+ a.executeTemplate(rw, r, tpl, tplPayload)
})
}
@@ -245,37 +168,7 @@ func (a *api) getPostHandler() http.Handler {
tpl := a.mustParseBasedTpl("post.html")
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
-
- id := strings.TrimSuffix(filepath.Base(r.URL.Path), ".html")
-
- storedPost, err := a.params.PostStore.GetByID(id)
-
- if errors.Is(err, post.ErrPostNotFound) {
- http.Error(rw, "Post not found", 404)
- return
- } else if err != nil {
- apiutil.InternalServerError(
- rw, r, fmt.Errorf("fetching post with id %q: %w", id, err),
- )
- return
- }
-
- tplPayload, err := a.postToPostTplPayload(storedPost)
-
- if err != nil {
- apiutil.InternalServerError(
- rw, r, fmt.Errorf(
- "generating template payload for post with id %q: %w",
- id, err,
- ),
- )
- return
- }
-
- executeTemplate(
- rw, r, tpl, tplPayload,
- executeTemplateWithTitlePrefix(storedPost.Title),
- )
+ a.executeTemplate(rw, r, tpl, nil)
})
}
@@ -319,7 +212,7 @@ func (a *api) managePostsHandler() http.Handler {
tplPayload.NextPage = page + 1
}
- executeTemplate(rw, r, tpl, tplPayload)
+ a.executeTemplate(rw, r, tpl, tplPayload)
})
}
@@ -378,7 +271,7 @@ func (a *api) editPostHandler(isDraft bool) http.Handler {
Formats: post.Formats,
}
- executeTemplate(rw, r, tpl, tplPayload)
+ a.executeTemplate(rw, r, tpl, tplPayload)
})
}
@@ -516,15 +409,7 @@ func (a *api) previewPostHandler() http.Handler {
PublishedAt: time.Now(),
}
- tplPayload, err := a.postToPostTplPayload(storedPost)
-
- if err != nil {
- apiutil.InternalServerError(
- rw, r, fmt.Errorf("generating template payload: %w", err),
- )
- return
- }
-
- executeTemplate(rw, r, tpl, tplPayload)
+ r = r.WithContext(render.WithPost(r.Context(), storedPost))
+ a.executeTemplate(rw, r, tpl, nil)
})
}
diff --git a/src/http/tpl.go b/src/http/tpl.go
index 24f2453..c623f2e 100644
--- a/src/http/tpl.go
+++ b/src/http/tpl.go
@@ -1,9 +1,12 @@
package http
import (
+ "bytes"
"embed"
+ "errors"
"fmt"
"html/template"
+ "io"
"io/fs"
"net/http"
"net/url"
@@ -12,6 +15,8 @@ import (
"time"
"dev.mediocregopher.com/mediocre-blog.git/src/http/apiutil"
+ "dev.mediocregopher.com/mediocre-blog.git/src/post"
+ "dev.mediocregopher.com/mediocre-blog.git/src/render"
)
//go:embed tpl
@@ -105,77 +110,84 @@ func (a *api) tplFuncs() template.FuncMap {
}
}
-func (a *api) parseTpl(name, tplBody string) (*template.Template, error) {
-
- tpl := template.New(name)
+func (a *api) emptyTpl() *template.Template {
+ tpl := template.New("")
tpl = tpl.Funcs(a.tplFuncs())
tpl = tpl.Funcs(template.FuncMap(a.postPreprocessFuncs().ToFuncMap()))
-
- var err error
-
- if tpl, err = tpl.Parse(tplBody); err != nil {
- return nil, err
- }
-
- return tpl, nil
+ return tpl
}
-func (a *api) mustParseTpl(name string) *template.Template {
- return template.Must(a.parseTpl(name, mustReadTplFile(name)))
+func mustParseTpl(tpl *template.Template, name string) *template.Template {
+ return template.Must(tpl.New(name).Parse(mustReadTplFile(name)))
}
func (a *api) mustParseBasedTpl(name string) *template.Template {
- tpl := a.mustParseTpl(name)
- tpl = template.Must(tpl.New("gemini-cta.html").Parse(mustReadTplFile("gemini-cta.html")))
- tpl = template.Must(tpl.New("base.html").Parse(mustReadTplFile("base.html")))
+ tpl := a.emptyTpl()
+ tpl = mustParseTpl(tpl, "gemini-cta.html")
+ tpl = mustParseTpl(tpl, "base.html")
+ tpl = mustParseTpl(tpl, name)
return tpl
}
type tplData struct {
+ *render.Methods
Payload interface{}
Title string
}
-func newTPLData(r *http.Request, payload interface{}) tplData {
+// WithTitlePrefix returns a copy of tplData but with the given string prefixed
+// to the page title. This is intended for use within templates, when nesting
+// the base template.
+func (d tplData) WithTitlePrefix(prefix string) tplData {
+ d.Title = prefix + " - " + d.Title
+ return d
+}
+
+func (a *api) newTPLData(r *http.Request, payload interface{}) tplData {
return tplData{
+ Methods: render.NewMethods(
+ r.Context(),
+ r.URL,
+ a.params.PublicURL,
+ a.params.GeminiGatewayURL,
+ a.params.PostStore,
+ a.postPreprocessFuncs(),
+ ),
Payload: payload,
Title: "mediocregopher's lil web corner",
}
}
-type executeTemplateOpt func(*tplData)
-
-func executeTemplateWithTitlePrefix(prefix string) executeTemplateOpt {
- return func(d *tplData) {
- d.Title = prefix + " - " + d.Title
- }
-}
-
// executeTemplate expects to be the final action in an http.Handler
-func executeTemplate(
- rw http.ResponseWriter, r *http.Request,
- tpl *template.Template, payload interface{},
- opts ...executeTemplateOpt,
+func (a *api) executeTemplate(
+ rw http.ResponseWriter,
+ r *http.Request,
+ tpl *template.Template,
+ payload interface{},
) {
- tplData := newTPLData(r, payload)
+ tplData := a.newTPLData(r, payload)
- for _, opt := range opts {
- opt(&tplData)
- }
+ buf := new(bytes.Buffer)
- if err := tpl.Execute(rw, tplData); err != nil {
+ err := tpl.Execute(buf, tplData)
+ if errors.Is(err, post.ErrPostNotFound) {
+ http.Error(rw, "Post not found", 404)
+ return
+ } else if err != nil {
apiutil.InternalServerError(
rw, r, fmt.Errorf("rendering template: %w", err),
)
return
}
+
+ io.Copy(rw, buf)
}
func (a *api) executeRedirectTpl(
rw http.ResponseWriter, r *http.Request, url string,
) {
- executeTemplate(rw, r, a.redirectTpl, struct {
+ a.executeTemplate(rw, r, a.redirectTpl, struct {
URL string
}{
URL: url,
@@ -188,7 +200,7 @@ func (a *api) renderDumbTplHandler(tplName string) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- tplData := newTPLData(r, nil)
+ tplData := a.newTPLData(r, nil)
if err := tpl.Execute(rw, tplData); err != nil {
apiutil.InternalServerError(
diff --git a/src/http/tpl/post.html b/src/http/tpl/post.html
index db69302..0cf3622 100644
--- a/src/http/tpl/post.html
+++ b/src/http/tpl/post.html
@@ -1,45 +1,51 @@
{{ define "body" }}
+{{ $post := .GetThisPost -}}
<h1 id="post-headline">
- {{ .Payload.Title }}
+ {{ $post.Title }}
</h1>
-{{ if ne .Payload.Description "" }}
+{{ if ne $post.Description "" }}
<p>
- <em>- {{ .Payload.Description }}</em>
+ <em>- {{ $post.Description }}</em>
</p>
{{ end }}
<hr/>
-{{ .Payload.Body }}
+{{ .PostHTMLBody $post }}
<p><em>
- Published {{ DateTimeFormat .Payload.PublishedAt }}
+ Published {{ DateTimeFormat $post.PublishedAt }}
</em></p>
-{{ if (or .Payload.SeriesPrevious .Payload.SeriesNext) }}
+{{- if $post.Series }}
+{{ $seriesNextPrev := .GetPostSeriesNextPrevious $post -}}
+{{ if or $seriesNextPrev.Next $seriesNextPrev.Previous }}
<hr/>
<p><em>
This post is part of a series.<br/>
- {{ if .Payload.SeriesPrevious }}
- Previously: <a href="{{ PostURL .Payload.SeriesPrevious.ID }}">{{ .Payload.SeriesPrevious.Title }}</a>
+ {{ if $seriesNextPrev.Next }}
+ Next: <a href="{{ PostURL $seriesNextPrev.Next.ID }}">{{ $seriesNextPrev.Next.Title }}</a>
{{ end }}
- {{ if (and .Payload.SeriesNext .Payload.SeriesPrevious) }}
+ {{ if and $seriesNextPrev.Next $seriesNextPrev.Previous }}
</br>
{{ end }}
- {{ if .Payload.SeriesNext }}
- Next: <a href="{{ PostURL .Payload.SeriesNext.ID }}">{{ .Payload.SeriesNext.Title }}</a></br>
+ {{ if $seriesNextPrev.Previous }}
+ Previously: <a href="{{ PostURL $seriesNextPrev.Previous.ID }}">{{ $seriesNextPrev.Previous.Title }}</a>
+ <br/>
{{ end }}
</em></p>
{{ end }}
+{{ end }}
{{ template "gemini-cta.html" . }}
{{ end }}
-{{ template "base.html" . }}
+{{ $post := .GetThisPost -}}
+{{ template "base.html" (.WithTitlePrefix $post.Title) }}