From 37a9e1f29c7282d37cc3521f0c99884971d922be Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Fri, 23 Sep 2022 11:05:07 +0200 Subject: [PATCH] Split status in status and visibility Closes #38 This aligns the implementation more with the IndieWeb standards and allows setting and updating status and visibility individually. --- authentication.go | 10 +++-- blogstats.go | 4 +- blogstats_test.go | 42 +++++++++++++++----- check.go | 7 +++- dbmigrations/00031.sql | 11 ++++++ editor.go | 19 +++++++-- geoMap.go | 24 +++++++----- go.mod | 2 +- go.sum | 4 +- http.go | 32 +++++++++------ micropub.go | 77 ++++++++++++++++++------------------ micropub_test.go | 4 +- nodeinfo.go | 3 +- posts.go | 52 +++++++++++++----------- postsDb.go | 89 +++++++++++++++++++++++++----------------- postsDb_test.go | 36 +++++++++-------- postsDeleter.go | 2 +- postsFuncs.go | 19 +++++---- postsScheduler.go | 4 +- postsScheduler_test.go | 27 +++++++------ reactions_test.go | 2 +- sitemap.go | 3 +- strings/de.yaml | 7 ++-- strings/default.yaml | 7 ++-- strings/es.yaml | 3 -- strings/pt-br.yaml | 3 -- telegram_test.go | 10 +++-- uiComponents.go | 8 ++++ webmentionSending.go | 2 +- 29 files changed, 304 insertions(+), 209 deletions(-) create mode 100644 dbmigrations/00031.sql diff --git a/authentication.go b/authentication.go index de06f34..3461a28 100644 --- a/authentication.go +++ b/authentication.go @@ -176,9 +176,13 @@ func (a *goBlog) serveLogout(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusFound) } -func (a *goBlog) getDefaultPostStatusse(r *http.Request) []postStatus { +func (a *goBlog) getDefaultPostStates(r *http.Request) (status []postStatus, visibility []postVisibility) { if a.isLoggedIn(r) { - return []postStatus{statusPublished, statusUnlisted, statusPrivate} + status = []postStatus{statusPublished} + visibility = []postVisibility{visibilityPublic, visibilityUnlisted, visibilityPrivate} + } else { + status = []postStatus{statusPublished} + visibility = []postVisibility{visibilityPublic} } - return []postStatus{statusPublished} + return } diff --git a/blogstats.go b/blogstats.go index 1e3e1bc..1437f03 100644 --- a/blogstats.go +++ b/blogstats.go @@ -65,7 +65,7 @@ with filtered as ( tolocal(published) as pub, mdtext(coalesce(content, '')) as content from posts - where status = @status and blog = @blog + where status = @status and visibility = @visibility and blog = @blog ) ) select * @@ -154,7 +154,7 @@ func (db *database) getBlogStats(blog string) (data *blogStatsData, err error) { Months: map[string][]blogStatsRow{}, } // Query and scan - rows, err := db.Query(blogStatsSql, sql.Named("status", statusPublished), sql.Named("blog", blog)) + rows, err := db.Query(blogStatsSql, sql.Named("status", statusPublished), sql.Named("visibility", visibilityPublic), sql.Named("blog", blog)) if err != nil { return nil, err } diff --git a/blogstats_test.go b/blogstats_test.go index fdd8b38..2b06b01 100644 --- a/blogstats_test.go +++ b/blogstats_test.go @@ -41,20 +41,42 @@ func Test_blogStats(t *testing.T) { // Insert post err := app.createPost(&post{ - Content: "This is a simple **test** post", - Blog: "en", - Section: "test", - Published: "2020-06-01", - Status: statusPublished, + Content: "This is a simple **test** post", + Blog: "en", + Section: "test", + Published: "2020-06-01", + Status: statusPublished, + Visibility: visibilityPublic, }) require.NoError(t, err) err = app.createPost(&post{ - Content: "This is another simple **test** post", - Blog: "en", - Section: "test", - Published: "2021-05-01", - Status: statusPublished, + Content: "This is another simple **test** post", + Blog: "en", + Section: "test", + Published: "2021-05-01", + Status: statusPublished, + Visibility: visibilityPublic, + }) + require.NoError(t, err) + + err = app.createPost(&post{ + Content: "This is a private post, that doesn't count", + Blog: "en", + Section: "test", + Published: "2021-05-01", + Status: statusPublished, + Visibility: visibilityPrivate, + }) + require.NoError(t, err) + + err = app.createPost(&post{ + Content: "Unlisted posts don't count as well", + Blog: "en", + Section: "test", + Published: "2021-05-01", + Status: statusPublished, + Visibility: visibilityUnlisted, }) require.NoError(t, err) diff --git a/check.go b/check.go index 16e13c8..ce45bb2 100644 --- a/check.go +++ b/check.go @@ -18,8 +18,11 @@ import ( ) func (a *goBlog) checkAllExternalLinks() { - // Get all published posts without parameters - posts, err := a.getPosts(&postsRequestConfig{status: statusPublished, withoutParameters: true}) + posts, err := a.getPosts(&postsRequestConfig{ + status: []postStatus{statusPublished}, + visibility: []postVisibility{visibilityPublic, visibilityUnlisted}, + withoutParameters: true, + }) if err != nil { log.Println(err.Error()) return diff --git a/dbmigrations/00031.sql b/dbmigrations/00031.sql new file mode 100644 index 0000000..7a85786 --- /dev/null +++ b/dbmigrations/00031.sql @@ -0,0 +1,11 @@ +alter table posts add visibility text not null default ''; +update posts set status = 'published', visibility = 'public' where status = 'published'; +update posts set status = 'published-deleted', visibility = 'public' where status = 'published-deleted'; +update posts set status = 'draft', visibility = 'public' where status = 'draft'; +update posts set status = 'draft-deleted', visibility = 'public' where status = 'draft-deleted'; +update posts set status = 'scheduled', visibility = 'public' where status = 'scheduled'; +update posts set status = 'scheduled-deleted', visibility = 'public' where status = 'scheduled-deleted'; +update posts set status = 'published', visibility = 'private' where status = 'private'; +update posts set status = 'published-deleted', visibility = 'private' where status = 'private-deleted'; +update posts set status = 'published', visibility = 'unlisted' where status = 'unlisted'; +update posts set status = 'published-deleted', visibility = 'unlisted' where status = 'unlisted-deleted'; \ No newline at end of file diff --git a/editor.go b/editor.go index f707b2a..0183773 100644 --- a/editor.go +++ b/editor.go @@ -194,6 +194,7 @@ func (*goBlog) editorPostTemplate(blog string, bc *configBlog) string { marsh("blog", blog) marsh("section", bc.DefaultSection) marsh("status", statusDraft) + marsh("visibility", visibilityPublic) marsh("priority", 0) marsh("slug", "") marsh("title", "") @@ -206,8 +207,8 @@ func (*goBlog) editorPostTemplate(blog string, bc *configBlog) string { func (a *goBlog) editorPostDesc(bc *configBlog) string { t := a.ts.GetTemplateStringVariant(bc.Lang, "editorpostdesc") - paramBuilder, statusBuilder := bufferpool.Get(), bufferpool.Get() - defer bufferpool.Put(paramBuilder, statusBuilder) + paramBuilder, statusBuilder, visibilityBuilder := bufferpool.Get(), bufferpool.Get(), bufferpool.Get() + defer bufferpool.Put(paramBuilder, statusBuilder, visibilityBuilder) for i, param := range []string{ "published", "updated", @@ -236,7 +237,7 @@ func (a *goBlog) editorPostDesc(bc *configBlog) string { paramBuilder.WriteByte('`') } for i, status := range []postStatus{ - statusDraft, statusPublished, statusUnlisted, statusScheduled, statusPrivate, + statusPublished, statusDraft, statusScheduled, } { if i > 0 { statusBuilder.WriteString(", ") @@ -245,5 +246,15 @@ func (a *goBlog) editorPostDesc(bc *configBlog) string { statusBuilder.WriteString(string(status)) statusBuilder.WriteByte('`') } - return fmt.Sprintf(t, paramBuilder.String(), "status", statusBuilder.String()) + for i, visibility := range []postVisibility{ + visibilityPublic, visibilityUnlisted, visibilityPrivate, + } { + if i > 0 { + visibilityBuilder.WriteString(", ") + } + visibilityBuilder.WriteByte('`') + visibilityBuilder.WriteString(string(visibility)) + visibilityBuilder.WriteByte('`') + } + return fmt.Sprintf(t, paramBuilder.String(), "status", "visibility", statusBuilder.String(), visibilityBuilder.String()) } diff --git a/geoMap.go b/geoMap.go index 01009e4..14e3389 100644 --- a/geoMap.go +++ b/geoMap.go @@ -17,12 +17,14 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) { mapPath := bc.getRelativePath(defaultIfEmpty(bc.Map.Path, defaultGeoMapPath)) canonical := a.getFullAddress(mapPath) - allPostsWithLocation, err := a.db.countPosts(&postsRequestConfig{ + allPostsWithLocationRequestConfig := &postsRequestConfig{ blog: blog, - statusse: a.getDefaultPostStatusse(r), parameters: []string{a.cfg.Micropub.LocationParam, gpxParameter}, withOnlyParameters: []string{a.cfg.Micropub.LocationParam, gpxParameter}, - }) + } + allPostsWithLocationRequestConfig.status, allPostsWithLocationRequestConfig.visibility = a.getDefaultPostStates(r) + + allPostsWithLocation, err := a.db.countPosts(allPostsWithLocationRequestConfig) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return @@ -55,14 +57,16 @@ const geoMapTracksSubpath = "/tracks.json" func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) { blog, _ := a.getBlog(r) - allPostsWithTracks, err := a.getPosts(&postsRequestConfig{ + allPostsWithTracksRequestConfig := &postsRequestConfig{ blog: blog, - statusse: a.getDefaultPostStatusse(r), parameters: []string{gpxParameter}, withOnlyParameters: []string{gpxParameter}, excludeParameter: showRouteParam, excludeParameterValue: "false", // Don't show hidden route tracks - }) + } + allPostsWithTracksRequestConfig.status, allPostsWithTracksRequestConfig.visibility = a.getDefaultPostStates(r) + + allPostsWithTracks, err := a.getPosts(allPostsWithTracksRequestConfig) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return @@ -101,12 +105,14 @@ const geoMapLocationsSubpath = "/locations.json" func (a *goBlog) serveGeoMapLocations(w http.ResponseWriter, r *http.Request) { blog, _ := a.getBlog(r) - allPostsWithLocations, err := a.getPosts(&postsRequestConfig{ + allPostsWithLocationRequestConfig := &postsRequestConfig{ blog: blog, - statusse: a.getDefaultPostStatusse(r), parameters: []string{a.cfg.Micropub.LocationParam}, withOnlyParameters: []string{a.cfg.Micropub.LocationParam}, - }) + } + allPostsWithLocationRequestConfig.status, allPostsWithLocationRequestConfig.visibility = a.getDefaultPostStates(r) + + allPostsWithLocations, err := a.getPosts(allPostsWithLocationRequestConfig) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return diff --git a/go.mod b/go.mod index 3642d19..033fdb8 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( // master github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 + golang.org/x/net v0.0.0-20220921203646-d300de134e69 golang.org/x/sync v0.0.0-20220907140024-f12130a52804 golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index d67b40d..382bcd9 100644 --- a/go.sum +++ b/go.sum @@ -624,8 +624,8 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 h1:asZqf0wXastQr+DudYagQS8uBO8bHKeYD1vbAvGmFL8= -golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= +golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= diff --git a/http.go b/http.go index 2120966..e5f9eaf 100644 --- a/http.go +++ b/http.go @@ -259,16 +259,16 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc { path := r.URL.Path row, err := a.db.QueryRow(` -- normal posts - select 'post', status, 200 from posts where path = @path + select 'post', status, visibility, 200 from posts where path = @path union all -- short paths - select 'alias', path, 301 from shortpath where printf('/s/%x', id) = @path + select 'alias', path, '', 301 from shortpath where printf('/s/%x', id) = @path union all -- post aliases - select 'alias', path, 302 from post_parameters where parameter = 'aliases' and value = @path + select 'alias', path, '', 302 from post_parameters where parameter = 'aliases' and value = @path union all -- deleted posts - select 'deleted', '', 410 from deleted where path = @path + select 'deleted', '', '', 410 from deleted where path = @path -- just select the first result limit 1 `, sql.Named("path", path)) @@ -276,9 +276,9 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return } - var pathType, value string + var pathType, value1, value2 string var status int - err = row.Scan(&pathType, &value, &status) + err = row.Scan(&pathType, &value1, &value2, &status) if err != nil { if !errors.Is(err, sql.ErrNoRows) { // Error @@ -290,26 +290,32 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc { // Found post or alias switch pathType { case "post": - // Is post, check status - switch postStatus(value) { - case statusPublished, statusUnlisted: - alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) + // Check status + switch postStatus(value1) { + case statusPublished: + // Check visibility + switch postVisibility(value2) { + case visibilityPublic, visibilityUnlisted: + alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) + default: // private, etc. + alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) + } return - case statusPublishedDeleted, statusUnlistedDeleted: + case statusPublishedDeleted: if a.isLoggedIn(r) { a.servePost(w, r) return } alicePrivate.Append(a.cacheMiddleware).ThenFunc(a.serve410).ServeHTTP(w, r) return - default: // private, draft, scheduled, etc. + default: // draft, scheduled, etc. alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) return } case "alias": // Is alias, redirect alicePrivate.Append(cacheLoggedIn, a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, value, status) + http.Redirect(w, r, value1, status) }).ServeHTTP(w, r) return case "deleted": diff --git a/micropub.go b/micropub.go index b26516f..6c006cf 100644 --- a/micropub.go +++ b/micropub.go @@ -168,19 +168,17 @@ func (a *goBlog) micropubParseValuePostParamsValueMap(entry *post, values map[st delete(values, "mp-channel") } // Status - statusStr := "" if status, ok := values["post-status"]; ok && len(status) > 0 { - statusStr = status[0] + statusStr := status[0] + entry.Status = micropubStatus(statusStr) delete(values, "post-status") } - visibilityStr := "" + // Visibility if visibility, ok := values["visibility"]; ok && len(visibility) > 0 { - visibilityStr = visibility[0] + visibilityStr := visibility[0] + entry.Visibility = micropubVisibility(visibilityStr) delete(values, "visibility") } - if finalStatus := micropubStatus(statusNil, statusStr, visibilityStr); finalStatus != statusNil { - entry.Status = finalStatus - } // Parameter if name, ok := values["name"]; ok { entry.Parameters["title"] = name @@ -297,16 +295,14 @@ func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) entry.setChannel(mf.Properties.MpChannel[0]) } // Status - status := "" if len(mf.Properties.PostStatus) > 0 { - status = mf.Properties.PostStatus[0] + status := mf.Properties.PostStatus[0] + entry.Status = micropubStatus(status) } - visibility := "" + // Visibility if len(mf.Properties.Visibility) > 0 { - visibility = mf.Properties.Visibility[0] - } - if finalStatus := micropubStatus(statusNil, status, visibility); finalStatus != statusNil { - entry.Status = finalStatus + visibility := mf.Properties.Visibility[0] + entry.Visibility = micropubVisibility(visibility) } // Parameter if len(mf.Properties.Name) > 0 { @@ -398,6 +394,10 @@ func (a *goBlog) extractParamsFromContent(p *post) error { p.Status = postStatus(status[0]) delete(p.Parameters, "status") } + if visibility := p.Parameters["visibility"]; len(visibility) == 1 { + p.Visibility = postVisibility(visibility[0]) + delete(p.Parameters, "visibility") + } if priority := p.Parameters["priority"]; len(priority) == 1 { p.Priority = cast.ToInt(priority[0]) delete(p.Parameters, "priority") @@ -513,13 +513,14 @@ func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string return } // Check if post is marked as deleted - if strings.HasSuffix(string(p.Status), statusDeletedSuffix) { + if p.Deleted() { a.serveError(w, r, "post is marked as deleted, undelete it first", http.StatusBadRequest) return } // Update post oldPath := p.Path oldStatus := p.Status + oldVisibility := p.Visibility a.micropubUpdateReplace(p, mf.Replace) a.micropubUpdateAdd(p, mf.Add) a.micropubUpdateDelete(p, mf.Delete) @@ -528,7 +529,7 @@ func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string a.serveError(w, r, err.Error(), http.StatusInternalServerError) return } - err = a.replacePost(p, oldPath, oldStatus) + err = a.replacePost(p, oldPath, oldStatus, oldVisibility) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return @@ -547,16 +548,14 @@ func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]any) { p.Updated = cast.ToStringSlice(updated)[0] } // Status - statusStr := "" if status, ok := replace["post-status"]; ok && len(status) > 0 { - statusStr = cast.ToStringSlice(status)[0] + statusStr := cast.ToStringSlice(status)[0] + p.Status = micropubStatus(statusStr) } - visibilityStr := "" + // Visibility if visibility, ok := replace["visibility"]; ok && len(visibility) > 0 { - visibilityStr = cast.ToStringSlice(visibility)[0] - } - if finalStatus := micropubStatus(p.Status, statusStr, visibilityStr); finalStatus != statusNil { - p.Status = finalStatus + visibilityStr := cast.ToStringSlice(visibility)[0] + p.Visibility = micropubVisibility(visibilityStr) } // Parameters if name, ok := replace["name"]; ok && name != nil { @@ -671,24 +670,22 @@ func (a *goBlog) micropubUpdateDelete(p *post, del any) { } } -func micropubStatus(defaultStatus postStatus, status string, visibility string) (final postStatus) { - final = defaultStatus +func micropubStatus(status string) postStatus { switch status { - case "published": - final = statusPublished case "draft": - final = statusDraft + return statusDraft + default: + return statusPublished + } +} + +func micropubVisibility(visibility string) postVisibility { + switch visibility { + case "unlisted": + return visibilityUnlisted + case "private": + return visibilityPrivate + default: + return visibilityPublic } - if final != statusDraft { - // Only override status if it's not a draft - switch visibility { - case "public": - final = statusPublished - case "unlisted": - final = statusUnlisted - case "private": - final = statusPrivate - } - } - return final } diff --git a/micropub_test.go b/micropub_test.go index 8c73e17..a3de7b8 100644 --- a/micropub_test.go +++ b/micropub_test.go @@ -44,12 +44,12 @@ func Test_micropubQuery(t *testing.T) { }, { query: "source&url=http://localhost:8080/test/post", - want: "{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}", + want: "{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\nvisibility: public\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}", wantStatus: http.StatusOK, }, { query: "source", - want: "{\"items\":[{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}]}", + want: "{\"items\":[{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\nvisibility: public\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}]}", wantStatus: http.StatusOK, }, { diff --git a/nodeinfo.go b/nodeinfo.go index bd6d14d..ea2000d 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -32,7 +32,8 @@ func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { localPosts, _ := a.db.countPosts(&postsRequestConfig{ - status: statusPublished, + status: []postStatus{statusPublished}, + visibility: []postVisibility{visibilityPublic}, }) buf := bufferpool.Get() defer bufferpool.Put(buf) diff --git a/posts.go b/posts.go index 094473c..86a1a25 100644 --- a/posts.go +++ b/posts.go @@ -25,6 +25,7 @@ type post struct { Blog string Section string Status postStatus + Visibility postVisibility Priority int // Not persisted Slug string @@ -32,21 +33,23 @@ type post struct { } type postStatus string +type postVisibility string const ( - statusDeletedSuffix string = "-deleted" + statusDeletedSuffix postStatus = "-deleted" statusNil postStatus = "" statusPublished postStatus = "published" - statusPublishedDeleted postStatus = "published-deleted" + statusPublishedDeleted postStatus = statusPublished + statusDeletedSuffix statusDraft postStatus = "draft" - statusDraftDeleted postStatus = "draft-deleted" - statusPrivate postStatus = "private" - statusPrivateDeleted postStatus = "private-deleted" - statusUnlisted postStatus = "unlisted" - statusUnlistedDeleted postStatus = "unlisted-deleted" + statusDraftDeleted postStatus = statusDraft + statusDeletedSuffix statusScheduled postStatus = "scheduled" - statusScheduledDeleted postStatus = "scheduled-deleted" + statusScheduledDeleted postStatus = statusScheduled + statusDeletedSuffix + + visibilityNil postVisibility = "" + visibilityPublic postVisibility = "public" + visibilityUnlisted postVisibility = "unlisted" + visibilityPrivate postVisibility = "private" ) func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) { @@ -76,7 +79,7 @@ func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) { } w.Header().Add("Link", fmt.Sprintf("<%s>; rel=shortlink", a.shortPostURL(p))) status := http.StatusOK - if strings.HasSuffix(string(p.Status), statusDeletedSuffix) { + if p.Deleted() { status = http.StatusGone } a.renderWithStatusCode(w, r, status, renderMethod, &renderData{ @@ -154,7 +157,7 @@ func (a *goBlog) serveDrafts(w http.ResponseWriter, r *http.Request) { path: bc.getRelativePath("/editor/drafts"), title: a.ts.GetTemplateStringVariant(bc.Lang, "drafts"), description: a.ts.GetTemplateStringVariant(bc.Lang, "draftsdesc"), - status: statusDraft, + status: []postStatus{statusDraft}, }))) } @@ -164,7 +167,8 @@ func (a *goBlog) servePrivate(w http.ResponseWriter, r *http.Request) { path: bc.getRelativePath("/editor/private"), title: a.ts.GetTemplateStringVariant(bc.Lang, "privateposts"), description: a.ts.GetTemplateStringVariant(bc.Lang, "privatepostsdesc"), - status: statusPrivate, + status: []postStatus{statusPublished}, + visibility: []postVisibility{visibilityPrivate}, }))) } @@ -174,7 +178,8 @@ func (a *goBlog) serveUnlisted(w http.ResponseWriter, r *http.Request) { path: bc.getRelativePath("/editor/unlisted"), title: a.ts.GetTemplateStringVariant(bc.Lang, "unlistedposts"), description: a.ts.GetTemplateStringVariant(bc.Lang, "unlistedpostsdesc"), - status: statusUnlisted, + status: []postStatus{statusPublished}, + visibility: []postVisibility{visibilityUnlisted}, }))) } @@ -184,7 +189,7 @@ func (a *goBlog) serveScheduled(w http.ResponseWriter, r *http.Request) { path: bc.getRelativePath("/editor/scheduled"), title: a.ts.GetTemplateStringVariant(bc.Lang, "scheduledposts"), description: a.ts.GetTemplateStringVariant(bc.Lang, "scheduledpostsdesc"), - status: statusScheduled, + status: []postStatus{statusScheduled}, }))) } @@ -194,7 +199,7 @@ func (a *goBlog) serveDeleted(w http.ResponseWriter, r *http.Request) { path: bc.getRelativePath("/editor/deleted"), title: a.ts.GetTemplateStringVariant(bc.Lang, "deletedposts"), description: a.ts.GetTemplateStringVariant(bc.Lang, "deletedpostsdesc"), - statusse: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted, statusPrivateDeleted, statusUnlistedDeleted}, + status: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted}, }))) } @@ -253,8 +258,8 @@ type indexConfig struct { title string description string summaryTemplate summaryTyp - status postStatus - statusse []postStatus + status []postStatus + visibility []postVisibility } const defaultPhotosPath = "/photos" @@ -277,12 +282,14 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) { sections = append(sections, sectionKey) } } - statusse := ic.statusse - if ic.status != statusNil { - statusse = []postStatus{ic.status} + defaultStatus, defaultVisibility := a.getDefaultPostStates(r) + status := ic.status + if len(status) == 0 { + status = defaultStatus } - if len(statusse) == 0 { - statusse = a.getDefaultPostStatusse(r) + visibility := ic.visibility + if len(visibility) == 0 { + visibility = defaultVisibility } p := paginator.New(&postPaginationAdapter{config: &postsRequestConfig{ blog: blog, @@ -294,7 +301,8 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) { publishedYear: ic.year, publishedMonth: ic.month, publishedDay: ic.day, - statusse: statusse, + status: status, + visibility: visibility, priorityOrder: true, }, a: a}, bc.Pagination) p.SetPage(stringToInt(chi.URLParam(r, "page"))) diff --git a/postsDb.go b/postsDb.go index 98a69f5..6580abe 100644 --- a/postsDb.go +++ b/postsDb.go @@ -59,7 +59,7 @@ func (a *goBlog) checkPost(p *post) (err error) { // Fix content p.Content = strings.TrimSuffix(strings.TrimPrefix(p.Content, "\n"), "\n") // Check status - if p.Status == "" { + if p.Status == statusNil { p.Status = statusPublished if p.Published != "" { // If published time is in the future, set status to scheduled @@ -72,6 +72,10 @@ func (a *goBlog) checkPost(p *post) (err error) { } } } + // Check visibility + if p.Visibility == visibilityNil { + p.Visibility = visibilityPublic + } // Cleanup params for pk, pvs := range p.Parameters { pvs = lo.Filter(pvs, func(s string, _ int) bool { return s != "" }) @@ -126,14 +130,15 @@ func (a *goBlog) createPost(p *post) error { return a.createOrReplacePost(p, &postCreationOptions{new: true}) } -func (a *goBlog) replacePost(p *post, oldPath string, oldStatus postStatus) error { - return a.createOrReplacePost(p, &postCreationOptions{new: false, oldPath: oldPath, oldStatus: oldStatus}) +func (a *goBlog) replacePost(p *post, oldPath string, oldStatus postStatus, oldVisibility postVisibility) error { + return a.createOrReplacePost(p, &postCreationOptions{new: false, oldPath: oldPath, oldStatus: oldStatus, oldVisibility: oldVisibility}) } type postCreationOptions struct { - new bool - oldPath string - oldStatus postStatus + new bool + oldPath string + oldStatus postStatus + oldVisibility postVisibility } func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error { @@ -152,8 +157,8 @@ func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error { return err } // Trigger hooks - if p.Status == statusPublished || p.Status == statusUnlisted { - if o.new || (o.oldStatus != statusPublished && o.oldStatus != statusUnlisted) { + if p.Status == statusPublished && (p.Visibility == visibilityPublic || p.Visibility == visibilityUnlisted) { + if o.new || (o.oldStatus != statusPublished && o.oldVisibility != visibilityPublic && o.oldVisibility != visibilityUnlisted) { defer a.postPostHooks(p) } else { defer a.postUpdateHooks(p) @@ -183,15 +188,15 @@ func (db *database) savePost(p *post, o *postCreationOptions) error { // Update or create post if o.new { // New post, create it - sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status, priority) values (?, ?, ?, ?, ?, ?, ?, ?);") - sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Priority) + sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status, visibility, priority) values (?, ?, ?, ?, ?, ?, ?, ?, ?);") + sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Visibility, p.Priority) } else { // Delete post parameters sqlBuilder.WriteString("delete from post_parameters where path = ?;") sqlArgs = append(sqlArgs, o.oldPath) // Update old post - sqlBuilder.WriteString("update posts set path = ?, content = ?, published = ?, updated = ?, blog = ?, section = ?, status = ?, priority = ? where path = ?;") - sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Priority, o.oldPath) + sqlBuilder.WriteString("update posts set path = ?, content = ?, published = ?, updated = ?, blog = ?, section = ?, status = ?, visibility = ?, priority = ? where path = ?;") + sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Visibility, p.Priority, o.oldPath) } // Insert post parameters for param, value := range p.Parameters { @@ -227,7 +232,7 @@ func (a *goBlog) deletePost(path string) error { return err } // Post exists, check if it's already marked as deleted - if strings.HasSuffix(string(p.Status), statusDeletedSuffix) { + if p.Deleted() { // Post is already marked as deleted, delete it from database if _, err = a.db.Exec( `begin; delete from posts where path = ?; insert or ignore into deleted (path) values (?); commit;`, @@ -242,7 +247,7 @@ func (a *goBlog) deletePost(path string) error { a.deleteReactionsCache(p.Path) } else { // Update post status - p.Status = postStatus(string(p.Status) + statusDeletedSuffix) + p.Status = p.Status + statusDeletedSuffix // Add parameter deletedTime := utcNowString() if p.Parameters == nil { @@ -279,7 +284,7 @@ func (a *goBlog) undeletePost(path string) error { return err } // Post exists, update status and parameters - p.Status = postStatus(strings.TrimSuffix(string(p.Status), statusDeletedSuffix)) + p.Status = postStatus(strings.TrimSuffix(string(p.Status), string(statusDeletedSuffix))) // Remove parameter p.Parameters["deleted"] = nil // Update database @@ -337,8 +342,8 @@ type postsRequestConfig struct { limit int offset int sections []string - status postStatus - statusse []postStatus + status []postStatus + visibility []postVisibility taxonomy *configTaxonomy taxonomyValue string parameters []string // Ignores parameterValue @@ -375,13 +380,9 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg queryBuilder.WriteString(" and path = @path") args = append(args, sql.Named("path", c.path)) } - if c.status != "" && c.status != statusNil { - queryBuilder.WriteString(" and status = @status") - args = append(args, sql.Named("status", c.status)) - } - if c.statusse != nil && len(c.statusse) > 0 { + if c.status != nil && len(c.status) > 0 { queryBuilder.WriteString(" and status in (") - for i, status := range c.statusse { + for i, status := range c.status { if i > 0 { queryBuilder.WriteString(", ") } @@ -392,6 +393,19 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg } queryBuilder.WriteByte(')') } + if c.visibility != nil && len(c.visibility) > 0 { + queryBuilder.WriteString(" and visibility in (") + for i, visibility := range c.visibility { + if i > 0 { + queryBuilder.WriteString(", ") + } + named := "visibility" + strconv.Itoa(i) + queryBuilder.WriteByte('@') + queryBuilder.WriteString(named) + args = append(args, sql.Named(named, visibility)) + } + queryBuilder.WriteByte(')') + } if c.blog != "" { queryBuilder.WriteString(" and blog = @blog") args = append(args, sql.Named("blog", c.blog)) @@ -541,28 +555,29 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err func (a *goBlog) getPosts(config *postsRequestConfig) (posts []*post, err error) { // Query posts - query, queryParams := buildPostsQuery(config, "path, coalesce(content, ''), coalesce(published, ''), coalesce(updated, ''), blog, coalesce(section, ''), status, priority") + query, queryParams := buildPostsQuery(config, "path, coalesce(content, ''), coalesce(published, ''), coalesce(updated, ''), blog, coalesce(section, ''), status, visibility, priority") rows, err := a.db.Query(query, queryParams...) if err != nil { return nil, err } // Prepare row scanning - var path, content, published, updated, blog, section, status string + var path, content, published, updated, blog, section, status, visibility string var priority int for rows.Next() { - if err = rows.Scan(&path, &content, &published, &updated, &blog, §ion, &status, &priority); err != nil { + if err = rows.Scan(&path, &content, &published, &updated, &blog, §ion, &status, &visibility, &priority); err != nil { return nil, err } // Create new post, fill and add to list p := &post{ - Path: path, - Content: content, - Published: toLocalSafe(published), - Updated: toLocalSafe(updated), - Blog: blog, - Section: section, - Status: postStatus(status), - Priority: priority, + Path: path, + Content: content, + Published: toLocalSafe(published), + Updated: toLocalSafe(updated), + Blog: blog, + Section: section, + Status: postStatus(status), + Visibility: postVisibility(visibility), + Priority: priority, } posts = append(posts, p) } @@ -620,8 +635,10 @@ func (a *goBlog) getRandomPostPath(blog string) (path string, err error) { } func (d *database) allTaxonomyValues(blog string, taxonomy string) ([]string, error) { - // TODO: Query posts the normal way - rows, err := d.Query("select distinct value from post_parameters where parameter = @tax and length(coalesce(value, '')) > 0 and path in (select path from posts where blog = @blog and status = @status) order by value", sql.Named("tax", taxonomy), sql.Named("blog", blog), sql.Named("status", statusPublished)) + rows, err := d.Query( + "select distinct value from post_parameters where parameter = @tax and length(coalesce(value, '')) > 0 and path in (select path from posts where blog = @blog and status = @status and visibility = @visibility) order by value", + sql.Named("tax", taxonomy), sql.Named("blog", blog), sql.Named("status", statusPublished), sql.Named("visibility", visibilityPublic), + ) if err != nil { return nil, err } diff --git a/postsDb_test.go b/postsDb_test.go index 2b2695c..0e11804 100644 --- a/postsDb_test.go +++ b/postsDb_test.go @@ -34,13 +34,14 @@ func Test_postsDb(t *testing.T) { // Save post err := app.db.savePost(&post{ - Path: "/test/abc", - Content: "ABC", - Published: now, - Updated: nowPlus1Hour, - Blog: "en", - Section: "test", - Status: statusDraft, + Path: "/test/abc", + Content: "ABC", + Published: now, + Updated: nowPlus1Hour, + Blog: "en", + Section: "test", + Status: statusDraft, + Visibility: visibilityPublic, Parameters: map[string][]string{ "title": {"Title"}, "tags": {"C", "A", "B"}, @@ -65,7 +66,7 @@ func Test_postsDb(t *testing.T) { // Check drafts drafts, _ := app.getPosts(&postsRequestConfig{ blog: "en", - status: statusDraft, + status: []postStatus{statusDraft}, }) is.Len(drafts, 1) @@ -97,10 +98,10 @@ func Test_postsDb(t *testing.T) { must.NoError(err) // Check if post is marked as deleted - count, err = app.db.countPosts(&postsRequestConfig{status: statusDraft}) + count, err = app.db.countPosts(&postsRequestConfig{status: []postStatus{statusDraft}}) must.NoError(err) is.Equal(0, count) - count, err = app.db.countPosts(&postsRequestConfig{status: statusDraftDeleted}) + count, err = app.db.countPosts(&postsRequestConfig{status: []postStatus{statusDraftDeleted}}) must.NoError(err) is.Equal(1, count) @@ -115,13 +116,14 @@ func Test_postsDb(t *testing.T) { // Save published post err = app.db.savePost(&post{ - Path: "/test/abc", - Content: "ABC", - Published: "2021-06-10 10:00:00", - Updated: "2021-06-15 10:00:00", - Blog: "en", - Section: "test", - Status: statusPublished, + Path: "/test/abc", + Content: "ABC", + Published: "2021-06-10 10:00:00", + Updated: "2021-06-15 10:00:00", + Blog: "en", + Section: "test", + Status: statusPublished, + Visibility: visibilityPublic, Parameters: map[string][]string{ "tags": {"Test", "Blog", "A"}, }, diff --git a/postsDeleter.go b/postsDeleter.go index 6468ded..aaa8274 100644 --- a/postsDeleter.go +++ b/postsDeleter.go @@ -16,7 +16,7 @@ func (a *goBlog) initPostsDeleter() { func (a *goBlog) checkDeletedPosts() { // Get all posts with `deleted` parameter and a deleted status postsToDelete, err := a.getPosts(&postsRequestConfig{ - statusse: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted}, + status: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted}, parameter: "deleted", }) if err != nil { diff --git a/postsFuncs.go b/postsFuncs.go index fa495f3..007d90f 100644 --- a/postsFuncs.go +++ b/postsFuncs.go @@ -142,7 +142,7 @@ func (a *goBlog) postTranslations(p *post) []*post { } func (p *post) isPublishedSectionPost() bool { - return p.Published != "" && p.Section != "" && p.Status == statusPublished + return p.Published != "" && p.Section != "" && p.Status == statusPublished && p.Visibility == visibilityPublic } func (a *goBlog) postToMfItem(p *post) *microformatItem { @@ -150,20 +150,18 @@ func (a *goBlog) postToMfItem(p *post) *microformatItem { switch p.Status { case statusDraft: mfStatus = "draft" - case statusPublished, statusScheduled, statusUnlisted, statusPrivate: + case statusPublished, statusScheduled: mfStatus = "published" - case statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted: + case statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted: mfStatus = "deleted" } - switch p.Status { - case statusDraft, statusScheduled, statusPublished: + switch p.Visibility { + case visibilityPublic: mfVisibility = "public" - case statusUnlisted: + case visibilityUnlisted: mfVisibility = "unlisted" - case statusPrivate: + case visibilityPrivate: mfVisibility = "private" - case statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted: - mfVisibility = "deleted" } return µformatItem{ Type: []string{"h-entry"}, @@ -244,6 +242,7 @@ func (p *post) contentWithParams() string { params["published"] = p.Published params["updated"] = p.Updated params["status"] = string(p.Status) + params["visibility"] = string(p.Visibility) params["priority"] = p.Priority pb, _ := yaml.Marshal(params) return fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content) @@ -290,5 +289,5 @@ func (p *post) TTS() string { } func (p *post) Deleted() bool { - return strings.HasSuffix(string(p.Status), statusDeletedSuffix) + return strings.HasSuffix(string(p.Status), string(statusDeletedSuffix)) } diff --git a/postsScheduler.go b/postsScheduler.go index b41de1b..b3685c5 100644 --- a/postsScheduler.go +++ b/postsScheduler.go @@ -27,7 +27,7 @@ func (a *goBlog) startPostsScheduler() { func (a *goBlog) checkScheduledPosts() { postsToPublish, err := a.getPosts(&postsRequestConfig{ - status: statusScheduled, + status: []postStatus{statusScheduled}, publishedBefore: time.Now(), }) if err != nil { @@ -36,7 +36,7 @@ func (a *goBlog) checkScheduledPosts() { } for _, post := range postsToPublish { post.Status = statusPublished - err := a.replacePost(post, post.Path, statusScheduled) + err := a.replacePost(post, post.Path, statusScheduled, post.Visibility) if err != nil { log.Println("Error publishing scheduled post:", err) continue diff --git a/postsScheduler_test.go b/postsScheduler_test.go index ce6824c..f9dcd63 100644 --- a/postsScheduler_test.go +++ b/postsScheduler_test.go @@ -26,23 +26,24 @@ func Test_postsScheduler(t *testing.T) { _ = app.initCache() err := app.db.savePost(&post{ - Path: "/test/abc", - Content: "ABC", - Published: toLocalSafe(time.Now().Add(-1 * time.Hour).String()), - Blog: "en", - Section: "test", - Status: statusScheduled, + Path: "/test/abc", + Content: "ABC", + Published: toLocalSafe(time.Now().Add(-1 * time.Hour).String()), + Blog: "en", + Section: "test", + Status: statusScheduled, + Visibility: visibilityPublic, }, &postCreationOptions{new: true}) require.NoError(t, err) - count, err := app.db.countPosts(&postsRequestConfig{status: statusPublished}) - require.NoError(t, err) - assert.Equal(t, 0, count) - - app.checkScheduledPosts() - - count, err = app.db.countPosts(&postsRequestConfig{status: statusPublished}) + count, err := app.db.countPosts(&postsRequestConfig{status: []postStatus{statusScheduled}}) require.NoError(t, err) assert.Equal(t, 1, count) + app.checkScheduledPosts() + + count, err = app.db.countPosts(&postsRequestConfig{status: []postStatus{statusScheduled}}) + require.NoError(t, err) + assert.Equal(t, 0, count) + } diff --git a/reactions_test.go b/reactions_test.go index d5db963..757cd9d 100644 --- a/reactions_test.go +++ b/reactions_test.go @@ -50,7 +50,7 @@ func Test_reactionsLowLevel(t *testing.T) { Path: "/newpost", Content: "test", Status: statusPublished, - }, "/testpost", statusPublished) + }, "/testpost", statusPublished, visibilityPublic) require.NoError(t, err) // Check if reaction count is 4 diff --git a/sitemap.go b/sitemap.go index e333138..75769b8 100644 --- a/sitemap.go +++ b/sitemap.go @@ -155,7 +155,8 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) { // Request posts blog, _ := a.getBlog(r) posts, _ := a.getPosts(&postsRequestConfig{ - status: statusPublished, + status: []postStatus{statusPublished}, + visibility: []postVisibility{visibilityPublic}, blog: blog, withoutParameters: true, }) diff --git a/strings/de.yaml b/strings/de.yaml index 3119fb3..5c806e1 100644 --- a/strings/de.yaml +++ b/strings/de.yaml @@ -19,7 +19,7 @@ download: "Herunterladen" drafts: "Entwürfe" draftsdesc: "Posts mit dem Status `draft`." editor: "Editor" -editorpostdesc: "💡 Leere Parameter werden automatisch entfernt. Mehr mögliche Parameter: %s. Mögliche Zustände für `%s`: %s." +editorpostdesc: "💡 Leere Parameter werden automatisch entfernt. Mehr mögliche Parameter: %s. Mögliche Zustände für `%s` und `%s`: %s und %s." emailopt: "E-Mail (optional)" fileuses: "Datei-Verwendungen" general: "Allgemein" @@ -50,7 +50,7 @@ posts: "Posts" postsections: "Post-Bereiche" prev: "Zurück" privateposts: "Private Posts" -privatepostsdesc: "Posts mit dem Status `private`, die nur eingeloggt sichtbar sind." +privatepostsdesc: "Veröffentlichte Posts mit der Sichtbarkeit `private`, die nur eingeloggt sichtbar sind." publishedon: "Veröffentlicht am" replyto: "Antwort an" scheduledposts: "Geplante Posts" @@ -74,11 +74,12 @@ translate: "Ãœbersetzen" translations: "Ãœbersetzungen" undelete: "Wiederherstellen" unlistedposts: "Ungelistete Posts" -unlistedpostsdesc: "Posts mit dem Status `unlisted`, die nicht in Archiven angezeigt werden." +unlistedpostsdesc: "Veröffentlichte Posts mit der Sichtbarkeit `unlisted`, die nicht in Archiven angezeigt werden." update: "Aktualisieren" updatedon: "Aktualisiert am" upload: "Hochladen" view: "Anschauen" +visibility: "Sichtbarkeit" whatistor: "Was ist Tor?" withoutdate: "Ohne Datum" words: "Wörter" diff --git a/strings/default.yaml b/strings/default.yaml index 770c9f4..aabf92d 100644 --- a/strings/default.yaml +++ b/strings/default.yaml @@ -22,7 +22,7 @@ download: "Download" drafts: "Drafts" draftsdesc: "Posts with status `draft`." editor: "Editor" -editorpostdesc: "💡 Empty parameters are removed automatically. More possible parameters: %s. Possible states for `%s`: %s." +editorpostdesc: "💡 Empty parameters are removed automatically. More possible parameters: %s. Possible states for `%s` and `%s`: %s and %s." emailopt: "Email (optional)" feed: "Feed" fileuses: "file uses" @@ -60,7 +60,7 @@ posts: "Posts" postsections: "Post sections" prev: "Previous" privateposts: "Private posts" -privatepostsdesc: "Posts with status `private` that are visible only when logged in." +privatepostsdesc: "Published posts with visibility `private` that are visible only when logged in." publishedon: "Published on" replyto: "Reply to" reverify: "Reverify" @@ -87,13 +87,14 @@ translate: "Translate" translations: "Translations" undelete: "Undelete" unlistedposts: "Unlisted posts" -unlistedpostsdesc: "Posts with status `unlisted` that are not displayed in archives." +unlistedpostsdesc: "Published posts with visibility `unlisted` that are not displayed in archives." update: "Update" updatedon: "Updated on" upload: "Upload" username: "Username" verified: "Verified" view: "View" +visibility: "Visibility" webmentions: "Webmentions" websiteopt: "Website (optional)" whatistor: "What is Tor?" diff --git a/strings/es.yaml b/strings/es.yaml index b32c923..b54baff 100644 --- a/strings/es.yaml +++ b/strings/es.yaml @@ -22,7 +22,6 @@ download: "Descargar" drafts: "Borradores" draftsdesc: "Posts con status `draft` (borrador)." editor: "Editor" -editorpostdesc: "💡 Los parámetros vacíos se eliminan automáticamente. Más parámetros posibles: %s. Variables para (estado) status `%s`: %s." emailopt: "Email (opcional)" feed: "Feed" fileuses: "usos de archivo" @@ -57,7 +56,6 @@ posts: "Posts" postsections: "Secciones de Posts" prev: "Anterior" privateposts: "Posts Privados" -privatepostsdesc: "Posts con status `private` que son visibles solo al iniciar sesión." publishedon: "Publicado en" replyto: "Respuesta a" reverify: "Reverificar" @@ -84,7 +82,6 @@ translate: "Traducir" translations: "Traducciones" undelete: "Undelete" unlistedposts: "Posts No Listados" -unlistedpostsdesc: "Los Posts con status `unlisted` no se muestran en el archivo." update: "Actualizar" updatedon: "Actualizado en" upload: "Cargar" diff --git a/strings/pt-br.yaml b/strings/pt-br.yaml index 0d0391e..207ccf0 100644 --- a/strings/pt-br.yaml +++ b/strings/pt-br.yaml @@ -22,7 +22,6 @@ download: "Baixar" drafts: "Rascunho" draftsdesc: "Posts com status `draft`." editor: "Editor" -editorpostdesc: "💡 Parâmetros vazios são removidos automaticamente. Mais parâmetros possíveis: %s. Possíveis estados para `%s`: %s." emailopt: "Email (opcional)" feed: "Feed" fileuses: "arquivo usa" @@ -60,7 +59,6 @@ posts: "Posts" postsections: "Seções dos posts" prev: "Anterior" privateposts: "Posts privados" -privatepostsdesc: "Posts com status `private` que são visíveis apenas quando logado." publishedon: "Publicado em" replyto: "Responder para" reverify: "Reverificar" @@ -87,7 +85,6 @@ translate: "Traduzir" translations: "Traduções" undelete: "Desfazer exclusão" unlistedposts: "Posts não listados" -unlistedpostsdesc: "Posts com status `unlisted` que não são mostrados nos arquivos." update: "Atualizar" updatedon: "Atualizado em" upload: "Enviar" diff --git a/telegram_test.go b/telegram_test.go index eb71969..56a281f 100644 --- a/telegram_test.go +++ b/telegram_test.go @@ -134,6 +134,7 @@ func Test_telegram(t *testing.T) { Section: "test", Blog: "en", Status: statusPublished, + Visibility: visibilityPublic, } app.pPostHooks[0](p) @@ -163,10 +164,11 @@ func Test_telegram(t *testing.T) { Parameters: map[string][]string{ "title": {"Title"}, }, - Published: time.Now().String(), - Section: "test", - Blog: "default", - Status: statusPublished, + Published: time.Now().String(), + Section: "test", + Blog: "default", + Status: statusPublished, + Visibility: visibilityPublic, }) assert.Nil(t, fakeClient.req) diff --git a/uiComponents.go b/uiComponents.go index 2d1e80d..3416466 100644 --- a/uiComponents.go +++ b/uiComponents.go @@ -218,6 +218,14 @@ func (a *goBlog) renderPostMeta(hb *htmlbuilder.HtmlBuilder, p *post, b *configB hb.WriteEscaped(string(p.Status)) hb.WriteElementClose("div") } + // Visibility + if p.Visibility != visibilityPublic { + hb.WriteElementOpen("div") + hb.WriteEscaped(a.ts.GetTemplateStringVariant(b.Lang, "visibility")) + hb.WriteEscaped(": ") + hb.WriteEscaped(string(p.Visibility)) + hb.WriteElementClose("div") + } } if typ == "summary" || typ == "post" { hb.WriteElementClose("div") diff --git a/webmentionSending.go b/webmentionSending.go index 958ddf9..1033042 100644 --- a/webmentionSending.go +++ b/webmentionSending.go @@ -20,7 +20,7 @@ import ( const postParamWebmention = "webmention" func (a *goBlog) sendWebmentions(p *post) error { - if p.Status != statusPublished && p.Status != statusUnlisted { + if p.Status != statusPublished && p.Visibility != visibilityPublic && p.Visibility != visibilityUnlisted { // Not published or unlisted return nil }