From fa82364b704fd270e0301d5f056752d0332206d4 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Sun, 9 Jan 2022 21:08:38 +0100 Subject: [PATCH] Remove useless template includes and start rendering parts of the HTML directly with Go instead of templates --- go.mod | 2 +- go.sum | 4 +- render.go | 8 +- templateAssets.go | 10 +- templates/author.gohtml | 10 -- templates/base.gohtml | 38 +++-- templates/blogroll.gohtml | 4 +- templates/blogstats.gohtml | 4 +- templates/comment.gohtml | 4 +- templates/editorpreview.gohtml | 8 +- templates/footer.gohtml | 16 --- templates/geomap.gohtml | 4 +- templates/header.gohtml | 25 ---- templates/index.gohtml | 2 +- templates/interactions.gohtml | 22 --- templates/login.gohtml | 2 +- templates/mentions.gohtml | 19 --- templates/oldcontentwarning.gohtml | 5 - templates/post.gohtml | 49 +++++-- templates/postactions.gohtml | 18 --- templates/postmeta.gohtml | 12 -- templates/posttax.gohtml | 15 -- templates/statichome.gohtml | 8 +- templates/trackdetails.gohtml | 19 --- templates/trackheader.gohtml | 6 - testdata/interactionstest.html | 1 + tts.go | 4 +- ui.go | 216 +++++++++++++++++++++++++++++ ui_test.go | 154 ++++++++++++++++++++ 29 files changed, 460 insertions(+), 229 deletions(-) delete mode 100644 templates/author.gohtml delete mode 100644 templates/footer.gohtml delete mode 100644 templates/header.gohtml delete mode 100644 templates/interactions.gohtml delete mode 100644 templates/mentions.gohtml delete mode 100644 templates/oldcontentwarning.gohtml delete mode 100644 templates/postactions.gohtml delete mode 100644 templates/postmeta.gohtml delete mode 100644 templates/posttax.gohtml delete mode 100644 templates/trackdetails.gohtml delete mode 100644 templates/trackheader.gohtml create mode 100644 testdata/interactionstest.html create mode 100644 ui.go create mode 100644 ui_test.go diff --git a/go.mod b/go.mod index 2dcc387..3a67bc3 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/net v0.0.0-20211216030914-fe4d6282115f + golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b diff --git a/go.sum b/go.sum index ff2a31d..72c1b64 100644 --- a/go.sum +++ b/go.sum @@ -533,8 +533,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d h1:62NvYBuaanGXR2ZOfwDFkhhl6X1DUgf8qg3GuQvxZsE= +golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/render.go b/render.go index 3a1b384..77a42c8 100644 --- a/render.go +++ b/render.go @@ -63,6 +63,12 @@ func (a *goBlog) initRendering() error { "liketitle": a.likeTitle, "photolinks": a.photoLinks, "gettrack": a.getTrack, + // Code based rendering + "posttax": a.renderPostTax, + "oldcontentwarning": a.renderOldContentWarning, + "interactions": a.renderInteractions, + "author": a.renderAuthor, + "tor": a.renderTorNotice, // Others "dateformat": dateFormat, "isodate": isoDateFormat, @@ -72,9 +78,7 @@ func (a *goBlog) initRendering() error { "string": a.ts.GetTemplateStringVariantFunc(), "include": a.includeRenderedTemplate, "urlize": urlize, - "sort": sortedStrings, "absolute": a.getFullAddress, - "mentions": a.db.getWebmentionsByAddress, "geotitle": a.geoTitle, "geolink": geoOSMLink, "opensearch": openSearchUrl, diff --git a/templateAssets.go b/templateAssets.go index 1bb272e..dba9efc 100644 --- a/templateAssets.go +++ b/templateAssets.go @@ -21,10 +21,10 @@ type assetFile struct { body []byte } -func (a *goBlog) initTemplateAssets() (err error) { +func (a *goBlog) initTemplateAssets() error { a.assetFileNames = map[string]string{} a.assetFiles = map[string]*assetFile{} - err = filepath.Walk(assetsFolder, func(path string, info os.FileInfo, err error) error { + if err := filepath.Walk(assetsFolder, func(path string, info os.FileInfo, err error) error { if info.Mode().IsRegular() { // Open file file, err := os.Open(path) @@ -43,13 +43,11 @@ func (a *goBlog) initTemplateAssets() (err error) { } } return nil - }) - if err != nil { + }); err != nil { return err } // Add syntax highlighting CSS - err = a.initChromaCSS() - if err != nil { + if err := a.initChromaCSS(); err != nil { return err } return nil diff --git a/templates/author.gohtml b/templates/author.gohtml deleted file mode 100644 index 722de35..0000000 --- a/templates/author.gohtml +++ /dev/null @@ -1,10 +0,0 @@ -{{ define "author" }} - {{ with .User }} -
- {{ with .Picture }}{{ end }} - {{ if .Name }} - {{ .Name }} - {{ end }} -
- {{ end }} -{{ end }} \ No newline at end of file diff --git a/templates/base.gohtml b/templates/base.gohtml index 86cbf19..acfd63a 100644 --- a/templates/base.gohtml +++ b/templates/base.gohtml @@ -5,9 +5,7 @@ {{ with .Canonical }}{{ end }} - {{ block "title" . }} - {{ mdtitle .Blog.Title }} - {{ end }} + {{ block "title" . }}{{ mdtitle .Blog.Title }}{{ end }} @@ -17,14 +15,32 @@ {{ with .User }}{{ range .Identities }}{{ end }}{{ end }} {{ $os := opensearch .Blog }} - {{ if $os }} - - {{ end }} - {{ include "header" . }} + {{ if $os }}{{ end }} + {{ with .Blog.Announcement }}{{ with .Text }}
{{ md . }}
{{ end }}{{ end }} +
+

{{ mdtitle .Blog.Title }}

+ {{ with .Blog.Description }}

{{ . }}

{{ end }} + {{ with index .Blog.Menus "main" }} + + {{ end }} + {{ if .LoggedIn }} + + {{ end }} +
{{ block "main" . }}{{ end }} - {{ include "footer" . }} - {{ if .EasterEgg }} - - {{ end }} + + {{ if .EasterEgg }}{{ end }} {{ end }} \ No newline at end of file diff --git a/templates/blogroll.gohtml b/templates/blogroll.gohtml index 0facfd8..427ac6a 100644 --- a/templates/blogroll.gohtml +++ b/templates/blogroll.gohtml @@ -21,9 +21,7 @@ {{ end }} - {{ if .CommentsEnabled }} - {{ include "interactions" . }} - {{ end }} + {{ if .CommentsEnabled }}{{ interactions .Blog .Canonical }}{{ end }} {{ end }} {{ define "blogroll" }} diff --git a/templates/blogstats.gohtml b/templates/blogstats.gohtml index ed1f0ef..b049e59 100644 --- a/templates/blogstats.gohtml +++ b/templates/blogstats.gohtml @@ -9,9 +9,7 @@

{{ string .Blog.Lang "loading" }}

- {{ if .CommentsEnabled }} - {{ include "interactions" . }} - {{ end }} + {{ if .CommentsEnabled }}{{ interactions .Blog .Canonical }}{{ end }} {{ end }} {{ define "blogstats" }} diff --git a/templates/comment.gohtml b/templates/comment.gohtml index 38a1fb1..a05041d 100644 --- a/templates/comment.gohtml +++ b/templates/comment.gohtml @@ -13,9 +13,7 @@ {{ html .Data.Comment }}

- {{ if .CommentsEnabled }} - {{ include "interactions" . }} - {{ end }} + {{ if .CommentsEnabled }}{{ interactions .Blog .Canonical }}{{ end }} {{ end }} {{ define "comment" }} diff --git a/templates/editorpreview.gohtml b/templates/editorpreview.gohtml index 41c90aa..b5689f8 100644 --- a/templates/editorpreview.gohtml +++ b/templates/editorpreview.gohtml @@ -1,10 +1,6 @@ {{ define "editorpreview" }} {{ with .Data.RenderedTitle }}

{{ . }}

{{ end }} {{ include "summaryandpostmeta" . }} - {{ if .Data.Content }} -
- {{ content .Data true }} -
- {{ end }} - {{ include "posttax" . }} + {{ if .Data.Content }}
{{ content .Data true }}
{{ end }} + {{ posttax .Data .Blog }} {{ end }} \ No newline at end of file diff --git a/templates/footer.gohtml b/templates/footer.gohtml deleted file mode 100644 index 6fb2b80..0000000 --- a/templates/footer.gohtml +++ /dev/null @@ -1,16 +0,0 @@ -{{ define "footer" }} - -{{ end }} \ No newline at end of file diff --git a/templates/geomap.gohtml b/templates/geomap.gohtml index 307740e..ac9a4c0 100644 --- a/templates/geomap.gohtml +++ b/templates/geomap.gohtml @@ -21,9 +21,7 @@ {{ end }} - {{ if .CommentsEnabled }} - {{ include "interactions" . }} - {{ end }} + {{ if .CommentsEnabled }}{{ interactions .Blog .Canonical }}{{ end }} {{ end }} {{ define "geomap" }} diff --git a/templates/header.gohtml b/templates/header.gohtml deleted file mode 100644 index a95b46a..0000000 --- a/templates/header.gohtml +++ /dev/null @@ -1,25 +0,0 @@ -{{ define "header" }} - {{ with .Blog.Announcement }}{{ with .Text }} -
- {{ md . }} -
- {{ end }}{{ end }} -
-

{{ mdtitle .Blog.Title }}

- {{ with .Blog.Description }}

{{ . }}

{{ end }} - - {{ if .LoggedIn }} - - {{ end }} -
-{{ end }} \ No newline at end of file diff --git a/templates/index.gohtml b/templates/index.gohtml index acc2cd3..576bf89 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -27,7 +27,7 @@ {{ if .Data.HasNext }}

{{ string .Blog.Lang "next" }}

{{ end }} - {{ include "author" . }} + {{ author }} {{ end }} diff --git a/templates/interactions.gohtml b/templates/interactions.gohtml deleted file mode 100644 index bf4a0bb..0000000 --- a/templates/interactions.gohtml +++ /dev/null @@ -1,22 +0,0 @@ -{{ define "interactions" }} -
- {{ string .Blog.Lang "interactions" }} - {{ $rd := . }} - {{ with ( mentions .Canonical ) }} - {{ include "mentions" $rd . }} - {{ end }} -
- - - - -
-
- - - - - -
-
-{{ end }} \ No newline at end of file diff --git a/templates/login.gohtml b/templates/login.gohtml index 478b672..4f7e776 100644 --- a/templates/login.gohtml +++ b/templates/login.gohtml @@ -17,7 +17,7 @@ {{ end }} - {{ include "author" . }} + {{ author }} {{ end }} diff --git a/templates/mentions.gohtml b/templates/mentions.gohtml deleted file mode 100644 index 75a469b..0000000 --- a/templates/mentions.gohtml +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "mentions" }} - {{ $rd := . }} - -{{ end }} \ No newline at end of file diff --git a/templates/oldcontentwarning.gohtml b/templates/oldcontentwarning.gohtml deleted file mode 100644 index 8ea02cb..0000000 --- a/templates/oldcontentwarning.gohtml +++ /dev/null @@ -1,5 +0,0 @@ -{{ define "oldcontentwarning" }} - {{ if .Data.Old }} - {{ string .Blog.Lang "oldcontent" }} - {{ end }} -{{ end }} \ No newline at end of file diff --git a/templates/post.gohtml b/templates/post.gohtml index bdcddb0..09dd931 100644 --- a/templates/post.gohtml +++ b/templates/post.gohtml @@ -3,7 +3,10 @@ {{ with .Data.RenderedTitle }}{{ . }} - {{end}}{{ mdtitle .Blog.Title }} {{ include "postheadmeta" . }} {{ with shorturl .Data }}{{ end }} - {{ include "trackheader" . }} + {{ if .Data.HasTrack }} + + + {{ end }} {{ end }} {{ define "main" }} @@ -11,18 +14,40 @@
{{ with .Data.RenderedTitle }}

