diff options
Diffstat (limited to 'srv/src/http/tpl')
-rw-r--r-- | srv/src/http/tpl/assets.html | 51 | ||||
-rw-r--r-- | srv/src/http/tpl/base.html | 66 | ||||
-rw-r--r-- | srv/src/http/tpl/edit-post.html | 101 | ||||
-rw-r--r-- | srv/src/http/tpl/follow.html | 152 | ||||
-rw-r--r-- | srv/src/http/tpl/index.html | 36 | ||||
-rw-r--r-- | srv/src/http/tpl/post.html | 48 | ||||
-rw-r--r-- | srv/src/http/tpl/posts.html | 61 | ||||
-rw-r--r-- | srv/src/http/tpl/redirect.html | 9 |
8 files changed, 524 insertions, 0 deletions
diff --git a/srv/src/http/tpl/assets.html b/srv/src/http/tpl/assets.html new file mode 100644 index 0000000..aa5e422 --- /dev/null +++ b/srv/src/http/tpl/assets.html @@ -0,0 +1,51 @@ +{{ define "body" }} + +{{ $csrfFormInput := .CSRFFormInput }} + +<h2>Upload Asset</h2> + +<p> + If the given ID is the same as an existing asset's ID, then that asset will be + overwritten. +</p> + +<form action="{{ BlogURL "assets/" }}" method="POST" enctype="multipart/form-data"> + {{ $csrfFormInput }} + <div class="row"> + <div class="four columns"> + <input type="text" placeholder="Unique ID" name="id" /> + </div> + <div class="four columns"> + <input type="file" name="file" /><br/> + </div> + <div class="four columns"> + <input type="submit" value="Upload" /> + </div> + </div> +</form> + +<h2>Existing Assets</h2> + +<table> + + {{ range .Payload.IDs }} + <tr> + <td><a href="{{ AssetURL . }}" target="_blank">{{ . }}</a></td> + <td> + <form + action="{{ AssetURL . }}?method=delete" + method="POST" + style="margin-bottom: 0;" + > + {{ $csrfFormInput }} + <input type="submit" value="Delete" /> + </form> + </td> + </tr> + {{ end }} + +</table> + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/base.html b/srv/src/http/tpl/base.html new file mode 100644 index 0000000..6031919 --- /dev/null +++ b/srv/src/http/tpl/base.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html lang="en"> + + <head> + <meta charset="utf-8"> + <link rel="stylesheet" href="/assets/normalize.css"> + <link rel="stylesheet" href="/assets/skeleton.css"> + <link rel="stylesheet" href="/assets/friendly.css"> + <link rel="stylesheet" href="/assets/main.css"> + <link rel="stylesheet" href="/assets/fontawesome/css/all.css"> + </head> + + <body> + + <div class="container"> + + <header id="title-header" role="banner"> + <div class="row"> + <div class="seven columns" style="margin-bottom: 3rem;"> + <h1 class="title"> + <a href="{{ BlogURL "/" }}">Mediocre Blog</a> + </h1> + <div class="light social"> + <span>By Brian Picciano</span> + <span> + Even more @ + <a href="https://mediocregopher.eth.link" target="_blank">https://mediocregopher.eth.link</a> + </span> + </div> + </div> + + <div class="five columns light"> + <span style="display:block; margin-bottom:0.5rem;">Get notified when new posts are published!</span> + <a href="{{ BlogURL "follow.html" }}"> + <button class="button-primary"> + <i class="far fa-envelope"></i> + Follow + </button> + </a> + + <a href="{{ BlogURL "feed.xml" }}"> + <button class="button"> + <i class="fas fa-rss"></i> + RSS + </button> + </a> + </div> + + </div> + </header> + + {{ template "body" . }} + + <footer> + <p class="license light"> + Unless otherwised specified, all works are licensed under the + <a href="/assets/wtfpl.txt">WTFPL</a>. + </p> + </footer> + + </div> + + </body> + +</html> + diff --git a/srv/src/http/tpl/edit-post.html b/srv/src/http/tpl/edit-post.html new file mode 100644 index 0000000..9ccfa2a --- /dev/null +++ b/srv/src/http/tpl/edit-post.html @@ -0,0 +1,101 @@ +{{ define "body" }} + + <form method="POST" action="{{ BlogURL "posts/" }}"> + + {{ .CSRFFormInput }} + + <div class="row"> + + <div class="columns six"> + <label for="idInput">Unique ID</label> + {{ if eq .Payload.ID "" }} + <input + id="idInput" + name="id" + class="u-full-width" + type="text" + placeholder="e.g. how-to-fly-a-kite" + value="{{ .Payload.ID }}" /> + {{ else }} + <a href="{{ PostURL .Payload.ID }}" target="_blank">{{ .Payload.ID }}</a> + <input name="id" type="hidden" value="{{ .Payload.ID }}" /> + {{ end }} + </div> + + <div class="columns three"> + <label for="tagsInput">Tags (space separated)</label> + <input + id="tagsInput" + name="tags" + class="u-full-width" + type="text" + value="{{ range $i, $tag := .Payload.Tags }}{{ if ne $i 0 }} {{ end }}{{ $tag }}{{ end }}" /> + </div> + + <div class="columns three"> + <label for="seriesInput">Series</label> + <input + id="seriesInput" + name="series" + class="u-full-width" + type="text" + value="{{ .Payload.Series }}" /> + </div> + + </div> + + <div class="row"> + + <div class="columns six"> + <label for="titleInput">Title</label> + <input + id="titleInput" + name="title" + class="u-full-width" + type="text" + value="{{ .Payload.Title }}" /> + </div> + + <div class="columns six"> + <label for="descrInput">Description</label> + <input + id="descrInput" + name="description" + class="u-full-width" + type="text" + value="{{ .Payload.Description }}" /> + </div> + + </div> + + <div class="row"> + <div class="columns twelve"> + <textarea + name="body" + class="u-full-width" + placeholder="Blog body" + style="height: 50vh;" + > + {{- .Payload.Body -}} + </textarea> + </div> + </div> + + <input + type="submit" + value="Preview" + formaction="{{ BlogURL "posts/" }}{{ .Payload.ID }}?method=preview" + formtarget="_blank" + /> + + <input type="submit" value="Save" formaction="{{ BlogURL "posts/" }}" /> + + <a href="{{ BlogURL "posts/" }}"> + <button type="button">Cancel</button> + </a> + + </form> + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/follow.html b/srv/src/http/tpl/follow.html new file mode 100644 index 0000000..8cf9dc6 --- /dev/null +++ b/srv/src/http/tpl/follow.html @@ -0,0 +1,152 @@ +{{ define "body" }} + +<script async type="module" src="/assets/api.js"></script> + +<p> + Here's your options for receiving updates about new blog posts: +</p> + +<h2>Option 1: Email</h2> + +<p> + Email is by far my preferred option for notifying followers of new posts. +</p> + +<p> + The entire email list system for this blog, from storing subscriber email + addresses to the email server which sends the notifications out, has been + designed from scratch and is completely self-hosted in my living room. +</p> + +<p> + I solemnly swear that: +</p> + +<ul> + + <li> + You will never receive an email from this blog except to notify of a new + post. + </li> + + <li> + Your email will never be provided or sold to anyone else for any reason. + </li> + +</ul> + +<p> + With all that said, if you'd like to receive an email everytime a new blog + post is published then input your email below and smash that subscribe button! + You will need to verify your email, so be sure to check your spam folder to + complete the process if you don't immediately see anything in your inbox. +</p> + +<style> + +#emailStatus.success { + color: green; +} + +#emailStatus.fail { + color: red; +} + +</style> + +<input type="email" placeholder="name@host.com" id="emailAddress" /> +<input class="button-primary" type="submit" value="Subscribe" id="emailSubscribe" /> +<span id="emailStatus"></span> + +<script> + +const emailAddress = document.getElementById("emailAddress"); +const emailSubscribe = document.getElementById("emailSubscribe"); +const emailSubscribeOrigValue = emailSubscribe.value; +const emailStatus = document.getElementById("emailStatus"); + +emailSubscribe.onclick = async () => { + + const api = await import("/assets/api.js"); + + emailSubscribe.disabled = true; + emailSubscribe.className = ""; + emailSubscribe.value = "Please hold..."; + emailStatus.innerHTML = ''; + + try { + + if (!window.isSecureContext) { + throw "The browser environment is not secure."; + } + + await api.call('/api/mailinglist/subscribe', { + body: { email: emailAddress.value }, + requiresPow: true, + }); + + emailStatus.className = "success"; + emailStatus.innerHTML = "Verification email sent (check your spam folder)"; + + } catch (e) { + emailStatus.className = "fail"; + emailStatus.innerHTML = e; + + } finally { + emailSubscribe.disabled = false; + emailSubscribe.className = "button-primary"; + emailSubscribe.value = emailSubscribeOrigValue; + } + +}; + +</script> + +<h2>Option 2: RSS</h2> + +<p> + RSS is the classic way to follow any blog. It comes from a time before + aggregators like reddit and twitter stole the show, when people felt capable + to manage their own content feeds. We should use it again. +</p> + +<p> + To follow over RSS give any RSS reader the following URL... +</p> + +<p> + <a href="{{ BlogURL "feed.xml" }}">{{ BlogURL "feed.xml" }}</a> +</p> + +<p> + ...and posts from this blog will show up in your RSS feed as soon as they are + published. There are literally thousands of RSS readers out there. Here's some + recommendations: +</p> + +<ul> + <li> + <a href="https://chrome.google.com/webstore/detail/rss-feed-reader/pnjaodmkngahhkoihejjehlcdlnohgmp"> + Google Chrome Browser Extension + </a> + </li> + + <li> + <a href="https://f-droid.org/en/packages/net.etuldan.sparss.floss/"> + spaRSS + </a> + is my preferred android RSS reader, but you'll need to install + <a href="https://f-droid.org/">f-droid</a> on your device to use it (a + good thing to do anyway, imo). + </li> + + <li> + <a href="https://ranchero.com/netnewswire/">NetNewsWire</a> + is a good reader for iPhone/iPad/Mac devices, so I'm told. Their homepage + description makes a much better sales pitch for RSS than I ever could. + </li> +</ul> + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/index.html b/srv/src/http/tpl/index.html new file mode 100644 index 0000000..e27cbef --- /dev/null +++ b/srv/src/http/tpl/index.html @@ -0,0 +1,36 @@ +{{ define "body" }} + + <ul id="posts-list"> + + {{ range .Payload.Posts }} + <li> + <h2> + <a href="{{ PostURL .ID }}">{{ .Title }}</a> + </h2> + <span>{{ DateTimeFormat .PublishedAt }}</span> + {{ if not .LastUpdatedAt.IsZero }} + <span>(Updated {{ DateTimeFormat .LastUpdatedAt }})</span> + {{ end }} + <p>{{ .Description }}</p> + </li> + {{ end }} + + </ul> + + {{ if or (ge .Payload.PrevPage 0) (ge .Payload.NextPage 0) }} + <div id="page-turner"> + + {{ if ge .Payload.PrevPage 0 }} + <a style="float: left;" href="?p={{ .Payload.PrevPage}}">Newer</a> + {{ end }} + + {{ if ge .Payload.NextPage 0 }} + <a style="float:right;" href="?p={{ .Payload.NextPage}}">Older</a> + {{ end }} + + </div> + {{ end }} + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/post.html b/srv/src/http/tpl/post.html new file mode 100644 index 0000000..474d7c2 --- /dev/null +++ b/srv/src/http/tpl/post.html @@ -0,0 +1,48 @@ +{{ define "body" }} + +<header id="post-header"> + <h1 id="post-headline"> + {{ .Payload.Title }} + </h1> + <div class="light"> + {{ DateTimeFormat .Payload.PublishedAt }} + • + {{ if not .Payload.LastUpdatedAt.IsZero }} + (Updated {{ DateTimeFormat .Payload.LastUpdatedAt }}) + • + {{ end }} + <em>{{ .Payload.Description }}</em> + </div> +</header> + +{{ if (or .Payload.SeriesPrevious .Payload.SeriesNext) }} +<p class="light"><em> + This post is part of a series:<br/> + {{ if .Payload.SeriesPrevious }} + Previously: <a href="{{ PostURL .Payload.SeriesPrevious.ID }}">{{ .Payload.SeriesPrevious.Title }}</a></br> + {{ end }} + {{ if .Payload.SeriesNext }} + Next: <a href="{{ PostURL .Payload.SeriesNext.ID }}">{{ .Payload.SeriesNext.Title }}</a></br> + {{ end }} +</em></p> +{{ end }} + +<div id="post-content"> + {{ .Payload.Body }} +</div> + +{{ if (or .Payload.SeriesPrevious .Payload.SeriesNext) }} +<p class="light"><em> + If you liked this post, consider checking out other posts in the series:<br/> + {{ if .Payload.SeriesPrevious }} + Previously: <a href="{{ PostURL .Payload.SeriesPrevious.ID }}">{{ .Payload.SeriesPrevious.Title }}</a></br> + {{ end }} + {{ if .Payload.SeriesNext }} + Next: <a href="{{ PostURL .Payload.SeriesNext.ID }}">{{ .Payload.SeriesNext.Title }}</a></br> + {{ end }} +</em></p> +{{ end }} + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/posts.html b/srv/src/http/tpl/posts.html new file mode 100644 index 0000000..714cf07 --- /dev/null +++ b/srv/src/http/tpl/posts.html @@ -0,0 +1,61 @@ +{{ define "posts-nextprev" }} + + {{ if or (ge .Payload.PrevPage 0) (ge .Payload.NextPage 0) }} + <div id="page-turner"> + + {{ if ge .Payload.PrevPage 0 }} + <a style="float: left;" href="?p={{ .Payload.PrevPage}}">Newer</a> + {{ end }} + + {{ if ge .Payload.NextPage 0 }} + <a style="float:right;" href="?p={{ .Payload.NextPage}}">Older</a> + {{ end }} + + </div> + {{ end }} + +{{ end }} + +{{ define "body" }} + + {{ $csrfFormInput := .CSRFFormInput }} + + + <p style="text-align: center;"> + <a href="{{ BlogURL "posts/" }}?method=edit"> + <button>New Post</button> + </a> + </p> + + {{ template "posts-nextprev" . }} + + <table style="margin-top: 2rem;"> + + {{ range .Payload.Posts }} + <tr> + <td>{{ .PublishedAt }}</td> + <td><a href="{{ PostURL .ID }}" target="_blank">{{ .Title }}</a></td> + <td> + <a href="{{ PostURL .ID }}?method=edit"> + <button>Edit</button> + </a> + </td> + <td> + <form + action="{{ PostURL .ID }}?method=delete" + method="POST" + > + {{ $csrfFormInput }} + <input type="submit" value="Delete" /> + </form> + </td> + </tr> + {{ end }} + + </table> + + {{ template "posts-nextprev" . }} + +{{ end }} + +{{ template "base.html" . }} diff --git a/srv/src/http/tpl/redirect.html b/srv/src/http/tpl/redirect.html new file mode 100644 index 0000000..ed12a2e --- /dev/null +++ b/srv/src/http/tpl/redirect.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="refresh" content="0; url='{{ BlogURL .Payload.Path }}'" /> + </head> + <body> + <p>Redirecting...</p> + </body> +</html> |