mirror of https://github.com/jlelse/GoBlog
parent
63cb57ffaa
commit
9ea6104863
|
@ -55,5 +55,6 @@ Here's an (incomplete) list of features:
|
|||
|
||||
- [How to install and run GoBlog](./install.md)
|
||||
- [How to build GoBlog](./build.md)
|
||||
- [How to use GoBlog](./usage.md)
|
||||
- [Administration paths](./admin-paths.md)
|
||||
- [GoBlog's storage system](./storage.md)
|
|
@ -0,0 +1,9 @@
|
|||
# How to use GoBlog
|
||||
|
||||
This section of the documentation is a work in progress!
|
||||
|
||||
## Posting
|
||||
|
||||
### Scheduling posts
|
||||
|
||||
To schedule a post, create a post with `status: scheduled` and set the `published` field to the desired date. A scheduler runs in the background and checks every 10 seconds if a scheduled post should be published. If there's a post to publish, the post status is changed to `published`. That will also trigger configured hooks. Scheduled posts are only visible when logged in.
|
|
@ -227,6 +227,8 @@ func (a *goBlog) editorPostDesc(blog string) string {
|
|||
t := a.ts.GetTemplateStringVariant(bc.Lang, "editorpostdesc")
|
||||
var paramBuilder, statusBuilder strings.Builder
|
||||
for i, param := range []string{
|
||||
"published",
|
||||
"updated",
|
||||
"summary",
|
||||
"translationkey",
|
||||
"original",
|
||||
|
@ -252,7 +254,7 @@ func (a *goBlog) editorPostDesc(blog string) string {
|
|||
paramBuilder.WriteByte('`')
|
||||
}
|
||||
for i, status := range []postStatus{
|
||||
statusDraft, statusPublished, statusUnlisted, statusPrivate,
|
||||
statusDraft, statusPublished, statusUnlisted, statusScheduled, statusPrivate,
|
||||
} {
|
||||
if i > 0 {
|
||||
statusBuilder.WriteString(", ")
|
||||
|
|
2
http.go
2
http.go
|
@ -265,7 +265,7 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc {
|
|||
case statusPublished, statusUnlisted:
|
||||
alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
||||
return
|
||||
case statusDraft, statusPrivate:
|
||||
default: // private, draft, scheduled
|
||||
alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
|
1
main.go
1
main.go
|
@ -175,6 +175,7 @@ func (app *goBlog) initComponents(logging bool) {
|
|||
app.initBlogStats()
|
||||
app.initSessions()
|
||||
app.initIndieAuth()
|
||||
app.startPostsScheduler()
|
||||
// Log finish
|
||||
if logging {
|
||||
log.Println("Initialized components")
|
||||
|
|
1
posts.go
1
posts.go
|
@ -42,6 +42,7 @@ const (
|
|||
statusDraft postStatus = "draft"
|
||||
statusPrivate postStatus = "private"
|
||||
statusUnlisted postStatus = "unlisted"
|
||||
statusScheduled postStatus = "scheduled"
|
||||
)
|
||||
|
||||
func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -276,6 +276,7 @@ type postsRequestConfig struct {
|
|||
parameter string // Ignores parameters
|
||||
parameterValue string
|
||||
publishedYear, publishedMonth, publishedDay int
|
||||
publishedBefore time.Time
|
||||
randomOrder bool
|
||||
priorityOrder bool
|
||||
withoutParameters bool
|
||||
|
@ -360,6 +361,10 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
|||
queryBuilder.WriteString(" and substr(tolocal(published), 9, 2) = @publishedday")
|
||||
args = append(args, sql.Named("publishedday", fmt.Sprintf("%02d", c.publishedDay)))
|
||||
}
|
||||
if !c.publishedBefore.IsZero() {
|
||||
queryBuilder.WriteString(" and toutc(published) < @publishedbefore")
|
||||
args = append(args, sql.Named("publishedbefore", c.publishedBefore.UTC().Format(time.RFC3339)))
|
||||
}
|
||||
// Order
|
||||
queryBuilder.WriteString(" order by ")
|
||||
if c.randomOrder {
|
||||
|
|
|
@ -137,7 +137,7 @@ func (a *goBlog) postToMfItem(p *post) *microformatItem {
|
|||
switch p.Status {
|
||||
case statusDraft:
|
||||
mfStatus = "draft"
|
||||
case statusPublished:
|
||||
case statusPublished, statusScheduled:
|
||||
mfStatus = "published"
|
||||
mfVisibility = "public"
|
||||
case statusUnlisted:
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (a *goBlog) startPostsScheduler() {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.checkScheduledPosts()
|
||||
}
|
||||
}
|
||||
}()
|
||||
a.shutdown.Add(func() {
|
||||
ticker.Stop()
|
||||
done <- true
|
||||
log.Println("Posts scheduler stopped")
|
||||
})
|
||||
}
|
||||
|
||||
func (a *goBlog) checkScheduledPosts() {
|
||||
postsToPublish, err := a.getPosts(&postsRequestConfig{
|
||||
status: "scheduled",
|
||||
publishedBefore: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("Error getting scheduled posts:", err)
|
||||
return
|
||||
}
|
||||
for _, post := range postsToPublish {
|
||||
post.Status = "published"
|
||||
err := a.replacePost(post, post.Path, statusScheduled)
|
||||
if err != nil {
|
||||
log.Println("Error publishing scheduled post:", err)
|
||||
continue
|
||||
}
|
||||
log.Println("Published scheduled post:", post.Path)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_postsScheduler(t *testing.T) {
|
||||
|
||||
app := &goBlog{
|
||||
cfg: &config{
|
||||
Db: &configDb{
|
||||
File: filepath.Join(t.TempDir(), "test.db"),
|
||||
},
|
||||
Server: &configServer{
|
||||
PublicAddress: "https://example.com",
|
||||
},
|
||||
DefaultBlog: "en",
|
||||
Blogs: map[string]*configBlog{
|
||||
"en": {
|
||||
Sections: map[string]*configSection{
|
||||
"test": {},
|
||||
},
|
||||
Lang: "en",
|
||||
},
|
||||
},
|
||||
Micropub: &configMicropub{},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.initDatabase(false)
|
||||
app.initComponents(false)
|
||||
|
||||
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,
|
||||
}, &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})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
}
|
|
@ -13,6 +13,12 @@ import (
|
|||
func (a *goBlog) initTelegram() {
|
||||
a.pPostHooks = append(a.pPostHooks, func(p *post) {
|
||||
if tg := a.cfg.Blogs[p.Blog].Telegram; tg.enabled() && p.isPublishedSectionPost() {
|
||||
tgChat := p.firstParameter("telegramchat")
|
||||
tgMsg := p.firstParameter("telegrammsg")
|
||||
if tgChat != "" && tgMsg != "" {
|
||||
// Already posted
|
||||
return
|
||||
}
|
||||
// Generate HTML
|
||||
html := tg.generateHTML(p.RenderedTitle, a.fullPostURL(p), a.shortPostURL(p))
|
||||
if html == "" {
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
{{ end }}
|
||||
{{ $short := shorturl .Data }}
|
||||
{{ if $short }}<div>{{ string .Blog.Lang "shorturl" }} <a href="{{ $short }}" rel="shortlink">{{ $short }}</a></div>{{ end }}
|
||||
{{ if ne .Data.Status "published" }}<div>{{ string .Blog.Lang "status" }}: {{ .Data.Status }}</div>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
|
@ -48,6 +48,7 @@ send: "Senden (zur Überprüfung)"
|
|||
share: "Online teilen"
|
||||
shorturl: "Kurz-Link:"
|
||||
speak: "Vorlesen"
|
||||
status: "Status"
|
||||
stopspeak: "Vorlesen stoppen"
|
||||
submit: "Abschicken"
|
||||
total: "Gesamt"
|
||||
|
|
|
@ -60,6 +60,7 @@ send: "Send (to review)"
|
|||
share: "Share online"
|
||||
shorturl: "Short link:"
|
||||
speak: "Read aloud"
|
||||
status: "Status"
|
||||
stopspeak: "Stop reading aloud"
|
||||
submit: "Submit"
|
||||
total: "Total"
|
||||
|
|
|
@ -60,6 +60,7 @@ send: "Enviar (para revisão)"
|
|||
share: "Compartilhar online"
|
||||
shorturl: "Link curto:"
|
||||
speak: "Leia"
|
||||
status: "Status"
|
||||
stopspeak: "Pare de ler"
|
||||
submit: "Enviar"
|
||||
total: "Total"
|
||||
|
|
Loading…
Reference in New Issue