{{ . }}

{{ end }} - {{ include "postmeta" . }} - {{ include "postactions" . }} +
+ {{ include "summaryandpostmeta" . }} + {{ $translations := (translations .Data) }} + {{ if gt (len $translations) 0 }} +
{{ string .Blog.Lang "translations" }}: {{ $delimiter := "" }}{{ range $i, $t := $translations }}{{ $delimiter }}{{ $t.RenderedTitle }}{{ $delimiter = ", " }}{{ end }}
+ {{ end }} + {{ $short := shorturl .Data }} + {{ if $short }}
{{ string .Blog.Lang "shorturl" }} {{ $short }}
{{ end }} + {{ if ne .Data.Status "published" }}
{{ string .Blog.Lang "status" }}: {{ .Data.Status }}
{{ end }} +
+
+ {{ string .Blog.Lang "share" }} + {{ string .Blog.Lang "translate" }} + + + +
+ {{ if .Data.TTS }}
{{ end }} {{ if .Data.Content }} - {{ include "oldcontentwarning" . }} -
- {{ content .Data false }} -
+ {{ oldcontentwarning .Data .Blog }} +
{{ content .Data false }}
{{ end }} - {{ include "trackdetails" . }} - {{ include "posttax" . }} + {{ if .Data.HasTrack }} + {{ $track := (gettrack .Data) }} + {{ if $track }}{{ if $track.HasPoints }} + {{ $lang := .Blog.Lang }} +

