Compare commits
3 Commits
f536b16e17
...
acec797048
Author | SHA1 | Date | |
---|---|---|---|
|
acec797048 | ||
|
5b5a043868 | ||
|
1cfdac5e8c |
116
src/gmi/tpl.go
116
src/gmi/tpl.go
@ -35,9 +35,10 @@ type rendererGetPostSeriesNextPreviousRes struct {
|
||||
}
|
||||
|
||||
type renderer struct {
|
||||
url *url.URL
|
||||
postStore post.Store
|
||||
httpPublicURL *url.URL
|
||||
url *url.URL
|
||||
publicURL *url.URL
|
||||
postStore post.Store
|
||||
preprocessFuncs post.PreprocessFunctions
|
||||
}
|
||||
|
||||
func (r renderer) GetPosts(page, count int) (rendererGetPostsRes, error) {
|
||||
@ -90,39 +91,9 @@ func (r renderer) GetPostSeriesNextPrevious(p post.StoredPost) (rendererGetPostS
|
||||
|
||||
func (r renderer) PostBody(p post.StoredPost) (string, error) {
|
||||
|
||||
preprocessFuncs := post.PreprocessFunctions{
|
||||
BlogURL: func(path string) string {
|
||||
return filepath.Join("/", path)
|
||||
},
|
||||
AssetURL: func(id string) string {
|
||||
return filepath.Join("/assets", id)
|
||||
},
|
||||
PostURL: func(id string) string {
|
||||
return filepath.Join("/posts", id)
|
||||
},
|
||||
StaticURL: func(path string) string {
|
||||
httpPublicURL := *r.httpPublicURL
|
||||
httpPublicURL.Path = filepath.Join(httpPublicURL.Path, "/static", path)
|
||||
return httpPublicURL.String()
|
||||
},
|
||||
Image: func(args ...string) (string, error) {
|
||||
|
||||
var (
|
||||
id = args[0]
|
||||
descr = "Image"
|
||||
)
|
||||
|
||||
if len(args) > 1 {
|
||||
descr = args[1]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("=> %s %s", filepath.Join("/assets", id), descr), nil
|
||||
},
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := p.PreprocessBody(buf, preprocessFuncs); err != nil {
|
||||
if err := p.PreprocessBody(buf, r.preprocessFuncs); err != nil {
|
||||
return "", fmt.Errorf("preprocessing post body: %w", err)
|
||||
}
|
||||
|
||||
@ -154,12 +125,82 @@ func (r renderer) GetQueryIntValue(key string, def int) (int, error) {
|
||||
return strconv.Atoi(vStr)
|
||||
}
|
||||
|
||||
func (r renderer) GetPath() (string, error) {
|
||||
basePath := filepath.Join("/", r.publicURL.Path) // in case it's empty
|
||||
return filepath.Rel(basePath, r.url.Path)
|
||||
}
|
||||
|
||||
func (r renderer) Add(a, b int) int { return a + b }
|
||||
|
||||
func (a *api) tplHandler() (gemini.Handler, error) {
|
||||
|
||||
blogURL := func(path string, abs bool) string {
|
||||
// filepath.Join strips trailing slash, but we want to keep it
|
||||
trailingSlash := strings.HasSuffix(path, "/")
|
||||
|
||||
path = filepath.Join("/", a.params.PublicURL.Path, path)
|
||||
|
||||
if trailingSlash && path != "/" {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
if !abs {
|
||||
return path
|
||||
}
|
||||
|
||||
u := *a.params.PublicURL
|
||||
u.Path = path
|
||||
return u.String()
|
||||
}
|
||||
|
||||
preprocessFuncs := post.PreprocessFunctions{
|
||||
BlogURL: func(path string) string {
|
||||
return blogURL(path, false)
|
||||
},
|
||||
AssetURL: func(id string) string {
|
||||
path := filepath.Join("assets", id)
|
||||
return blogURL(path, false)
|
||||
},
|
||||
PostURL: func(id string) string {
|
||||
path := filepath.Join("posts", id) + ".gmi"
|
||||
return blogURL(path, false)
|
||||
},
|
||||
StaticURL: func(path string) string {
|
||||
httpPublicURL := *a.params.HTTPPublicURL
|
||||
httpPublicURL.Path = filepath.Join(httpPublicURL.Path, "/static", path)
|
||||
return httpPublicURL.String()
|
||||
},
|
||||
Image: func(args ...string) (string, error) {
|
||||
|
||||
var (
|
||||
id = args[0]
|
||||
descr = "Image"
|
||||
)
|
||||
|
||||
if len(args) > 1 {
|
||||
descr = args[1]
|
||||
}
|
||||
|
||||
path := blogURL(filepath.Join("assets", id), false)
|
||||
|
||||
return fmt.Sprintf("\n=> %s %s", path, descr), nil
|
||||
},
|
||||
}
|
||||
|
||||
allTpls := template.New("")
|
||||
|
||||
allTpls.Funcs(preprocessFuncs.ToFuncsMap())
|
||||
|
||||
allTpls.Funcs(template.FuncMap{
|
||||
"BlogURLAbs": func(path string) string {
|
||||
return blogURL(path, true)
|
||||
},
|
||||
"PostURLAbs": func(id string) string {
|
||||
path := filepath.Join("posts", id) + ".gmi"
|
||||
return blogURL(path, true)
|
||||
},
|
||||
})
|
||||
|
||||
err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error {
|
||||
|
||||
if err != nil {
|
||||
@ -218,9 +259,10 @@ func (a *api) tplHandler() (gemini.Handler, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
err := tpl.Execute(buf, renderer{
|
||||
url: r.URL,
|
||||
postStore: a.params.PostStore,
|
||||
httpPublicURL: a.params.HTTPPublicURL,
|
||||
url: r.URL,
|
||||
publicURL: a.params.PublicURL,
|
||||
postStore: a.params.PostStore,
|
||||
preprocessFuncs: preprocessFuncs,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
28
src/gmi/tpl/feed.xml
Normal file
28
src/gmi/tpl/feed.xml
Normal file
@ -0,0 +1,28 @@
|
||||
{{ $getPostsRes := .GetPosts 0 20 -}}
|
||||
{{ $posts := $getPostsRes.Posts -}}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>mediocregopher's lil web corner</title>
|
||||
<id>{{ BlogURLAbs "/" }}</id>
|
||||
{{ if gt (len $posts) 0 -}}
|
||||
<updated>{{ (index $posts 0).PublishedAt.Format "2006-01-02T15:04:05Z07:00" }}</updated>
|
||||
{{ end -}}
|
||||
<link href="{{ BlogURLAbs "/" }}"></link>
|
||||
<author>
|
||||
<name>mediocregopher</name>
|
||||
</author>
|
||||
{{ range $posts -}}
|
||||
<entry>
|
||||
<title>{{ .Title }}</title>
|
||||
<updated>{{ .PublishedAt.Format "2006-01-02T15:04:05Z07:00" }}</updated>
|
||||
<id>{{ PostURLAbs .ID }}</id>
|
||||
<link href="{{ PostURLAbs .ID }}" rel="alternate"></link>
|
||||
{{ if .Description -}}
|
||||
<summary type="html">{{ .Description }}</summary>
|
||||
{{ end -}}
|
||||
<author>
|
||||
<name>mediocregopher</name>
|
||||
</author>
|
||||
</entry>
|
||||
{{ end -}}
|
||||
</feed>
|
8
src/gmi/tpl/footer.gmi
Normal file
8
src/gmi/tpl/footer.gmi
Normal file
@ -0,0 +1,8 @@
|
||||
================================================================================
|
||||
|
||||
{{ if ne .GetPath "index.gmi" -}}
|
||||
=> {{ BlogURL "/" }} Home
|
||||
|
||||
{{ end -}}
|
||||
|
||||
=> {{ BlogURL "wtfpl.txt" }} License for all content, if you must have one
|
@ -1,3 +1,61 @@
|
||||
# mediocregopher's lil web corner
|
||||
|
||||
=> /posts/ See all posts
|
||||
This here's my little corner of the web, where I publish posts about projects
|
||||
I'm working on and things that interest me (which you can follow, if you like).
|
||||
|
||||
=> {{ BlogURL "posts/" }} Browse all posts
|
||||
|
||||
{{ $getPostsRes := .GetPosts 0 1 -}}
|
||||
{{ if gt (len $getPostsRes.Posts) 0 -}}
|
||||
{{ $post := index $getPostsRes.Posts 0 -}}
|
||||
=> {{ PostURL $post.ID }} (Latest post: {{ $post.Title }})
|
||||
|
||||
{{ end -}}
|
||||
|
||||
Below you'll find other information and links related to me.
|
||||
|
||||
## Social
|
||||
|
||||
Feel free to hmu on any of these if you'd like to get in touch.
|
||||
|
||||
* Matrix: @mediocregopher:waffle.farm
|
||||
|
||||
* Mastodon: @mediocregopher@social.cryptic.io
|
||||
|
||||
* Email: mediocregopher@gmail.com
|
||||
|
||||
## Dev
|
||||
|
||||
=> https://code.betamike.com/mediocregopher Gitea is for newer open-source code I've written.
|
||||
|
||||
=> https://github.com/mediocregopher Github is for older open-source code I've written.
|
||||
|
||||
=> gemini://godocs.io/github.com/mediocregopher/radix/v4 radix is for using redis with go.
|
||||
|
||||
=> https://code.betamike.com/cryptic-io/cryptic-net cryptic-net implements the foundation of a community infrastucture.
|
||||
|
||||
=> https://code.betamike.com/mediocregopher/mediocre-blog mediocre-blog is the CMS I designed from scratch to run this site.
|
||||
|
||||
## Art
|
||||
|
||||
=> https://opensea.io/mediocregopher?tab=created OpenSea lists some NFTs I've made.
|
||||
|
||||
=> https://exchange.art/artists/mediocregopher/series Exchange.art lists others.
|
||||
|
||||
## Other
|
||||
|
||||
=> https://bgpicciano.com/ bgpicciano.com is my cover site/resume.
|
||||
|
||||
=> https://news.cryptic.io/ Cryptic News aggregates interesting blogs.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
I'm not affiliated with these, but they're worth listing.
|
||||
|
||||
=> https://search.marginalia.nu/ Marginalia reminds me of the old internet.
|
||||
|
||||
=> https://www.nts.live/ NTS is a great internet radio station.
|
||||
|
||||
=> https://yamakan.place/palestine/# Radio alHara is another great internet radio station.
|
||||
|
||||
{{ template "footer.gmi" . }}
|
||||
|
@ -1,21 +1,30 @@
|
||||
# mediocregopher's Posts
|
||||
|
||||
{{ $page := .GetQueryIntValue "page" 0 -}}
|
||||
{{ $getPostsRes := .GetPosts $page 15 -}}
|
||||
|
||||
{{ if eq $page 0 -}}
|
||||
Here you'll find an archive of all published posts. The content varies almost as
|
||||
much as the quality!
|
||||
|
||||
{{ end -}}
|
||||
|
||||
{{ $getPostsRes := .GetPosts $page 20 -}}
|
||||
|
||||
{{ if gt $page 0 -}}
|
||||
=> /posts/?page={{ .Add $page -1 }} Previous Page
|
||||
=> {{ BlogURL "posts" }}/?page={{ .Add $page -1 }} Previous Page
|
||||
|
||||
{{ end -}}
|
||||
|
||||
{{ range $getPostsRes.Posts -}}
|
||||
=> /posts/{{ .ID }}.gmi {{ .PublishedAt.Format "2006-01-02" }} - {{ .Title }}
|
||||
=> {{ PostURL .ID }} {{ .PublishedAt.Format "2006-01-02" }} - {{ .Title }}
|
||||
|
||||
{{ end -}}
|
||||
|
||||
{{ if $getPostsRes.HasMore -}}
|
||||
=> /posts/?page={{ .Add $page 1 }} Next page
|
||||
=> {{ BlogURL "posts" }}/?page={{ .Add $page 1 }} Next page
|
||||
{{ end }}
|
||||
================================================================================
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
=> / Home
|
||||
=> {{ BlogURL "feed.xml" }} Subscribe to the RSS feed for updates
|
||||
|
||||
{{ template "footer.gmi" . }}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{{ if ne $post.Description "" -}}
|
||||
> {{ $post.Description }}
|
||||
|
||||
{{ end -}}
|
||||
|
||||
{{ .PostBody $post }}
|
||||
@ -19,15 +20,14 @@ This post is part of a series!
|
||||
{{ $seriesNextPrev := .GetPostSeriesNextPrevious $post -}}
|
||||
|
||||
{{ if $seriesNextPrev.Next -}}
|
||||
=> /posts/{{ $seriesNextPrev.Next.ID }}.gmi Next: {{ $seriesNextPrev.Next.Title }}
|
||||
=> {{ BlogURL "posts" }}/{{ $seriesNextPrev.Next.ID }}.gmi Next in the series: {{ $seriesNextPrev.Next.Title }}
|
||||
{{ end -}}
|
||||
|
||||
{{ if $seriesNextPrev.Previous -}}
|
||||
=> /posts/{{ $seriesNextPrev.Previous.ID }}.gmi Previously: {{ $seriesNextPrev.Previous.Title }}
|
||||
=> {{ BlogURL "posts" }}/{{ $seriesNextPrev.Previous.ID }}.gmi Prevous in the series: {{ $seriesNextPrev.Previous.Title }}
|
||||
{{ end -}}
|
||||
|
||||
{{ end }}
|
||||
================================================================================
|
||||
=> {{ BlogURL "posts/" }} Browse all posts
|
||||
|
||||
=> /posts/ More posts
|
||||
=> / Home
|
||||
{{ template "footer.gmi" . }}
|
||||
|
@ -39,10 +39,9 @@ func (a *api) renderFeedHandler() http.Handler {
|
||||
publicURL := a.params.PublicURL.String()
|
||||
|
||||
feed := feeds.Feed{
|
||||
Title: "Mediocre Blog",
|
||||
Link: &feeds.Link{Href: publicURL + "/"},
|
||||
Description: "A mix of tech, art, travel, and who knows what else.",
|
||||
Author: author,
|
||||
Title: "mediocregopher's lil web corner",
|
||||
Link: &feeds.Link{Href: publicURL + "/"},
|
||||
Author: author,
|
||||
}
|
||||
|
||||
for _, post := range posts {
|
||||
|
@ -31,14 +31,16 @@ func (a *api) blogURL(path string, abs bool) string {
|
||||
// filepath.Join strips trailing slash, but we want to keep it
|
||||
trailingSlash := strings.HasSuffix(path, "/")
|
||||
|
||||
res := filepath.Join("/", path)
|
||||
res := filepath.Join("/", a.params.PublicURL.Path, path)
|
||||
|
||||
if trailingSlash && res != "/" {
|
||||
res += "/"
|
||||
}
|
||||
|
||||
if abs {
|
||||
res = a.params.PublicURL.String() + res
|
||||
u := *a.params.PublicURL
|
||||
u.Path = res
|
||||
res = u.String()
|
||||
}
|
||||
|
||||
return res
|
||||
@ -88,6 +90,9 @@ func (a *api) tplFuncs() template.FuncMap {
|
||||
"BlogURL": func(path string) string {
|
||||
return a.blogURL(path, false)
|
||||
},
|
||||
"BlogURLAbs": func(path string) string {
|
||||
return a.blogURL(path, true)
|
||||
},
|
||||
"StaticURL": func(path string) string {
|
||||
path = filepath.Join("static", path)
|
||||
return a.blogURL(path, false)
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>mediocregopher's lil web corner</title>
|
||||
<style>{{ StaticInlineCSS "new.css" }}</style>
|
||||
<style>{{ StaticInlineCSS "mediocre.css" }}</style>
|
||||
|
||||
|
@ -114,7 +114,7 @@ emailSubscribe.onclick = async () => {
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{{ BlogURL "feed.xml" }}">{{ BlogURL "feed.xml" }}</a>
|
||||
<a href="{{ BlogURLAbs "feed.xml" }}">{{ BlogURLAbs "feed.xml" }}</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -1,20 +1,19 @@
|
||||
{{ define "body" }}
|
||||
|
||||
<p>
|
||||
Hi! I'm Brian, and this here's my little corner of the web. Here I write
|
||||
This here's my little corner of the web, where I publish
|
||||
<a href="{{ BlogURL "posts" }}">posts</a>
|
||||
about projects I'm working on and things that interest me (which you can
|
||||
<a href="{{ BlogURL "follow" }}">follow</a>,
|
||||
if you like). Beyond that I've listed various links related to me below.
|
||||
if you like).
|
||||
</p>
|
||||
|
||||
<h2>Social</h2>
|
||||
<p>Feel free to hmu on any of these if you'd like to get in touch.</p>
|
||||
<ul>
|
||||
<li><a href="https://matrix.to/#/@mediocregopher:waffle.farm">@mediocregopher:waffle.farm</a> is my matrix handle.</li>
|
||||
<li><a href="https://social.cryptic.io/@mediocregopher">@mediocregopher@social.cryptic.io</a> is my mastodon handle.</li>
|
||||
<li><a href="https://bgpicciano.com">bgpicciano.com</a> is my cover site/resume.</li>
|
||||
<li><a href="mailto:mediocregopher@gmail.com">mediocregopher@gmail.com</a> is my email.</li>
|
||||
<li><a href="https://news.cryptic.io">Cryptic News</a> aggregates interesting blogs.</li>
|
||||
<li>Matrix: <a href="https://matrix.to/#/@mediocregopher:waffle.farm">@mediocregopher:waffle.farm</a></li>
|
||||
<li>Mastodon: <a href="https://social.cryptic.io/@mediocregopher">@mediocregopher@social.cryptic.io</a></li>
|
||||
<li>Email: <a href="mailto:mediocregopher@gmail.com">mediocregopher@gmail.com</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Dev</h2>
|
||||
@ -36,12 +35,17 @@
|
||||
<li><a href="https://exchange.art/artists/mediocregopher/series">Exchange.art</a> lists others.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Other</h2>
|
||||
<ul>
|
||||
<li><a href="https://bgpicciano.com">bgpicciano.com</a> is my cover site/resume.</li>
|
||||
<li><a href="https://news.cryptic.io">Cryptic News</a> aggregates interesting blogs.</li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>I'm not affiliated with these, but they're worth listing.</p>
|
||||
<ul>
|
||||
<li><a href="https://search.marginalia.nu/">Marginalia</a> reminds me of the old internet.</li>
|
||||
<li><a href="https://drewdevault.com/2020/11/01/What-is-Gemini-anyway.html">Gemini</a> is a protocol I soon hope to add to this site.</li>
|
||||
<li><a href="https://www.nts.live/">NTS</a> is a great internet radio station.</li>
|
||||
<li><a href="https://yamakan.place/palestine/#">Radio alHara</a> is another great internet radio station.</li>
|
||||
</ul>
|
||||
|
@ -6,8 +6,8 @@
|
||||
</p>
|
||||
{{ else }}
|
||||
<p>
|
||||
Posts are listed in chronological order. If you aren't sure of where to
|
||||
start I recommend picking at random.
|
||||
Here you'll find an archive of all published posts. The content varies
|
||||
almost as much as the quality!
|
||||
</p>
|
||||
{{ end }}
|
||||
|
||||
|
@ -37,6 +37,16 @@ type PreprocessFunctions struct {
|
||||
Image func(args ...string) (string, error)
|
||||
}
|
||||
|
||||
func (funcs PreprocessFunctions) ToFuncsMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"BlogURL": funcs.BlogURL,
|
||||
"AssetURL": funcs.AssetURL,
|
||||
"PostURL": funcs.PostURL,
|
||||
"StaticURL": funcs.StaticURL,
|
||||
"Image": funcs.Image,
|
||||
}
|
||||
}
|
||||
|
||||
// PreprocessBody interprets the Post's Body as a text template which may use
|
||||
// any of the functions found in PreprocessFunctions (all must be set). It
|
||||
// executes the template and writes the result to the given writer.
|
||||
@ -44,13 +54,7 @@ func (p Post) PreprocessBody(into io.Writer, funcs PreprocessFunctions) error {
|
||||
|
||||
tpl := template.New("")
|
||||
|
||||
tpl.Funcs(template.FuncMap{
|
||||
"BlogURL": funcs.BlogURL,
|
||||
"AssetURL": funcs.AssetURL,
|
||||
"PostURL": funcs.PostURL,
|
||||
"StaticURL": funcs.StaticURL,
|
||||
"Image": funcs.Image,
|
||||
})
|
||||
tpl.Funcs(funcs.ToFuncsMap())
|
||||
|
||||
tpl, err := tpl.Parse(p.Body)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user