You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.0 KiB
96 lines
2.0 KiB
package post
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
|
|
"github.com/gomarkdown/markdown"
|
|
"github.com/gomarkdown/markdown/html"
|
|
"github.com/gomarkdown/markdown/parser"
|
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/tpl"
|
|
)
|
|
|
|
// RenderablePost is a Post wrapped with extra information necessary for
|
|
// rendering.
|
|
type RenderablePost struct {
|
|
StoredPost
|
|
SeriesPrevious, SeriesNext *StoredPost
|
|
}
|
|
|
|
// NewRenderablePost wraps an existing Post such that it can be rendered.
|
|
func NewRenderablePost(store Store, post StoredPost) (RenderablePost, error) {
|
|
|
|
renderablePost := RenderablePost{
|
|
StoredPost: post,
|
|
}
|
|
|
|
if post.Series != "" {
|
|
|
|
seriesPosts, err := store.GetBySeries(post.Series)
|
|
if err != nil {
|
|
return RenderablePost{}, fmt.Errorf(
|
|
"fetching posts for series %q: %w",
|
|
post.Series, err,
|
|
)
|
|
}
|
|
|
|
var foundThis bool
|
|
|
|
for i := range seriesPosts {
|
|
|
|
seriesPost := seriesPosts[i]
|
|
|
|
if seriesPost.ID == post.ID {
|
|
foundThis = true
|
|
continue
|
|
}
|
|
|
|
if !foundThis {
|
|
renderablePost.SeriesPrevious = &seriesPost
|
|
continue
|
|
}
|
|
|
|
renderablePost.SeriesNext = &seriesPost
|
|
break
|
|
}
|
|
}
|
|
|
|
return renderablePost, nil
|
|
}
|
|
|
|
// Renderer takes a Post and renders it to some encoding.
|
|
type Renderer interface {
|
|
Render(io.Writer, RenderablePost) error
|
|
}
|
|
|
|
func mdBodyToHTML(body []byte) []byte {
|
|
parserExt := parser.CommonExtensions | parser.AutoHeadingIDs
|
|
parser := parser.NewWithExtensions(parserExt)
|
|
|
|
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
|
htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags})
|
|
|
|
return markdown.ToHTML(body, parser, htmlRenderer)
|
|
}
|
|
|
|
type mdHTMLRenderer struct{}
|
|
|
|
// NewMarkdownToHTMLRenderer renders Posts from markdown to HTML.
|
|
func NewMarkdownToHTMLRenderer() Renderer {
|
|
return mdHTMLRenderer{}
|
|
}
|
|
|
|
func (r mdHTMLRenderer) Render(into io.Writer, post RenderablePost) error {
|
|
|
|
data := struct {
|
|
RenderablePost
|
|
Body template.HTML
|
|
}{
|
|
RenderablePost: post,
|
|
Body: template.HTML(mdBodyToHTML([]byte(post.Body))),
|
|
}
|
|
|
|
return tpl.HTML.ExecuteTemplate(into, "post.html", data)
|
|
}
|
|
|