{{ with $track.Name }}{{ . }} {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}

+
+ + {{ end }}{{ end }} + {{ end }} + {{ posttax .Data .Blog }}
- {{ include "author" . }} + {{ author }} {{ if .LoggedIn }}
@@ -53,9 +78,7 @@
{{ end }} - {{ if .CommentsEnabled }} - {{ include "interactions" . }} - {{ end }} + {{ if .CommentsEnabled }}{{ interactions .Blog .Canonical }}{{ end }} {{ end }} {{ define "post" }} diff --git a/templates/postactions.gohtml b/templates/postactions.gohtml deleted file mode 100644 index 0f97a85..0000000 --- a/templates/postactions.gohtml +++ /dev/null @@ -1,18 +0,0 @@ -{{ define "postactions" }} -
- {{ string .Blog.Lang "share" }} - {{ string .Blog.Lang "translate" }} - - - {{ if .Data.TTS }} - - {{ else }} - - {{ end }} -
-{{ if .Data.TTS }} -
- -
-{{ end }} -{{ end }} \ No newline at end of file diff --git a/templates/postmeta.gohtml b/templates/postmeta.gohtml deleted file mode 100644 index 8817205..0000000 --- a/templates/postmeta.gohtml +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "postmeta" }} -
- {{ include "summaryandpostmeta" . }} - {{ $translations := (translations .Data) }} - {{ if gt (len $translations) 0 }} -
{{ string .Blog.Lang "translations" }}: {{ $delimiter := "" }}{{ range $i, $t := $translations }}{{ $delimiter }}{{ $t.RenderedTitle }}{{ $delimiter = ", " }}{{ end }}
- {{ end }} - {{ $short := shorturl .Data }} - {{ if $short }}
{{ string .Blog.Lang "shorturl" }} {{ $short }}
{{ end }} - {{ if ne .Data.Status "published" }}
{{ string .Blog.Lang "status" }}: {{ .Data.Status }}
{{ end }} -
-{{ end }} \ No newline at end of file diff --git a/templates/posttax.gohtml b/templates/posttax.gohtml deleted file mode 100644 index 8cb2544..0000000 --- a/templates/posttax.gohtml +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "posttax" }} - {{ $post := .Data }} - {{ $blog := .Blog }} - {{ range $i, $tax := $blog.Taxonomies }} - {{ $tvs := sort (ps $post $tax.Name) }} - {{ if $tvs }}{{ if not (eq (len $tvs) 0) }} -

