mirror of https://github.com/jlelse/GoBlog
29 changed files with 460 additions and 229 deletions
@ -1,10 +0,0 @@
|
||||
{{ define "author" }} |
||||
{{ with .User }} |
||||
<div class="p-author h-card hide"> |
||||
{{ with .Picture }}<data class="u-photo" value="{{ . }}"></data>{{ end }} |
||||
{{ if .Name }} |
||||
<a href="{{ with .Link }}{{ . }}{{ else }}/{{ end }}" class="p-name u-url" rel="me">{{ .Name }}</a> |
||||
{{ end }} |
||||
</div> |
||||
{{ end }} |
||||
{{ end }} |
@ -1,10 +1,6 @@
|
||||
{{ define "editorpreview" }} |
||||
{{ with .Data.RenderedTitle }}<h1>{{ . }}</h1>{{ end }} |
||||
{{ include "summaryandpostmeta" . }} |
||||
{{ if .Data.Content }} |
||||
<div> |
||||
{{ content .Data true }} |
||||
</div> |
||||
{{ end }} |
||||
{{ include "posttax" . }} |
||||
{{ if .Data.Content }}<div>{{ content .Data true }}</div>{{ end }} |
||||
{{ posttax .Data .Blog }} |
||||
{{ end }} |
@ -1,16 +0,0 @@
|
||||
{{ define "footer" }} |
||||
<footer> |
||||
{{ with index .Blog.Menus "footer" }} |
||||
<nav>{{ range $i, $item := .Items }}{{ if ne $i 0 }} • {{ end }}<a href="{{ $item.Link }}">{{ mdtitle $item.Title }}</a>{{ end }} |
||||
</nav> |
||||
{{ end }} |
||||
<p translate="no">© {{ dateformat now "2006" }} {{ with .User.Name }}{{ . }}{{ else }}{{ mdtitle .Blog.Title }}{{ end }}</p> |
||||
{{ if .TorUsed }} |
||||
<p id="tor">🔐 {{ string .Blog.Lang "connectedviator" }}</p> |
||||
{{ else }} |
||||
{{ if .TorAddress }} |
||||
<p id="tor">🔓 <a href="{{ .TorAddress }}">{{ string .Blog.Lang "connectviator" }}</a> <a href="https://www.torproject.org/" target="_blank" rel="nofollow noopener noreferrer">{{ string .Blog.Lang "whatistor" }}</a></p> |
||||
{{ end }} |
||||
{{ end }} |
||||
</footer> |
||||
{{ end }} |
@ -1,25 +0,0 @@
|
||||
{{ define "header" }} |
||||
{{ with .Blog.Announcement }}{{ with .Text }} |
||||
<div id="announcement" data-nosnippet> |
||||
{{ md . }} |
||||
</div> |
||||
{{ end }}{{ end }} |
||||
<header> |
||||
<h1><a href="{{ .Blog.RelativePath "/" }}" rel="home" title="{{ mdtitle .Blog.Title }}" translate="no">{{ mdtitle .Blog.Title }}</a></h1> |
||||
{{ with .Blog.Description }}<p><i>{{ . }}</i></p>{{ end }} |
||||
<nav> |
||||
{{ with index .Blog.Menus "main" }} |
||||
{{ range $i, $item := .Items }}{{ if ne $i 0 }} • {{ end }}<a href="{{ $item.Link }}">{{ mdtitle $item.Title }}</a>{{ end }} |
||||
{{ end }} |
||||
</nav> |
||||
{{ if .LoggedIn }} |
||||
<nav> |
||||
<a href="{{ .Blog.RelativePath "/editor" }}">{{ string .Blog.Lang "editor" }}</a> |
||||
• <a href="/notifications">{{ string .Blog.Lang "notifications" }}</a> |
||||
{{ if .WebmentionReceivingEnabled }}• <a href="/webmention">{{ string .Blog.Lang "webmentions" }}</a>{{ end }} |
||||
{{ if .CommentsEnabled }}• <a href="{{ .Blog.RelativePath "/comment" }}">{{ string .Blog.Lang "comments" }}</a>{{ end }} |
||||
• <a href="/logout">{{ string .Blog.Lang "logout" }}</a> |
||||
</nav> |
||||
{{ end }} |
||||
</header> |
||||
{{ end }} |
@ -1,22 +0,0 @@
|
||||
{{ define "interactions" }} |
||||
<details class="p" id="interactions"> |
||||
<summary><b>{{ string .Blog.Lang "interactions" }}</b></summary> |
||||
{{ $rd := . }} |
||||
{{ with ( mentions .Canonical ) }} |
||||
{{ include "mentions" $rd . }} |
||||
{{ end }} |
||||
<form class="fw p" method="post" action="/webmention"> |
||||
<label for="wm-source" class="p">{{ string .Blog.Lang "interactionslabel" }}</label> |
||||
<input id="wm-source" type="url" name="source" placeholder="URL" required> |
||||
<input type="hidden" name="target" value="{{ .Canonical }}"> |
||||
<input type="submit" value="{{ string .Blog.Lang "send" }}"> |
||||
</form> |
||||
<form class="fw p" method="post" action="{{ .Blog.RelativePath "/comment" }}"> |
||||
<input type="hidden" name="target" value="{{ .Canonical }}"> |
||||
<input type="text" name="name" placeholder="{{ string .Blog.Lang "nameopt" }}"> |
||||
<input type="url" name="website" placeholder="{{ string .Blog.Lang "websiteopt" }}"> |
||||
<textarea name="comment" required placeholder="{{ string .Blog.Lang "comment" }}"></textarea> |
||||
<input type="submit" value="{{ string .Blog.Lang "docomment" }}"> |
||||
</form> |
||||
</details> |
||||
{{ end }} |
@ -1,19 +0,0 @@
|
||||
{{ define "mentions" }} |
||||
{{ $rd := . }} |
||||
<ul> |
||||
{{ range $i, $mention := .Data }} |
||||
<li> |
||||
<a href="{{ $mention.Url }}" target="_blank" rel="nofollow noopener noreferrer ugc"> |
||||
{{ if $mention.Author }} |
||||
{{ $mention.Author }} |
||||
{{ else }} |
||||
{{ $mention.Url }} |
||||
{{ end }} |
||||
</a> |
||||
{{ with $mention.Title }} <b>{{.}}</b>{{ end }} |
||||
{{ with $mention.Content }} <i>{{.}}</i>{{ end }} |
||||
{{ with $mention.Submentions }}{{ include "mentions" $rd . }}{{ end }} |
||||
</li> |
||||
{{ end }} |
||||
</ul> |
||||
{{ end }} |
@ -1,5 +0,0 @@
|
||||
{{ define "oldcontentwarning" }} |
||||
{{ if .Data.Old }} |
||||
<strong class="p border-top border-bottom">{{ string .Blog.Lang "oldcontent" }}</strong> |
||||
{{ end }} |
||||
{{ end }} |
@ -1,18 +0,0 @@
|
||||
{{ define "postactions" }} |
||||
<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> |
||||
<script defer src="{{ asset "js/translate.js" }}"></script> |
||||
<button id="speakBtn" class="hide" data-speak="{{ string .Blog.Lang "speak" }}" data-stopspeak="{{ string .Blog.Lang "stopspeak" }}"></button> |
||||
{{ if .Data.TTS }} |
||||
<script defer src="{{ asset "js/tts.js" }}"></script> |
||||
{{ else }} |
||||
<script defer src="{{ asset "js/speak.js" }}"></script> |
||||
{{ end }} |
||||
</div> |
||||
{{ if .Data.TTS }} |
||||
<div class="p hide" id="tts"> |
||||
<audio controls preload=none id="tts-audio"><source src="{{ .Data.TTS }}"/></audio> |
||||
</div> |
||||
{{ end }} |
||||
{{ end }} |
@ -1,12 +0,0 @@
|
||||
{{ define "postmeta" }} |
||||
<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> |
||||
{{ end }} |
@ -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) }} |
||||
<p> |
||||
<b>{{ mdtitle $tax.Title }}</b>: |
||||
{{ range $j, $tv := $tvs }} |
||||
<a class="p-category" rel="tag" href="{{ $blog.RelativePath ( printf "/%s/%s" $tax.Name (urlize $tv) ) }}">{{ mdtitle $tv }}</a> |
||||
{{ end }} |
||||
</p> |
||||
{{ end }}{{ end }} |
||||
{{ end }} |
||||
{{ end }} |
@ -1,19 +0,0 @@
|
||||
{{ define "trackdetails" }} |
||||
{{ if .Data.HasTrack }} |
||||
{{ $track := (gettrack .Data) }} |
||||
{{ if $track }} |
||||
{{ if $track.HasPoints }} |
||||
{{ $lang := .Blog.Lang }} |
||||
<p>{{ with $track.Name }}<b>{{ . }}</b> {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}</p> |
||||
<div class="p" id="map" |
||||
data-paths="{{ $track.PathsJSON }}" |
||||
data-points="{{ $track.PointsJSON }}" |
||||
data-minzoom={{ $track.MinZoom }} |
||||
data-maxzoom={{ $track.MaxZoom }} |
||||
data-attribution="{{ $track.MapAttribution }}" |
||||
></div> |
||||
<script defer src="{{ asset "js/geotrack.js" }}"></script> |
||||
{{ end }} |
||||
{{ end }} |
||||
{{ end }} |
||||
{{ end }} |
@ -1,6 +0,0 @@
|
||||
{{ define "trackheader" }} |
||||
{{ if .Data.HasTrack }} |
||||
<link rel="stylesheet" href="/-/leaflet/leaflet.css"/> |
||||
<script src="/-/leaflet/leaflet.js"></script> |
||||
{{ end }} |
||||
{{ end }} |
@ -0,0 +1 @@
|
||||
<details class="p" id="interactions"><summary><strong>Interactions & Comments</strong></summary><ul><li><a href="https://example.com/testpost2" target="_blank" rel="nofollow noopener noreferrer ugc">https://example.com/testpost2</a> <strong>Test-Title</strong> <i>Test</i><ul><li><a href="https://example.com/testpost3" target="_blank" rel="nofollow noopener noreferrer ugc">https://example.com/testpost3</a> <strong>Test-Title</strong> <i>Test</i></li></ul></li></ul><form class="fw p" method="post" action="/webmention"><label for="wm-source" class="p">Have you published a response to this? Paste the URL here.</label><input id="wm-source" type="url" name="source" placeholder="URL" required=""><input type="hidden" name="target" value="https://example.com/testpost1"><input type="submit" value="Send (to review)"></form><form class="fw p" method="post" action="/comment"><input type="hidden" name="target" value="https://example.com/testpost1"><input type="text" name="name" placeholder="Name (optional)"><input type="url" name="website" placeholder="Website (optional)"><textarea name="comment" required="" placeholder="Comment"></textarea><input type="submit" value="Comment"></form></details> |
@ -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(`</`) |
||||
h.write(tag) |
||||
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() |
||||
} |
Loading…
Reference in new issue