Improve templates and stuff

pull/7/head
Jan-Lukas Else 2 years ago
parent d13b0a5394
commit f300c3d498
  1. 1
      activityStreams.go
  2. 2
      config.go
  3. 5
      customPages.go
  4. 70
      example-config.yaml
  5. 24
      feeds.go
  6. 5
      go.mod
  7. 10
      go.sum
  8. 44
      http.go
  9. 7
      posts.go
  10. 24
      postsDb.go
  11. 63
      render.go
  12. 45
      templates/assets/js/speak.js
  13. 10
      templates/author.gohtml
  14. 7
      templates/base.gohtml
  15. 12
      templates/footer.gohtml
  16. 10
      templates/header.gohtml
  17. 11
      templates/menu.gohtml
  18. 17
      templates/photosummary.gohtml
  19. 39
      templates/post.gohtml
  20. 8
      templates/postactions.gohtml
  21. 13
      templates/postheadmeta.gohtml
  22. 17
      templates/postmeta.gohtml
  23. 14
      templates/posttax.gohtml
  24. 11
      templates/strings/de.yaml
  25. 5
      templates/strings/de_DE.yaml
  26. 8
      templates/strings/default.yaml
  27. 6
      templates/summary.gohtml
  28. 11
      templates/summarymeta.gohtml
  29. 12
      utils.go

@ -150,5 +150,4 @@ func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter) {
}
}
_ = json.NewEncoder(w).Encode(asBlog)
}

@ -44,6 +44,7 @@ type configCache struct {
type configBlog struct {
Path string `mapstructure:"path"`
Lang string `mapstructure:"lang"`
TimeLang string `mapstructure:"timelang"`
Title string `mapstructure:"title"`
Description string `mapstructure:"description"`
Pagination int `mapstructure:"pagination"`
@ -104,6 +105,7 @@ type configUser struct {
Name string `mapstructure:"name"`
Password string `mapstructure:"password"`
Picture string `mapstructure:"picture"`
Link string `mapstructure:"link"`
}
type configHooks struct {

@ -5,8 +5,9 @@ import "net/http"
func serveCustomPage(blog *configBlog, page *customPage) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
render(w, page.Template, &renderData{
Blog: blog,
Data: page.Data,
Blog: blog,
Canonical: appConfig.Server.PublicAddress + page.Path,
Data: page.Data,
})
}
}

@ -1,70 +0,0 @@
server:
logging: false
debug: false
port: 8080
domain: example.com
publicAddress: http://localhost:8080
publicHttps: false
letsEncryptMail: mail@example.com
localHttps: false
database:
file: data/db.sqlite
cache:
enable: true
expiration: 600
blogs:
main:
path: /
lang: en_US
title: My blog
description: This is my blog
sections:
- name: posts
title: Posts
description: "**Posts** on this blog"
taxonomies:
- name: tags
title: Tags
description: "**Tags** on this blog"
menus:
main:
items:
- title: Home
link: /
- title: Posts
link: /posts
photos:
enable: true
parameter: images
path: /photos
title: Photos
description: "Photos on this blog"
activitystreams:
enable: true
replyParameter: replylink
imagesParameter: images
user:
nick: admin
name: Admin
password: secret
hugo:
frontmatter:
- meta: title
parameter: title
- meta: tags
parameter: tags
micropub:
categoryParam: tags
replyParam: replylink
likeParam: likelink
bookmarkParam: link
audioParam: audio
photoParam: images
photoDescriptionParam: imagealts
pathRedirects:
- from: "\\/index\\.xml"
to: ".rss"
- from: "\\/feed\\.json"
to: ".json"
- from: "\\/(feed|rss)\\/?$"
to: ".rss"

@ -5,6 +5,7 @@ import (
"strings"
"time"
"github.com/araddon/dateparse"
"github.com/gorilla/feeds"
)
@ -15,6 +16,10 @@ const (
rssFeed feedType = "rss"
atomFeed feedType = "atom"
jsonFeed feedType = "json"
feedAudioURL = "audio"
feedAudioType = "audiomime"
feedAudioLength = "audiolength"
)
func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Request, posts []*post, title string, description string) {
@ -30,14 +35,33 @@ func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Reques
Description: description,
Link: &feeds.Link{Href: appConfig.Server.PublicAddress + strings.TrimSuffix(r.URL.Path, "."+string(f))},
Created: now,
Author: &feeds.Author{
Name: appConfig.User.Name,
},
Image: &feeds.Image{
Url: appConfig.User.Picture,
},
}
for _, p := range posts {
created, _ := dateparse.ParseIn(p.Published, time.Local)
updated, _ := dateparse.ParseIn(p.Updated, time.Local)
var enc *feeds.Enclosure
if p.firstParameter(feedAudioURL) != "" {
enc = &feeds.Enclosure{
Url: p.firstParameter(feedAudioURL),
Type: p.firstParameter(feedAudioType),
Length: p.firstParameter(feedAudioLength),
}
}
feed.Add(&feeds.Item{
Title: p.title(),
Link: &feeds.Link{Href: appConfig.Server.PublicAddress + p.Path},
Description: p.summary(),
Id: p.Path,
Content: string(p.html()),
Created: created,
Updated: updated,
Enclosure: enc,
})
}
var feedStr string

