diff --git a/blogstats.go b/blogstats.go index 784e142..1b190e9 100644 --- a/blogstats.go +++ b/blogstats.go @@ -63,7 +63,7 @@ with filtered as ( from ( select path, - coalesce(published, '') as pub, + tolocal(published) as pub, mdtext(coalesce(content, '')) as content from posts where status = @status and blog = @blog diff --git a/captcha_test.go b/captcha_test.go index b8bf2a0..cb676b8 100644 --- a/captcha_test.go +++ b/captcha_test.go @@ -30,7 +30,7 @@ func Test_captchaMiddleware(t *testing.T) { }, } - app.initDatabase(false) + _ = app.initDatabase(false) app.initSessions() _ = app.initTemplateStrings() _ = app.initRendering() diff --git a/check.go b/check.go index 25e2d9f..8a41248 100644 --- a/check.go +++ b/check.go @@ -22,7 +22,7 @@ func (a *goBlog) checkAllExternalLinks() { log.Println(err.Error()) return } - a.checkLinks(log.Writer(), posts...) + _ = a.checkLinks(log.Writer(), posts...) } func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error { diff --git a/databaseMigrations.go b/databaseMigrations.go index 94f2b00..56d2ecb 100644 --- a/databaseMigrations.go +++ b/databaseMigrations.go @@ -230,18 +230,20 @@ func migrateDb(db *sql.DB, logging bool) error { return err }, }, + &migrator.Migration{ + Name: "00019", + Func: func(tx *sql.Tx) error { + _, err := tx.Exec(` + update posts set published = toutc(published), updated = toutc(updated); + insert into posts_fts(posts_fts) values ('rebuild'); + `) + return err + }, + }, ), ) if err != nil { return err } - err = m.Migrate(db) - if err != nil { - return err - } - // Update times in database to local time - _, err = db.Exec(` - update posts set published = tolocal(published), updated = tolocal(updated); - `) - return err + return m.Migrate(db) } diff --git a/editor.go b/editor.go index ff55a71..99d12e3 100644 --- a/editor.go +++ b/editor.go @@ -17,9 +17,7 @@ func (a *goBlog) serveEditor(w http.ResponseWriter, r *http.Request) { blog := r.Context().Value(blogContextKey).(string) a.render(w, r, templateEditor, &renderData{ BlogString: blog, - Data: map[string]interface{}{ - "Drafts": a.db.getDrafts(blog), - }, + Data: map[string]interface{}{}, }) } @@ -32,7 +30,6 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) { BlogString: blog, Data: map[string]interface{}{ "DeleteURL": r.FormValue("url"), - "Drafts": a.db.getDrafts(blog), }, }) case "loadupdate": @@ -51,7 +48,6 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) { Data: map[string]interface{}{ "UpdatePostURL": parsedURL.String(), "UpdatePostContent": a.postToMfItem(post).Properties.Content[0], - "Drafts": a.db.getDrafts(blog), }, }) case "updatepost": @@ -77,14 +73,6 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) { a.editorMicropubPost(w, req, false) case "upload": a.editorMicropubPost(w, r, true) - case "viewdraft": - parsedURL, err := url.Parse(r.FormValue("url")) - if err != nil { - a.serveError(w, r, err.Error(), http.StatusBadRequest) - return - } - http.Redirect(w, r, parsedURL.Path, http.StatusFound) - return default: a.serveError(w, r, "Unknown editoraction", http.StatusBadRequest) } diff --git a/go.mod b/go.mod index 91e0b01..5a6a75b 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/jonboulle/clockwork v0.2.2 // indirect github.com/kaorimatz/go-opml v0.0.0-20210201121027-bc8e2852d7f9 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible - github.com/lestrrat-go/strftime v1.0.4 // indirect + github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/lib/pq v1.9.0 // indirect github.com/lopezator/migrator v0.3.0 github.com/mattn/go-sqlite3 v1.14.7 diff --git a/go.sum b/go.sum index a32b71e..eb2cc93 100644 --- a/go.sum +++ b/go.sum @@ -273,8 +273,8 @@ github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2t github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= -github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8= -github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= +github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= +github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= diff --git a/http.go b/http.go index 49a337a..83ad1c8 100644 --- a/http.go +++ b/http.go @@ -198,6 +198,15 @@ func (a *goBlog) buildStaticHandlersRouters() error { a.editorRouter.Get("/files", a.serveEditorFiles) a.editorRouter.Post("/files/view", a.serveEditorFilesView) a.editorRouter.Post("/files/delete", a.serveEditorFilesDelete) + a.editorRouter.Get("/drafts", a.serveDrafts) + a.editorRouter.Get("/drafts"+feedPath, a.serveDrafts) + a.editorRouter.Get("/drafts"+paginationPath, a.serveDrafts) + a.editorRouter.Get("/private", a.servePrivate) + a.editorRouter.Get("/private"+feedPath, a.servePrivate) + a.editorRouter.Get("/private"+paginationPath, a.servePrivate) + a.editorRouter.Get("/unlisted", a.serveUnlisted) + a.editorRouter.Get("/unlisted"+feedPath, a.serveUnlisted) + a.editorRouter.Get("/unlisted"+paginationPath, a.serveUnlisted) a.commentsRouter = chi.NewRouter() a.commentsRouter.Use(a.privateModeHandler...) @@ -331,7 +340,7 @@ func (a *goBlog) buildDynamicRouter() (*chi.Mux, error) { r.Mount(notificationsPath, a.notificationsRouter) // Posts - pp, err := a.db.allPostPaths(statusPublished) + pp, err := a.db.getPostPaths(statusPublished) if err != nil { return nil, err } @@ -343,8 +352,33 @@ func (a *goBlog) buildDynamicRouter() (*chi.Mux, error) { } }) - // Drafts - dp, err := a.db.allPostPaths(statusDraft) + // Unlisted posts + up, err := a.db.getPostPaths(statusUnlisted) + if err != nil { + return nil, err + } + r.Group(func(r chi.Router) { + r.Use(a.privateModeHandler...) + r.Use(a.checkActivityStreamsRequest, a.cache.cacheMiddleware) + for _, path := range up { + r.Get(path, a.servePost) + } + }) + + // Private posts + priv, err := a.db.getPostPaths(statusPrivate) + if err != nil { + return nil, err + } + r.Group(func(r chi.Router) { + r.Use(a.authMiddleware) + for _, path := range priv { + r.Get(path, a.servePost) + } + }) + + // Draft posts + dp, err := a.db.getPostPaths(statusDraft) if err != nil { return nil, err } diff --git a/posts.go b/posts.go index b1f1f72..da40ebd 100644 --- a/posts.go +++ b/posts.go @@ -40,6 +40,8 @@ const ( statusNil postStatus = "" statusPublished postStatus = "published" statusDraft postStatus = "draft" + statusPrivate postStatus = "private" + statusUnlisted postStatus = "unlisted" ) func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) { @@ -121,6 +123,33 @@ func (a *goBlog) serveHome(w http.ResponseWriter, r *http.Request) { }))) } +func (a *goBlog) serveDrafts(w http.ResponseWriter, r *http.Request) { + blog := r.Context().Value(blogContextKey).(string) + a.serveIndex(w, r.WithContext(context.WithValue(r.Context(), indexConfigKey, &indexConfig{ + path: a.getRelativePath(blog, "/editor/drafts"), + title: a.ts.GetTemplateStringVariant(a.cfg.Blogs[blog].Lang, "drafts"), + status: statusDraft, + }))) +} + +func (a *goBlog) servePrivate(w http.ResponseWriter, r *http.Request) { + blog := r.Context().Value(blogContextKey).(string) + a.serveIndex(w, r.WithContext(context.WithValue(r.Context(), indexConfigKey, &indexConfig{ + path: a.getRelativePath(blog, "/editor/private"), + title: a.ts.GetTemplateStringVariant(a.cfg.Blogs[blog].Lang, "privateposts"), + status: statusPrivate, + }))) +} + +func (a *goBlog) serveUnlisted(w http.ResponseWriter, r *http.Request) { + blog := r.Context().Value(blogContextKey).(string) + a.serveIndex(w, r.WithContext(context.WithValue(r.Context(), indexConfigKey, &indexConfig{ + path: a.getRelativePath(blog, "/editor/unlisted"), + title: a.ts.GetTemplateStringVariant(a.cfg.Blogs[blog].Lang, "unlistedposts"), + status: statusUnlisted, + }))) +} + func (a *goBlog) serveDate(w http.ResponseWriter, r *http.Request) { var year, month, day int if ys := chi.URLParam(r, "year"); ys != "" && ys != "x" { @@ -176,6 +205,7 @@ type indexConfig struct { title string description string summaryTemplate string + status postStatus } const defaultPhotosPath = "/photos" @@ -203,6 +233,10 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) { sections = append(sections, sectionKey) } } + status := ic.status + if status == statusNil { + status = statusPublished + } p := paginator.New(&postPaginationAdapter{config: &postsRequestConfig{ blog: blog, sections: sections, @@ -213,7 +247,7 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) { publishedYear: ic.year, publishedMonth: ic.month, publishedDay: ic.day, - status: statusPublished, + status: status, priorityOrder: true, }, db: a.db}, a.cfg.Blogs[blog].Pagination) p.SetPage(pageNo) diff --git a/postsDb.go b/postsDb.go index 30fdf1d..84db4e6 100644 --- a/postsDb.go +++ b/postsDb.go @@ -132,7 +132,7 @@ func (a *goBlog) createOrReplacePost(p *post, o *postCreationOptions) error { } // Trigger hooks if p.Status == statusPublished { - if o.new || o.oldStatus == statusDraft { + if o.new || o.oldStatus != statusPublished { defer a.postPostHooks(p) } else { defer a.postUpdateHooks(p) @@ -163,7 +163,7 @@ func (db *database) savePost(p *post, o *postCreationOptions) error { } // Insert new post sqlBuilder.WriteString("insert into posts (path, content, published, updated, blog, section, status, priority) values (?, ?, ?, ?, ?, ?, ?, ?);") - sqlArgs = append(sqlArgs, p.Path, p.Content, p.Published, 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.Priority) // Insert post parameters for param, value := range p.Parameters { for _, value := range value { @@ -278,15 +278,15 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg wheres = append(wheres, ws) } if c.publishedYear != 0 { - wheres = append(wheres, "substr(published, 1, 4) = @publishedyear") + wheres = append(wheres, "substr(tolocal(published), 1, 4) = @publishedyear") args = append(args, sql.Named("publishedyear", fmt.Sprintf("%0004d", c.publishedYear))) } if c.publishedMonth != 0 { - wheres = append(wheres, "substr(published, 6, 2) = @publishedmonth") + wheres = append(wheres, "substr(tolocal(published), 6, 2) = @publishedmonth") args = append(args, sql.Named("publishedmonth", fmt.Sprintf("%02d", c.publishedMonth))) } if c.publishedDay != 0 { - wheres = append(wheres, "substr(published, 9, 2) = @publishedday") + wheres = append(wheres, "substr(tolocal(published), 9, 2) = @publishedday") args = append(args, sql.Named("publishedday", fmt.Sprintf("%02d", c.publishedDay))) } if len(wheres) > 0 { @@ -385,11 +385,6 @@ func (d *database) getPost(path string) (*post, error) { return posts[0], nil } -func (d *database) getDrafts(blog string) []*post { - ps, _ := d.getPosts(&postsRequestConfig{status: statusDraft, blog: blog}) - return ps -} - func (d *database) countPosts(config *postsRequestConfig) (count int, err error) { query, params := buildPostsQuery(config, "path") row, err := d.queryRow("select count(distinct path) from ("+query+")", params...) @@ -400,7 +395,7 @@ func (d *database) countPosts(config *postsRequestConfig) (count int, err error) return } -func (d *database) allPostPaths(status postStatus) ([]string, error) { +func (d *database) getPostPaths(status postStatus) ([]string, error) { var postPaths []string rows, err := d.query("select path from posts where status = @status", sql.Named("status", status)) if err != nil { @@ -456,7 +451,7 @@ type publishedDate struct { } func (d *database) allPublishedDates(blog string) (dates []publishedDate, err error) { - rows, err := d.query("select distinct substr(published, 1, 4) as year, substr(published, 6, 2) as month, substr(published, 9, 2) as day from posts where blog = @blog and status = @status and year != '' and month != '' and day != ''", sql.Named("blog", blog), sql.Named("status", statusPublished)) + rows, err := d.query("select distinct substr(tolocal(published), 1, 4) as year, substr(tolocal(published), 6, 2) as month, substr(tolocal(published), 9, 2) as day from posts where blog = @blog and status = @status and year != '' and month != '' and day != ''", sql.Named("blog", blog), sql.Named("status", statusPublished)) if err != nil { return nil, err } diff --git a/postsDb_test.go b/postsDb_test.go index 561827b..8a4037c 100644 --- a/postsDb_test.go +++ b/postsDb_test.go @@ -28,7 +28,7 @@ func Test_postsDb(t *testing.T) { }, }, } - app.initDatabase(false) + _ = app.initDatabase(false) now := toLocalSafe(time.Now().String()) nowPlus1Hour := toLocalSafe(time.Now().Add(1 * time.Hour).String()) @@ -64,18 +64,21 @@ func Test_postsDb(t *testing.T) { is.Equal([]string{"C", "A", "B"}, p.Parameters["tags"]) // Check number of post paths - pp, err := app.db.allPostPaths(statusDraft) + pp, err := app.db.getPostPaths(statusDraft) must.NoError(err) if is.Len(pp, 1) { is.Equal("/test/abc", pp[0]) } - pp, err = app.db.allPostPaths(statusPublished) + pp, err = app.db.getPostPaths(statusPublished) must.NoError(err) is.Len(pp, 0) // Check drafts - drafts := app.db.getDrafts("en") + drafts, _ := app.db.getPosts(&postsRequestConfig{ + blog: "en", + status: statusDraft, + }) is.Len(drafts, 1) // Check by parameter @@ -222,7 +225,7 @@ func Test_ftsWithoutTitle(t *testing.T) { }, }, } - app.initDatabase(false) + _ = app.initDatabase(false) err := app.db.savePost(&post{ Path: "/test/abc", @@ -252,7 +255,7 @@ func Test_postsPriority(t *testing.T) { }, }, } - app.initDatabase(false) + _ = app.initDatabase(false) err := app.db.savePost(&post{ Path: "/test/abc", @@ -301,7 +304,7 @@ func Test_usesOfMediaFile(t *testing.T) { }, }, } - app.initDatabase(false) + _ = app.initDatabase(false) err := app.db.savePost(&post{ Path: "/test/abc", diff --git a/telegram_test.go b/telegram_test.go index bece0f8..60a94ca 100644 --- a/telegram_test.go +++ b/telegram_test.go @@ -133,7 +133,7 @@ func Test_telegram(t *testing.T) { }, httpClient: fakeClient, } - app.initDatabase(false) + _ = app.initDatabase(false) app.initTelegram() @@ -177,7 +177,7 @@ func Test_telegram(t *testing.T) { }, httpClient: fakeClient, } - app.initDatabase(false) + _ = app.initDatabase(false) app.initTelegram() diff --git a/templates/editor.gohtml b/templates/editor.gohtml index cbf5865..6710e01 100644 --- a/templates/editor.gohtml +++ b/templates/editor.gohtml @@ -44,6 +44,10 @@ tags: {{ end }} +

