mirror of https://github.com/jlelse/GoBlog
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.
This commit is contained in:
parent
c7da67b581
commit
37a9e1f29c
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
7
check.go
7
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
|
||||
|
|
|
@ -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';
|
19
editor.go
19
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())
|
||||
}
|
||||
|
|
24
geoMap.go
24
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
|
||||
|
|
2
go.mod
2
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
32
http.go
32
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":
|
||||
|
|
77
micropub.go
77
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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
52
posts.go
52
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")))
|
||||
|
|
89
postsDb.go
89
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
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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?"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|