This commit is contained in:
Jan-Lukas Else 2021-01-15 21:56:46 +01:00
parent 55902a2de9
commit d1850300e5
19 changed files with 201 additions and 82 deletions

9
api.go
View File

@ -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)
} }

View File

@ -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)

View File

@ -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 {

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View File

@ -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 {

View File

@ -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 &microformatItem{ return &microformatItem{
@ -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

View File

@ -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),

View File

@ -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

View File

@ -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, &parameterName, &parameterValue) err = rows.Scan(&p.Path, &p.Content, &p.Published, &p.Updated, &p.Blog, &p.Section, &p.Status, &parameterName, &parameterValue)
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
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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 }}

View File

@ -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"

View File

@ -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)