{{ string .Blog.Lang "posts" }}

+

{{ string .Blog.Lang "drafts" }}

+

{{ string .Blog.Lang "privateposts" }}

+

{{ string .Blog.Lang "unlistedposts" }}

{{ string .Blog.Lang "upload" }}

@@ -51,18 +55,6 @@ tags:

{{ string .Blog.Lang "mediafiles" }}

- {{ if .Data.Drafts }} -

{{ string .Blog.Lang "drafts" }}

-
- - - -
- {{ end }}

{{ string .Blog.Lang "location" }}

diff --git a/templates/strings/de.yaml b/templates/strings/de.yaml index cf30714..5b6d2d2 100644 --- a/templates/strings/de.yaml +++ b/templates/strings/de.yaml @@ -29,6 +29,7 @@ oldcontent: "⚠️ Dieser Eintrag ist bereits über ein Jahr alt. Er ist mögli pinned: "Angepinnt" posts: "Posts" prev: "Zurück" +privateposts: "Private Posts" publishedon: "Veröffentlicht am" replyto: "Antwort an" search: "Suchen" @@ -40,6 +41,7 @@ stopspeak: "Vorlesen stoppen" total: "Gesamt" translate: "Übersetzen" translations: "Übersetzungen" +unlistedposts: "Ungelistete Posts" update: "Aktualisieren" updatedon: "Aktualisiert am" upload: "Hochladen" diff --git a/templates/strings/default.yaml b/templates/strings/default.yaml index df65b41..d5cb1cf 100644 --- a/templates/strings/default.yaml +++ b/templates/strings/default.yaml @@ -41,6 +41,7 @@ password: "Password" pinned: "Pinned" posts: "Posts" prev: "Previous" +privateposts: "Private posts" publishedon: "Published on" replyto: "Reply to" reverify: "Reverify" @@ -56,6 +57,7 @@ total: "Total" totp: "TOTP" translate: "Translate" translations: "Translations" +unlistedposts: "Unlisted posts" update: "Update" updatedon: "Updated on" upload: "Upload" diff --git a/webmention.go b/webmention.go index e9d920e..512447a 100644 --- a/webmention.go +++ b/webmention.go @@ -34,9 +34,7 @@ type mention struct { func (a *goBlog) initWebmention() { // Add hooks hookFunc := func(p *post) { - if p.Status == statusPublished { - _ = a.sendWebmentions(p) - } + _ = a.sendWebmentions(p) } a.pPostHooks = append(a.pPostHooks, hookFunc) a.pUpdateHooks = append(a.pUpdateHooks, hookFunc) diff --git a/webmentionSending.go b/webmentionSending.go index 2c77521..8a6a35e 100644 --- a/webmentionSending.go +++ b/webmentionSending.go @@ -15,6 +15,10 @@ import ( ) func (a *goBlog) sendWebmentions(p *post) error { + if p.Status != statusPublished && p.Status != statusUnlisted { + // Not published or unlisted + return nil + } if wm := a.cfg.Webmention; wm != nil && wm.DisableSending { // Just ignore the mentions return nil @@ -43,10 +47,7 @@ func (a *goBlog) sendWebmentions(p *post) error { // Private mode, don't send external mentions continue } - if wm := a.cfg.Webmention; wm != nil && wm.DisableSending { - // Just ignore the mention - continue - } + // Send webmention endpoint := a.discoverEndpoint(link) if endpoint == "" { continue