mirror of https://github.com/jlelse/GoBlog
Add support for MicroPub channels and fix a few issues with post checking and the editor preview
This commit is contained in:
parent
d501855450
commit
1aa8b1b35f
10
editor.go
10
editor.go
|
@ -65,16 +65,20 @@ func (a *goBlog) createMarkdownPreview(w io.Writer, blog string, markdown io.Rea
|
||||||
Blog: blog,
|
Blog: blog,
|
||||||
Content: string(md),
|
Content: string(md),
|
||||||
}
|
}
|
||||||
if err = a.computeExtraPostParameters(p); err != nil {
|
if err = a.extractParamsFromContent(p); err != nil {
|
||||||
|
_, _ = io.WriteString(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := a.checkPost(p); err != nil {
|
||||||
_, _ = io.WriteString(w, err.Error())
|
_, _ = io.WriteString(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t := p.Title(); t != "" {
|
if t := p.Title(); t != "" {
|
||||||
p.RenderedTitle = a.renderMdTitle(t)
|
p.RenderedTitle = a.renderMdTitle(t)
|
||||||
}
|
}
|
||||||
// Render post
|
// Render post (using post's blog config)
|
||||||
hb := newHtmlBuilder(w)
|
hb := newHtmlBuilder(w)
|
||||||
a.renderEditorPreview(hb, a.cfg.Blogs[blog], p)
|
a.renderEditorPreview(hb, a.cfg.Blogs[p.Blog], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -28,7 +28,7 @@ require (
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hacdias/indieauth/v2 v2.1.0
|
github.com/hacdias/indieauth/v2 v2.1.0
|
||||||
github.com/jlaffaye/ftp v0.0.0-20220524001917-dfa1e758f3af
|
github.com/jlaffaye/ftp v0.0.0-20220612151834-60a941566ce4
|
||||||
// master
|
// master
|
||||||
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
|
@ -49,7 +49,7 @@ require (
|
||||||
github.com/spf13/cast v1.5.0
|
github.com/spf13/cast v1.5.0
|
||||||
github.com/spf13/viper v1.12.0
|
github.com/spf13/viper v1.12.0
|
||||||
github.com/stretchr/testify v1.7.2
|
github.com/stretchr/testify v1.7.2
|
||||||
github.com/tdewolff/minify/v2 v2.11.9
|
github.com/tdewolff/minify/v2 v2.11.10
|
||||||
// master
|
// master
|
||||||
github.com/tkrajina/gpxgo v1.2.2-0.20220217201249-321f19554eec
|
github.com/tkrajina/gpxgo v1.2.2-0.20220217201249-321f19554eec
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||||
|
@ -58,7 +58,7 @@ require (
|
||||||
// master
|
// master
|
||||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
||||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
@ -137,7 +137,7 @@ require (
|
||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
|
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
|
||||||
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.5.33 // indirect
|
github.com/tdewolff/parse/v2 v2.6.0 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
|
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect
|
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -296,8 +296,8 @@ github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
|
||||||
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
|
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jlaffaye/ftp v0.0.0-20220524001917-dfa1e758f3af h1:sh8vAWJ+vr9izhkDAMS3JRGDIjj0tNVwxfwd+2U2xMo=
|
github.com/jlaffaye/ftp v0.0.0-20220612151834-60a941566ce4 h1:gO/ufFrST8nt0py0FvlYHsSW81RCYeqflr8czF+UBys=
|
||||||
github.com/jlaffaye/ftp v0.0.0-20220524001917-dfa1e758f3af/go.mod h1:oZaomI+9/et52UBjvNU9LCIqmgt816+7ljXCx0EIPzo=
|
github.com/jlaffaye/ftp v0.0.0-20220612151834-60a941566ce4/go.mod h1:YFstjM4Y5zZdsON18Az8MNRgObXGJgor/UBMEQtZJes=
|
||||||
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4 h1:d2oKwfgLl3ef0PyYDkzjsVyYlBZzNpOpXitDraOnVXc=
|
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4 h1:d2oKwfgLl3ef0PyYDkzjsVyYlBZzNpOpXitDraOnVXc=
|
||||||
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4/go.mod h1:vt0iOV52/wq97Ql/jp7mUkqyrlEiGQuhHic4bVoHy0c=
|
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4/go.mod h1:vt0iOV52/wq97Ql/jp7mUkqyrlEiGQuhHic4bVoHy0c=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
@ -467,10 +467,10 @@ github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQ
|
||||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
||||||
github.com/tdewolff/minify/v2 v2.11.9 h1:1q5728c0QICKlp2X1n7OiaiiFFzCzsq7uxAkv+eykT8=
|
github.com/tdewolff/minify/v2 v2.11.10 h1:2tk9nuKfc8YOTD8glZ7JF/VtE8W5HOgmepWdjcPtRro=
|
||||||
github.com/tdewolff/minify/v2 v2.11.9/go.mod h1:XHKhaRF/vTa3EP4JX8oZ2CO4crGEtVOiSoqUED953wM=
|
github.com/tdewolff/minify/v2 v2.11.10/go.mod h1:dHOS3dk+nJ0M3q3uM3VlNzTb70cou+ov0ki7C4PAFgM=
|
||||||
github.com/tdewolff/parse/v2 v2.5.33 h1:D75KlhAeCSQg4Na8cWKehJdPJoZxwdpRbTZw7lZFWNQ=
|
github.com/tdewolff/parse/v2 v2.6.0 h1:f2D7w32JtqjCv6SczWkfwK+m15et42qEtDnZXHoNY70=
|
||||||
github.com/tdewolff/parse/v2 v2.5.33/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
github.com/tdewolff/parse/v2 v2.6.0/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||||
|
@ -613,8 +613,8 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022 h1:0qjDla5xICC2suMtyRH/QqX3B1btXTfNsIt/i4LFgO0=
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
51
micropub.go
51
micropub.go
|
@ -3,13 +3,13 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
@ -24,10 +24,23 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
var result any
|
var result any
|
||||||
switch query := r.URL.Query(); query.Get("q") {
|
switch query := r.URL.Query(); query.Get("q") {
|
||||||
case "config":
|
case "config":
|
||||||
type micropubConfig struct {
|
channels := []map[string]any{}
|
||||||
MediaEndpoint string `json:"media-endpoint"`
|
for b, bc := range a.cfg.Blogs {
|
||||||
|
channels = append(channels, map[string]any{
|
||||||
|
"name": fmt.Sprintf("%s: %s", b, bc.Title),
|
||||||
|
"uid": b,
|
||||||
|
})
|
||||||
|
for s, sc := range bc.Sections {
|
||||||
|
channels = append(channels, map[string]any{
|
||||||
|
"name": fmt.Sprintf("%s/%s: %s", b, s, sc.Name),
|
||||||
|
"uid": fmt.Sprintf("%s/%s", b, s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = map[string]any{
|
||||||
|
"channels": channels,
|
||||||
|
"media-endpoint": a.getFullAddress(micropubPath + micropubMediaSubPath),
|
||||||
}
|
}
|
||||||
result = micropubConfig{MediaEndpoint: a.getFullAddress(micropubPath + micropubMediaSubPath)}
|
|
||||||
case "source":
|
case "source":
|
||||||
if urlString := query.Get("url"); urlString != "" {
|
if urlString := query.Get("url"); urlString != "" {
|
||||||
u, err := url.Parse(query.Get("url"))
|
u, err := url.Parse(query.Get("url"))
|
||||||
|
@ -150,6 +163,10 @@ func (a *goBlog) micropubParseValuePostParamsValueMap(entry *post, values map[st
|
||||||
entry.Slug = slug[0]
|
entry.Slug = slug[0]
|
||||||
delete(values, "mp-slug")
|
delete(values, "mp-slug")
|
||||||
}
|
}
|
||||||
|
if channel, ok := values["mp-channel"]; ok && len(channel) > 0 {
|
||||||
|
entry.setChannel(channel[0])
|
||||||
|
delete(values, "mp-channel")
|
||||||
|
}
|
||||||
// Status
|
// Status
|
||||||
statusStr := ""
|
statusStr := ""
|
||||||
if status, ok := values["post-status"]; ok && len(status) > 0 {
|
if status, ok := values["post-status"]; ok && len(status) > 0 {
|
||||||
|
@ -252,6 +269,7 @@ type microformatProperties struct {
|
||||||
MpSlug []string `json:"mp-slug,omitempty"`
|
MpSlug []string `json:"mp-slug,omitempty"`
|
||||||
Photo []any `json:"photo,omitempty"`
|
Photo []any `json:"photo,omitempty"`
|
||||||
Audio []string `json:"audio,omitempty"`
|
Audio []string `json:"audio,omitempty"`
|
||||||
|
MpChannel []string `json:"mp-channel,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) error {
|
func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem) error {
|
||||||
|
@ -275,6 +293,9 @@ func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem)
|
||||||
if len(mf.Properties.MpSlug) > 0 {
|
if len(mf.Properties.MpSlug) > 0 {
|
||||||
entry.Slug = mf.Properties.MpSlug[0]
|
entry.Slug = mf.Properties.MpSlug[0]
|
||||||
}
|
}
|
||||||
|
if len(mf.Properties.MpChannel) > 0 {
|
||||||
|
entry.setChannel(mf.Properties.MpChannel[0])
|
||||||
|
}
|
||||||
// Status
|
// Status
|
||||||
status := ""
|
status := ""
|
||||||
if len(mf.Properties.PostStatus) > 0 {
|
if len(mf.Properties.PostStatus) > 0 {
|
||||||
|
@ -320,7 +341,7 @@ func (a *goBlog) micropubParsePostParamsMfItem(entry *post, mf *microformatItem)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) computeExtraPostParameters(p *post) error {
|
func (a *goBlog) extractParamsFromContent(p *post) error {
|
||||||
if p.Parameters == nil {
|
if p.Parameters == nil {
|
||||||
p.Parameters = map[string][]string{}
|
p.Parameters = map[string][]string{}
|
||||||
}
|
}
|
||||||
|
@ -352,8 +373,6 @@ func (a *goBlog) computeExtraPostParameters(p *post) error {
|
||||||
if blog := p.Parameters["blog"]; len(blog) == 1 && blog[0] != "" {
|
if blog := p.Parameters["blog"]; len(blog) == 1 && blog[0] != "" {
|
||||||
p.Blog = blog[0]
|
p.Blog = blog[0]
|
||||||
delete(p.Parameters, "blog")
|
delete(p.Parameters, "blog")
|
||||||
} else {
|
|
||||||
p.Blog = a.cfg.DefaultBlog
|
|
||||||
}
|
}
|
||||||
if path := p.Parameters["path"]; len(path) == 1 {
|
if path := p.Parameters["path"]; len(path) == 1 {
|
||||||
p.Path = path[0]
|
p.Path = path[0]
|
||||||
|
@ -383,14 +402,6 @@ func (a *goBlog) computeExtraPostParameters(p *post) error {
|
||||||
p.Priority = cast.ToInt(priority[0])
|
p.Priority = cast.ToInt(priority[0])
|
||||||
delete(p.Parameters, "priority")
|
delete(p.Parameters, "priority")
|
||||||
}
|
}
|
||||||
if p.Path == "" && p.Section == "" {
|
|
||||||
// Has no path or section -> default section
|
|
||||||
p.Section = a.cfg.Blogs[p.Blog].DefaultSection
|
|
||||||
}
|
|
||||||
if p.Published == "" && p.Section != "" {
|
|
||||||
// Has no published date, but section -> published now
|
|
||||||
p.Published = time.Now().Local().Format(time.RFC3339)
|
|
||||||
}
|
|
||||||
// Add images not in content
|
// Add images not in content
|
||||||
images := p.Parameters[a.cfg.Micropub.PhotoParam]
|
images := p.Parameters[a.cfg.Micropub.PhotoParam]
|
||||||
imageAlts := p.Parameters[a.cfg.Micropub.PhotoDescriptionParam]
|
imageAlts := p.Parameters[a.cfg.Micropub.PhotoDescriptionParam]
|
||||||
|
@ -404,12 +415,6 @@ func (a *goBlog) computeExtraPostParameters(p *post) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove all parameters where there are just empty strings
|
|
||||||
for pk, pvs := range p.Parameters {
|
|
||||||
if len(pvs) == 0 || strings.Join(pvs, "") == "" {
|
|
||||||
delete(p.Parameters, pk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +450,7 @@ func (a *goBlog) micropubCreate(w http.ResponseWriter, r *http.Request, p *post)
|
||||||
if !a.micropubCheckScope(w, r, "create") {
|
if !a.micropubCheckScope(w, r, "create") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := a.computeExtraPostParameters(p); err != nil {
|
if err := a.extractParamsFromContent(p); err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -518,7 +523,7 @@ func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u string
|
||||||
a.micropubUpdateReplace(p, mf.Replace)
|
a.micropubUpdateReplace(p, mf.Replace)
|
||||||
a.micropubUpdateAdd(p, mf.Add)
|
a.micropubUpdateAdd(p, mf.Add)
|
||||||
a.micropubUpdateDelete(p, mf.Delete)
|
a.micropubUpdateDelete(p, mf.Delete)
|
||||||
err = a.computeExtraPostParameters(p)
|
err = a.extractParamsFromContent(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -38,17 +38,17 @@ func Test_micropubQuery(t *testing.T) {
|
||||||
testCases := []testCase{
|
testCases := []testCase{
|
||||||
{
|
{
|
||||||
query: "config",
|
query: "config",
|
||||||
want: "{\"media-endpoint\":\"http://localhost:8080/micropub/media\"}",
|
want: "{\"channels\":[{\"name\":\"default: My Blog\",\"uid\":\"default\"},{\"name\":\"default/posts: posts\",\"uid\":\"default/posts\"}],\"media-endpoint\":\"http://localhost:8080/micropub/media\"}",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: "source&url=http://localhost:8080/test/post",
|
query: "source&url=http://localhost:8080/test/post",
|
||||||
want: "{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"]}}",
|
want: "{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: "source",
|
query: "source",
|
||||||
want: "{\"items\":[{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"]}}]}",
|
want: "{\"items\":[{\"type\":[\"h-entry\"],\"properties\":{\"published\":[\"\"],\"updated\":[\"\"],\"post-status\":[\"published\"],\"visibility\":[\"public\"],\"category\":[\"test\",\"test2\"],\"content\":[\"---\\nblog: default\\npath: /test/post\\npriority: 0\\npublished: \\\"\\\"\\nsection: \\\"\\\"\\nstatus: published\\ntags:\\n - test\\n - test2\\nupdated: \\\"\\\"\\n---\\nTest post\"],\"url\":[\"http://localhost:8080/test/post\"],\"mp-slug\":[\"\"],\"mp-channel\":[\"default\"]}}]}",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
72
postsDb.go
72
postsDb.go
|
@ -18,10 +18,32 @@ func (a *goBlog) checkPost(p *post) (err error) {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return errors.New("no post")
|
return errors.New("no post")
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now().Local()
|
||||||
// Fix content
|
// Maybe add blog
|
||||||
p.Content = strings.TrimSuffix(strings.TrimPrefix(p.Content, "\n"), "\n")
|
if p.Blog == "" {
|
||||||
// Fix date strings
|
p.Blog = a.cfg.DefaultBlog
|
||||||
|
}
|
||||||
|
// Check blog
|
||||||
|
if _, ok := a.cfg.Blogs[p.Blog]; !ok {
|
||||||
|
return errors.New("blog doesn't exist")
|
||||||
|
}
|
||||||
|
// Maybe add section
|
||||||
|
if p.Path == "" && p.Section == "" {
|
||||||
|
// Has no path or section -> default section
|
||||||
|
p.Section = a.cfg.Blogs[p.Blog].DefaultSection
|
||||||
|
}
|
||||||
|
// Check section
|
||||||
|
if p.Section != "" {
|
||||||
|
if _, ok := a.cfg.Blogs[p.Blog].Sections[p.Section]; !ok {
|
||||||
|
return errors.New("section doesn't exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Maybe add published date
|
||||||
|
if p.Published == "" && p.Section != "" {
|
||||||
|
// Has no published date, but section -> published now
|
||||||
|
p.Published = now.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
// Fix and check date strings
|
||||||
if p.Published != "" {
|
if p.Published != "" {
|
||||||
p.Published, err = toLocal(p.Published)
|
p.Published, err = toLocal(p.Published)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,6 +56,8 @@ func (a *goBlog) checkPost(p *post) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Fix content
|
||||||
|
p.Content = strings.TrimSuffix(strings.TrimPrefix(p.Content, "\n"), "\n")
|
||||||
// Check status
|
// Check status
|
||||||
if p.Status == "" {
|
if p.Status == "" {
|
||||||
p.Status = statusPublished
|
p.Status = statusPublished
|
||||||
|
@ -43,52 +67,32 @@ func (a *goBlog) checkPost(p *post) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if publishedTime.After(time.Now()) {
|
if publishedTime.After(now) {
|
||||||
p.Status = statusScheduled
|
p.Status = statusScheduled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cleanup params
|
// Cleanup params
|
||||||
for key, value := range p.Parameters {
|
for pk, pvs := range p.Parameters {
|
||||||
if value == nil {
|
pvs = lo.Filter(pvs, func(s string, _ int) bool { return s != "" })
|
||||||
delete(p.Parameters, key)
|
if len(pvs) == 0 {
|
||||||
|
delete(p.Parameters, pk)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
allValues := []string{}
|
p.Parameters[pk] = pvs
|
||||||
for _, v := range value {
|
|
||||||
if v != "" {
|
|
||||||
allValues = append(allValues, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(allValues) >= 1 {
|
|
||||||
p.Parameters[key] = allValues
|
|
||||||
} else {
|
|
||||||
delete(p.Parameters, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check blog
|
|
||||||
if p.Blog == "" {
|
|
||||||
p.Blog = a.cfg.DefaultBlog
|
|
||||||
}
|
|
||||||
if _, ok := a.cfg.Blogs[p.Blog]; !ok {
|
|
||||||
return errors.New("blog doesn't exist")
|
|
||||||
}
|
|
||||||
// Check if section exists
|
|
||||||
if _, ok := a.cfg.Blogs[p.Blog].Sections[p.Section]; p.Section != "" && !ok {
|
|
||||||
return errors.New("section doesn't exist")
|
|
||||||
}
|
}
|
||||||
// Check path
|
// Check path
|
||||||
if p.Path != "/" {
|
if p.Path != "/" {
|
||||||
p.Path = strings.TrimSuffix(p.Path, "/")
|
p.Path = strings.TrimSuffix(p.Path, "/")
|
||||||
}
|
}
|
||||||
if p.Path == "" {
|
if p.Path == "" {
|
||||||
if p.Section == "" {
|
published, err := dateparse.ParseLocal(p.Published)
|
||||||
p.Section = a.cfg.Blogs[p.Blog].DefaultSection
|
if err != nil {
|
||||||
|
published, err = now, nil
|
||||||
}
|
}
|
||||||
if p.Slug == "" {
|
if p.Slug == "" {
|
||||||
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), randomString(5))
|
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", published.Year(), int(published.Month()), published.Day(), randomString(5))
|
||||||
}
|
}
|
||||||
published := timeNoErr(dateparse.ParseLocal(p.Published))
|
|
||||||
pathTmplString := defaultIfEmpty(
|
pathTmplString := defaultIfEmpty(
|
||||||
a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate,
|
a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate,
|
||||||
"{{printf \""+a.getRelativePath(p.Blog, "/%v/%02d/%02d/%v")+"\" .Section .Year .Month .Slug}}",
|
"{{printf \""+a.getRelativePath(p.Blog, "/%v/%02d/%02d/%v")+"\" .Section .Year .Month .Slug}}",
|
||||||
|
|
|
@ -168,6 +168,7 @@ func (a *goBlog) postToMfItem(p *post) *microformatItem {
|
||||||
BookmarkOf: p.Parameters[a.cfg.Micropub.BookmarkParam],
|
BookmarkOf: p.Parameters[a.cfg.Micropub.BookmarkParam],
|
||||||
MpSlug: []string{p.Slug},
|
MpSlug: []string{p.Slug},
|
||||||
Audio: p.Parameters[a.cfg.Micropub.AudioParam],
|
Audio: p.Parameters[a.cfg.Micropub.AudioParam],
|
||||||
|
MpChannel: []string{p.getChannel()},
|
||||||
// TODO: Photos
|
// TODO: Photos
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -235,6 +236,24 @@ func (p *post) contentWithParams() string {
|
||||||
return fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
return fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *post) setChannel(channel string) {
|
||||||
|
if channel == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channelParts := strings.SplitN(channel, "/", 2)
|
||||||
|
p.Blog = channelParts[0]
|
||||||
|
if len(channelParts) > 1 {
|
||||||
|
p.Section = channelParts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *post) getChannel() string {
|
||||||
|
if p.Section == "" {
|
||||||
|
return p.Blog
|
||||||
|
}
|
||||||
|
return p.Blog + "/" + p.Section
|
||||||
|
}
|
||||||
|
|
||||||
// Public because of rendering
|
// Public because of rendering
|
||||||
|
|
||||||
func (p *post) Title() string {
|
func (p *post) Title() string {
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (a *goBlog) startPostsScheduler() {
|
||||||
|
|
||||||
func (a *goBlog) checkScheduledPosts() {
|
func (a *goBlog) checkScheduledPosts() {
|
||||||
postsToPublish, err := a.getPosts(&postsRequestConfig{
|
postsToPublish, err := a.getPosts(&postsRequestConfig{
|
||||||
status: "scheduled",
|
status: statusScheduled,
|
||||||
publishedBefore: time.Now(),
|
publishedBefore: time.Now(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,7 +35,7 @@ func (a *goBlog) checkScheduledPosts() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, post := range postsToPublish {
|
for _, post := range postsToPublish {
|
||||||
post.Status = "published"
|
post.Status = statusPublished
|
||||||
err := a.replacePost(post, post.Path, statusScheduled)
|
err := a.replacePost(post, post.Path, statusScheduled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error publishing scheduled post:", err)
|
log.Println("Error publishing scheduled post:", err)
|
||||||
|
|
Loading…
Reference in New Issue