One editor per blog

This commit is contained in:
Jan-Lukas Else 2021-01-17 12:53:07 +01:00
parent f3744227e1
commit 89b48b1d37
12 changed files with 153 additions and 130 deletions

View File

@ -38,7 +38,7 @@ func serveBlogStats(blog, statsPath string) func(w http.ResponseWriter, r *http.
counts = append(counts, count)
}
render(w, templateBlogStats, &renderData{
blogString: blog,
BlogString: blog,
Canonical: statsPath,
Data: map[string]interface{}{
"total": totalCount,

132
editor.go
View File

@ -10,72 +10,78 @@ import (
const editorPath = "/editor"
func serveEditor(w http.ResponseWriter, _ *http.Request) {
render(w, templateEditor, &renderData{
Data: map[string]interface{}{
"Drafts": loadDrafts(),
},
})
}
func serveEditorPost(w http.ResponseWriter, r *http.Request) {
if action := r.FormValue("editoraction"); action != "" {
switch action {
case "loadupdate":
parsedURL, err := url.Parse(r.FormValue("url"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
post, err := getPost(parsedURL.Path)
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
mf := post.toMfItem()
render(w, templateEditor, &renderData{
Data: map[string]interface{}{
"UpdatePostURL": parsedURL.String(),
"UpdatePostContent": mf.Properties.Content[0],
"Drafts": loadDrafts(),
},
})
case "updatepost":
urlValue := r.FormValue("url")
content := r.FormValue("content")
mf := map[string]interface{}{
"action": actionUpdate,
"url": urlValue,
"replace": map[string][]string{
"content": {
content,
},
},
}
jsonBytes, err := json.Marshal(mf)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
req, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(jsonBytes))
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
req.Header.Set(contentType, contentTypeJSON)
editorMicropubPost(w, req, false)
case "upload":
editorMicropubPost(w, r, true)
default:
serveError(w, r, "Unknown editoraction", http.StatusBadRequest)
}
return
func serveEditor(blog string) func(w http.ResponseWriter, _ *http.Request) {
return func(w http.ResponseWriter, _ *http.Request) {
render(w, templateEditor, &renderData{
BlogString: blog,
Data: map[string]interface{}{
"Drafts": loadDrafts(blog),
},
})
}
editorMicropubPost(w, r, false)
}
func loadDrafts() []*post {
ps, _ := getPosts(&postsRequestConfig{status: statusDraft})
func serveEditorPost(blog string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if action := r.FormValue("editoraction"); action != "" {
switch action {
case "loadupdate":
parsedURL, err := url.Parse(r.FormValue("url"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
post, err := getPost(parsedURL.Path)
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
mf := post.toMfItem()
render(w, templateEditor, &renderData{
BlogString: blog,
Data: map[string]interface{}{
"UpdatePostURL": parsedURL.String(),
"UpdatePostContent": mf.Properties.Content[0],
"Drafts": loadDrafts(blog),
},
})
case "updatepost":
urlValue := r.FormValue("url")
content := r.FormValue("content")
mf := map[string]interface{}{
"action": actionUpdate,
"url": urlValue,
"replace": map[string][]string{
"content": {
content,
},
},
}
jsonBytes, err := json.Marshal(mf)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
req, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(jsonBytes))
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
req.Header.Set(contentType, contentTypeJSON)
editorMicropubPost(w, req, false)
case "upload":
editorMicropubPost(w, r, true)
default:
serveError(w, r, "Unknown editoraction", http.StatusBadRequest)
}
return
}
editorMicropubPost(w, r, false)
}
}
func loadDrafts(blog string) []*post {
ps, _ := getPosts(&postsRequestConfig{status: statusDraft, blog: blog})
return ps
}

2
go.mod
View File

@ -62,7 +62,7 @@ require (
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

4
go.sum
View File

@ -497,8 +497,8 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnf
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 h1:K+NlvTLy0oONtRtkl1jRD9xIhnItbG2PiE7YOdjPb+k=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

14
http.go
View File

@ -106,13 +106,6 @@ func buildHandler() (http.Handler, error) {
mpRouter.Post(micropubMediaSubPath, serveMicropubMedia)
})
// Editor
r.Route("/editor", func(mpRouter chi.Router) {
mpRouter.Use(middleware.NoCache, minifier.Middleware, authMiddleware)
mpRouter.Get("/", serveEditor)
mpRouter.Post("/", serveEditorPost)
})
// IndieAuth
r.Route("/indieauth", func(indieauthRouter chi.Router) {
indieauthRouter.Use(middleware.NoCache, minifier.Middleware)
@ -325,6 +318,13 @@ func buildHandler() (http.Handler, error) {
r.With(minifier.Middleware).Get(cp.Path, handler)
}
}
// Editor
r.Route(blogPath+"/editor", func(mpRouter chi.Router) {
mpRouter.Use(middleware.NoCache, minifier.Middleware, authMiddleware)
mpRouter.Get("/", serveEditor(blog))
mpRouter.Post("/", serveEditorPost(blog))
})
}
// Sitemap

View File

@ -67,7 +67,7 @@ func servePost(w http.ResponseWriter, r *http.Request) {
}
w.Header().Add("Link", fmt.Sprintf("<%s>; rel=shortlink", p.shortURL()))
render(w, template, &renderData{
blogString: p.Blog,
BlogString: p.Blog,
Canonical: canonical,
Data: p,
})
@ -279,7 +279,7 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
summaryTemplate = templateSummary
}
render(w, templateIndex, &renderData{
blogString: ic.blog,
BlogString: ic.blog,
Canonical: appConfig.Server.PublicAddress + path,
Data: map[string]interface{}{
"Title": title,

View File

@ -222,7 +222,7 @@ func initRendering() error {
}
type renderData struct {
blogString string
BlogString string
Canonical string
Blog *configBlog
Data interface{}
@ -231,10 +231,18 @@ type renderData struct {
func render(w http.ResponseWriter, template string, data *renderData) {
// Check render data
if data.Blog == nil {
if len(data.blogString) == 0 {
data.blogString = appConfig.DefaultBlog
if len(data.BlogString) == 0 {
data.BlogString = appConfig.DefaultBlog
}
data.Blog = appConfig.Blogs[data.BlogString]
}
if data.BlogString == "" {
for s, b := range appConfig.Blogs {
if b == data.Blog {
data.BlogString = s
break
}
}
data.Blog = appConfig.Blogs[data.blogString]
}
if data.Data == nil {
data.Data = map[string]interface{}{}

View File

@ -21,7 +21,7 @@ func serveSearch(blog string, path string) func(w http.ResponseWriter, r *http.R
return
}
render(w, templateSearch, &renderData{
blogString: blog,
BlogString: blog,
Canonical: appConfig.Server.PublicAddress + path,
})
}

View File

@ -10,7 +10,7 @@ func serveTaxonomy(blog string, tax *taxonomy) func(w http.ResponseWriter, r *ht
return
}
render(w, templateTaxonomy, &renderData{
blogString: blog,
BlogString: blog,
Canonical: appConfig.Server.PublicAddress + r.URL.Path,
Data: map[string]interface{}{
"Taxonomy": tax,

View File

@ -9,10 +9,13 @@
<form class="fw-form p" method="post">
<input type="hidden" name="h" value="entry">
<textarea name="content" class="monospace h400p">---
title:
status: draft
blog: {{ .BlogString }}
section:
slug:
blog:
title:
tags:
-
---
</textarea>
<input class="fw" type="submit" value="{{ string .Blog.Lang "create" }}">

View File

@ -1,21 +1,27 @@
publishedon: "Veröffentlicht am"
updatedon: "Aktualisiert am"
next: "Weiter"
prev: "Zurück"
view: "Anschauen"
replyto: "Antwort an"
anoncomment: "Du kannst auch einen anonymen Kommentar verfassen."
count: "Anzahl"
create: "Erstellen"
delete: "Löschen"
drafts: "Entwürfe"
editor: "Editor"
interactions: "Interaktionen & Kommentare"
interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein."
likeof: "Gefällt mir von"
translations: "Übersetzungen"
next: "Weiter"
oldcontent: "⚠️ Dieser Eintrag ist bereits über ein Jahr alt. Er ist möglicherweise nicht mehr aktuell. Meinungen können sich geändert haben."
prev: "Zurück"
publishedon: "Veröffentlicht am"
replyto: "Antwort an"
search: "Suchen"
send: "Senden (zur Überprüfung)"
share: "Teilen"
shorturl: "Kurz-Link:"
speak: "Lies mir bitte vor."
stopspeak: "Hör auf zu sprechen!"
oldcontent: "⚠️ Dieser Eintrag ist bereits über ein Jahr alt. Er ist möglicherweise nicht mehr aktuell. Meinungen können sich geändert haben."
search: "Suchen"
shorturl: "Kurz-URL:"
year: "Jahr"
count: "Anzahl"
total: "Gesamt"
anoncomment: "Du kannst auch einen anonymen Kommentar verfassen."
interactions: "Interaktionen & Kommentare"
send: "Senden (zur Überprüfung)"
interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein."
translations: "Übersetzungen"
update: "Aktualisieren"
updatedon: "Aktualisiert am"
upload: "Hochladen"
view: "Anschauen"
year: "Jahr"

View File

@ -1,37 +1,37 @@
publishedon: "Published on"
updatedon: "Updated on"
next: "Next"
prev: "Previous"
view: "View"
anoncomment: "You can also create an anonymous comment."
approve: "Approve"
approved: "Approved"
authenticate: "Authenticate"
scopes: "Scopes"
count: "Count"
create: "Create"
delete: "Delete"
drafts: "Drafts"
editor: "Editor"
indieauth: "IndieAuth"
replyto: "Reply to"
interactions: "Interactions & Comments"
interactionslabel: "Have you published a response to this? Paste the URL here."
likeof: "Like of"
translations: "Translations"
login: "Login"
next: "Next"
oldcontent: "⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed."
password: "Password"
prev: "Previous"
publishedon: "Published on"
replyto: "Reply to"
scopes: "Scopes"
search: "Search"
send: "Send (to review)"
share: "Share"
shorturl: "Short link:"
speak: "Read to me, please."
stopspeak: "Stop speaking!"
oldcontent: "⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed."
webmentions: "Webmentions"
verified: "Verified"
approved: "Approved"
delete: "Delete"
approve: "Approve"
anoncomment: "You can also create an anonymous comment."
interactions: "Interactions & Comments"
send: "Send (to review)"
interactionslabel: "Have you published a response to this? Paste the URL here."
search: "Search"
editor: "Editor"
create: "Create"
update: "Update"
upload: "Upload"
login: "Login"
username: "Username"
password: "Password"
shorturl: "Short URL:"
year: "Year"
count: "Count"
total: "Total"
drafts: "Drafts"
translations: "Translations"
update: "Update"
updatedon: "Updated on"
upload: "Upload"
username: "Username"
verified: "Verified"
view: "View"
webmentions: "Webmentions"
year: "Year"