mirror of https://github.com/jlelse/GoBlog
More parts of the rendering in Go functions
This commit is contained in:
parent
fa82364b70
commit
7fba9bc0bc
23
editor.go
23
editor.go
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -58,13 +57,13 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (a *goBlog) createMarkdownPreview(blog string, markdown []byte) (rendered []byte, err error) {
|
||||
p := post{
|
||||
p := &post{
|
||||
Content: string(markdown),
|
||||
Blog: blog,
|
||||
Path: "/editor/preview",
|
||||
Published: localNowString(),
|
||||
}
|
||||
err = a.computeExtraPostParameters(&p)
|
||||
err = a.computeExtraPostParameters(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -72,21 +71,9 @@ func (a *goBlog) createMarkdownPreview(blog string, markdown []byte) (rendered [
|
|||
p.RenderedTitle = a.renderMdTitle(t)
|
||||
}
|
||||
// Render post
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/editor/preview", nil)
|
||||
a.render(rec, req, templateEditorPreview, &renderData{
|
||||
BlogString: p.Blog,
|
||||
Data: &p,
|
||||
})
|
||||
res := rec.Result()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("failed to render preview")
|
||||
}
|
||||
defer res.Body.Close()
|
||||
rendered, err = io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var hb htmlBuilder
|
||||
a.renderEditorPreview(&hb, a.cfg.Blogs[blog], p)
|
||||
rendered = []byte(hb.String())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ func Test_editorPreview(t *testing.T) {
|
|||
app.initComponents(false)
|
||||
|
||||
h := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
app.serveEditorPreview(rw, r.WithContext(context.WithValue(r.Context(), blogKey, "en")))
|
||||
app.serveEditorPreview(rw, r.WithContext(context.WithValue(r.Context(), blogKey, "default")))
|
||||
})
|
||||
d := wstest.NewDialer(h)
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -47,7 +47,7 @@ require (
|
|||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tdewolff/minify/v2 v2.9.27
|
||||
github.com/thoas/go-funk v0.9.1
|
||||
github.com/tkrajina/gpxgo v1.1.2
|
||||
github.com/tkrajina/gpxgo v1.2.0
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
||||
github.com/yuin/goldmark v1.4.4
|
||||
|
|
4
go.sum
4
go.sum
|
@ -410,8 +410,8 @@ github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
|||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/tkrajina/gpxgo v1.1.2 h1:il6rjS6IGm3yqa/yr7+fKBlF3ufWDEPZrYi/kxI1Jv0=
|
||||
github.com/tkrajina/gpxgo v1.1.2/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0=
|
||||
github.com/tkrajina/gpxgo v1.2.0 h1:WzEMWKmsYZm6Nqvro4FibSA9p8kMph5u7nfaNWLGtqY=
|
||||
github.com/tkrajina/gpxgo v1.2.0/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
|
||||
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
|
||||
|
|
|
@ -27,10 +27,6 @@ func (a *goBlog) shortPostURL(p *post) string {
|
|||
return a.getFullAddress(s)
|
||||
}
|
||||
|
||||
func postParameter(p *post, parameter string) []string {
|
||||
return p.Parameters[parameter]
|
||||
}
|
||||
|
||||
func (p *post) firstParameter(parameter string) (result string) {
|
||||
if pp := p.Parameters[parameter]; len(pp) > 0 {
|
||||
result = pp[0]
|
||||
|
|
62
render.go
62
render.go
|
@ -39,7 +39,6 @@ const (
|
|||
templateBlogroll = "blogroll"
|
||||
templateGeoMap = "geomap"
|
||||
templateContact = "contact"
|
||||
templateEditorPreview = "editorpreview"
|
||||
templateIndieAuth = "indieauth"
|
||||
)
|
||||
|
||||
|
@ -50,25 +49,48 @@ func (a *goBlog) initRendering() error {
|
|||
"mdtitle": a.renderMdTitle,
|
||||
"html": wrapStringAsHTML,
|
||||
// Post specific
|
||||
"ps": postParameter,
|
||||
"content": a.postHtml,
|
||||
"summary": a.postSummary,
|
||||
"translations": a.postTranslations,
|
||||
"shorturl": a.shortPostURL,
|
||||
"showfull": a.showFull,
|
||||
"geouri": a.geoURI,
|
||||
"replylink": a.replyLink,
|
||||
"replytitle": a.replyTitle,
|
||||
"likelink": a.likeLink,
|
||||
"liketitle": a.likeTitle,
|
||||
"photolinks": a.photoLinks,
|
||||
"gettrack": a.getTrack,
|
||||
"content": a.postHtml,
|
||||
"summary": a.postSummary,
|
||||
"shorturl": a.shortPostURL,
|
||||
"showfull": a.showFull,
|
||||
"photolinks": a.photoLinks,
|
||||
"gettrack": a.getTrack,
|
||||
// Code based rendering
|
||||
"posttax": a.renderPostTax,
|
||||
"oldcontentwarning": a.renderOldContentWarning,
|
||||
"interactions": a.renderInteractions,
|
||||
"author": a.renderAuthor,
|
||||
"tor": a.renderTorNotice,
|
||||
"posttax": func(p *post, b *configBlog) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderPostTax(&hb, p, b)
|
||||
return hb.html()
|
||||
},
|
||||
"postmeta": func(p *post, b *configBlog, typ string) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderPostMeta(&hb, p, b, typ)
|
||||
return hb.html()
|
||||
},
|
||||
"oldcontentwarning": func(p *post, b *configBlog) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderOldContentWarning(&hb, p, b)
|
||||
return hb.html()
|
||||
},
|
||||
"interactions": func(b *configBlog, c string) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderInteractions(&hb, b, c)
|
||||
return hb.html()
|
||||
},
|
||||
"author": func() template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderAuthor(&hb)
|
||||
return hb.html()
|
||||
},
|
||||
"postheadmeta": func(p *post, c string) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderPostHeadMeta(&hb, p, c)
|
||||
return hb.html()
|
||||
},
|
||||
"tor": func(b *configBlog, torUsed bool, torAddress string) template.HTML {
|
||||
var hb htmlBuilder
|
||||
a.renderTorNotice(&hb, b, torUsed, torAddress)
|
||||
return hb.html()
|
||||
},
|
||||
// Others
|
||||
"dateformat": dateFormat,
|
||||
"isodate": isoDateFormat,
|
||||
|
@ -79,8 +101,6 @@ func (a *goBlog) initRendering() error {
|
|||
"include": a.includeRenderedTemplate,
|
||||
"urlize": urlize,
|
||||
"absolute": a.getFullAddress,
|
||||
"geotitle": a.geoTitle,
|
||||
"geolink": geoOSMLink,
|
||||
"opensearch": openSearchUrl,
|
||||
"mbytes": mBytesString,
|
||||
"editortemplate": a.editorPostTemplate,
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{{ define "editorpreview" }}
|
||||
{{ with .Data.RenderedTitle }}<h1>{{ . }}</h1>{{ end }}
|
||||
{{ include "summaryandpostmeta" . }}
|
||||
{{ if .Data.Content }}<div>{{ content .Data true }}</div>{{ end }}
|
||||
{{ posttax .Data .Blog }}
|
||||
{{ end }}
|
|
@ -8,7 +8,7 @@
|
|||
</a>
|
||||
</h2>
|
||||
{{ end }}
|
||||
{{ include "summarymeta" . }}
|
||||
{{ postmeta .Data .Blog "summary" }}
|
||||
{{ range $i, $photo := (photolinks .Data) }}
|
||||
{{ md ( printf "![](%s)" $photo ) }}
|
||||
{{ end }}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{ define "title" }}
|
||||
<link rel="stylesheet" href="{{ asset "css/chroma.css" }}">
|
||||
<title>{{ with .Data.RenderedTitle }}{{ . }} - {{end}}{{ mdtitle .Blog.Title }}</title>
|
||||
{{ include "postheadmeta" . }}
|
||||
{{ postheadmeta .Data .Canonical }}
|
||||
{{ with shorturl .Data }}<link rel="shortlink" href="{{ . }}">{{ end }}
|
||||
{{ if .Data.HasTrack }}
|
||||
<link rel="stylesheet" href="/-/leaflet/leaflet.css"/>
|
||||
|
@ -14,16 +14,7 @@
|
|||
<article>
|
||||
<data class="u-url hide" value="{{ absolute .Data.Path }}"></data>
|
||||
{{ with .Data.RenderedTitle }}<h1 class=p-name>{{ . }}</h1>{{ end }}
|
||||
<div class="p">
|
||||
{{ include "summaryandpostmeta" . }}
|
||||
{{ $translations := (translations .Data) }}
|
||||
{{ if gt (len $translations) 0 }}
|
||||
<div>{{ string .Blog.Lang "translations" }}: {{ $delimiter := "" }}{{ range $i, $t := $translations }}{{ $delimiter }}<a href="{{ $t.Path }}" translate="no">{{ $t.RenderedTitle }}</a>{{ $delimiter = ", " }}{{ end }}</div>
|
||||
{{ end }}
|
||||
{{ $short := shorturl .Data }}
|
||||
{{ if $short }}<div>{{ string .Blog.Lang "shorturl" }} <a href="{{ $short }}" rel="shortlink">{{ $short }}</a></div>{{ end }}
|
||||
{{ if ne .Data.Status "published" }}<div>{{ string .Blog.Lang "status" }}: {{ .Data.Status }}</div>{{ end }}
|
||||
</div>
|
||||
{{ postmeta .Data .Blog "post" }}
|
||||
<div id="post-actions">
|
||||
<a href="https://www.addtoany.com/share#url={{ shorturl .Data }}{{ with .Data.RenderedTitle }}&title={{ . }}{{ end }}" target="_blank" rel="nofollow noopener noreferrer" class="button">{{ string .Blog.Lang "share" }}</a>
|
||||
<a id="translateBtn" href="https://translate.google.com/translate?u={{ absolute .Data.Path }}" target="_blank" rel="nofollow noopener noreferrer" class="button">{{ string .Blog.Lang "translate" }}</a>
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{{ define "postheadmeta" }}
|
||||
{{ with .Canonical }}
|
||||
<meta property="og:url" content="{{ . }}">
|
||||
<meta property="twitter:url" content="{{ . }}">
|
||||
{{ end }}
|
||||
{{ with .Data.RenderedTitle }}
|
||||
<meta property="og:title" content="{{ . }}">
|
||||
<meta property="twitter:title" content="{{ . }}">
|
||||
{{ end }}
|
||||
{{ with summary .Data }}
|
||||
<meta name="description" content="{{ . }}">
|
||||
<meta property="og:description" content="{{ . }}">
|
||||
<meta property="twitter:description" content="{{ . }}">
|
||||
{{ end }}
|
||||
{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}
|
||||
{{ with .Data.Published }}
|
||||
<meta itemprop="datePublished" content="{{ dateformat . $ISO8601 }}">
|
||||
{{ end }}
|
||||
{{ with .Data.Updated }}
|
||||
<meta itemprop="dateModified" content="{{ dateformat . $ISO8601 }}">
|
||||
{{ end }}
|
||||
{{ range $key, $image := photolinks .Data }}
|
||||
<meta itemprop="image" content="{{ $image }}">
|
||||
<meta property="og:image" content="{{ $image }}">
|
||||
<meta property="twitter:image" content="{{ $image }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
|
@ -1,6 +1,6 @@
|
|||
{{ define "title" }}
|
||||
<title>{{ mdtitle .Blog.Title }}</title>
|
||||
{{ include "postheadmeta" . }}
|
||||
{{ postheadmeta .Data .Canonical }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</a>
|
||||
</h2>
|
||||
{{ end }}
|
||||
{{ include "summarymeta" . }}
|
||||
{{ postmeta .Data .Blog "summary" }}
|
||||
{{ if showfull .Data }}
|
||||
<div class=e-content>{{ content .Data false }}</div>
|
||||
{{ else }}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
{{ define "summaryandpostmeta" }}
|
||||
{{ $section := (index .Blog.Sections .Data.Section) }}
|
||||
{{ if .Data.Published }}<div>{{ string .Blog.Lang "publishedon" }} <time class="dt-published" datetime="{{ dateformat .Data.Published "2006-01-02T15:04:05Z07:00"}}">{{ isodate .Data.Published }}</time>{{ if $section }} in <a href="{{ .Blog.RelativePath $section.Name }}">{{ mdtitle $section.Title }}</a>{{ end }}</div>{{ end }}
|
||||
{{ if .Data.Updated }}<div>{{ string .Blog.Lang "updatedon" }} <time class="dt-updated" datetime="{{ dateformat .Data.Updated "2006-01-02T15:04:05Z07:00"}}">{{ isodate .Data.Updated }}</time></div>{{ end }}
|
||||
{{ if replylink .Data }}
|
||||
<div>{{ string .Blog.Lang "replyto" }}: <a class="u-in-reply-to" href="{{ replylink .Data }}" target="_blank" rel="noopener">{{ with (replytitle .Data) }}{{ . }}{{ else }}{{ replylink .Data }}{{ end }}</a></div>
|
||||
{{ end }}
|
||||
{{ if likelink .Data }}
|
||||
<div>{{ string .Blog.Lang "likeof" }}: <a class="u-like-of" href="{{ likelink .Data }}" target="_blank" rel="noopener">{{ with (liketitle .Data) }}{{ . }}{{ else }}{{ likelink .Data }}{{ end }}</a></div>
|
||||
{{ end }}
|
||||
{{ $geo := geouri .Data }}
|
||||
{{ if $geo }}
|
||||
<div>📍 <a class="p-location h-geo" href="{{ geolink $geo }}" target="_blank" rel="nofollow noopener noreferrer">
|
||||
<span class="p-name">{{ geotitle $geo .Blog.Lang }}</span>
|
||||
<data class="p-longitude" value="{{ $geo.Longitude }}" />
|
||||
<data class="p-latitude" value="{{ $geo.Latitude }}" />
|
||||
</a></div>
|
||||
{{ end }}
|
||||
{{ end }}
|
|
@ -1,5 +0,0 @@
|
|||
{{ define "summarymeta" }}
|
||||
<div class="p">
|
||||
{{ include "summaryandpostmeta" . }}
|
||||
</div>
|
||||
{{ end }}
|
203
ui.go
203
ui.go
|
@ -50,12 +50,27 @@ func (h *htmlBuilder) html() template.HTML {
|
|||
return template.HTML(h.String())
|
||||
}
|
||||
|
||||
// Render the HTML to show the list of post taxonomy values (tags, series, etc.)
|
||||
func (a *goBlog) renderPostTax(p *post, b *configBlog) template.HTML {
|
||||
if b == nil || p == nil {
|
||||
return ""
|
||||
// Render the HTML for the editor preview
|
||||
func (a *goBlog) renderEditorPreview(hb *htmlBuilder, bc *configBlog, p *post) {
|
||||
if p.RenderedTitle != "" {
|
||||
hb.writeElementOpen("h1")
|
||||
hb.writeEscaped(p.RenderedTitle)
|
||||
hb.writeElementClose("h1")
|
||||
}
|
||||
a.renderPostMeta(hb, p, bc, "preview")
|
||||
if p.Content != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.write(string(a.postHtml(p, true)))
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
a.renderPostTax(hb, p, bc)
|
||||
}
|
||||
|
||||
// Render the HTML to show the list of post taxonomy values (tags, series, etc.)
|
||||
func (a *goBlog) renderPostTax(hb *htmlBuilder, p *post, b *configBlog) {
|
||||
if b == nil || p == nil {
|
||||
return
|
||||
}
|
||||
var hb htmlBuilder
|
||||
// Iterate over all taxonomies
|
||||
for _, tax := range b.Taxonomies {
|
||||
// Get all sorted taxonomy values for this post
|
||||
|
@ -85,27 +100,145 @@ func (a *goBlog) renderPostTax(p *post, b *configBlog) template.HTML {
|
|||
hb.writeElementClose("p")
|
||||
}
|
||||
}
|
||||
return hb.html()
|
||||
}
|
||||
|
||||
// Render the HTML for the post meta information.
|
||||
// typ can be "summary", "post" or "preview".
|
||||
func (a *goBlog) renderPostMeta(hb *htmlBuilder, p *post, b *configBlog, typ string) {
|
||||
if b == nil || p == nil || typ != "summary" && typ != "post" && typ != "preview" {
|
||||
return
|
||||
}
|
||||
if typ == "summary" || typ == "post" {
|
||||
hb.writeElementOpen("div", "class", "p")
|
||||
}
|
||||
// Published time
|
||||
if published := p.Published; published != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "publishedon"))
|
||||
hb.write(" ")
|
||||
hb.writeElementOpen("time", "class", "dt-published", "datetime", dateFormat(published, "2006-01-02T15:04:05Z07:00"))
|
||||
hb.writeEscaped(isoDateFormat(published))
|
||||
hb.writeElementClose("time")
|
||||
// Section
|
||||
if p.Section != "" {
|
||||
if section := b.Sections[p.Section]; section != nil {
|
||||
hb.write(" in ") // TODO: Replace with a proper translation
|
||||
hb.writeElementOpen("a", "href", b.getRelativePath(section.Name))
|
||||
hb.writeEscaped(a.renderMdTitle(section.Title))
|
||||
hb.writeElementClose("a")
|
||||
}
|
||||
}
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Updated time
|
||||
if updated := p.Updated; updated != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "updatedon"))
|
||||
hb.write(" ")
|
||||
hb.writeElementOpen("time", "class", "dt-updated", "datetime", dateFormat(updated, "2006-01-02T15:04:05Z07:00"))
|
||||
hb.writeEscaped(isoDateFormat(updated))
|
||||
hb.writeElementClose("time")
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// IndieWeb Meta
|
||||
// Reply ("u-in-reply-to")
|
||||
if replyLink := a.replyLink(p); replyLink != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "replyto"))
|
||||
hb.writeEscaped(": ")
|
||||
hb.writeElementOpen("a", "class", "u-in-reply-to", "rel", "noopener", "target", "_blank", "href", replyLink)
|
||||
if replyTitle := a.replyTitle(p); replyTitle != "" {
|
||||
hb.writeEscaped(replyTitle)
|
||||
} else {
|
||||
hb.writeEscaped(replyLink)
|
||||
}
|
||||
hb.writeElementClose("a")
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Like ("u-like-of")
|
||||
if likeLink := a.likeLink(p); likeLink != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "likeof"))
|
||||
hb.writeEscaped(": ")
|
||||
hb.writeElementOpen("a", "class", "u-like-of", "rel", "noopener", "target", "_blank", "href", likeLink)
|
||||
if likeTitle := a.likeTitle(p); likeTitle != "" {
|
||||
hb.writeEscaped(likeTitle)
|
||||
} else {
|
||||
hb.writeEscaped(likeLink)
|
||||
}
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Geo
|
||||
if geoURI := a.geoURI(p); geoURI != nil {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped("📍 ")
|
||||
hb.writeElementOpen("a", "class", "p-location h-geo", "target", "_blank", "rel", "nofollow noopener noreferrer", "href", geoOSMLink(geoURI))
|
||||
hb.writeElementOpen("span", "class", "p-name")
|
||||
hb.writeEscaped(a.geoTitle(geoURI, b.Lang))
|
||||
hb.writeElementClose("span")
|
||||
hb.writeElementOpen("data", "class", "p-longitude", "value", fmt.Sprintf("%f", geoURI.Longitude))
|
||||
hb.writeElementClose("data")
|
||||
hb.writeElementOpen("data", "class", "p-latitude", "value", fmt.Sprintf("%f", geoURI.Latitude))
|
||||
hb.writeElementClose("data")
|
||||
hb.writeElementClose("a")
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Post specific elements
|
||||
if typ == "post" {
|
||||
// Translations
|
||||
if translations := a.postTranslations(p); len(translations) > 0 {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "translations"))
|
||||
hb.writeEscaped(": ")
|
||||
for i, translation := range translations {
|
||||
if i > 0 {
|
||||
hb.writeEscaped(", ")
|
||||
}
|
||||
hb.writeElementOpen("a", "translate", "no", "href", translation.Path)
|
||||
hb.writeEscaped(translation.RenderedTitle)
|
||||
hb.writeElementClose("a")
|
||||
}
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Short link
|
||||
if shortLink := a.shortPostURL(p); shortLink != "" {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "shorturl"))
|
||||
hb.writeEscaped(" ")
|
||||
hb.writeElementOpen("a", "rel", "shortlink", "href", shortLink)
|
||||
hb.writeEscaped(shortLink)
|
||||
hb.writeElementClose("a")
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
// Status
|
||||
if p.Status != statusPublished {
|
||||
hb.writeElementOpen("div")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "status"))
|
||||
hb.writeEscaped(": ")
|
||||
hb.writeEscaped(string(p.Status))
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
}
|
||||
if typ == "summary" || typ == "post" {
|
||||
hb.writeElementClose("div")
|
||||
}
|
||||
}
|
||||
|
||||
// Render the HTML to show a warning for old posts
|
||||
func (a *goBlog) renderOldContentWarning(p *post, b *configBlog) template.HTML {
|
||||
func (a *goBlog) renderOldContentWarning(hb *htmlBuilder, p *post, b *configBlog) {
|
||||
if b == nil || p == nil || !p.Old() {
|
||||
return ""
|
||||
return
|
||||
}
|
||||
var hb htmlBuilder
|
||||
hb.writeElementOpen("strong", "class", "p border-top border-bottom")
|
||||
hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "oldcontent"))
|
||||
hb.writeElementClose("strong")
|
||||
return hb.html()
|
||||
}
|
||||
|
||||
// Render the HTML to show interactions
|
||||
func (a *goBlog) renderInteractions(b *configBlog, canonical string) template.HTML {
|
||||
func (a *goBlog) renderInteractions(hb *htmlBuilder, b *configBlog, canonical string) {
|
||||
if b == nil || canonical == "" {
|
||||
return ""
|
||||
return
|
||||
}
|
||||
var hb htmlBuilder
|
||||
// Start accordion
|
||||
hb.writeElementOpen("details", "class", "p", "id", "interactions")
|
||||
hb.writeElementOpen("summary")
|
||||
|
@ -165,16 +298,14 @@ func (a *goBlog) renderInteractions(b *configBlog, canonical string) template.HT
|
|||
hb.writeElementClose("form")
|
||||
// Finish accordion
|
||||
hb.writeElementClose("details")
|
||||
return hb.html()
|
||||
}
|
||||
|
||||
// Render HTML for author h-card
|
||||
func (a *goBlog) renderAuthor() template.HTML {
|
||||
func (a *goBlog) renderAuthor(hb *htmlBuilder) {
|
||||
user := a.cfg.User
|
||||
if user == nil {
|
||||
return ""
|
||||
return
|
||||
}
|
||||
var hb htmlBuilder
|
||||
hb.writeElementOpen("div", "class", "p-author h-card hide")
|
||||
if user.Picture != "" {
|
||||
hb.writeElementOpen("data", "class", "u-photo", "value", user.Picture)
|
||||
|
@ -186,15 +317,44 @@ func (a *goBlog) renderAuthor() template.HTML {
|
|||
hb.writeElementClose("a")
|
||||
}
|
||||
hb.writeElementClose("div")
|
||||
return hb.html()
|
||||
}
|
||||
|
||||
// Render HTML that includes the head meta tags for a post
|
||||
func (a *goBlog) renderPostHeadMeta(hb *htmlBuilder, p *post, canonical string) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
if canonical != "" {
|
||||
hb.writeElementOpen("meta", "property", "og:url", "content", canonical)
|
||||
hb.writeElementOpen("meta", "property", "twitter:url", "content", canonical)
|
||||
}
|
||||
if p.RenderedTitle != "" {
|
||||
hb.writeElementOpen("meta", "property", "og:title", "content", p.RenderedTitle)
|
||||
hb.writeElementOpen("meta", "property", "twitter:title", "content", p.RenderedTitle)
|
||||
}
|
||||
if summary := a.postSummary(p); summary != "" {
|
||||
hb.writeElementOpen("meta", "name", "description", "content", summary)
|
||||
hb.writeElementOpen("meta", "property", "og:description", "content", summary)
|
||||
hb.writeElementOpen("meta", "property", "twitter:description", "content", summary)
|
||||
}
|
||||
if p.Published != "" {
|
||||
hb.writeElementOpen("meta", "itemprop", "datePublished", "content", dateFormat(p.Published, "2006-01-02T15:04:05-07:00"))
|
||||
}
|
||||
if p.Updated != "" {
|
||||
hb.writeElementOpen("meta", "itemprop", "dateModified", "content", dateFormat(p.Updated, "2006-01-02T15:04:05-07:00"))
|
||||
}
|
||||
for _, img := range a.photoLinks(p) {
|
||||
hb.writeElementOpen("meta", "itemprop", "image", "content", img)
|
||||
hb.writeElementOpen("meta", "property", "og:image", "content", img)
|
||||
hb.writeElementOpen("meta", "property", "twitter:image", "content", img)
|
||||
}
|
||||
}
|
||||
|
||||
// Render HTML for TOR notice in the footer
|
||||
func (a *goBlog) renderTorNotice(b *configBlog, torUsed bool, torAddress string) template.HTML {
|
||||
func (a *goBlog) renderTorNotice(hb *htmlBuilder, b *configBlog, torUsed bool, torAddress string) {
|
||||
if !a.cfg.Server.Tor || b == nil || !torUsed && torAddress == "" {
|
||||
return ""
|
||||
return
|
||||
}
|
||||
var hb htmlBuilder
|
||||
if torUsed {
|
||||
hb.writeElementOpen("p", "id", "tor")
|
||||
hb.writeEscaped("🔐 ")
|
||||
|
@ -212,5 +372,4 @@ func (a *goBlog) renderTorNotice(b *configBlog, torUsed bool, torAddress string)
|
|||
hb.writeElementClose("a")
|
||||
hb.writeElementClose("p")
|
||||
}
|
||||
return hb.html()
|
||||
}
|
||||
|
|
24
ui_test.go
24
ui_test.go
|
@ -25,7 +25,9 @@ func Test_renderPostTax(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
res := app.renderPostTax(p, app.cfg.Blogs["default"])
|
||||
var hb htmlBuilder
|
||||
app.renderPostTax(&hb, p, app.cfg.Blogs["default"])
|
||||
res := hb.html()
|
||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -44,7 +46,9 @@ func Test_renderOldContentWarning(t *testing.T) {
|
|||
Published: "2018-01-01",
|
||||
}
|
||||
|
||||
res := app.renderOldContentWarning(p, app.cfg.Blogs["default"])
|
||||
var hb htmlBuilder
|
||||
app.renderOldContentWarning(&hb, p, app.cfg.Blogs["default"])
|
||||
res := hb.html()
|
||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -103,7 +107,9 @@ func Test_renderInteractions(t *testing.T) {
|
|||
err = app.db.approveWebmentionId(2)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := app.renderInteractions(app.cfg.Blogs["default"], "https://example.com/testpost1")
|
||||
var hb htmlBuilder
|
||||
app.renderInteractions(&hb, app.cfg.Blogs["default"], "https://example.com/testpost1")
|
||||
res := hb.html()
|
||||
_, err = goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -123,7 +129,9 @@ func Test_renderAuthor(t *testing.T) {
|
|||
_ = app.initDatabase(false)
|
||||
app.initComponents(false)
|
||||
|
||||
res := app.renderAuthor()
|
||||
var hb htmlBuilder
|
||||
app.renderAuthor(&hb)
|
||||
res := hb.html()
|
||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -140,13 +148,17 @@ func Test_renderTorNotice(t *testing.T) {
|
|||
|
||||
app.cfg.Server.Tor = true
|
||||
|
||||
res := app.renderTorNotice(app.cfg.Blogs["default"], true, "http://abc.onion:80/test")
|
||||
var hb htmlBuilder
|
||||
app.renderTorNotice(&hb, app.cfg.Blogs["default"], true, "http://abc.onion:80/test")
|
||||
res := hb.html()
|
||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, template.HTML("<p id=\"tor\">🔐 Connected via Tor.</p>"), res)
|
||||
|
||||
res = app.renderTorNotice(app.cfg.Blogs["default"], false, "http://abc.onion:80/test")
|
||||
hb.Reset()
|
||||
app.renderTorNotice(&hb, app.cfg.Blogs["default"], false, "http://abc.onion:80/test")
|
||||
res = hb.html()
|
||||
_, err = goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
Loading…
Reference in New Issue