- {{ mdtitle $tax.Title }}: - {{ range $j, $tv := $tvs }} - - {{ end }} -

- {{ end }}{{ end }} - {{ end }} -{{ end }} \ No newline at end of file diff --git a/templates/statichome.gohtml b/templates/statichome.gohtml index 7a93cc0..064152b 100644 --- a/templates/statichome.gohtml +++ b/templates/statichome.gohtml @@ -7,13 +7,9 @@
- {{ if .Data.Content }} -
- {{ content .Data false }} -
- {{ end }} + {{ if .Data.Content }}
{{ content .Data false }}
{{ end }}
- {{ include "author" . }} + {{ author }}
{{ if .LoggedIn }}
diff --git a/templates/trackdetails.gohtml b/templates/trackdetails.gohtml deleted file mode 100644 index a879409..0000000 --- a/templates/trackdetails.gohtml +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "trackdetails" }} - {{ if .Data.HasTrack }} - {{ $track := (gettrack .Data) }} - {{ if $track }} - {{ if $track.HasPoints }} - {{ $lang := .Blog.Lang }} -

{{ with $track.Name }}{{ . }} {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}

-
- - {{ end }} - {{ end }} - {{ end }} -{{ end }} \ No newline at end of file diff --git a/templates/trackheader.gohtml b/templates/trackheader.gohtml deleted file mode 100644 index 8aac7b0..0000000 --- a/templates/trackheader.gohtml +++ /dev/null @@ -1,6 +0,0 @@ -{{ define "trackheader" }} - {{ if .Data.HasTrack }} - - - {{ end }} -{{ end }} \ No newline at end of file diff --git a/testdata/interactionstest.html b/testdata/interactionstest.html new file mode 100644 index 0000000..3f80fed --- /dev/null +++ b/testdata/interactionstest.html @@ -0,0 +1 @@ +
Interactions & Comments
\ No newline at end of file diff --git a/tts.go b/tts.go index 55d9dc7..23889bb 100644 --- a/tts.go +++ b/tts.go @@ -106,7 +106,9 @@ func (a *goBlog) createPostTTSAudio(p *post) error { // Merge partsBuffers into final buffer var final bytes.Buffer - mp3merge.MergeMP3(&final, partsBuffers...) + if err := mp3merge.MergeMP3(&final, partsBuffers...); err != nil { + return err + } // Save audio audioReader := bytes.NewReader(final.Bytes()) diff --git a/ui.go b/ui.go new file mode 100644 index 0000000..f4e6d74 --- /dev/null +++ b/ui.go @@ -0,0 +1,216 @@ +package main + +import ( + "fmt" + "html/template" + "strings" +) + +// This file includes some functions that render parts of the HTML + +type htmlBuilder struct { + strings.Builder +} + +func (h *htmlBuilder) write(s string) { + _, _ = h.WriteString(s) +} + +func (h *htmlBuilder) writeEscaped(s string) { + if len(s) == 0 { + return + } + template.HTMLEscape(h, []byte(s)) +} + +func (h *htmlBuilder) writeAttribute(attr, val string) { + h.write(` `) + h.write(attr) + h.write(`="`) + h.writeEscaped(val) + h.write(`"`) +} + +func (h *htmlBuilder) writeElementOpen(tag string, attrs ...string) { + h.write(`<`) + h.write(tag) + for i := 0; i < len(attrs); i += 2 { + h.writeAttribute(attrs[i], attrs[i+1]) + } + h.write(`>`) +} + +func (h *htmlBuilder) writeElementClose(tag string) { + h.write(``) +} + +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 "" + } + var hb htmlBuilder + // Iterate over all taxonomies + for _, tax := range b.Taxonomies { + // Get all sorted taxonomy values for this post + if taxValues := sortedStrings(p.Parameters[tax.Name]); len(taxValues) > 0 { + // Start new paragraph + hb.writeElementOpen("p") + // Add taxonomy name + hb.writeElementOpen("strong") + hb.writeEscaped(a.renderMdTitle(tax.Title)) + hb.writeElementClose("strong") + hb.write(": ") + // Add taxonomy values + for i, taxValue := range taxValues { + if i > 0 { + hb.write(", ") + } + hb.writeElementOpen( + "a", + "class", "p-category", + "rel", "tag", + "href", b.getRelativePath(fmt.Sprintf("/%s/%s", tax.Name, urlize(taxValue))), + ) + hb.writeEscaped(a.renderMdTitle(taxValue)) + hb.writeElementClose("a") + } + // End paragraph + hb.writeElementClose("p") + } + } + return hb.html() +} + +// Render the HTML to show a warning for old posts +func (a *goBlog) renderOldContentWarning(p *post, b *configBlog) template.HTML { + if b == nil || p == nil || !p.Old() { + 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 { + if b == nil || canonical == "" { + return "" + } + var hb htmlBuilder + // Start accordion + hb.writeElementOpen("details", "class", "p", "id", "interactions") + hb.writeElementOpen("summary") + hb.writeElementOpen("strong") + hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "interactions")) + hb.writeElementClose("strong") + hb.writeElementClose("summary") + // Render mentions + var renderMentions func(m []*mention) + renderMentions = func(m []*mention) { + if len(m) == 0 { + return + } + hb.writeElementOpen("ul") + for _, mention := range m { + hb.writeElementOpen("li") + hb.writeElementOpen("a", "href", mention.Url, "target", "_blank", "rel", "nofollow noopener noreferrer ugc") + hb.writeEscaped(defaultIfEmpty(mention.Author, mention.Url)) + hb.writeElementClose("a") + if mention.Title != "" { + hb.write(" ") + hb.writeElementOpen("strong") + hb.writeEscaped(mention.Title) + hb.writeElementClose("strong") + } + if mention.Content != "" { + hb.write(" ") + hb.writeElementOpen("i") + hb.writeEscaped(mention.Content) + hb.writeElementClose("i") + } + if len(mention.Submentions) > 0 { + renderMentions(mention.Submentions) + } + hb.writeElementClose("li") + } + hb.writeElementClose("ul") + } + renderMentions(a.db.getWebmentionsByAddress(canonical)) + // Show form to send a webmention + hb.writeElementOpen("form", "class", "fw p", "method", "post", "action", "/webmention") + hb.writeElementOpen("label", "for", "wm-source", "class", "p") + hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "interactionslabel")) + hb.writeElementClose("label") + hb.writeElementOpen("input", "id", "wm-source", "type", "url", "name", "source", "placeholder", "URL", "required", "") + hb.writeElementOpen("input", "type", "hidden", "name", "target", "value", canonical) + hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(b.Lang, "send")) + hb.writeElementClose("form") + // Show form to create a new comment + hb.writeElementOpen("form", "class", "fw p", "method", "post", "action", "/comment") + hb.writeElementOpen("input", "type", "hidden", "name", "target", "value", canonical) + hb.writeElementOpen("input", "type", "text", "name", "name", "placeholder", a.ts.GetTemplateStringVariant(b.Lang, "nameopt")) + hb.writeElementOpen("input", "type", "url", "name", "website", "placeholder", a.ts.GetTemplateStringVariant(b.Lang, "websiteopt")) + hb.writeElementOpen("textarea", "name", "comment", "required", "", "placeholder", a.ts.GetTemplateStringVariant(b.Lang, "comment")) + hb.writeElementClose("textarea") + hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(b.Lang, "docomment")) + hb.writeElementClose("form") + // Finish accordion + hb.writeElementClose("details") + return hb.html() +} + +// Render HTML for author h-card +func (a *goBlog) renderAuthor() template.HTML { + user := a.cfg.User + if user == nil { + 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) + hb.writeElementClose("data") + } + if user.Name != "" { + hb.writeElementOpen("a", "class", "p-name u-url", "rel", "me", "href", defaultIfEmpty(user.Link, "/")) + hb.writeEscaped(user.Name) + hb.writeElementClose("a") + } + hb.writeElementClose("div") + return hb.html() +} + +// Render HTML for TOR notice in the footer +func (a *goBlog) renderTorNotice(b *configBlog, torUsed bool, torAddress string) template.HTML { + if !a.cfg.Server.Tor || b == nil || !torUsed && torAddress == "" { + return "" + } + var hb htmlBuilder + if torUsed { + hb.writeElementOpen("p", "id", "tor") + hb.writeEscaped("🔐 ") + hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "connectedviator")) + hb.writeElementClose("p") + } else if torAddress != "" { + hb.writeElementOpen("p", "id", "tor") + hb.writeEscaped("🔓 ") + hb.writeElementOpen("a", "href", torAddress) + hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "connectviator")) + hb.writeElementClose("a") + hb.writeEscaped(" ") + hb.writeElementOpen("a", "href", "https://www.torproject.org/", "target", "_blank", "rel", "nofollow noopener noreferrer") + hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "whatistor")) + hb.writeElementClose("a") + hb.writeElementClose("p") + } + return hb.html() +} diff --git a/ui_test.go b/ui_test.go new file mode 100644 index 0000000..b7d8993 --- /dev/null +++ b/ui_test.go @@ -0,0 +1,154 @@ +package main + +import ( + "html/template" + "os" + "strings" + "testing" + + "github.com/PuerkitoBio/goquery" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_renderPostTax(t *testing.T) { + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + _ = app.initConfig() + _ = app.initDatabase(false) + app.initComponents(false) + + p := &post{ + Parameters: map[string][]string{ + "tags": {"Foo", "Bar"}, + }, + } + + res := app.renderPostTax(p, app.cfg.Blogs["default"]) + _, err := goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + assert.Equal(t, template.HTML("

Tags: Bar, Foo

"), res) +} + +func Test_renderOldContentWarning(t *testing.T) { + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + _ = app.initConfig() + _ = app.initDatabase(false) + app.initComponents(false) + + p := &post{ + Published: "2018-01-01", + } + + res := app.renderOldContentWarning(p, app.cfg.Blogs["default"]) + _, err := goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + assert.Equal(t, template.HTML("⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed."), res) +} + +func Test_renderInteractions(t *testing.T) { + var err error + + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + app.cfg.Server.PublicAddress = "https://example.com" + _ = app.initConfig() + _ = app.initDatabase(false) + app.initComponents(false) + app.d, err = app.buildRouter() + require.NoError(t, err) + + err = app.createPost(&post{ + Path: "/testpost1", + }) + require.NoError(t, err) + + err = app.createPost(&post{ + Path: "/testpost2", + Content: "[Test](/testpost1)", + Parameters: map[string][]string{ + "title": {"Test-Title"}, + }, + }) + require.NoError(t, err) + + err = app.verifyMention(&mention{ + Source: "https://example.com/testpost2", + Target: "https://example.com/testpost1", + }) + require.NoError(t, err) + err = app.db.approveWebmentionId(1) + require.NoError(t, err) + + err = app.createPost(&post{ + Path: "/testpost3", + Content: "[Test](/testpost2)", + Parameters: map[string][]string{ + "title": {"Test-Title"}, + }, + }) + require.NoError(t, err) + + err = app.verifyMention(&mention{ + Source: "https://example.com/testpost3", + Target: "https://example.com/testpost2", + }) + require.NoError(t, err) + err = app.db.approveWebmentionId(2) + require.NoError(t, err) + + res := app.renderInteractions(app.cfg.Blogs["default"], "https://example.com/testpost1") + _, err = goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + expected, err := os.ReadFile("testdata/interactionstest.html") + require.NoError(t, err) + + assert.Equal(t, template.HTML(expected), res) +} + +func Test_renderAuthor(t *testing.T) { + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + app.cfg.User.Picture = "https://example.com/picture.jpg" + app.cfg.User.Name = "John Doe" + _ = app.initConfig() + _ = app.initDatabase(false) + app.initComponents(false) + + res := app.renderAuthor() + _, err := goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + assert.Equal(t, template.HTML(""), res) +} + +func Test_renderTorNotice(t *testing.T) { + app := &goBlog{ + cfg: createDefaultTestConfig(t), + } + _ = app.initConfig() + _ = app.initDatabase(false) + app.initComponents(false) + + app.cfg.Server.Tor = true + + res := app.renderTorNotice(app.cfg.Blogs["default"], true, "http://abc.onion:80/test") + _, err := goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + assert.Equal(t, template.HTML("

🔐 Connected via Tor.

"), res) + + res = app.renderTorNotice(app.cfg.Blogs["default"], false, "http://abc.onion:80/test") + _, err = goquery.NewDocumentFromReader(strings.NewReader(string(res))) + require.NoError(t, err) + + assert.Equal(t, template.HTML("

🔓 Connect via Tor. What is Tor?

"), res) +}