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)
|
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) {
|
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,
|
tolocal(published) as pub,
|
||||||
mdtext(coalesce(content, '')) as content
|
mdtext(coalesce(content, '')) as content
|
||||||
from posts
|
from posts
|
||||||
where status = @status and blog = @blog
|
where status = @status and visibility = @visibility and blog = @blog
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
select *
|
select *
|
||||||
|
@ -154,7 +154,7 @@ func (db *database) getBlogStats(blog string) (data *blogStatsData, err error) {
|
||||||
Months: map[string][]blogStatsRow{},
|
Months: map[string][]blogStatsRow{},
|
||||||
}
|
}
|
||||||
// Query and scan
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,20 +41,42 @@ func Test_blogStats(t *testing.T) {
|
||||||
// Insert post
|
// Insert post
|
||||||
|
|
||||||
err := app.createPost(&post{
|
err := app.createPost(&post{
|
||||||
Content: "This is a simple **test** post",
|
Content: "This is a simple **test** post",
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Published: "2020-06-01",
|
Published: "2020-06-01",
|
||||||
Status: statusPublished,
|
Status: statusPublished,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = app.createPost(&post{
|
err = app.createPost(&post{
|
||||||
Content: "This is another simple **test** post",
|
Content: "This is another simple **test** post",
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Published: "2021-05-01",
|
Published: "2021-05-01",
|
||||||
Status: statusPublished,
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
7
check.go
7
check.go
|
@ -18,8 +18,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) checkAllExternalLinks() {
|
func (a *goBlog) checkAllExternalLinks() {
|
||||||
// Get all published posts without parameters
|
posts, err := a.getPosts(&postsRequestConfig{
|
||||||
posts, err := a.getPosts(&postsRequestConfig{status: statusPublished, withoutParameters: true})
|
status: []postStatus{statusPublished},
|
||||||
|
visibility: []postVisibility{visibilityPublic, visibilityUnlisted},
|
||||||
|
withoutParameters: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
return
|
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("blog", blog)
|
||||||
marsh("section", bc.DefaultSection)
|
marsh("section", bc.DefaultSection)
|
||||||
marsh("status", statusDraft)
|
marsh("status", statusDraft)
|
||||||
|
marsh("visibility", visibilityPublic)
|
||||||
marsh("priority", 0)
|
marsh("priority", 0)
|
||||||
marsh("slug", "")
|
marsh("slug", "")
|
||||||
marsh("title", "")
|
marsh("title", "")
|
||||||
|
@ -206,8 +207,8 @@ func (*goBlog) editorPostTemplate(blog string, bc *configBlog) string {
|
||||||
|
|
||||||
func (a *goBlog) editorPostDesc(bc *configBlog) string {
|
func (a *goBlog) editorPostDesc(bc *configBlog) string {
|
||||||
t := a.ts.GetTemplateStringVariant(bc.Lang, "editorpostdesc")
|
t := a.ts.GetTemplateStringVariant(bc.Lang, "editorpostdesc")
|
||||||
paramBuilder, statusBuilder := bufferpool.Get(), bufferpool.Get()
|
paramBuilder, statusBuilder, visibilityBuilder := bufferpool.Get(), bufferpool.Get(), bufferpool.Get()
|
||||||
defer bufferpool.Put(paramBuilder, statusBuilder)
|
defer bufferpool.Put(paramBuilder, statusBuilder, visibilityBuilder)
|
||||||
for i, param := range []string{
|
for i, param := range []string{
|
||||||
"published",
|
"published",
|
||||||
"updated",
|
"updated",
|
||||||
|
@ -236,7 +237,7 @@ func (a *goBlog) editorPostDesc(bc *configBlog) string {
|
||||||
paramBuilder.WriteByte('`')
|
paramBuilder.WriteByte('`')
|
||||||
}
|
}
|
||||||
for i, status := range []postStatus{
|
for i, status := range []postStatus{
|
||||||
statusDraft, statusPublished, statusUnlisted, statusScheduled, statusPrivate,
|
statusPublished, statusDraft, statusScheduled,
|
||||||
} {
|
} {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
statusBuilder.WriteString(", ")
|
statusBuilder.WriteString(", ")
|
||||||
|
@ -245,5 +246,15 @@ func (a *goBlog) editorPostDesc(bc *configBlog) string {
|
||||||
statusBuilder.WriteString(string(status))
|
statusBuilder.WriteString(string(status))
|
||||||
statusBuilder.WriteByte('`')
|
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))
|
mapPath := bc.getRelativePath(defaultIfEmpty(bc.Map.Path, defaultGeoMapPath))
|
||||||
canonical := a.getFullAddress(mapPath)
|
canonical := a.getFullAddress(mapPath)
|
||||||
|
|
||||||
allPostsWithLocation, err := a.db.countPosts(&postsRequestConfig{
|
allPostsWithLocationRequestConfig := &postsRequestConfig{
|
||||||
blog: blog,
|
blog: blog,
|
||||||
statusse: a.getDefaultPostStatusse(r),
|
|
||||||
parameters: []string{a.cfg.Micropub.LocationParam, gpxParameter},
|
parameters: []string{a.cfg.Micropub.LocationParam, gpxParameter},
|
||||||
withOnlyParameters: []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 {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -55,14 +57,16 @@ const geoMapTracksSubpath = "/tracks.json"
|
||||||
func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) {
|
||||||
blog, _ := a.getBlog(r)
|
blog, _ := a.getBlog(r)
|
||||||
|
|
||||||
allPostsWithTracks, err := a.getPosts(&postsRequestConfig{
|
allPostsWithTracksRequestConfig := &postsRequestConfig{
|
||||||
blog: blog,
|
blog: blog,
|
||||||
statusse: a.getDefaultPostStatusse(r),
|
|
||||||
parameters: []string{gpxParameter},
|
parameters: []string{gpxParameter},
|
||||||
withOnlyParameters: []string{gpxParameter},
|
withOnlyParameters: []string{gpxParameter},
|
||||||
excludeParameter: showRouteParam,
|
excludeParameter: showRouteParam,
|
||||||
excludeParameterValue: "false", // Don't show hidden route tracks
|
excludeParameterValue: "false", // Don't show hidden route tracks
|
||||||
})
|
}
|
||||||
|
allPostsWithTracksRequestConfig.status, allPostsWithTracksRequestConfig.visibility = a.getDefaultPostStates(r)
|
||||||
|
|
||||||
|
allPostsWithTracks, err := a.getPosts(allPostsWithTracksRequestConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -101,12 +105,14 @@ const geoMapLocationsSubpath = "/locations.json"
|
||||||
func (a *goBlog) serveGeoMapLocations(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveGeoMapLocations(w http.ResponseWriter, r *http.Request) {
|
||||||
blog, _ := a.getBlog(r)
|
blog, _ := a.getBlog(r)
|
||||||
|
|
||||||
allPostsWithLocations, err := a.getPosts(&postsRequestConfig{
|
allPostsWithLocationRequestConfig := &postsRequestConfig{
|
||||||
blog: blog,
|
blog: blog,
|
||||||
statusse: a.getDefaultPostStatusse(r),
|
|
||||||
parameters: []string{a.cfg.Micropub.LocationParam},
|
parameters: []string{a.cfg.Micropub.LocationParam},
|
||||||
withOnlyParameters: []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 {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -59,7 +59,7 @@ require (
|
||||||
// master
|
// master
|
||||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
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/sync v0.0.0-20220907140024-f12130a52804
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
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-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-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-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-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps=
|
||||||
golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/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
|
path := r.URL.Path
|
||||||
row, err := a.db.QueryRow(`
|
row, err := a.db.QueryRow(`
|
||||||
-- normal posts
|
-- normal posts
|
||||||
select 'post', status, 200 from posts where path = @path
|
select 'post', status, visibility, 200 from posts where path = @path
|
||||||
union all
|
union all
|
||||||
-- short paths
|
-- 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
|
union all
|
||||||
-- post aliases
|
-- 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
|
union all
|
||||||
-- deleted posts
|
-- deleted posts
|
||||||
select 'deleted', '', 410 from deleted where path = @path
|
select 'deleted', '', '', 410 from deleted where path = @path
|
||||||
-- just select the first result
|
-- just select the first result
|
||||||
limit 1
|
limit 1
|
||||||
`, sql.Named("path", path))
|
`, sql.Named("path", path))
|
||||||
|
@ -276,9 +276,9 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var pathType, value string
|
var pathType, value1, value2 string
|
||||||
var status int
|
var status int
|
||||||
err = row.Scan(&pathType, &value, &status)
|
err = row.Scan(&pathType, &value1, &value2, &status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, sql.ErrNoRows) {
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
// Error
|
// Error
|
||||||
|
@ -290,26 +290,32 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc {
|
||||||
// Found post or alias
|
// Found post or alias
|
||||||
switch pathType {
|
switch pathType {
|
||||||
case "post":
|
case "post":
|
||||||
// Is post, check status
|
// Check status
|
||||||
switch postStatus(value) {
|
switch postStatus(value1) {
|
||||||
case statusPublished, statusUnlisted:
|
case statusPublished:
|
||||||
alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
// 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
|
return
|
||||||
case statusPublishedDeleted, statusUnlistedDeleted:
|
case statusPublishedDeleted:
|
||||||
if a.isLoggedIn(r) {
|
if a.isLoggedIn(r) {
|
||||||
a.servePost(w, r)
|
a.servePost(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
alicePrivate.Append(a.cacheMiddleware).ThenFunc(a.serve410).ServeHTTP(w, r)
|
alicePrivate.Append(a.cacheMiddleware).ThenFunc(a.serve410).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
default: // private, draft, scheduled, etc.
|
default: // draft, scheduled, etc.
|
||||||
alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "alias":
|
case "alias":
|
||||||
// Is alias, redirect
|
// Is alias, redirect
|
||||||
alicePrivate.Append(cacheLoggedIn, a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) {
|
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)
|
}).ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
case "deleted":
|
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")
|
delete(values, "mp-channel")
|
||||||
}
|
}
|
||||||
// Status
|
// Status
|
||||||
statusStr := ""
|
|
||||||
if status, ok := values["post-status"]; ok && len(status) > 0 {
|
if status, ok := values["post-status"]; ok && len(status) > 0 {
|
||||||
statusStr = status[0]
|
statusStr := status[0]
|
||||||
|
entry.Status = micropubStatus(statusStr)
|
||||||
delete(values, "post-status")
|
delete(values, "post-status")
|
||||||
}
|
}
|
||||||
visibilityStr := ""
|
// Visibility
|
||||||
if visibility, ok := values["visibility"]; ok && len(visibility) > 0 {
|
if visibility, ok := values["visibility"]; ok && len(visibility) > 0 {
|
||||||
visibilityStr = visibility[0]
|
visibilityStr := visibility[0]
|
||||||
|
entry.Visibility = micropubVisibility(visibilityStr)
|
||||||
delete(values, "visibility")
|
delete(values, "visibility")
|
||||||
}
|
}
|
||||||
if finalStatus := micropubStatus(statusNil, statusStr, visibilityStr); finalStatus != statusNil {
|
|
||||||
entry.Status = finalStatus
|
|
||||||
}
|
|
||||||
// Parameter
|
// Parameter
|
||||||
if name, ok := values["name"]; ok {
|
if name, ok := values["name"]; ok {
|
||||||
entry.Parameters["title"] = name
|
entry.Parameters["title"] = name
|
||||||
|
@ -297,16 +295,14 @@ func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem)
|
||||||
entry.setChannel(mf.Properties.MpChannel[0])
|
entry.setChannel(mf.Properties.MpChannel[0])
|
||||||
}
|
}
|
||||||
// Status
|
// Status
|
||||||
status := ""
|
|
||||||
if len(mf.Properties.PostStatus) > 0 {
|
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 {
|
if len(mf.Properties.Visibility) > 0 {
|
||||||
visibility = mf.Properties.Visibility[0]
|
visibility := mf.Properties.Visibility[0]
|
||||||
}
|
entry.Visibility = micropubVisibility(visibility)
|
||||||
if finalStatus := micropubStatus(statusNil, status, visibility); finalStatus != statusNil {
|
|
||||||
entry.Status = finalStatus
|
|
||||||
}
|
}
|
||||||
// Parameter
|
// Parameter
|
||||||
if len(mf.Properties.Name) > 0 {
|
if len(mf.Properties.Name) > 0 {
|
||||||
|
@ -398,6 +394,10 @@ func (a *goBlog) extractParamsFromContent(p *post) error {
|
||||||
p.Status = postStatus(status[0])
|
p.Status = postStatus(status[0])
|
||||||
delete(p.Parameters, "status")
|
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 {
|
if priority := p.Parameters["priority"]; len(priority) == 1 {
|
||||||
p.Priority = cast.ToInt(priority[0])
|
p.Priority = cast.ToInt(priority[0])
|
||||||
delete(p.Parameters, "priority")
|
delete(p.Parameters, "priority")
|
||||||
|
@ -513,13 +513,14 @@ func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if post is marked as deleted
|
// 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)
|
a.serveError(w, r, "post is marked as deleted, undelete it first", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Update post
|
// Update post
|
||||||
oldPath := p.Path
|
oldPath := p.Path
|
||||||
oldStatus := p.Status
|
oldStatus := p.Status
|
||||||
|
oldVisibility := p.Visibility
|
||||||
a.micropubUpdateReplace(p, mf.Replace)
|
a.micropubUpdateReplace(p, mf.Replace)
|
||||||
a.micropubUpdateAdd(p, mf.Add)
|
a.micropubUpdateAdd(p, mf.Add)
|
||||||
a.micropubUpdateDelete(p, mf.Delete)
|
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)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = a.replacePost(p, oldPath, oldStatus)
|
err = a.replacePost(p, oldPath, oldStatus, oldVisibility)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -547,16 +548,14 @@ func (a *goBlog) micropubUpdateReplace(p *post, replace map[string][]any) {
|
||||||
p.Updated = cast.ToStringSlice(updated)[0]
|
p.Updated = cast.ToStringSlice(updated)[0]
|
||||||
}
|
}
|
||||||
// Status
|
// Status
|
||||||
statusStr := ""
|
|
||||||
if status, ok := replace["post-status"]; ok && len(status) > 0 {
|
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 {
|
if visibility, ok := replace["visibility"]; ok && len(visibility) > 0 {
|
||||||
visibilityStr = cast.ToStringSlice(visibility)[0]
|
visibilityStr := cast.ToStringSlice(visibility)[0]
|
||||||
}
|
p.Visibility = micropubVisibility(visibilityStr)
|
||||||
if finalStatus := micropubStatus(p.Status, statusStr, visibilityStr); finalStatus != statusNil {
|
|
||||||
p.Status = finalStatus
|
|
||||||
}
|
}
|
||||||
// Parameters
|
// Parameters
|
||||||
if name, ok := replace["name"]; ok && name != nil {
|
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) {
|
func micropubStatus(status string) postStatus {
|
||||||
final = defaultStatus
|
|
||||||
switch status {
|
switch status {
|
||||||
case "published":
|
|
||||||
final = statusPublished
|
|
||||||
case "draft":
|
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",
|
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,
|
wantStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: "source",
|
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,
|
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) {
|
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
localPosts, _ := a.db.countPosts(&postsRequestConfig{
|
localPosts, _ := a.db.countPosts(&postsRequestConfig{
|
||||||
status: statusPublished,
|
status: []postStatus{statusPublished},
|
||||||
|
visibility: []postVisibility{visibilityPublic},
|
||||||
})
|
})
|
||||||
buf := bufferpool.Get()
|
buf := bufferpool.Get()
|
||||||
defer bufferpool.Put(buf)
|
defer bufferpool.Put(buf)
|
||||||
|
|
52
posts.go
52
posts.go
|
@ -25,6 +25,7 @@ type post struct {
|
||||||
Blog string
|
Blog string
|
||||||
Section string
|
Section string
|
||||||
Status postStatus
|
Status postStatus
|
||||||
|
Visibility postVisibility
|
||||||
Priority int
|
Priority int
|
||||||
// Not persisted
|
// Not persisted
|
||||||
Slug string
|
Slug string
|
||||||
|
@ -32,21 +33,23 @@ type post struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type postStatus string
|
type postStatus string
|
||||||
|
type postVisibility string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
statusDeletedSuffix string = "-deleted"
|
statusDeletedSuffix postStatus = "-deleted"
|
||||||
|
|
||||||
statusNil postStatus = ""
|
statusNil postStatus = ""
|
||||||
statusPublished postStatus = "published"
|
statusPublished postStatus = "published"
|
||||||
statusPublishedDeleted postStatus = "published-deleted"
|
statusPublishedDeleted postStatus = statusPublished + statusDeletedSuffix
|
||||||
statusDraft postStatus = "draft"
|
statusDraft postStatus = "draft"
|
||||||
statusDraftDeleted postStatus = "draft-deleted"
|
statusDraftDeleted postStatus = statusDraft + statusDeletedSuffix
|
||||||
statusPrivate postStatus = "private"
|
|
||||||
statusPrivateDeleted postStatus = "private-deleted"
|
|
||||||
statusUnlisted postStatus = "unlisted"
|
|
||||||
statusUnlistedDeleted postStatus = "unlisted-deleted"
|
|
||||||
statusScheduled postStatus = "scheduled"
|
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) {
|
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)))
|
w.Header().Add("Link", fmt.Sprintf("<%s>; rel=shortlink", a.shortPostURL(p)))
|
||||||
status := http.StatusOK
|
status := http.StatusOK
|
||||||
if strings.HasSuffix(string(p.Status), statusDeletedSuffix) {
|
if p.Deleted() {
|
||||||
status = http.StatusGone
|
status = http.StatusGone
|
||||||
}
|
}
|
||||||
a.renderWithStatusCode(w, r, status, renderMethod, &renderData{
|
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"),
|
path: bc.getRelativePath("/editor/drafts"),
|
||||||
title: a.ts.GetTemplateStringVariant(bc.Lang, "drafts"),
|
title: a.ts.GetTemplateStringVariant(bc.Lang, "drafts"),
|
||||||
description: a.ts.GetTemplateStringVariant(bc.Lang, "draftsdesc"),
|
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"),
|
path: bc.getRelativePath("/editor/private"),
|
||||||
title: a.ts.GetTemplateStringVariant(bc.Lang, "privateposts"),
|
title: a.ts.GetTemplateStringVariant(bc.Lang, "privateposts"),
|
||||||
description: a.ts.GetTemplateStringVariant(bc.Lang, "privatepostsdesc"),
|
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"),
|
path: bc.getRelativePath("/editor/unlisted"),
|
||||||
title: a.ts.GetTemplateStringVariant(bc.Lang, "unlistedposts"),
|
title: a.ts.GetTemplateStringVariant(bc.Lang, "unlistedposts"),
|
||||||
description: a.ts.GetTemplateStringVariant(bc.Lang, "unlistedpostsdesc"),
|
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"),
|
path: bc.getRelativePath("/editor/scheduled"),
|
||||||
title: a.ts.GetTemplateStringVariant(bc.Lang, "scheduledposts"),
|
title: a.ts.GetTemplateStringVariant(bc.Lang, "scheduledposts"),
|
||||||
description: a.ts.GetTemplateStringVariant(bc.Lang, "scheduledpostsdesc"),
|
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"),
|
path: bc.getRelativePath("/editor/deleted"),
|
||||||
title: a.ts.GetTemplateStringVariant(bc.Lang, "deletedposts"),
|
title: a.ts.GetTemplateStringVariant(bc.Lang, "deletedposts"),
|
||||||
description: a.ts.GetTemplateStringVariant(bc.Lang, "deletedpostsdesc"),
|
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
|
title string
|
||||||
description string
|
description string
|
||||||
summaryTemplate summaryTyp
|
summaryTemplate summaryTyp
|
||||||
status postStatus
|
status []postStatus
|
||||||
statusse []postStatus
|
visibility []postVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPhotosPath = "/photos"
|
const defaultPhotosPath = "/photos"
|
||||||
|
@ -277,12 +282,14 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
sections = append(sections, sectionKey)
|
sections = append(sections, sectionKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statusse := ic.statusse
|
defaultStatus, defaultVisibility := a.getDefaultPostStates(r)
|
||||||
if ic.status != statusNil {
|
status := ic.status
|
||||||
statusse = []postStatus{ic.status}
|
if len(status) == 0 {
|
||||||
|
status = defaultStatus
|
||||||
}
|
}
|
||||||
if len(statusse) == 0 {
|
visibility := ic.visibility
|
||||||
statusse = a.getDefaultPostStatusse(r)
|
if len(visibility) == 0 {
|
||||||
|
visibility = defaultVisibility
|
||||||
}
|
}
|
||||||
p := paginator.New(&postPaginationAdapter{config: &postsRequestConfig{
|
p := paginator.New(&postPaginationAdapter{config: &postsRequestConfig{
|
||||||
blog: blog,
|
blog: blog,
|
||||||
|
@ -294,7 +301,8 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
publishedYear: ic.year,
|
publishedYear: ic.year,
|
||||||
publishedMonth: ic.month,
|
publishedMonth: ic.month,
|
||||||
publishedDay: ic.day,
|
publishedDay: ic.day,
|
||||||
statusse: statusse,
|
status: status,
|
||||||
|
visibility: visibility,
|
||||||
priorityOrder: true,
|
priorityOrder: true,
|
||||||
}, a: a}, bc.Pagination)
|
}, a: a}, bc.Pagination)
|
||||||
p.SetPage(stringToInt(chi.URLParam(r, "page")))
|
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
|
// Fix content
|
||||||
p.Content = strings.TrimSuffix(strings.TrimPrefix(p.Content, "\n"), "\n")
|
p.Content = strings.TrimSuffix(strings.TrimPrefix(p.Content, "\n"), "\n")
|
||||||
// Check status
|
// Check status
|
||||||
if p.Status == "" {
|
if p.Status == statusNil {
|
||||||
p.Status = statusPublished
|
p.Status = statusPublished
|
||||||
if p.Published != "" {
|
if p.Published != "" {
|
||||||
// If published time is in the future, set status to scheduled
|
// 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
|
// Cleanup params
|
||||||
for pk, pvs := range p.Parameters {
|
for pk, pvs := range p.Parameters {
|
||||||
pvs = lo.Filter(pvs, func(s string, _ int) bool { return s != "" })
|
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})
|
return a.createOrReplacePost(p, &postCreationOptions{new: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) replacePost(p *post, oldPath string, oldStatus postStatus) error {
|
func (a *goBlog) replacePost(p *post, oldPath string, oldStatus postStatus, oldVisibility postVisibility) error {
|
||||||
return a.createOrReplacePost(p, &postCreationOptions{new: false, oldPath: oldPath, oldStatus: oldStatus})
|
return a.createOrReplacePost(p, &postCreationOptions{new: false, oldPath: oldPath, oldStatus: oldStatus, oldVisibility: oldVisibility})
|
||||||
}
|
}
|
||||||
|
|
||||||
type postCreationOptions struct {
|
type postCreationOptions struct {
|
||||||
new bool
|
new bool
|
||||||
oldPath string
|
oldPath string
|
||||||
oldStatus postStatus
|
oldStatus postStatus
|
||||||
|
oldVisibility postVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error {
|
func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error {
|
||||||
|
@ -152,8 +157,8 @@ func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Trigger hooks
|
// Trigger hooks
|
||||||
if p.Status == statusPublished || p.Status == statusUnlisted {
|
if p.Status == statusPublished && (p.Visibility == visibilityPublic || p.Visibility == visibilityUnlisted) {
|
||||||
if o.new || (o.oldStatus != statusPublished && o.oldStatus != statusUnlisted) {
|
if o.new || (o.oldStatus != statusPublished && o.oldVisibility != visibilityPublic && o.oldVisibility != visibilityUnlisted) {
|
||||||
defer a.postPostHooks(p)
|
defer a.postPostHooks(p)
|
||||||
} else {
|
} else {
|
||||||
defer a.postUpdateHooks(p)
|
defer a.postUpdateHooks(p)
|
||||||
|
@ -183,15 +188,15 @@ func (db *database) savePost(p *post, o *postCreationOptions) error {
|
||||||
// Update or create post
|
// Update or create post
|
||||||
if o.new {
|
if o.new {
|
||||||
// New post, create it
|
// New post, create it
|
||||||
sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status, priority) values (?, ?, ?, ?, ?, ?, ?, ?);")
|
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.Priority)
|
sqlArgs = append(sqlArgs, p.Path, p.Content, toUTCSafe(p.Published), toUTCSafe(p.Updated), p.Blog, p.Section, p.Status, p.Visibility, p.Priority)
|
||||||
} else {
|
} else {
|
||||||
// Delete post parameters
|
// Delete post parameters
|
||||||
sqlBuilder.WriteString("delete from post_parameters where path = ?;")
|
sqlBuilder.WriteString("delete from post_parameters where path = ?;")
|
||||||
sqlArgs = append(sqlArgs, o.oldPath)
|
sqlArgs = append(sqlArgs, o.oldPath)
|
||||||
// Update old post
|
// Update old post
|
||||||
sqlBuilder.WriteString("update posts set path = ?, content = ?, published = ?, updated = ?, blog = ?, section = ?, status = ?, priority = ? where path = ?;")
|
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.Priority, o.oldPath)
|
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
|
// Insert post parameters
|
||||||
for param, value := range p.Parameters {
|
for param, value := range p.Parameters {
|
||||||
|
@ -227,7 +232,7 @@ func (a *goBlog) deletePost(path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Post exists, check if it's already marked as deleted
|
// 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
|
// Post is already marked as deleted, delete it from database
|
||||||
if _, err = a.db.Exec(
|
if _, err = a.db.Exec(
|
||||||
`begin; delete from posts where path = ?; insert or ignore into deleted (path) values (?); commit;`,
|
`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)
|
a.deleteReactionsCache(p.Path)
|
||||||
} else {
|
} else {
|
||||||
// Update post status
|
// Update post status
|
||||||
p.Status = postStatus(string(p.Status) + statusDeletedSuffix)
|
p.Status = p.Status + statusDeletedSuffix
|
||||||
// Add parameter
|
// Add parameter
|
||||||
deletedTime := utcNowString()
|
deletedTime := utcNowString()
|
||||||
if p.Parameters == nil {
|
if p.Parameters == nil {
|
||||||
|
@ -279,7 +284,7 @@ func (a *goBlog) undeletePost(path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Post exists, update status and parameters
|
// 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
|
// Remove parameter
|
||||||
p.Parameters["deleted"] = nil
|
p.Parameters["deleted"] = nil
|
||||||
// Update database
|
// Update database
|
||||||
|
@ -337,8 +342,8 @@ type postsRequestConfig struct {
|
||||||
limit int
|
limit int
|
||||||
offset int
|
offset int
|
||||||
sections []string
|
sections []string
|
||||||
status postStatus
|
status []postStatus
|
||||||
statusse []postStatus
|
visibility []postVisibility
|
||||||
taxonomy *configTaxonomy
|
taxonomy *configTaxonomy
|
||||||
taxonomyValue string
|
taxonomyValue string
|
||||||
parameters []string // Ignores parameterValue
|
parameters []string // Ignores parameterValue
|
||||||
|
@ -375,13 +380,9 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
||||||
queryBuilder.WriteString(" and path = @path")
|
queryBuilder.WriteString(" and path = @path")
|
||||||
args = append(args, sql.Named("path", c.path))
|
args = append(args, sql.Named("path", c.path))
|
||||||
}
|
}
|
||||||
if c.status != "" && c.status != statusNil {
|
if c.status != nil && len(c.status) > 0 {
|
||||||
queryBuilder.WriteString(" and status = @status")
|
|
||||||
args = append(args, sql.Named("status", c.status))
|
|
||||||
}
|
|
||||||
if c.statusse != nil && len(c.statusse) > 0 {
|
|
||||||
queryBuilder.WriteString(" and status in (")
|
queryBuilder.WriteString(" and status in (")
|
||||||
for i, status := range c.statusse {
|
for i, status := range c.status {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
queryBuilder.WriteString(", ")
|
queryBuilder.WriteString(", ")
|
||||||
}
|
}
|
||||||
|
@ -392,6 +393,19 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
||||||
}
|
}
|
||||||
queryBuilder.WriteByte(')')
|
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 != "" {
|
if c.blog != "" {
|
||||||
queryBuilder.WriteString(" and blog = @blog")
|
queryBuilder.WriteString(" and blog = @blog")
|
||||||
args = append(args, sql.Named("blog", c.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) {
|
func (a *goBlog) getPosts(config *postsRequestConfig) (posts []*post, err error) {
|
||||||
// Query posts
|
// 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...)
|
rows, err := a.db.Query(query, queryParams...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Prepare row scanning
|
// 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
|
var priority int
|
||||||
for rows.Next() {
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create new post, fill and add to list
|
// Create new post, fill and add to list
|
||||||
p := &post{
|
p := &post{
|
||||||
Path: path,
|
Path: path,
|
||||||
Content: content,
|
Content: content,
|
||||||
Published: toLocalSafe(published),
|
Published: toLocalSafe(published),
|
||||||
Updated: toLocalSafe(updated),
|
Updated: toLocalSafe(updated),
|
||||||
Blog: blog,
|
Blog: blog,
|
||||||
Section: section,
|
Section: section,
|
||||||
Status: postStatus(status),
|
Status: postStatus(status),
|
||||||
Priority: priority,
|
Visibility: postVisibility(visibility),
|
||||||
|
Priority: priority,
|
||||||
}
|
}
|
||||||
posts = append(posts, p)
|
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) {
|
func (d *database) allTaxonomyValues(blog string, taxonomy string) ([]string, error) {
|
||||||
// TODO: Query posts the normal way
|
rows, err := d.Query(
|
||||||
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))
|
"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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,14 @@ func Test_postsDb(t *testing.T) {
|
||||||
|
|
||||||
// Save post
|
// Save post
|
||||||
err := app.db.savePost(&post{
|
err := app.db.savePost(&post{
|
||||||
Path: "/test/abc",
|
Path: "/test/abc",
|
||||||
Content: "ABC",
|
Content: "ABC",
|
||||||
Published: now,
|
Published: now,
|
||||||
Updated: nowPlus1Hour,
|
Updated: nowPlus1Hour,
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Status: statusDraft,
|
Status: statusDraft,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
Parameters: map[string][]string{
|
Parameters: map[string][]string{
|
||||||
"title": {"Title"},
|
"title": {"Title"},
|
||||||
"tags": {"C", "A", "B"},
|
"tags": {"C", "A", "B"},
|
||||||
|
@ -65,7 +66,7 @@ func Test_postsDb(t *testing.T) {
|
||||||
// Check drafts
|
// Check drafts
|
||||||
drafts, _ := app.getPosts(&postsRequestConfig{
|
drafts, _ := app.getPosts(&postsRequestConfig{
|
||||||
blog: "en",
|
blog: "en",
|
||||||
status: statusDraft,
|
status: []postStatus{statusDraft},
|
||||||
})
|
})
|
||||||
is.Len(drafts, 1)
|
is.Len(drafts, 1)
|
||||||
|
|
||||||
|
@ -97,10 +98,10 @@ func Test_postsDb(t *testing.T) {
|
||||||
must.NoError(err)
|
must.NoError(err)
|
||||||
|
|
||||||
// Check if post is marked as deleted
|
// 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)
|
must.NoError(err)
|
||||||
is.Equal(0, count)
|
is.Equal(0, count)
|
||||||
count, err = app.db.countPosts(&postsRequestConfig{status: statusDraftDeleted})
|
count, err = app.db.countPosts(&postsRequestConfig{status: []postStatus{statusDraftDeleted}})
|
||||||
must.NoError(err)
|
must.NoError(err)
|
||||||
is.Equal(1, count)
|
is.Equal(1, count)
|
||||||
|
|
||||||
|
@ -115,13 +116,14 @@ func Test_postsDb(t *testing.T) {
|
||||||
|
|
||||||
// Save published post
|
// Save published post
|
||||||
err = app.db.savePost(&post{
|
err = app.db.savePost(&post{
|
||||||
Path: "/test/abc",
|
Path: "/test/abc",
|
||||||
Content: "ABC",
|
Content: "ABC",
|
||||||
Published: "2021-06-10 10:00:00",
|
Published: "2021-06-10 10:00:00",
|
||||||
Updated: "2021-06-15 10:00:00",
|
Updated: "2021-06-15 10:00:00",
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Status: statusPublished,
|
Status: statusPublished,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
Parameters: map[string][]string{
|
Parameters: map[string][]string{
|
||||||
"tags": {"Test", "Blog", "A"},
|
"tags": {"Test", "Blog", "A"},
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,7 @@ func (a *goBlog) initPostsDeleter() {
|
||||||
func (a *goBlog) checkDeletedPosts() {
|
func (a *goBlog) checkDeletedPosts() {
|
||||||
// Get all posts with `deleted` parameter and a deleted status
|
// Get all posts with `deleted` parameter and a deleted status
|
||||||
postsToDelete, err := a.getPosts(&postsRequestConfig{
|
postsToDelete, err := a.getPosts(&postsRequestConfig{
|
||||||
statusse: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted},
|
status: []postStatus{statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted},
|
||||||
parameter: "deleted",
|
parameter: "deleted",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -142,7 +142,7 @@ func (a *goBlog) postTranslations(p *post) []*post {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) isPublishedSectionPost() bool {
|
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 {
|
func (a *goBlog) postToMfItem(p *post) *microformatItem {
|
||||||
|
@ -150,20 +150,18 @@ func (a *goBlog) postToMfItem(p *post) *microformatItem {
|
||||||
switch p.Status {
|
switch p.Status {
|
||||||
case statusDraft:
|
case statusDraft:
|
||||||
mfStatus = "draft"
|
mfStatus = "draft"
|
||||||
case statusPublished, statusScheduled, statusUnlisted, statusPrivate:
|
case statusPublished, statusScheduled:
|
||||||
mfStatus = "published"
|
mfStatus = "published"
|
||||||
case statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted:
|
case statusPublishedDeleted, statusDraftDeleted, statusScheduledDeleted:
|
||||||
mfStatus = "deleted"
|
mfStatus = "deleted"
|
||||||
}
|
}
|
||||||
switch p.Status {
|
switch p.Visibility {
|
||||||
case statusDraft, statusScheduled, statusPublished:
|
case visibilityPublic:
|
||||||
mfVisibility = "public"
|
mfVisibility = "public"
|
||||||
case statusUnlisted:
|
case visibilityUnlisted:
|
||||||
mfVisibility = "unlisted"
|
mfVisibility = "unlisted"
|
||||||
case statusPrivate:
|
case visibilityPrivate:
|
||||||
mfVisibility = "private"
|
mfVisibility = "private"
|
||||||
case statusPublishedDeleted, statusDraftDeleted, statusPrivateDeleted, statusUnlistedDeleted, statusScheduledDeleted:
|
|
||||||
mfVisibility = "deleted"
|
|
||||||
}
|
}
|
||||||
return µformatItem{
|
return µformatItem{
|
||||||
Type: []string{"h-entry"},
|
Type: []string{"h-entry"},
|
||||||
|
@ -244,6 +242,7 @@ func (p *post) contentWithParams() string {
|
||||||
params["published"] = p.Published
|
params["published"] = p.Published
|
||||||
params["updated"] = p.Updated
|
params["updated"] = p.Updated
|
||||||
params["status"] = string(p.Status)
|
params["status"] = string(p.Status)
|
||||||
|
params["visibility"] = string(p.Visibility)
|
||||||
params["priority"] = p.Priority
|
params["priority"] = p.Priority
|
||||||
pb, _ := yaml.Marshal(params)
|
pb, _ := yaml.Marshal(params)
|
||||||
return fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
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 {
|
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() {
|
func (a *goBlog) checkScheduledPosts() {
|
||||||
postsToPublish, err := a.getPosts(&postsRequestConfig{
|
postsToPublish, err := a.getPosts(&postsRequestConfig{
|
||||||
status: statusScheduled,
|
status: []postStatus{statusScheduled},
|
||||||
publishedBefore: time.Now(),
|
publishedBefore: time.Now(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,7 +36,7 @@ func (a *goBlog) checkScheduledPosts() {
|
||||||
}
|
}
|
||||||
for _, post := range postsToPublish {
|
for _, post := range postsToPublish {
|
||||||
post.Status = statusPublished
|
post.Status = statusPublished
|
||||||
err := a.replacePost(post, post.Path, statusScheduled)
|
err := a.replacePost(post, post.Path, statusScheduled, post.Visibility)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error publishing scheduled post:", err)
|
log.Println("Error publishing scheduled post:", err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -26,23 +26,24 @@ func Test_postsScheduler(t *testing.T) {
|
||||||
_ = app.initCache()
|
_ = app.initCache()
|
||||||
|
|
||||||
err := app.db.savePost(&post{
|
err := app.db.savePost(&post{
|
||||||
Path: "/test/abc",
|
Path: "/test/abc",
|
||||||
Content: "ABC",
|
Content: "ABC",
|
||||||
Published: toLocalSafe(time.Now().Add(-1 * time.Hour).String()),
|
Published: toLocalSafe(time.Now().Add(-1 * time.Hour).String()),
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Status: statusScheduled,
|
Status: statusScheduled,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
}, &postCreationOptions{new: true})
|
}, &postCreationOptions{new: true})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
count, err := app.db.countPosts(&postsRequestConfig{status: statusPublished})
|
count, err := app.db.countPosts(&postsRequestConfig{status: []postStatus{statusScheduled}})
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, 0, count)
|
|
||||||
|
|
||||||
app.checkScheduledPosts()
|
|
||||||
|
|
||||||
count, err = app.db.countPosts(&postsRequestConfig{status: statusPublished})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 1, count)
|
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",
|
Path: "/newpost",
|
||||||
Content: "test",
|
Content: "test",
|
||||||
Status: statusPublished,
|
Status: statusPublished,
|
||||||
}, "/testpost", statusPublished)
|
}, "/testpost", statusPublished, visibilityPublic)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Check if reaction count is 4
|
// Check if reaction count is 4
|
||||||
|
|
|
@ -155,7 +155,8 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
|
||||||
// Request posts
|
// Request posts
|
||||||
blog, _ := a.getBlog(r)
|
blog, _ := a.getBlog(r)
|
||||||
posts, _ := a.getPosts(&postsRequestConfig{
|
posts, _ := a.getPosts(&postsRequestConfig{
|
||||||
status: statusPublished,
|
status: []postStatus{statusPublished},
|
||||||
|
visibility: []postVisibility{visibilityPublic},
|
||||||
blog: blog,
|
blog: blog,
|
||||||
withoutParameters: true,
|
withoutParameters: true,
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,7 +19,7 @@ download: "Herunterladen"
|
||||||
drafts: "Entwürfe"
|
drafts: "Entwürfe"
|
||||||
draftsdesc: "Posts mit dem Status `draft`."
|
draftsdesc: "Posts mit dem Status `draft`."
|
||||||
editor: "Editor"
|
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)"
|
emailopt: "E-Mail (optional)"
|
||||||
fileuses: "Datei-Verwendungen"
|
fileuses: "Datei-Verwendungen"
|
||||||
general: "Allgemein"
|
general: "Allgemein"
|
||||||
|
@ -50,7 +50,7 @@ posts: "Posts"
|
||||||
postsections: "Post-Bereiche"
|
postsections: "Post-Bereiche"
|
||||||
prev: "Zurück"
|
prev: "Zurück"
|
||||||
privateposts: "Private Posts"
|
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"
|
publishedon: "Veröffentlicht am"
|
||||||
replyto: "Antwort an"
|
replyto: "Antwort an"
|
||||||
scheduledposts: "Geplante Posts"
|
scheduledposts: "Geplante Posts"
|
||||||
|
@ -74,11 +74,12 @@ translate: "Übersetzen"
|
||||||
translations: "Übersetzungen"
|
translations: "Übersetzungen"
|
||||||
undelete: "Wiederherstellen"
|
undelete: "Wiederherstellen"
|
||||||
unlistedposts: "Ungelistete Posts"
|
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"
|
update: "Aktualisieren"
|
||||||
updatedon: "Aktualisiert am"
|
updatedon: "Aktualisiert am"
|
||||||
upload: "Hochladen"
|
upload: "Hochladen"
|
||||||
view: "Anschauen"
|
view: "Anschauen"
|
||||||
|
visibility: "Sichtbarkeit"
|
||||||
whatistor: "Was ist Tor?"
|
whatistor: "Was ist Tor?"
|
||||||
withoutdate: "Ohne Datum"
|
withoutdate: "Ohne Datum"
|
||||||
words: "Wörter"
|
words: "Wörter"
|
||||||
|
|
|
@ -22,7 +22,7 @@ download: "Download"
|
||||||
drafts: "Drafts"
|
drafts: "Drafts"
|
||||||
draftsdesc: "Posts with status `draft`."
|
draftsdesc: "Posts with status `draft`."
|
||||||
editor: "Editor"
|
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)"
|
emailopt: "Email (optional)"
|
||||||
feed: "Feed"
|
feed: "Feed"
|
||||||
fileuses: "file uses"
|
fileuses: "file uses"
|
||||||
|
@ -60,7 +60,7 @@ posts: "Posts"
|
||||||
postsections: "Post sections"
|
postsections: "Post sections"
|
||||||
prev: "Previous"
|
prev: "Previous"
|
||||||
privateposts: "Private posts"
|
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"
|
publishedon: "Published on"
|
||||||
replyto: "Reply to"
|
replyto: "Reply to"
|
||||||
reverify: "Reverify"
|
reverify: "Reverify"
|
||||||
|
@ -87,13 +87,14 @@ translate: "Translate"
|
||||||
translations: "Translations"
|
translations: "Translations"
|
||||||
undelete: "Undelete"
|
undelete: "Undelete"
|
||||||
unlistedposts: "Unlisted posts"
|
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"
|
update: "Update"
|
||||||
updatedon: "Updated on"
|
updatedon: "Updated on"
|
||||||
upload: "Upload"
|
upload: "Upload"
|
||||||
username: "Username"
|
username: "Username"
|
||||||
verified: "Verified"
|
verified: "Verified"
|
||||||
view: "View"
|
view: "View"
|
||||||
|
visibility: "Visibility"
|
||||||
webmentions: "Webmentions"
|
webmentions: "Webmentions"
|
||||||
websiteopt: "Website (optional)"
|
websiteopt: "Website (optional)"
|
||||||
whatistor: "What is Tor?"
|
whatistor: "What is Tor?"
|
||||||
|
|
|
@ -22,7 +22,6 @@ download: "Descargar"
|
||||||
drafts: "Borradores"
|
drafts: "Borradores"
|
||||||
draftsdesc: "Posts con status `draft` (borrador)."
|
draftsdesc: "Posts con status `draft` (borrador)."
|
||||||
editor: "Editor"
|
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)"
|
emailopt: "Email (opcional)"
|
||||||
feed: "Feed"
|
feed: "Feed"
|
||||||
fileuses: "usos de archivo"
|
fileuses: "usos de archivo"
|
||||||
|
@ -57,7 +56,6 @@ posts: "Posts"
|
||||||
postsections: "Secciones de Posts"
|
postsections: "Secciones de Posts"
|
||||||
prev: "Anterior"
|
prev: "Anterior"
|
||||||
privateposts: "Posts Privados"
|
privateposts: "Posts Privados"
|
||||||
privatepostsdesc: "Posts con status `private` que son visibles solo al iniciar sesión."
|
|
||||||
publishedon: "Publicado en"
|
publishedon: "Publicado en"
|
||||||
replyto: "Respuesta a"
|
replyto: "Respuesta a"
|
||||||
reverify: "Reverificar"
|
reverify: "Reverificar"
|
||||||
|
@ -84,7 +82,6 @@ translate: "Traducir"
|
||||||
translations: "Traducciones"
|
translations: "Traducciones"
|
||||||
undelete: "Undelete"
|
undelete: "Undelete"
|
||||||
unlistedposts: "Posts No Listados"
|
unlistedposts: "Posts No Listados"
|
||||||
unlistedpostsdesc: "Los Posts con status `unlisted` no se muestran en el archivo."
|
|
||||||
update: "Actualizar"
|
update: "Actualizar"
|
||||||
updatedon: "Actualizado en"
|
updatedon: "Actualizado en"
|
||||||
upload: "Cargar"
|
upload: "Cargar"
|
||||||
|
|
|
@ -22,7 +22,6 @@ download: "Baixar"
|
||||||
drafts: "Rascunho"
|
drafts: "Rascunho"
|
||||||
draftsdesc: "Posts com status `draft`."
|
draftsdesc: "Posts com status `draft`."
|
||||||
editor: "Editor"
|
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)"
|
emailopt: "Email (opcional)"
|
||||||
feed: "Feed"
|
feed: "Feed"
|
||||||
fileuses: "arquivo usa"
|
fileuses: "arquivo usa"
|
||||||
|
@ -60,7 +59,6 @@ posts: "Posts"
|
||||||
postsections: "Seções dos posts"
|
postsections: "Seções dos posts"
|
||||||
prev: "Anterior"
|
prev: "Anterior"
|
||||||
privateposts: "Posts privados"
|
privateposts: "Posts privados"
|
||||||
privatepostsdesc: "Posts com status `private` que são visíveis apenas quando logado."
|
|
||||||
publishedon: "Publicado em"
|
publishedon: "Publicado em"
|
||||||
replyto: "Responder para"
|
replyto: "Responder para"
|
||||||
reverify: "Reverificar"
|
reverify: "Reverificar"
|
||||||
|
@ -87,7 +85,6 @@ translate: "Traduzir"
|
||||||
translations: "Traduções"
|
translations: "Traduções"
|
||||||
undelete: "Desfazer exclusão"
|
undelete: "Desfazer exclusão"
|
||||||
unlistedposts: "Posts não listados"
|
unlistedposts: "Posts não listados"
|
||||||
unlistedpostsdesc: "Posts com status `unlisted` que não são mostrados nos arquivos."
|
|
||||||
update: "Atualizar"
|
update: "Atualizar"
|
||||||
updatedon: "Atualizado em"
|
updatedon: "Atualizado em"
|
||||||
upload: "Enviar"
|
upload: "Enviar"
|
||||||
|
|
|
@ -134,6 +134,7 @@ func Test_telegram(t *testing.T) {
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Status: statusPublished,
|
Status: statusPublished,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
}
|
}
|
||||||
|
|
||||||
app.pPostHooks[0](p)
|
app.pPostHooks[0](p)
|
||||||
|
@ -163,10 +164,11 @@ func Test_telegram(t *testing.T) {
|
||||||
Parameters: map[string][]string{
|
Parameters: map[string][]string{
|
||||||
"title": {"Title"},
|
"title": {"Title"},
|
||||||
},
|
},
|
||||||
Published: time.Now().String(),
|
Published: time.Now().String(),
|
||||||
Section: "test",
|
Section: "test",
|
||||||
Blog: "default",
|
Blog: "default",
|
||||||
Status: statusPublished,
|
Status: statusPublished,
|
||||||
|
Visibility: visibilityPublic,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Nil(t, fakeClient.req)
|
assert.Nil(t, fakeClient.req)
|
||||||
|
|
|
@ -218,6 +218,14 @@ func (a *goBlog) renderPostMeta(hb *htmlbuilder.HtmlBuilder, p *post, b *configB
|
||||||
hb.WriteEscaped(string(p.Status))
|
hb.WriteEscaped(string(p.Status))
|
||||||
hb.WriteElementClose("div")
|
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" {
|
if typ == "summary" || typ == "post" {
|
||||||
hb.WriteElementClose("div")
|
hb.WriteElementClose("div")
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
const postParamWebmention = "webmention"
|
const postParamWebmention = "webmention"
|
||||||
|
|
||||||
func (a *goBlog) sendWebmentions(p *post) error {
|
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
|
// Not published or unlisted
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue