diff --git a/srv/src/api/api.go b/srv/src/api/api.go index adecff5..191a7bf 100644 --- a/srv/src/api/api.go +++ b/srv/src/api/api.go @@ -222,6 +222,7 @@ func (a *api) handler() http.Handler { "DELETE": authMiddleware(auther, formMiddleware(a.deletePostHandler()), ), + "PREVIEW": formMiddleware(a.previewPostHandler()), }), )) v2Mux.Handle("/assets/", http.StripPrefix("/assets", diff --git a/srv/src/api/posts.go b/srv/src/api/posts.go index 845d9ff..ab11474 100644 --- a/srv/src/api/posts.go +++ b/srv/src/api/posts.go @@ -16,6 +16,59 @@ import ( "github.com/mediocregopher/blog.mediocregopher.com/srv/post" ) +type postTplPayload struct { + post.StoredPost + SeriesPrevious, SeriesNext *post.StoredPost + Body template.HTML +} + +func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) { + parserExt := parser.CommonExtensions | parser.AutoHeadingIDs + parser := parser.NewWithExtensions(parserExt) + + htmlFlags := html.CommonFlags | html.HrefTargetBlank + htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags}) + + renderedBody := markdown.ToHTML([]byte(storedPost.Body), 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.SeriesPrevious = &seriesPost + continue + } + + tplPayload.SeriesNext = &seriesPost + break + } + } + + return tplPayload, nil +} + func (a *api) renderPostHandler() http.Handler { tpl := a.mustParseBasedTpl("post.html") @@ -42,53 +95,16 @@ func (a *api) renderPostHandler() http.Handler { return } - parserExt := parser.CommonExtensions | parser.AutoHeadingIDs - parser := parser.NewWithExtensions(parserExt) - - htmlFlags := html.CommonFlags | html.HrefTargetBlank - htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags}) - - renderedBody := markdown.ToHTML([]byte(storedPost.Body), parser, htmlRenderer) - - tplPayload := struct { - post.StoredPost - SeriesPrevious, SeriesNext *post.StoredPost - Body template.HTML - }{ - StoredPost: storedPost, - Body: template.HTML(renderedBody), - } - - if series := storedPost.Series; series != "" { - - seriesPosts, err := a.params.PostStore.GetBySeries(series) - if err != nil { - apiutil.InternalServerError( - rw, r, - fmt.Errorf("fetching posts for series %q: %w", series, err), - ) - return - } - - var foundThis bool + tplPayload, err := a.postToPostTplPayload(storedPost) - for i := range seriesPosts { - - seriesPost := seriesPosts[i] - - if seriesPost.ID == storedPost.ID { - foundThis = true - continue - } - - if !foundThis { - tplPayload.SeriesPrevious = &seriesPost - continue - } - - tplPayload.SeriesNext = &seriesPost - break - } + 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) @@ -169,21 +185,28 @@ func (a *api) editPostHandler() http.Handler { }) } +func postFromPostReq(r *http.Request) post.Post { + + p := post.Post{ + ID: r.PostFormValue("id"), + Title: r.PostFormValue("title"), + Description: r.PostFormValue("description"), + Tags: strings.Fields(r.PostFormValue("tags")), + Series: r.PostFormValue("series"), + } + + p.Body = strings.TrimSpace(r.PostFormValue("body")) + // textareas encode newlines as CRLF for historical reasons + p.Body = strings.ReplaceAll(p.Body, "\r\n", "\n") + + return p +} + func (a *api) postPostHandler() http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - p := post.Post{ - ID: r.PostFormValue("id"), - Title: r.PostFormValue("title"), - Description: r.PostFormValue("description"), - Tags: strings.Fields(r.PostFormValue("tags")), - Series: r.PostFormValue("series"), - } - - p.Body = strings.TrimSpace(r.PostFormValue("body")) - // textareas encode newlines as CRLF for historical reasons - p.Body = strings.ReplaceAll(p.Body, "\r\n", "\n") + p := postFromPostReq(r) if err := a.params.PostStore.Set(p, time.Now()); err != nil { apiutil.InternalServerError( @@ -225,3 +248,27 @@ func (a *api) deletePostHandler() http.Handler { }) } + +func (a *api) previewPostHandler() http.Handler { + + tpl := a.mustParseBasedTpl("post.html") + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + + storedPost := post.StoredPost{ + Post: postFromPostReq(r), + 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) + }) +} diff --git a/srv/src/api/tpl/edit-post.html b/srv/src/api/tpl/edit-post.html index 708858d..9ccfa2a 100644 --- a/srv/src/api/tpl/edit-post.html +++ b/srv/src/api/tpl/edit-post.html @@ -1,11 +1,5 @@ {{ define "body" }} -

- - - -

-
{{ .CSRFFormInput }} @@ -87,7 +81,18 @@ - + + + + + + +