diff --git a/docs/usage.md b/docs/usage.md
index a1bfd1d..ab14214 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -1,6 +1,6 @@
# How to use GoBlog
-This section of the documentation is a work in progress!
+This section of the documentation is a **work in progress**!
## Posting
diff --git a/editorFiles.go b/editorFiles.go
index aefe875..2f0fcf4 100644
--- a/editorFiles.go
+++ b/editorFiles.go
@@ -16,8 +16,8 @@ func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
}
// Check if files at all
if len(files) == 0 {
- a.render(w, r, templateEditorFiles, &renderData{
- Data: map[string]interface{}{},
+ a.renderNew(w, r, a.renderEditorFiles, &renderData{
+ Data: &editorFilesRenderData{},
})
return
}
@@ -39,10 +39,10 @@ func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
return
}
// Serve HTML
- a.render(w, r, templateEditorFiles, &renderData{
- Data: map[string]interface{}{
- "Files": files,
- "Uses": uses,
+ a.renderNew(w, r, a.renderEditorFiles, &renderData{
+ Data: &editorFilesRenderData{
+ files: files,
+ uses: uses,
},
})
}
diff --git a/go.mod b/go.mod
index a02d34e..86d0eea 100644
--- a/go.mod
+++ b/go.mod
@@ -55,7 +55,7 @@ require (
// master
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-20220112180741-5e0467b6c7ce
+ golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7
diff --git a/go.sum b/go.sum
index 3a097d5..099479c 100644
--- a/go.sum
+++ b/go.sum
@@ -472,8 +472,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8 h1:kACShD3qhmr/3rLmg1yXyt+N4HcwutKyPRB93s54TIU=
+golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
diff --git a/indieAuthServer.go b/indieAuthServer.go
index 7c6d586..f1859f1 100644
--- a/indieAuthServer.go
+++ b/indieAuthServer.go
@@ -31,7 +31,7 @@ func (a *goBlog) indieAuthRequest(w http.ResponseWriter, r *http.Request) {
return
}
// Render page that let's the user authorize the app
- a.render(w, r, templateIndieAuth, &renderData{
+ a.renderNew(w, r, a.renderIndieAuth, &renderData{
Data: iareq,
})
}
diff --git a/notifications.go b/notifications.go
index ae3eeaf..a82f382 100644
--- a/notifications.go
+++ b/notifications.go
@@ -154,13 +154,13 @@ func (a *goBlog) notificationsAdmin(w http.ResponseWriter, r *http.Request) {
}
nextPath = fmt.Sprintf("%s/page/%d", notificationsPath, nextPage)
// Render
- a.render(w, r, templateNotificationsAdmin, &renderData{
- Data: map[string]interface{}{
- "Notifications": notifications,
- "HasPrev": hasPrev,
- "HasNext": hasNext,
- "Prev": prevPath,
- "Next": nextPath,
+ a.renderNew(w, r, a.renderNotificationsAdmin, &renderData{
+ Data: ¬ificationsRenderData{
+ notifications: notifications,
+ hasPrev: hasPrev,
+ hasNext: hasNext,
+ prev: prevPath,
+ next: nextPath,
},
})
}
diff --git a/postsDb.go b/postsDb.go
index 0e9c192..b971659 100644
--- a/postsDb.go
+++ b/postsDb.go
@@ -628,7 +628,7 @@ select name, count(path) as count from (
group by name;
`
-func (db *database) usesOfMediaFile(names ...string) (counts map[string]int, err error) {
+func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
sqlArgs := []interface{}{dbNoCache}
var nameValues strings.Builder
for i, n := range names {
@@ -645,7 +645,7 @@ func (db *database) usesOfMediaFile(names ...string) (counts map[string]int, err
if err != nil {
return nil, err
}
- counts = map[string]int{}
+ counts = make([]int, len(names))
var name string
var count int
for rows.Next() {
@@ -653,7 +653,12 @@ func (db *database) usesOfMediaFile(names ...string) (counts map[string]int, err
if err != nil {
return nil, err
}
- counts[name] = count
+ for i, n := range names {
+ if n == name {
+ counts[i] = count
+ break
+ }
+ }
}
return counts, nil
}
diff --git a/postsDb_test.go b/postsDb_test.go
index 82c2b11..4767cc5 100644
--- a/postsDb_test.go
+++ b/postsDb_test.go
@@ -358,7 +358,7 @@ func Test_usesOfMediaFile(t *testing.T) {
counts, err := app.db.usesOfMediaFile("test.jpg")
require.NoError(t, err)
assert.Len(t, counts, 1)
- if assert.NotNil(t, counts["test.jpg"]) {
- assert.Equal(t, 2, counts["test.jpg"])
+ if assert.NotEmpty(t, counts) {
+ assert.Equal(t, 2, counts[0])
}
}
diff --git a/render.go b/render.go
index 342b385..8a88967 100644
--- a/render.go
+++ b/render.go
@@ -16,13 +16,10 @@ const (
templatesDir = "templates"
templatesExt = ".gohtml"
- templateBase = "base"
- templateEditor = "editor"
- templateEditorFiles = "editorfiles"
- templateCommentsAdmin = "commentsadmin"
- templateNotificationsAdmin = "notificationsadmin"
- templateWebmentionAdmin = "webmentionadmin"
- templateIndieAuth = "indieauth"
+ templateBase = "base"
+ templateEditor = "editor"
+ templateCommentsAdmin = "commentsadmin"
+ templateWebmentionAdmin = "webmentionadmin"
)
func (a *goBlog) initRendering() error {
@@ -42,14 +39,12 @@ func (a *goBlog) initRendering() error {
},
// Others
"dateformat": dateFormat,
- "isodate": isoDateFormat,
"unixtodate": unixToLocalDateString,
"now": localNowString,
"asset": a.assetFileName,
"string": a.ts.GetTemplateStringVariantFunc(),
"absolute": a.getFullAddress,
"opensearch": openSearchUrl,
- "mbytes": mBytesString,
"editortemplate": a.editorPostTemplate,
"editorpostdesc": a.editorPostDesc,
}
diff --git a/templates/editorfiles.gohtml b/templates/editorfiles.gohtml
deleted file mode 100644
index 86db73d..0000000
--- a/templates/editorfiles.gohtml
+++ /dev/null
@@ -1,29 +0,0 @@
-{{ define "title" }}
-
{{ string .Blog.Lang "mediafiles" }} - {{ mdtitle .Blog.Title }}
-{{ end }}
-
-{{ define "main" }}
-
- {{ $blog := .Blog }}
- {{ string .Blog.Lang "mediafiles" }}
- {{ if .Data.Files }}
-
-
- {{ else }}
- {{ string .Blog.Lang "nofiles" }}
- {{ end }}
-
-{{ end }}
-
-{{ define "editorfiles" }}
- {{ template "base" . }}
-{{ end }}
\ No newline at end of file
diff --git a/templates/indieauth.gohtml b/templates/indieauth.gohtml
deleted file mode 100644
index c372dc0..0000000
--- a/templates/indieauth.gohtml
+++ /dev/null
@@ -1,33 +0,0 @@
-{{ define "title" }}
- {{ string .Blog.Lang "indieauth" }} - {{ .Blog.Title }}
-{{ end }}
-
-{{ define "main" }}
-
- {{ string .Blog.Lang "indieauth" }}
-
-
-{{ end }}
-
-{{ define "indieauth" }}
- {{ template "base" . }}
-{{ end }}
\ No newline at end of file
diff --git a/templates/notificationsadmin.gohtml b/templates/notificationsadmin.gohtml
deleted file mode 100644
index b4ac168..0000000
--- a/templates/notificationsadmin.gohtml
+++ /dev/null
@@ -1,29 +0,0 @@
-{{ define "title" }}
- {{ string "notifications" }} - {{ mdtitle .Blog.Title }}
-{{ end }}
-
-{{ define "main" }}
-
- {{ string "notifications" }}
- {{ range $i, $notification := .Data.Notifications }}
-
-
{{ unixtodate $notification.Time }}
- {{ md $notification.Text }}
-
-
- {{ end }}
- {{ if .Data.HasPrev }}
- {{ string "prev" }}
- {{ end }}
- {{ if .Data.HasNext }}
- {{ string "next" }}
- {{ end }}
-
-{{ end }}
-
-{{ define "notificationsadmin" }}
- {{ template "base" . }}
-{{ end }}
\ No newline at end of file
diff --git a/ui.go b/ui.go
index 554283a..5d1ca52 100644
--- a/ui.go
+++ b/ui.go
@@ -5,6 +5,7 @@ import (
"strings"
"time"
+ "github.com/hacdias/indieauth"
"github.com/kaorimatz/go-opml"
"github.com/thoas/go-funk"
)
@@ -398,20 +399,7 @@ func (a *goBlog) renderIndex(hb *htmlBuilder, rd *renderData) {
hb.writeElementClose("p")
}
// Navigation
- if id.hasPrev {
- hb.writeElementOpen("p")
- hb.writeElementOpen("a", "href", id.prev) // TODO: rel=prev?
- hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "prev"))
- hb.writeElementClose("a")
- hb.writeElementClose("p")
- }
- if id.hasNext {
- hb.writeElementOpen("p")
- hb.writeElementOpen("a", "href", id.next) // TODO: rel=next?
- hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "next"))
- hb.writeElementClose("a")
- hb.writeElementClose("p")
- }
+ a.renderPagination(hb, rd.Blog, id.hasPrev, id.hasNext, id.prev, id.next)
// Author
a.renderAuthor(hb)
hb.writeElementClose("main")
@@ -1054,3 +1042,172 @@ func (a *goBlog) renderStaticHome(hb *htmlBuilder, rd *renderData) {
},
)
}
+
+func (a *goBlog) renderIndieAuth(hb *htmlBuilder, rd *renderData) {
+ indieAuthRequest, ok := rd.Data.(*indieauth.AuthenticationRequest)
+ if !ok {
+ return
+ }
+ a.renderBase(
+ hb, rd,
+ func(hb *htmlBuilder) {
+ a.renderTitleTag(hb, rd.Blog, a.ts.GetTemplateStringVariant(rd.Blog.Lang, "indieauth"))
+ },
+ func(hb *htmlBuilder) {
+ hb.writeElementOpen("main")
+ // Title
+ hb.writeElementOpen("h1")
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "indieauth"))
+ hb.writeElementClose("h1")
+ hb.writeElementClose("main")
+ // Form
+ hb.writeElementOpen("form", "method", "post", "action", "/indieauth/accept", "class", "p")
+ // Scopes
+ if scopes := indieAuthRequest.Scopes; len(scopes) > 0 {
+ hb.writeElementOpen("h3")
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "scopes"))
+ hb.writeElementClose("h3")
+ hb.writeElementOpen("ul")
+ for _, scope := range scopes {
+ hb.writeElementOpen("li")
+ hb.writeElementOpen("input", "type", "checkbox", "name", "scopes", "value", scope, "id", "scope-"+scope, "checked", "")
+ hb.writeElementOpen("label", "for", "scope-"+scope)
+ hb.writeEscaped(scope)
+ hb.writeElementClose("label")
+ hb.writeElementClose("li")
+ }
+ hb.writeElementClose("ul")
+ }
+ // Client ID
+ hb.writeElementOpen("p")
+ hb.writeElementOpen("strong")
+ hb.writeEscaped("client_id:")
+ hb.writeElementClose("strong")
+ hb.write(" ")
+ hb.writeEscaped(indieAuthRequest.ClientID)
+ hb.writeElementClose("p")
+ // Redirect URI
+ hb.writeElementOpen("p")
+ hb.writeElementOpen("strong")
+ hb.writeEscaped("redirect_uri:")
+ hb.writeElementClose("strong")
+ hb.write(" ")
+ hb.writeEscaped(indieAuthRequest.RedirectURI)
+ hb.writeElementClose("p")
+ // Hidden form fields
+ hb.writeElementOpen("input", "type", "hidden", "name", "client_id", "value", indieAuthRequest.ClientID)
+ hb.writeElementOpen("input", "type", "hidden", "name", "redirect_uri", "value", indieAuthRequest.RedirectURI)
+ hb.writeElementOpen("input", "type", "hidden", "name", "state", "value", indieAuthRequest.State)
+ hb.writeElementOpen("input", "type", "hidden", "name", "code_challenge", "value", indieAuthRequest.CodeChallenge)
+ hb.writeElementOpen("input", "type", "hidden", "name", "code_challenge_method", "value", indieAuthRequest.CodeChallengeMethod)
+ // Submit button
+ hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "authenticate"))
+ hb.writeElementClose("form")
+ },
+ )
+}
+
+type editorFilesRenderData struct {
+ files []*mediaFile
+ uses []int
+}
+
+func (a *goBlog) renderEditorFiles(hb *htmlBuilder, rd *renderData) {
+ ef, ok := rd.Data.(*editorFilesRenderData)
+ if !ok {
+ return
+ }
+ a.renderBase(
+ hb, rd,
+ func(hb *htmlBuilder) {
+ a.renderTitleTag(hb, rd.Blog, a.ts.GetTemplateStringVariant(rd.Blog.Lang, "mediafiles"))
+ },
+ func(hb *htmlBuilder) {
+ hb.writeElementOpen("main")
+ // Title
+ hb.writeElementOpen("h1")
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "mediafiles"))
+ hb.writeElementClose("h1")
+ // Files
+ if len(ef.files) > 0 {
+ // Form
+ hb.writeElementOpen("form", "method", "post", "class", "fw p")
+ // Select with number of uses
+ hb.writeElementOpen("select", "name", "filename")
+ usesString := a.ts.GetTemplateStringVariant(rd.Blog.Lang, "fileuses")
+ for i, f := range ef.files {
+ hb.writeElementOpen("option", "value", f.Name)
+ hb.writeEscaped(fmt.Sprintf("%s (%s), %s, ~%d %s", f.Name, isoDateFormat(f.Time.String()), mBytesString(f.Size), ef.uses[i], usesString))
+ hb.writeElementClose("option")
+ }
+ hb.writeElementClose("select")
+ // View button
+ hb.writeElementOpen(
+ "input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "view"),
+ "formaction", rd.Blog.getRelativePath("/editor/files/view"),
+ )
+ // Delete button
+ hb.writeElementOpen(
+ "input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "delete"),
+ "formaction", rd.Blog.getRelativePath("/editor/files/delete"),
+ "class", "confirm", "data-confirmmessage", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "confirmdelete"),
+ )
+ hb.writeElementOpen("script", "src", a.assetFileName("js/formconfirm.js"), "defer", "")
+ hb.writeElementClose("script")
+ hb.writeElementClose("form")
+ } else {
+ hb.writeElementOpen("p")
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "nofiles"))
+ hb.writeElementClose("p")
+ }
+ hb.writeElementClose("main")
+ },
+ )
+}
+
+type notificationsRenderData struct {
+ notifications []*notification
+ hasPrev, hasNext bool
+ prev, next string
+}
+
+func (a *goBlog) renderNotificationsAdmin(hb *htmlBuilder, rd *renderData) {
+ nrd, ok := rd.Data.(*notificationsRenderData)
+ if !ok {
+ return
+ }
+ a.renderBase(
+ hb, rd,
+ func(hb *htmlBuilder) {
+ a.renderTitleTag(hb, rd.Blog, a.ts.GetTemplateStringVariant(rd.Blog.Lang, "notifications"))
+ },
+ func(hb *htmlBuilder) {
+ hb.writeElementOpen("main")
+ // Title
+ hb.writeElementOpen("h1")
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "notifications"))
+ hb.writeElementClose("h1")
+ // Notifications
+ for _, n := range nrd.notifications {
+ hb.writeElementOpen("div", "class", "p")
+ // Date
+ hb.writeElementOpen("p")
+ hb.writeElementOpen("i")
+ hb.writeEscaped(unixToLocalDateString(n.Time))
+ hb.writeElementClose("i")
+ hb.writeElementClose("p")
+ // Message
+ a.renderMarkdownToWriter(hb, n.Text, false)
+ // Delete form
+ hb.writeElementOpen("form", "method", "post", "action", "/notifications/delete")
+ hb.writeElementOpen("input", "type", "hidden", "name", "notificationid", "value", n.ID)
+ hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "delete"))
+ hb.writeElementClose("form")
+ hb.writeElementClose("div")
+ }
+ // Pagination
+ a.renderPagination(hb, rd.Blog, nrd.hasPrev, nrd.hasNext, nrd.prev, nrd.next)
+ hb.writeElementClose("main")
+ },
+ )
+}
diff --git a/uiComponents.go b/uiComponents.go
index 16a31c3..2cf6989 100644
--- a/uiComponents.go
+++ b/uiComponents.go
@@ -384,3 +384,21 @@ func (a *goBlog) renderTitleTag(hb *htmlBuilder, blog *configBlog, optionalTitle
hb.writeEscaped(a.renderMdTitle(blog.Title))
hb.writeElementClose("title")
}
+
+func (a *goBlog) renderPagination(hb *htmlBuilder, blog *configBlog, hasPrev, hasNext bool, prev, next string) {
+ // Navigation
+ if hasPrev {
+ hb.writeElementOpen("p")
+ hb.writeElementOpen("a", "href", prev) // TODO: rel=prev?
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(blog.Lang, "prev"))
+ hb.writeElementClose("a")
+ hb.writeElementClose("p")
+ }
+ if hasNext {
+ hb.writeElementOpen("p")
+ hb.writeElementOpen("a", "href", next) // TODO: rel=next?
+ hb.writeEscaped(a.ts.GetTemplateStringVariant(blog.Lang, "next"))
+ hb.writeElementClose("a")
+ hb.writeElementClose("p")
+ }
+}