@ -36,9 +36,10 @@ require (
github.com/yuin/goldmark v1.2.1
github.com/yuin/goldmark-emoji v1.0.1
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/net v0.0.0-20201026091529-146b70c837a4 // indirect
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20201026133411-418715ba6fdd // indirect
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect
golang.org/x/text v0.3.4 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect

@ -342,8 +342,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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=
@ -374,14 +374,16 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201026133411-418715ba6fdd h1:+7OQgGrJBd80e8ASl94G3xIpokulXXzB/dikfre4ho0=
golang.org/x/sys v0.0.0-20201026133411-418715ba6fdd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

@ -6,7 +6,6 @@ import (
"net/http"
"os"
"strconv"
"strings"
"sync/atomic"
"github.com/go-chi/chi"
@ -185,9 +184,10 @@ func buildHandler() (http.Handler, error) {
for _, section := range blogConfig.Sections {
if section.Name != "" {
path := blogPath + "/" + section.Name
r.With(cacheMiddleware, minifier.Middleware).Get(path, serveSection(blog, path, section))
r.With(cacheMiddleware, minifier.Middleware).Get(path+feedPath, serveSection(blog, path, section))
r.With(cacheMiddleware, minifier.Middleware).Get(path+paginationPath, serveSection(blog, path, section))
handler := serveSection(blog, path, section)
r.With(cacheMiddleware, minifier.Middleware).Get(path, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(path+feedPath, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(path+paginationPath, handler)
}
}
@ -201,17 +201,19 @@ func buildHandler() (http.Handler, error) {
}
for _, tv := range values {
vPath := path + "/" + urlize(tv)
r.With(cacheMiddleware, minifier.Middleware).Get(vPath, serveTaxonomyValue(blog, vPath, taxonomy, tv))
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+feedPath, serveTaxonomyValue(blog, vPath, taxonomy, tv))
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+paginationPath, serveTaxonomyValue(blog, vPath, taxonomy, tv))
handler := serveTaxonomyValue(blog, vPath, taxonomy, tv)
r.With(cacheMiddleware, minifier.Middleware).Get(vPath, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+feedPath, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+paginationPath, handler)
}
}
}
// Photos
if blogConfig.Photos.Enabled {
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+blogConfig.Photos.Path, servePhotos(blog))
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+blogConfig.Photos.Path+paginationPath, servePhotos(blog))
handler := servePhotos(blog)
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+blogConfig.Photos.Path, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+blogConfig.Photos.Path+paginationPath, handler)
}
// Blog
@ -221,17 +223,18 @@ func buildHandler() (http.Handler, error) {
} else {
mw = []func(http.Handler) http.Handler{cacheMiddleware, minifier.Middleware}
}
r.With(mw...).Get(fullBlogPath, serveHome(blog, blogPath))
r.With(cacheMiddleware, minifier.Middleware).Get(fullBlogPath+feedPath, serveHome(blog, blogPath))
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+paginationPath, serveHome(blog, blogPath))
handler := serveHome(blog, blogPath)
r.With(mw...).Get(fullBlogPath, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(fullBlogPath+feedPath, handler)
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+paginationPath, handler)
// Custom pages
for _, cp := range blogConfig.CustomPages {
serveFunc := serveCustomPage(blogConfig, cp)
handler := serveCustomPage(blogConfig, cp)
if cp.Cache {
r.With(cacheMiddleware, minifier.Middleware).Get(cp.Path, serveFunc)
r.With(cacheMiddleware, minifier.Middleware).Get(cp.Path, handler)
} else {
r.With(minifier.Middleware).Get(cp.Path, serveFunc)
r.With(minifier.Middleware).Get(cp.Path, handler)
}
}
}
@ -268,14 +271,3 @@ func (d *dynamicHandler) swapHandler(h http.Handler) {
func (d *dynamicHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
d.realHandler.Load().(http.Handler).ServeHTTP(w, r)
}
func slashTrimmedPath(r *http.Request) string {
return trimSlash(r.URL.Path)
}
func trimSlash(s string) string {
if len(s) > 1 {
s = strings.TrimSuffix(s, "/")
}
return s
}

@ -46,8 +46,13 @@ func servePost(w http.ResponseWriter, r *http.Request) {
p.serveActivityStreams(w)
return
}
canonical := p.firstParameter("original")
if canonical == "" {
canonical = appConfig.Server.PublicAddress + p.Path
}
render(w, templatePost, &renderData{
blogString: p.Blog,
Canonical: canonical,
Data: p,
})
}
@ -121,6 +126,7 @@ func serveTaxonomy(blog string, tax *taxonomy) func(w http.ResponseWriter, r *ht
}
render(w, templateTaxonomy, &renderData{
blogString: blog,
Canonical: appConfig.Server.PublicAddress + slashTrimmedPath(r),
Data: struct {
Taxonomy *taxonomy
TaxonomyValues []string
@ -214,6 +220,7 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
}
render(w, template, &renderData{
blogString: ic.blog,
Canonical: appConfig.Server.PublicAddress + slashTrimmedPath(r),
Data: &indexTemplateData{
Title: title,
Description: description,

@ -66,21 +66,6 @@ func (p *post) checkPost() error {
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
}
published, _ := dateparse.ParseIn(p.Published, time.Local)
pathVars := struct {
BlogPath string
Year int
Month int
Day int
Slug string
Section string
}{
BlogPath: appConfig.Blogs[p.Blog].Path,
Year: published.Year(),
Month: int(published.Month()),
Day: published.Day(),
Slug: p.Slug,
Section: p.Section,
}
pathTmplString := appConfig.Blogs[p.Blog].Sections[p.Section].PathTemplate
if pathTmplString == "" {
return errors.New("path template empty")
@ -90,7 +75,14 @@ func (p *post) checkPost() error {
return errors.New("failed to parse location template")
}
var pathBuffer bytes.Buffer
err = pathTmpl.Execute(&pathBuffer, pathVars)
err = pathTmpl.Execute(&pathBuffer, map[string]interface{}{
"BlogPath": appConfig.Blogs[p.Blog].Path,
"Year": published.Year(),
"Month": int(published.Month()),
"Day": published.Day(),
"Slug": p.Slug,
"Section": p.Section,
})
if err != nil {
return errors.New("failed to execute location template")
}

@ -2,6 +2,7 @@ package main
import (
"bytes"
"errors"
"fmt"
"html/template"
"io/ioutil"
@ -10,6 +11,7 @@ import (
"os"
"path"
"path/filepath"
"reflect"
"strings"
"time"
@ -39,6 +41,9 @@ func initRendering() error {
"menu": func(blog *configBlog, id string) *menu {
return blog.Menus[id]
},
"user": func() *configUser {
return appConfig.User
},
"md": func(content string) template.HTML {
htmlContent, err := renderMarkdown(content)
if err != nil {
@ -85,18 +90,61 @@ func initRendering() error {
ml := monday.Locale(localeString)
return monday.Format(d, monday.LongFormatsByLocale[ml], ml)
},
"now": func() string {
return time.Now().String()
},
"asset": assetFile,
"string": getTemplateStringVariant,
"include": func(templateName string, blog *configBlog, data interface{}) (template.HTML, error) {
buf := new(bytes.Buffer)
err := templates[templateName].ExecuteTemplate(buf, templateName, &renderData{
Blog: blog,
Data: data,
})
return template.HTML(buf.String()), err
"include": func(templateName string, data ...interface{}) (template.HTML, error) {
if len(data) == 1 {
if rd, ok := data[0].(*renderData); ok {
buf := new(bytes.Buffer)
err := templates[templateName].ExecuteTemplate(buf, templateName, rd)
return template.HTML(buf.String()), err
}
return "", errors.New("wrong argument")
} else if len(data) == 2 {
if blog, ok := data[0].(*configBlog); ok {
buf := new(bytes.Buffer)
err := templates[templateName].ExecuteTemplate(buf, templateName, &renderData{
Blog: blog,
Data: data[1],
})
return template.HTML(buf.String()), err
}
return "", errors.New("wrong arguments")
}
return "", errors.New("wrong argument count")
},
"default": func(dflt interface{}, given ...interface{}) interface{} {
if len(given) == 0 {
return dflt
}
g := reflect.ValueOf(given[0])
if !g.IsValid() {
return dflt
}
set := false
switch g.Kind() {
case reflect.Bool:
set = true
case reflect.String, reflect.Array, reflect.Slice, reflect.Map:
set = g.Len() != 0
case reflect.Int:
set = g.Int() != 0
default:
set = !g.IsNil()
}
if set {
return given[0]
}
return dflt
},
"urlize": urlize,
"sort": sortedStrings,
"absolute": func(path string) string {
return appConfig.Server.PublicAddress + path
},
"blogRelative": func(blog *configBlog, path string) string {
return blog.getRelativePath(path)
},
@ -138,6 +186,7 @@ func initRendering() error {
type renderData struct {
blogString string
Canonical string
Blog *configBlog
Data interface{}
}

@ -0,0 +1,45 @@
"use strict";
function getVoice() {
if (window.speechSynthesis) {
return window.speechSynthesis.getVoices().filter(voice => voice.lang.startsWith(document.querySelector('html').lang))[0];
}
return false;
}
function initSpeak() {
if (window.speechSynthesis) {
let speakBtn = document.querySelector('#speakBtn');
speakBtn.style.display = '';
speakBtn.onclick = function() { speak() };
speakBtn.textContent = speakText;
}
}
function speak() {
console.log("Start speaking")
let speakBtn = document.querySelector('#speakBtn');
speakBtn.onclick = function() { stopSpeak() };
speakBtn.textContent = stopSpeakText;
let textContent =
((document.querySelector('article .p-name')) ? document.querySelector('article .p-name').innerText + "\n\n" : "")
+ document.querySelector('article .e-content').innerText;
let utterThis = new SpeechSynthesisUtterance(textContent);
utterThis.voice = getVoice();
utterThis.onerror = stopSpeak;
utterThis.onend = stopSpeak;
window.speechSynthesis.speak(utterThis);
}
function stopSpeak() {
console.log("Stop speaking")
window.speechSynthesis.cancel();
let speakBtn = document.querySelector('#speakBtn');
speakBtn.onclick = function() { speak() };
speakBtn.textContent = speakText;
}
window.onbeforeunload = function () {
stopSpeak();
}
initSpeak();

@ -0,0 +1,10 @@
{{ define "author" }}
{{ with user }}
<div class="p-author h-card hide">
{{ with .Picture }}<data class="u-photo" value="{{ . }}"></data>{{ end }}
{{ if .Name }}
<a href="{{ .Link | default "/" }}" class="p-name u-url" rel="me">{{ .Name }}</a>
{{ end }}
</div>
{{ end }}
{{ end }}

@ -5,8 +5,11 @@
<meta name=viewport content="width=device-width,initial-scale=1">
<meta http-equiv=x-ua-compatible content="IE=edge">
<link rel="stylesheet" href="{{ asset "css/styles.css" }}">
{{ with user.Picture }}<link rel="shortcut icon" href="{{ . }}">{{ end }}
{{ with .Canonical }}<link rel="canonical" href="{{ . }}">{{ end }}
{{ template "title" . }}
{{ include "micropub" .Blog .Data }}
{{ include "header" .Blog .Data }}
{{ include "micropub" . }}
{{ include "header" . }}
{{ template "main" . }}
{{ include "footer" . }}
{{ end }}

@ -0,0 +1,12 @@
{{ define "footer" }}
<footer>
{{ with menu .Blog "footer" }}
{{ $first := true }}
{{ range $i, $item := .Items }}
{{ if ne $first true }} &bull; {{ end }}<a
href="{{ $item.Link }}">{{ $item.Title }}</a>{{ $first = false }}
{{ end }}
{{ end }}
<p>&copy; {{ dateformat now "2006" }} {{ .Blog.Title }}</p>
</footer>
{{ end }}

@ -2,6 +2,14 @@
<header>
<h1><a href="{{ blogRelative .Blog "/" }}" rel="home" title="{{ .Blog.Title }}">{{ .Blog.Title }}</a></h1>
{{ with .Blog.Description }}<p><i>{{ . }}</i></p>{{ end }}
{{ include "menu" .Blog .Data }}
<nav>
{{ with menu .Blog "main" }}
{{ $first := true }}
{{ range $i, $item := .Items }}
{{ if ne $first true }} &bull; {{ end }}<a
href="{{ $item.Link }}">{{ $item.Title }}</a>{{ $first = false }}
{{ end }}
{{ end }}
</nav>
</header>
{{ end }}

@ -1,11 +0,0 @@
{{ define "menu" }}
<nav>
{{ with menu .Blog "main" }}
{{ $first := true }}
{{ range $i, $item := .Items }}
{{ if ne $first true }} &bull; {{ end }}<a
href="{{ $item.Link }}">{{ $item.Title }}</a>{{ $first = false }}
{{ end }}
{{ end }}
</nav>
{{ end }}

@ -1,12 +1,11 @@
{{ define "photosummary" }}
<article>
{{ with p .Data "title" }}<h2>{{ . }}</h2>{{ end }}
{{ if .Data.Published }}<p>{{ longDate .Data.Published .Blog.Lang }}</p>{{ end }}
{{ range $i, $photo := ( ps .Data .Blog.Photos.Parameter ) }}
{{ md ( printf "![](%s)" $photo ) }}
{{ end }}
<p>{{ summary .Data }}</p>
<a href="{{ .Data.Path }}">{{ string .Blog.Lang "view" }}</a>
<article class="h-entry border-bottom">
{{ with p .Data "title" }}<h2>{{ . }}</h2>{{ end }}
{{ include "postmeta" . }}
{{ range $i, $photo := ( ps .Data .Blog.Photos.Parameter ) }}
{{ md ( printf "![](%s)" $photo ) }}
{{ end }}
<p class="p-summary">{{ summary .Data }}</p>
<p>{{ if (hasp .Data "images") }}🖼 {{ end }}<a class="u-url" href="{{ .Data.Path }}">{{ string .Blog.Lang "view" }}</a></p>
</article>
<hr>
{{ end }}

@ -1,34 +1,29 @@
{{ define "title" }}
<title>{{ with p .Data "title" }}{{ . }} - {{end}}{{ .Blog.Title }}</title>
{{ include "postheadmeta" . }}
{{ end }}
{{ define "main" }}
<main class=h-entry>
<article>
{{ with title .Data }}<h1 class=p-name>{{ . }}</h1>{{ end }}
{{ if .Data.Published }}
<p>{{ string .Blog.Lang "publishedon" }} {{ longDate .Data.Published .Blog.Lang }}</p>{{ end }}
{{ if .Data.Updated }}
<p>{{ string .Blog.Lang "updatedon" }} {{ longDate .Data.Updated .Blog.Lang }}</p>{{ end }}
{{ if .Data.Content }}
<div class=e-content>{{ content .Data }}</div>
{{ end }}
</article>
{{ $post := .Data }}
{{ $blog := .Blog }}
{{ range $i, $tax := $blog.Taxonomies }}
{{ $tvs := ps $post $tax.Name }}
{{ if gt (len $tvs) 0 }}
<p>In <b>{{ $tax.Title }}</b>:
{{ range $j, $tv := $tvs }}
<a href="{{ blogRelative $blog ( printf "/%s/%s" $tax.Name (urlize $tv) ) }}">{{ $tv }}</a>
{{ end }}
</p>
<data class="u-url hide" value="{{ absolute .Data.Path }}"></data>
{{ with title .Data }}<h1 class=p-name>{{ . }}</h1>{{ end }}
{{ include "postmeta" . }}
{{ include "postactions" . }}
{{ if .Data.Content }}
<div class=e-content>
{{ with p .Data "audio" }}
<audio controls preload="metadata" class="fw"><source src="{{ . }}"/></audio>
{{ end }}
{{ content .Data }}
{{ with p .Data "link" }}
<p><a class="u-bookmark-of" href="{{ . }}" target="_blank" rel="noopener">{{ . }}</a></p>
{{ end }}
</div>
{{ end }}
{{ range $i, $t := (translations .Data) }}
<p><a href="{{ $t.Path }}">{{ (blog $t.Blog).Title }}</a></p>
{{ end }}
{{ include "posttax" . }}
</article>
{{ include "author" . }}
</main>
{{ end }}

@ -0,0 +1,8 @@
{{ define "postactions" }}
<div class="p flex" id="post-actions">
<a href="https://www.addtoany.com/share#url={{ absolute .Data.Path }}{{ with title .Data }}&title={{ . }}{{ end }}" target="_blank" rel="nofollow noopener noreferrer" class="button invert">{{ string .Blog.Lang "share" }}</a>&nbsp;
<button id="speakBtn" class="invert" style="display: none;"></button>
<script>const speakText = "{{ string .Blog.Lang "speak" }}";const stopSpeakText = "{{ string .Blog.Lang "stopspeak" }}";</script>
<script defer src="{{ asset "js/speak.js" }}"></script>
</div>
{{ end }}

@ -0,0 +1,13 @@
{{ define "postheadmeta" }}
<meta name="description" content="{{ with summary .Data }}{{ . }}{{ end }}">
{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}
{{ if .Data.Published }}
<meta itemprop="datePublished" content="{{ dateformat .Data.Published $ISO8601 }}">
{{ end }}
{{ if .Data.Updated }}
<meta itemprop="dateModified" content="{{ dateformat .Data.Updated $ISO8601 }}">
{{ end }}
{{ range $key, $image := ps .Data "images" }}
<meta itemprop="image" content="{{ $image }}">
{{ end }}
{{ end }}

@ -0,0 +1,17 @@
{{ define "postmeta" }}
<div class="p">
{{ $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"}}">{{ longDate .Data.Published .Blog.TimeLang }}</time>{{ if $section }} in <a href="{{ blogRelative .Blog $section.Name }}">{{ $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"}}">{{ longDate .Data.Updated .Blog.TimeLang }}</time></div>{{ end }}
{{ if p .Data "replylink" }}
<div>{{ string .Blog.Lang "replyto" }}: <a class="u-in-reply-to" href="{{ p .Data "replylink" }}" target="_blank" rel="noopener">{{ p .Data "replytitle" | default (p .Data "replylink") }}</a></div>
{{ end }}
{{ if p .Data "likelink" }}
<div>{{ string .Blog.Lang "likeof" }}: <a class="u-like-of" href="{{ p .Data "likelink" }}" target="_blank" rel="noopener">{{ p .Data "liketitle" | default (p .Data "likelink") }}</a></div>
{{ end }}
{{ $translations := (translations .Data) }}
{{ if gt (len $translations) 0 }}
<div>{{ string .Blog.Lang "translations" }}: {{ $delimiter := "" }}{{ range $i, $t := $translations }}{{ $delimiter }}<a href="{{ $t.Path }}">{{ title $t }}</a>{{ $delimiter = ", " }}{{ end }}</div>
{{ end }}
</div>
{{ end }}

@ -0,0 +1,14 @@
{{ define "posttax" }}
{{ $post := .Data }}
{{ $blog := .Blog }}
{{ range $i, $tax := $blog.Taxonomies }}
{{ $tvs := ps $post $tax.Name }}
{{ if gt (len $tvs) 0 }}
<p><b>{{ $tax.Title }}</b>:
{{ range $j, $tv := $tvs }}
<a class="p-category" rel="tag" href="{{ blogRelative $blog ( printf "/%s/%s" $tax.Name (urlize $tv) ) }}">{{ $tv }}</a>
{{ end }}
</p>
{{ end }}
{{ end }}
{{ end }}

@ -0,0 +1,11 @@
publishedon: "Veröffentlicht am"
updatedon: "Aktualisiert am"
next: "Weiter"
prev: "Zurück"
view: "Anschauen"
replyto: "Antwort an"
likeof: "Gefällt mir von"
translations: "Übersetzungen"
share: "Teilen"
speak: "Lies mir bitte vor."
stopspeak: "Hör auf zu sprechen!"

@ -1,5 +0,0 @@
publishedon: "Veröffentlicht am"
updatedon: "Aktualisiert am"
next: "Weiter"
prev: "Zurück"
view: "Anschauen"

@ -5,4 +5,10 @@ prev: "Previous"
view: "View"
authenticate: "Authenticate"
scopes: "Scopes"
indieauth: "IndieAuth"
indieauth: "IndieAuth"
replyto: "Reply to"
likeof: "Like of"
translations: "Translations"
share: "Share"
speak: "Read to me, please."
stopspeak: "Stop speaking!"

@ -1,13 +1,13 @@
{{ define "summary" }}
<article class="h-entry border-bottom">
{{ if p .Data "title" }}
{{ if p .Data "title" }}
<h2 class="p-name">
<a class="u-url" href="{{ .Data.Path }}">
{{ p .Data "title" }}
</a>
</h2>
{{ end }}
{{ include "summarymeta" .Blog .Data }}
{{ end }}
{{ include "postmeta" . }}
<p class="p-summary">{{ summary .Data }}</p>
<p>{{ if (hasp .Data "images") }}🖼 {{ end }}<a class="u-url" href="{{ .Data.Path }}">{{ string .Blog.Lang "view" }}</a></p>
</article>

@ -1,11 +0,0 @@
{{ define "summarymeta" }}
<div class="p">
{{ $section := (index .Blog.Sections .Data.Section) }}
{{ if .Data.Published }}
<div><time class="dt-published" datetime="{{ dateformat .Data.Published "2006-01-02T15:04:05Z07:00" }}">{{ longDate .Data.Published .Blog.Lang }}</time>{{ if $section }} in <a href="{{ blogRelative .Blog $section.Name }}">{{ $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" }}">{{ longDate .Data.Updated .Blog.Lang }}</time></div>
{{ end }}
</div>
{{ end }}

@ -2,6 +2,7 @@ package main
import (
"math/rand"
"net/http"
"sort"
"strings"
"time"
@ -35,3 +36,14 @@ func generateRandomString(chars int) string {
}
return string(b)
}
func slashTrimmedPath(r *http.Request) string {
return trimSlash(r.URL.Path)
}
func trimSlash(s string) string {
if len(s) > 1 {
s = strings.TrimSuffix(s, "/")
}
return s
}

Loading…
Cancel
Save