mirror of https://github.com/jlelse/GoBlog
Drafts
This commit is contained in:
parent
55902a2de9
commit
d1850300e5
9
api.go
9
api.go
|
@ -6,6 +6,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Not tested anymore
|
||||||
|
|
||||||
func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
|
func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.URL.Query().Get("blog")
|
blog := r.URL.Query().Get("blog")
|
||||||
path := r.URL.Query().Get("path")
|
path := r.URL.Query().Get("path")
|
||||||
|
@ -29,7 +31,7 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
|
||||||
p.Path = path
|
p.Path = path
|
||||||
p.Section = section
|
p.Section = section
|
||||||
p.Slug = slug
|
p.Slug = slug
|
||||||
err = p.replace()
|
err = p.create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@ -49,12 +51,11 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if len(aliases) > 0 {
|
if len(aliases) > 0 {
|
||||||
p.Parameters["aliases"] = aliases
|
p.Parameters["aliases"] = aliases
|
||||||
err = p.replace()
|
err = p.replace(p.Path, p.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Header().Set("Location", p.fullURL())
|
http.Redirect(w, r, p.fullURL(), http.StatusCreated)
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
}
|
}
|
||||||
|
|
17
blogstats.go
17
blogstats.go
|
@ -1,29 +1,36 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func serveBlogStats(blog, statsPath string) func(w http.ResponseWriter, r *http.Request) {
|
func serveBlogStats(blog, statsPath string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var totalCount int
|
// Build query
|
||||||
row, err := appDbQueryRow("select count(path) as count from posts where blog = @blog", sql.Named("blog", blog))
|
query, params := buildQuery(&postsRequestConfig{
|
||||||
|
blog: blog,
|
||||||
|
status: statusPublished,
|
||||||
|
})
|
||||||
|
// Count total posts
|
||||||
|
row, err := appDbQueryRow("select count(distinct path) from ("+query+")", params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var totalCount int
|
||||||
err = row.Scan(&totalCount)
|
err = row.Scan(&totalCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var years, counts []int
|
// Count posts per year
|
||||||
rows, err := appDbQuery("select substr(published, 1, 4) as year, count(path) as count from posts where blog = @blog and coalesce(published, '') != '' group by year order by year desc", sql.Named("blog", blog))
|
rows, err := appDbQuery(`select substr(published, 1, 4) as year, count(distinct path) as count from (`+query+`)
|
||||||
|
where published != '' group by year order by year desc`, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var years, counts []int
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var year, count int
|
var year, count int
|
||||||
rows.Scan(&year, &count)
|
rows.Scan(&year, &count)
|
||||||
|
|
|
@ -106,6 +106,21 @@ func migrateDb() error {
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&migrator.Migration{
|
||||||
|
Name: "00009",
|
||||||
|
Func: func(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec(`
|
||||||
|
alter table posts add status text;
|
||||||
|
update posts set status = 'published';
|
||||||
|
drop view view_posts_with_title;
|
||||||
|
drop table posts_fts;
|
||||||
|
create view view_posts_with_title as select id, path, title, content, published, updated, blog, section, status from (select p.rowid as id, p.path as path, pp.value as title, content, published, updated, blog, section, status from posts p left outer join post_parameters pp on p.path = pp.path where pp.parameter = 'title');
|
||||||
|
create virtual table posts_fts using fts5(path unindexed, title, content, published unindexed, updated unindexed, blog unindexed, section unindexed, status unindexed, content=view_posts_with_title, content_rowid=id);
|
||||||
|
insert into posts_fts(posts_fts) values ('rebuild');
|
||||||
|
`)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
14
editor.go
14
editor.go
|
@ -11,7 +11,11 @@ import (
|
||||||
const editorPath = "/editor"
|
const editorPath = "/editor"
|
||||||
|
|
||||||
func serveEditor(w http.ResponseWriter, _ *http.Request) {
|
func serveEditor(w http.ResponseWriter, _ *http.Request) {
|
||||||
render(w, templateEditor, &renderData{})
|
render(w, templateEditor, &renderData{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"Drafts": loadDrafts(),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
func serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -33,6 +37,7 @@ func serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"UpdatePostURL": parsedURL.String(),
|
"UpdatePostURL": parsedURL.String(),
|
||||||
"UpdatePostContent": mf.Properties.Content[0],
|
"UpdatePostContent": mf.Properties.Content[0],
|
||||||
|
"Drafts": loadDrafts(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "updatepost":
|
case "updatepost":
|
||||||
|
@ -69,6 +74,11 @@ func serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
||||||
editorMicropubPost(w, r, false)
|
editorMicropubPost(w, r, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadDrafts() []*post {
|
||||||
|
ps, _ := getPosts(&postsRequestConfig{status: statusDraft})
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
func editorMicropubPost(w http.ResponseWriter, r *http.Request, media bool) {
|
func editorMicropubPost(w http.ResponseWriter, r *http.Request, media bool) {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
if media {
|
if media {
|
||||||
|
@ -78,7 +88,7 @@ func editorMicropubPost(w http.ResponseWriter, r *http.Request, media bool) {
|
||||||
}
|
}
|
||||||
result := recorder.Result()
|
result := recorder.Result()
|
||||||
if location := result.Header.Get("Location"); location != "" {
|
if location := result.Header.Get("Location"); location != "" {
|
||||||
http.Redirect(w, r, result.Header.Get("Location"), http.StatusFound)
|
http.Redirect(w, r, location, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if result.StatusCode >= 200 && result.StatusCode <= 400 {
|
if result.StatusCode >= 200 && result.StatusCode <= 400 {
|
||||||
|
|
11
go.mod
11
go.mod
|
@ -4,7 +4,7 @@ go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/jlelse/tinify v0.0.0-20200123222407-7fc9c21822b0
|
codeberg.org/jlelse/tinify v0.0.0-20200123222407-7fc9c21822b0
|
||||||
github.com/PuerkitoBio/goquery v1.6.0
|
github.com/PuerkitoBio/goquery v1.6.1
|
||||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||||
github.com/araddon/dateparse v0.0.0-20201001162425-8aadafed4dc4
|
github.com/araddon/dateparse v0.0.0-20201001162425-8aadafed4dc4
|
||||||
github.com/caddyserver/certmagic v0.12.0
|
github.com/caddyserver/certmagic v0.12.0
|
||||||
|
@ -35,7 +35,7 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.6
|
github.com/mattn/go-sqlite3 v1.14.6
|
||||||
github.com/mholt/acmez v0.1.2 // indirect
|
github.com/mholt/acmez v0.1.2 // indirect
|
||||||
github.com/miekg/dns v1.1.35 // indirect
|
github.com/miekg/dns v1.1.35 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||||
github.com/snabb/sitemap v1.0.0
|
github.com/snabb/sitemap v1.0.0
|
||||||
|
@ -43,6 +43,7 @@ require (
|
||||||
github.com/spf13/cast v1.3.1
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
|
github.com/stretchr/testify v1.7.0 // indirect
|
||||||
github.com/tdewolff/minify/v2 v2.9.10
|
github.com/tdewolff/minify/v2 v2.9.10
|
||||||
github.com/tdewolff/parse/v2 v2.5.7 // indirect
|
github.com/tdewolff/parse/v2 v2.5.7 // indirect
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||||
|
@ -55,13 +56,13 @@ require (
|
||||||
go.uber.org/zap v1.16.0 // indirect
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
||||||
golang.org/x/mod v0.4.0 // indirect
|
golang.org/x/mod v0.4.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 // indirect
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||||
golang.org/x/text v0.3.5 // indirect
|
golang.org/x/text v0.3.5 // indirect
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e // indirect
|
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -18,8 +18,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94=
|
github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk=
|
||||||
github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
@ -232,8 +232,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
@ -309,6 +309,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tdewolff/minify/v2 v2.9.10 h1:p+ifTTl+JMFFLDYNAm7nxQ9XuCG10HTW00wlPAZ7aoE=
|
github.com/tdewolff/minify/v2 v2.9.10 h1:p+ifTTl+JMFFLDYNAm7nxQ9XuCG10HTW00wlPAZ7aoE=
|
||||||
|
@ -393,8 +395,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -454,8 +456,8 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 h1:DQmQoKxQWtyybCtX/3dIuDBcAhFszqq8YiNeS6sNu1c=
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
||||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
@ -495,8 +497,8 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnf
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I=
|
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 h1:K+NlvTLy0oONtRtkl1jRD9xIhnItbG2PiE7YOdjPb+k=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
|
2
hooks.go
2
hooks.go
|
@ -28,6 +28,7 @@ const (
|
||||||
var postHooks = map[postHookType][]func(*post){}
|
var postHooks = map[postHookType][]func(*post){}
|
||||||
|
|
||||||
func (p *post) postPostHooks() {
|
func (p *post) postPostHooks() {
|
||||||
|
// Hooks after post published
|
||||||
for _, cmdTmplString := range appConfig.Hooks.PostPost {
|
for _, cmdTmplString := range appConfig.Hooks.PostPost {
|
||||||
go func(p *post, cmdTmplString string) {
|
go func(p *post, cmdTmplString string) {
|
||||||
executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{
|
executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{
|
||||||
|
@ -42,6 +43,7 @@ func (p *post) postPostHooks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) postUpdateHooks() {
|
func (p *post) postUpdateHooks() {
|
||||||
|
// Hooks after post updated
|
||||||
for _, cmdTmplString := range appConfig.Hooks.PostUpdate {
|
for _, cmdTmplString := range appConfig.Hooks.PostUpdate {
|
||||||
go func(p *post, cmdTmplString string) {
|
go func(p *post, cmdTmplString string) {
|
||||||
executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{
|
executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{
|
||||||
|
|
19
http.go
19
http.go
|
@ -108,7 +108,7 @@ func buildHandler() (http.Handler, error) {
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
r.Route("/editor", func(mpRouter chi.Router) {
|
r.Route("/editor", func(mpRouter chi.Router) {
|
||||||
mpRouter.Use(authMiddleware, middleware.NoCache, minifier.Middleware)
|
mpRouter.Use(middleware.NoCache, minifier.Middleware, authMiddleware)
|
||||||
mpRouter.Get("/", serveEditor)
|
mpRouter.Get("/", serveEditor)
|
||||||
mpRouter.Post("/", serveEditorPost)
|
mpRouter.Post("/", serveEditorPost)
|
||||||
})
|
})
|
||||||
|
@ -137,13 +137,13 @@ func buildHandler() (http.Handler, error) {
|
||||||
r.Route("/webmention", func(webmentionRouter chi.Router) {
|
r.Route("/webmention", func(webmentionRouter chi.Router) {
|
||||||
webmentionRouter.Use(middleware.NoCache)
|
webmentionRouter.Use(middleware.NoCache)
|
||||||
webmentionRouter.Post("/", handleWebmention)
|
webmentionRouter.Post("/", handleWebmention)
|
||||||
webmentionRouter.With(authMiddleware, minifier.Middleware).Get("/", webmentionAdmin)
|
webmentionRouter.With(minifier.Middleware, authMiddleware).Get("/", webmentionAdmin)
|
||||||
webmentionRouter.With(authMiddleware).Post("/delete", webmentionAdminDelete)
|
webmentionRouter.With(authMiddleware).Post("/delete", webmentionAdminDelete)
|
||||||
webmentionRouter.With(authMiddleware).Post("/approve", webmentionAdminApprove)
|
webmentionRouter.With(authMiddleware).Post("/approve", webmentionAdminApprove)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Posts
|
// Posts
|
||||||
allPostPaths, err := allPostPaths()
|
pp, err := allPostPaths(statusPublished)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -153,12 +153,23 @@ func buildHandler() (http.Handler, error) {
|
||||||
} else {
|
} else {
|
||||||
postMW = []func(http.Handler) http.Handler{cacheMiddleware, minifier.Middleware}
|
postMW = []func(http.Handler) http.Handler{cacheMiddleware, minifier.Middleware}
|
||||||
}
|
}
|
||||||
for _, path := range allPostPaths {
|
for _, path := range pp {
|
||||||
if path != "" {
|
if path != "" {
|
||||||
r.With(postMW...).Get(path, servePost)
|
r.With(postMW...).Get(path, servePost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drafts
|
||||||
|
dp, err := allPostPaths(statusDraft)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, path := range dp {
|
||||||
|
if path != "" {
|
||||||
|
r.With(cacheMiddleware, minifier.Middleware, authMiddleware).Get(path, servePost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Post aliases
|
// Post aliases
|
||||||
allPostAliases, err := allPostAliases()
|
allPostAliases, err := allPostAliases()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
27
micropub.go
27
micropub.go
|
@ -90,6 +90,7 @@ func (p *post) toMfItem() *microformatItem {
|
||||||
params["blog"] = []string{p.Blog}
|
params["blog"] = []string{p.Blog}
|
||||||
params["published"] = []string{p.Published}
|
params["published"] = []string{p.Published}
|
||||||
params["updated"] = []string{p.Updated}
|
params["updated"] = []string{p.Updated}
|
||||||
|
params["status"] = []string{string(p.Status)}
|
||||||
pb, _ := yaml.Marshal(p.Parameters)
|
pb, _ := yaml.Marshal(p.Parameters)
|
||||||
content := fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
content := fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
||||||
return µformatItem{
|
return µformatItem{
|
||||||
|
@ -98,6 +99,7 @@ func (p *post) toMfItem() *microformatItem {
|
||||||
Name: p.Parameters["title"],
|
Name: p.Parameters["title"],
|
||||||
Published: []string{p.Published},
|
Published: []string{p.Published},
|
||||||
Updated: []string{p.Updated},
|
Updated: []string{p.Updated},
|
||||||
|
PostStatus: []string{string(p.Status)},
|
||||||
Category: p.Parameters[appConfig.Micropub.CategoryParam],
|
Category: p.Parameters[appConfig.Micropub.CategoryParam],
|
||||||
Content: []string{content},
|
Content: []string{content},
|
||||||
URL: []string{p.fullURL()},
|
URL: []string{p.fullURL()},
|
||||||
|
@ -205,6 +207,9 @@ func convertMPValueMapToPost(values map[string][]string) (*post, error) {
|
||||||
if updated, ok := values["updated"]; ok {
|
if updated, ok := values["updated"]; ok {
|
||||||
entry.Updated = updated[0]
|
entry.Updated = updated[0]
|
||||||
}
|
}
|
||||||
|
if status, ok := values["post-status"]; ok {
|
||||||
|
entry.Status = postStatus(status[0])
|
||||||
|
}
|
||||||
// Parameter
|
// Parameter
|
||||||
if name, ok := values["name"]; ok {
|
if name, ok := values["name"]; ok {
|
||||||
entry.Parameters["title"] = name
|
entry.Parameters["title"] = name
|
||||||
|
@ -269,6 +274,7 @@ type microformatProperties struct {
|
||||||
Name []string `json:"name,omitempty"`
|
Name []string `json:"name,omitempty"`
|
||||||
Published []string `json:"published,omitempty"`
|
Published []string `json:"published,omitempty"`
|
||||||
Updated []string `json:"updated,omitempty"`
|
Updated []string `json:"updated,omitempty"`
|
||||||
|
PostStatus []string `json:"post-status,omitempty"`
|
||||||
Category []string `json:"category,omitempty"`
|
Category []string `json:"category,omitempty"`
|
||||||
Content []string `json:"content,omitempty"`
|
Content []string `json:"content,omitempty"`
|
||||||
URL []string `json:"url,omitempty"`
|
URL []string `json:"url,omitempty"`
|
||||||
|
@ -297,6 +303,9 @@ func convertMPMfToPost(mf *microformatItem) (*post, error) {
|
||||||
if len(mf.Properties.Updated) == 1 {
|
if len(mf.Properties.Updated) == 1 {
|
||||||
entry.Updated = mf.Properties.Updated[0]
|
entry.Updated = mf.Properties.Updated[0]
|
||||||
}
|
}
|
||||||
|
if len(mf.Properties.PostStatus) == 1 {
|
||||||
|
entry.Status = postStatus(mf.Properties.PostStatus[0])
|
||||||
|
}
|
||||||
// Parameter
|
// Parameter
|
||||||
if len(mf.Properties.Name) == 1 {
|
if len(mf.Properties.Name) == 1 {
|
||||||
entry.Parameters["title"] = mf.Properties.Name
|
entry.Parameters["title"] = mf.Properties.Name
|
||||||
|
@ -370,26 +379,30 @@ func (p *post) computeExtraPostParameters() error {
|
||||||
} else {
|
} else {
|
||||||
p.Blog = appConfig.DefaultBlog
|
p.Blog = appConfig.DefaultBlog
|
||||||
}
|
}
|
||||||
if path := p.Parameters["path"]; len(path) == 1 && path[0] != "" {
|
if path := p.Parameters["path"]; len(path) == 1 {
|
||||||
p.Path = path[0]
|
p.Path = path[0]
|
||||||
delete(p.Parameters, "path")
|
delete(p.Parameters, "path")
|
||||||
}
|
}
|
||||||
if section := p.Parameters["section"]; len(section) == 1 && section[0] != "" {
|
if section := p.Parameters["section"]; len(section) == 1 {
|
||||||
p.Section = section[0]
|
p.Section = section[0]
|
||||||
delete(p.Parameters, "section")
|
delete(p.Parameters, "section")
|
||||||
}
|
}
|
||||||
if slug := p.Parameters["slug"]; len(slug) == 1 && slug[0] != "" {
|
if slug := p.Parameters["slug"]; len(slug) == 1 {
|
||||||
p.Slug = slug[0]
|
p.Slug = slug[0]
|
||||||
delete(p.Parameters, "slug")
|
delete(p.Parameters, "slug")
|
||||||
}
|
}
|
||||||
if published := p.Parameters["published"]; len(published) == 1 && published[0] != "" {
|
if published := p.Parameters["published"]; len(published) == 1 {
|
||||||
p.Published = published[0]
|
p.Published = published[0]
|
||||||
delete(p.Parameters, "published")
|
delete(p.Parameters, "published")
|
||||||
}
|
}
|
||||||
if updated := p.Parameters["updated"]; len(updated) == 1 && updated[0] != "" {
|
if updated := p.Parameters["updated"]; len(updated) == 1 {
|
||||||
p.Updated = updated[0]
|
p.Updated = updated[0]
|
||||||
delete(p.Parameters, "updated")
|
delete(p.Parameters, "updated")
|
||||||
}
|
}
|
||||||
|
if status := p.Parameters["status"]; len(status) == 1 {
|
||||||
|
p.Status = postStatus(status[0])
|
||||||
|
delete(p.Parameters, "status")
|
||||||
|
}
|
||||||
if p.Path == "" && p.Section == "" {
|
if p.Path == "" && p.Section == "" {
|
||||||
// Has no path or section -> default section
|
// Has no path or section -> default section
|
||||||
p.Section = appConfig.Blogs[p.Blog].DefaultSection
|
p.Section = appConfig.Blogs[p.Blog].DefaultSection
|
||||||
|
@ -437,6 +450,8 @@ func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *micr
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
oldPath := p.Path
|
||||||
|
oldStatus := p.Status
|
||||||
if mf.Replace != nil {
|
if mf.Replace != nil {
|
||||||
for key, value := range mf.Replace {
|
for key, value := range mf.Replace {
|
||||||
switch key {
|
switch key {
|
||||||
|
@ -551,7 +566,7 @@ func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *micr
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = p.replace()
|
err = p.replace(oldPath, oldStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -44,7 +44,9 @@ func (r *nodeInfoResolver) IsOpenRegistration() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *nodeInfoResolver) Usage() (nodeinfo.Usage, error) {
|
func (r *nodeInfoResolver) Usage() (nodeinfo.Usage, error) {
|
||||||
postCount, _ := countPosts(&postsRequestConfig{})
|
postCount, _ := countPosts(&postsRequestConfig{
|
||||||
|
status: statusPublished,
|
||||||
|
})
|
||||||
u := nodeinfo.Usage{
|
u := nodeinfo.Usage{
|
||||||
Users: nodeinfo.UsageUsers{
|
Users: nodeinfo.UsageUsers{
|
||||||
Total: len(appConfig.Blogs),
|
Total: len(appConfig.Blogs),
|
||||||
|
|
10
posts.go
10
posts.go
|
@ -25,12 +25,21 @@ type post struct {
|
||||||
Parameters map[string][]string `json:"parameters"`
|
Parameters map[string][]string `json:"parameters"`
|
||||||
Blog string `json:"blog"`
|
Blog string `json:"blog"`
|
||||||
Section string `json:"section"`
|
Section string `json:"section"`
|
||||||
|
Status postStatus `json:"status"`
|
||||||
// Not persisted
|
// Not persisted
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
rendered template.HTML
|
rendered template.HTML
|
||||||
absoluteRendered template.HTML
|
absoluteRendered template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type postStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusNil postStatus = ""
|
||||||
|
statusPublished postStatus = "published"
|
||||||
|
statusDraft postStatus = "draft"
|
||||||
|
)
|
||||||
|
|
||||||
func servePost(w http.ResponseWriter, r *http.Request) {
|
func servePost(w http.ResponseWriter, r *http.Request) {
|
||||||
as := strings.HasSuffix(r.URL.Path, ".as")
|
as := strings.HasSuffix(r.URL.Path, ".as")
|
||||||
if as {
|
if as {
|
||||||
|
@ -213,6 +222,7 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
|
||||||
publishedYear: ic.year,
|
publishedYear: ic.year,
|
||||||
publishedMonth: ic.month,
|
publishedMonth: ic.month,
|
||||||
publishedDay: ic.day,
|
publishedDay: ic.day,
|
||||||
|
status: statusPublished,
|
||||||
}}, appConfig.Blogs[ic.blog].Pagination)
|
}}, appConfig.Blogs[ic.blog].Pagination)
|
||||||
p.SetPage(pageNo)
|
p.SetPage(pageNo)
|
||||||
var posts []*post
|
var posts []*post
|
||||||
|
|
100
postsDb.go
100
postsDb.go
|
@ -32,6 +32,10 @@ func (p *post) checkPost() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check status
|
||||||
|
if p.Status == "" {
|
||||||
|
p.Status = statusPublished
|
||||||
|
}
|
||||||
// Cleanup params
|
// Cleanup params
|
||||||
for key, value := range p.Parameters {
|
for key, value := range p.Parameters {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
|
@ -103,45 +107,75 @@ func (p *post) checkPost() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) create() error {
|
func (p *post) create() error {
|
||||||
return p.createOrReplace(true)
|
return p.createOrReplace(&postCreationOptions{new: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) replace() error {
|
func (p *post) replace(oldPath string, oldStatus postStatus) error {
|
||||||
return p.createOrReplace(false)
|
return p.createOrReplace(&postCreationOptions{new: false, oldPath: oldPath, oldStatus: oldStatus})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) createOrReplace(new bool) error {
|
type postCreationOptions struct {
|
||||||
|
new bool
|
||||||
|
oldPath string
|
||||||
|
oldStatus postStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *post) createOrReplace(o *postCreationOptions) error {
|
||||||
err := p.checkPost()
|
err := p.checkPost()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
startWritingToDb()
|
startWritingToDb()
|
||||||
postExists := postExists(p.Path)
|
// Create transaction
|
||||||
if postExists && new {
|
|
||||||
finishWritingToDb()
|
|
||||||
return errors.New("post already exists at given path")
|
|
||||||
}
|
|
||||||
tx, err := appDb.Begin()
|
tx, err := appDb.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
finishWritingToDb()
|
finishWritingToDb()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if postExists {
|
if !o.new {
|
||||||
_, err := tx.Exec("delete from posts where path = @path", sql.Named("path", p.Path))
|
// Remove old post
|
||||||
|
path := p.Path
|
||||||
|
if o.oldPath != "" {
|
||||||
|
path = o.oldPath
|
||||||
|
}
|
||||||
|
_, err := tx.Exec("delete from posts where path = @path", sql.Named("path", path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
finishWritingToDb()
|
finishWritingToDb()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check if new path exists
|
||||||
|
postExists := func(path string) (bool, error) {
|
||||||
|
result := 0
|
||||||
|
row := tx.QueryRow("select exists(select 1 from posts where path = @path)", sql.Named("path", path))
|
||||||
|
if err = row.Scan(&result); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return result == 1, nil
|
||||||
|
}
|
||||||
|
if exists, err := postExists(p.Path); err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
finishWritingToDb()
|
||||||
|
return err
|
||||||
|
} else if exists {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
finishWritingToDb()
|
||||||
|
return errors.New("post already exists at given path")
|
||||||
|
}
|
||||||
|
// Create new post
|
||||||
_, err = tx.Exec(
|
_, err = tx.Exec(
|
||||||
"insert into posts (path, content, published, updated, blog, section) values (@path, @content, @published, @updated, @blog, @section)",
|
`insert into posts (path, content, published, updated, blog, section, status)
|
||||||
sql.Named("path", p.Path), sql.Named("content", p.Content), sql.Named("published", p.Published), sql.Named("updated", p.Updated), sql.Named("blog", p.Blog), sql.Named("section", p.Section))
|
values (@path, @content, @published, @updated, @blog, @section, @status)`,
|
||||||
|
sql.Named("path", p.Path), sql.Named("content", p.Content), sql.Named("published", p.Published),
|
||||||
|
sql.Named("updated", p.Updated), sql.Named("blog", p.Blog), sql.Named("section", p.Section),
|
||||||
|
sql.Named("status", p.Status))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
finishWritingToDb()
|
finishWritingToDb()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Create parameters
|
||||||
ppStmt, err := tx.Prepare("insert into post_parameters (path, parameter, value) values (@path, @parameter, @value)")
|
ppStmt, err := tx.Prepare("insert into post_parameters (path, parameter, value) values (@path, @parameter, @value)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
|
@ -160,17 +194,18 @@ func (p *post) createOrReplace(new bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = tx.Commit()
|
if tx.Commit() != nil {
|
||||||
if err != nil {
|
|
||||||
finishWritingToDb()
|
finishWritingToDb()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
finishWritingToDb()
|
finishWritingToDb()
|
||||||
rebuildFTSIndex()
|
rebuildFTSIndex()
|
||||||
if !postExists {
|
if p.Status == statusPublished {
|
||||||
defer p.postPostHooks()
|
if o.new || o.oldStatus == statusDraft {
|
||||||
} else {
|
defer p.postPostHooks()
|
||||||
defer p.postUpdateHooks()
|
} else {
|
||||||
|
defer p.postUpdateHooks()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return reloadRouter()
|
return reloadRouter()
|
||||||
}
|
}
|
||||||
|
@ -197,18 +232,6 @@ func rebuildFTSIndex() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func postExists(path string) bool {
|
|
||||||
result := 0
|
|
||||||
row, err := appDbQueryRow("select exists(select 1 from posts where path = @path)", sql.Named("path", path))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err = row.Scan(&result); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return result == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPost(path string) (*post, error) {
|
func getPost(path string) (*post, error) {
|
||||||
posts, err := getPosts(&postsRequestConfig{path: path})
|
posts, err := getPosts(&postsRequestConfig{path: path})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -226,6 +249,7 @@ type postsRequestConfig struct {
|
||||||
limit int
|
limit int
|
||||||
offset int
|
offset int
|
||||||
sections []string
|
sections []string
|
||||||
|
status postStatus
|
||||||
taxonomy *taxonomy
|
taxonomy *taxonomy
|
||||||
taxonomyValue string
|
taxonomyValue string
|
||||||
parameter string
|
parameter string
|
||||||
|
@ -235,12 +259,16 @@ type postsRequestConfig struct {
|
||||||
|
|
||||||
func buildQuery(config *postsRequestConfig) (query string, args []interface{}) {
|
func buildQuery(config *postsRequestConfig) (query string, args []interface{}) {
|
||||||
args = []interface{}{}
|
args = []interface{}{}
|
||||||
defaultSelection := "select p.path as path, coalesce(content, ''), coalesce(published, ''), coalesce(updated, ''), coalesce(blog, ''), coalesce(section, ''), coalesce(parameter, ''), coalesce(value, '') "
|
defaultSelection := "select p.path as path, coalesce(content, '') as content, coalesce(published, '') as published, coalesce(updated, '') as updated, coalesce(blog, '') as blog, coalesce(section, '') as section, coalesce(status, '') as status, coalesce(parameter, '') as parameter, coalesce(value, '') as value "
|
||||||
postsTable := "posts"
|
postsTable := "posts"
|
||||||
if config.search != "" {
|
if config.search != "" {
|
||||||
postsTable = "posts_fts(@search)"
|
postsTable = "posts_fts(@search)"
|
||||||
args = append(args, sql.Named("search", config.search))
|
args = append(args, sql.Named("search", config.search))
|
||||||
}
|
}
|
||||||
|
if config.status != "" && config.status != statusNil {
|
||||||
|
postsTable = "(select * from " + postsTable + " where status = @status)"
|
||||||
|
args = append(args, sql.Named("status", config.status))
|
||||||
|
}
|
||||||
if config.blog != "" {
|
if config.blog != "" {
|
||||||
postsTable = "(select * from " + postsTable + " where blog = @blog)"
|
postsTable = "(select * from " + postsTable + " where blog = @blog)"
|
||||||
args = append(args, sql.Named("blog", config.blog))
|
args = append(args, sql.Named("blog", config.blog))
|
||||||
|
@ -310,7 +338,7 @@ func getPosts(config *postsRequestConfig) (posts []*post, err error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
p := &post{}
|
p := &post{}
|
||||||
var parameterName, parameterValue string
|
var parameterName, parameterValue string
|
||||||
err = rows.Scan(&p.Path, &p.Content, &p.Published, &p.Updated, &p.Blog, &p.Section, ¶meterName, ¶meterValue)
|
err = rows.Scan(&p.Path, &p.Content, &p.Published, &p.Updated, &p.Blog, &p.Section, &p.Status, ¶meterName, ¶meterValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -342,9 +370,9 @@ func countPosts(config *postsRequestConfig) (count int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func allPostPaths() ([]string, error) {
|
func allPostPaths(status postStatus) ([]string, error) {
|
||||||
var postPaths []string
|
var postPaths []string
|
||||||
rows, err := appDbQuery("select path from posts")
|
rows, err := appDbQuery("select path from posts where status = @status", sql.Named("status", status))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -375,7 +403,7 @@ type publishedDate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func allPublishedDates(blog string) (dates []publishedDate, err error) {
|
func allPublishedDates(blog string) (dates []publishedDate, err error) {
|
||||||
rows, err := appDbQuery("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 year != '' and month != '' and day != ''", sql.Named("blog", blog))
|
rows, err := appDbQuery("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))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,5 +103,5 @@ func (p *post) translations() []*post {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) isPublishedSectionPost() bool {
|
func (p *post) isPublishedSectionPost() bool {
|
||||||
return p.Published != "" && p.Section != ""
|
return p.Published != "" && p.Section != "" && p.Status == statusPublished
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ import (
|
||||||
const sitemapPath = "/sitemap.xml"
|
const sitemapPath = "/sitemap.xml"
|
||||||
|
|
||||||
func serveSitemap(w http.ResponseWriter, r *http.Request) {
|
func serveSitemap(w http.ResponseWriter, r *http.Request) {
|
||||||
posts, err := getPosts(&postsRequestConfig{})
|
posts, err := getPosts(&postsRequestConfig{
|
||||||
|
status: statusPublished,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.sans-serif, input, textarea, button, .button, body {
|
.sans-serif, input, textarea, select, button, .button, body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, textarea, button, .button {
|
input, textarea, select, button, .button {
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
border: 1px solid var(--primary, #000);
|
border: 1px solid var(--primary, #000);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
@ -66,7 +66,7 @@ input, textarea, button, .button {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input, form textarea {
|
form input, form textarea, form select {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, textarea, button, .button {
|
input, textarea, select, button, .button {
|
||||||
@include color-border(border, 1px, solid, primary);
|
@include color-border(border, 1px, solid, primary);
|
||||||
@include color(background, background);
|
@include color(background, background);
|
||||||
@include color(color, primary);
|
@include color(color, primary);
|
||||||
|
@ -89,7 +89,7 @@ input, textarea, button, .button {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input, form textarea {
|
form input, form textarea, form select {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,16 @@ blog:
|
||||||
<input class="fw" type="file" name="file">
|
<input class="fw" type="file" name="file">
|
||||||
<input class="fw" type="submit" value="{{ string .Blog.Lang "upload" }}">
|
<input class="fw" type="submit" value="{{ string .Blog.Lang "upload" }}">
|
||||||
</form>
|
</form>
|
||||||
|
<h2>{{ string .Blog.Lang "drafts" }}</h2>
|
||||||
|
<form class="fw-form p" method="post">
|
||||||
|
<input type="hidden" name="editoraction" value="loadupdate">
|
||||||
|
<select name="url" class="fw">
|
||||||
|
{{ range $i, $draft := .Data.Drafts }}
|
||||||
|
<option value="{{ absolute $draft.Path }}">{{ (title $draft) | default $draft.Path }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
<input class="fw" type="submit" value="{{ string .Blog.Lang "update" }}">
|
||||||
|
</form>
|
||||||
</main>
|
</main>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
|
@ -33,4 +33,5 @@ password: "Password"
|
||||||
shorturl: "Short URL:"
|
shorturl: "Short URL:"
|
||||||
year: "Year"
|
year: "Year"
|
||||||
count: "Count"
|
count: "Count"
|
||||||
total: "Total"
|
total: "Total"
|
||||||
|
drafts: "Drafts"
|
|
@ -33,7 +33,9 @@ type mention struct {
|
||||||
func initWebmention() error {
|
func initWebmention() error {
|
||||||
// Add hooks
|
// Add hooks
|
||||||
hookFunc := func(p *post) {
|
hookFunc := func(p *post) {
|
||||||
p.sendWebmentions()
|
if p.Status == statusPublished {
|
||||||
|
p.sendWebmentions()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
postHooks[postPostHook] = append(postHooks[postPostHook], hookFunc)
|
postHooks[postPostHook] = append(postHooks[postPostHook], hookFunc)
|
||||||
postHooks[postUpdateHook] = append(postHooks[postUpdateHook], hookFunc)
|
postHooks[postUpdateHook] = append(postHooks[postUpdateHook], hookFunc)
|
||||||
|
|
Loading…
Reference in New Issue