GoBlog/micropub.go

603 lines
19 KiB
Go
Raw Normal View History

2020-10-06 17:07:48 +00:00
package main
import (
2021-01-21 16:59:47 +00:00
"encoding/json"
2020-10-06 17:07:48 +00:00
"errors"
"fmt"
2020-10-06 17:07:48 +00:00
"net/http"
"net/url"
"reflect"
"regexp"
2020-11-12 10:48:37 +00:00
"strconv"
2020-10-06 17:07:48 +00:00
"strings"
"time"
2020-10-06 17:07:48 +00:00
"github.com/spf13/cast"
"gopkg.in/yaml.v3"
)
const micropubPath = "/micropub"
2020-10-06 17:07:48 +00:00
type micropubConfig struct {
MediaEndpoint string `json:"media-endpoint,omitempty"`
}
func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
switch r.URL.Query().Get("q") {
case "config":
w.Header().Set(contentType, contentTypeJSONUTF8)
2020-10-06 17:07:48 +00:00
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(&micropubConfig{
MediaEndpoint: a.cfg.Server.PublicAddress + micropubPath + micropubMediaSubPath,
2021-01-10 14:59:43 +00:00
})
_, _ = writeMinified(w, contentTypeJSON, b)
case "source":
var mf interface{}
if urlString := r.URL.Query().Get("url"); urlString != "" {
u, err := url.Parse(r.URL.Query().Get("url"))
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
p, err := a.db.getPost(u.Path)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
mf = a.toMfItem(p)
} else {
2020-11-12 10:48:37 +00:00
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
posts, err := a.db.getPosts(&postsRequestConfig{
2020-11-12 10:48:37 +00:00
limit: limit,
offset: offset,
})
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
list := map[string][]*microformatItem{}
2020-10-15 15:32:46 +00:00
for _, p := range posts {
list["items"] = append(list["items"], a.toMfItem(p))
}
mf = list
}
w.Header().Set(contentType, contentTypeJSONUTF8)
2020-10-06 17:07:48 +00:00
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(mf)
_, _ = writeMinified(w, contentTypeJSON, b)
2020-11-12 11:44:54 +00:00
case "category":
allCategories := []string{}
for blog := range a.cfg.Blogs {
values, err := a.db.allTaxonomyValues(blog, a.cfg.Micropub.CategoryParam)
2020-11-12 11:44:54 +00:00
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
2020-11-12 11:44:54 +00:00
return
}
allCategories = append(allCategories, values...)
}
w.Header().Set(contentType, contentTypeJSONUTF8)
2020-11-12 11:44:54 +00:00
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(map[string]interface{}{
2020-11-12 11:44:54 +00:00
"categories": allCategories,
})
_, _ = writeMinified(w, contentTypeJSON, b)
default:
a.serve404(w, r)
}
}
func (a *goBlog) toMfItem(p *post) *microformatItem {
2020-10-15 15:32:46 +00:00
params := p.Parameters
params["path"] = []string{p.Path}
params["section"] = []string{p.Section}
params["blog"] = []string{p.Blog}
params["published"] = []string{p.Published}
params["updated"] = []string{p.Updated}
2021-01-15 20:56:46 +00:00
params["status"] = []string{string(p.Status)}
2020-10-15 15:32:46 +00:00
pb, _ := yaml.Marshal(p.Parameters)
content := fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
return &microformatItem{
Type: []string{"h-entry"},
Properties: &microformatProperties{
2020-11-12 10:48:37 +00:00
Name: p.Parameters["title"],
Published: []string{p.Published},
Updated: []string{p.Updated},
2021-01-15 20:56:46 +00:00
PostStatus: []string{string(p.Status)},
Category: p.Parameters[a.cfg.Micropub.CategoryParam],
2020-11-12 10:48:37 +00:00
Content: []string{content},
URL: []string{a.fullPostURL(p)},
InReplyTo: p.Parameters[a.cfg.Micropub.ReplyParam],
LikeOf: p.Parameters[a.cfg.Micropub.LikeParam],
BookmarkOf: p.Parameters[a.cfg.Micropub.BookmarkParam],
2020-11-12 10:48:37 +00:00
MpSlug: []string{p.Slug},
Audio: p.Parameters[a.cfg.Micropub.AudioParam],
2020-11-12 10:48:37 +00:00
// TODO: Photos
},
2020-10-06 17:07:48 +00:00
}
}
func (a *goBlog) serveMicropubPost(w http.ResponseWriter, r *http.Request) {
2020-10-06 17:07:48 +00:00
defer r.Body.Close()
2020-10-15 15:32:46 +00:00
var p *post
if ct := r.Header.Get(contentType); strings.Contains(ct, contentTypeWWWForm) || strings.Contains(ct, contentTypeMultipartForm) {
var err error
if strings.Contains(ct, contentTypeMultipartForm) {
2020-11-22 19:30:02 +00:00
err = r.ParseMultipartForm(0)
} else {
err = r.ParseForm()
}
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
2020-11-22 19:30:02 +00:00
return
2020-10-06 17:07:48 +00:00
}
if action := micropubAction(r.Form.Get("action")); action != "" {
u, err := url.Parse(r.Form.Get("url"))
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
2020-11-22 19:30:02 +00:00
return
}
if action == actionDelete {
a.micropubDelete(w, r, u)
return
}
a.serveError(w, r, "Action not supported", http.StatusNotImplemented)
2020-10-06 17:07:48 +00:00
return
}
p, err = a.convertMPValueMapToPost(r.Form)
2020-10-06 17:07:48 +00:00
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
2020-10-06 17:07:48 +00:00
return
}
} else if strings.Contains(ct, contentTypeJSON) {
parsedMfItem := &microformatItem{}
err := json.NewDecoder(r.Body).Decode(parsedMfItem)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
2020-10-06 17:07:48 +00:00
return
}
if parsedMfItem.Action != "" {
u, err := url.Parse(parsedMfItem.URL)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
2020-11-22 19:30:02 +00:00
return
}
if parsedMfItem.Action == actionDelete {
a.micropubDelete(w, r, u)
return
}
if parsedMfItem.Action == actionUpdate {
a.micropubUpdate(w, r, u, parsedMfItem)
return
}
a.serveError(w, r, "Action not supported", http.StatusNotImplemented)
return
}
p, err = a.convertMPMfToPost(parsedMfItem)
2020-10-06 17:07:48 +00:00
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
2020-10-06 17:07:48 +00:00
return
}
} else {
a.serveError(w, r, "wrong content type", http.StatusBadRequest)
2020-10-06 17:07:48 +00:00
return
}
2021-02-08 17:51:07 +00:00
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "create") {
a.serveError(w, r, "create scope missing", http.StatusForbidden)
2020-11-22 19:30:02 +00:00
return
}
err := a.createPost(p)
2020-10-06 17:07:48 +00:00
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
2020-10-06 17:07:48 +00:00
return
}
http.Redirect(w, r, a.fullPostURL(p), http.StatusAccepted)
2020-10-06 17:07:48 +00:00
}
func (a *goBlog) convertMPValueMapToPost(values map[string][]string) (*post, error) {
2020-10-06 17:07:48 +00:00
if h, ok := values["h"]; ok && (len(h) != 1 || h[0] != "entry") {
return nil, errors.New("only entry type is supported so far")
}
delete(values, "h")
2020-10-15 15:32:46 +00:00
entry := &post{
2020-10-06 17:07:48 +00:00
Parameters: map[string][]string{},
}
if content, ok := values["content"]; ok {
entry.Content = content[0]
delete(values, "content")
2020-10-06 17:07:48 +00:00
}
if published, ok := values["published"]; ok {
entry.Published = published[0]
delete(values, "published")
}
if updated, ok := values["updated"]; ok {
entry.Updated = updated[0]
delete(values, "updated")
}
2021-01-15 20:56:46 +00:00
if status, ok := values["post-status"]; ok {
entry.Status = postStatus(status[0])
delete(values, "post-status")
}
if slug, ok := values["mp-slug"]; ok {
entry.Slug = slug[0]
delete(values, "mp-slug")
2021-01-15 20:56:46 +00:00
}
2020-10-06 17:07:48 +00:00
// Parameter
if name, ok := values["name"]; ok {
entry.Parameters["title"] = name
delete(values, "name")
2020-10-06 17:07:48 +00:00
}
if category, ok := values["category"]; ok {
entry.Parameters[a.cfg.Micropub.CategoryParam] = category
delete(values, "category")
2020-10-06 17:07:48 +00:00
} else if categories, ok := values["category[]"]; ok {
entry.Parameters[a.cfg.Micropub.CategoryParam] = categories
delete(values, "category[]")
2020-10-06 17:07:48 +00:00
}
if inReplyTo, ok := values["in-reply-to"]; ok {
entry.Parameters[a.cfg.Micropub.ReplyParam] = inReplyTo
delete(values, "in-reply-to")
2020-10-06 17:07:48 +00:00
}
if likeOf, ok := values["like-of"]; ok {
entry.Parameters[a.cfg.Micropub.LikeParam] = likeOf
delete(values, "like-of")
2020-10-06 17:07:48 +00:00
}
if bookmarkOf, ok := values["bookmark-of"]; ok {
entry.Parameters[a.cfg.Micropub.BookmarkParam] = bookmarkOf
delete(values, "bookmark-of")
2020-10-06 17:07:48 +00:00
}
if audio, ok := values["audio"]; ok {
entry.Parameters[a.cfg.Micropub.AudioParam] = audio
delete(values, "audio")
2020-10-06 17:07:48 +00:00
} else if audio, ok := values["audio[]"]; ok {
entry.Parameters[a.cfg.Micropub.AudioParam] = audio
delete(values, "audio[]")
2020-10-06 17:07:48 +00:00
}
if photo, ok := values["photo"]; ok {
entry.Parameters[a.cfg.Micropub.PhotoParam] = photo
delete(values, "photo")
2020-10-06 17:07:48 +00:00
} else if photos, ok := values["photo[]"]; ok {
entry.Parameters[a.cfg.Micropub.PhotoParam] = photos
delete(values, "photo[]")
2020-10-06 17:07:48 +00:00
}
if photoAlt, ok := values["mp-photo-alt"]; ok {
entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = photoAlt
delete(values, "mp-photo-alt")
2020-10-06 17:07:48 +00:00
} else if photoAlts, ok := values["mp-photo-alt[]"]; ok {
entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = photoAlts
delete(values, "mp-photo-alt[]")
2020-10-06 17:07:48 +00:00
}
if location, ok := values["location"]; ok {
entry.Parameters[a.cfg.Micropub.LocationParam] = location
delete(values, "location")
}
for n, p := range values {
entry.Parameters[n] = append(entry.Parameters[n], p...)
2020-10-06 17:07:48 +00:00
}
err := a.computeExtraPostParameters(entry)
2020-10-06 17:07:48 +00:00
if err != nil {
return nil, err
}
return entry, nil
}
type micropubAction string
const (
actionUpdate micropubAction = "update"
2021-02-08 17:51:07 +00:00
actionDelete micropubAction = "delete"
)
2020-10-06 17:07:48 +00:00
type microformatItem struct {
Type []string `json:"type,omitempty"`
URL string `json:"url,omitempty"`
Action micropubAction `json:"action,omitempty"`
Properties *microformatProperties `json:"properties,omitempty"`
Replace map[string][]interface{} `json:"replace,omitempty"`
Add map[string][]interface{} `json:"add,omitempty"`
Delete interface{} `json:"delete,omitempty"`
2020-10-06 17:07:48 +00:00
}
type microformatProperties struct {
Name []string `json:"name,omitempty"`
Published []string `json:"published,omitempty"`
Updated []string `json:"updated,omitempty"`
2021-01-15 20:56:46 +00:00
PostStatus []string `json:"post-status,omitempty"`
Category []string `json:"category,omitempty"`
Content []string `json:"content,omitempty"`
URL []string `json:"url,omitempty"`
InReplyTo []string `json:"in-reply-to,omitempty"`
LikeOf []string `json:"like-of,omitempty"`
BookmarkOf []string `json:"bookmark-of,omitempty"`
MpSlug []string `json:"mp-slug,omitempty"`
Photo []interface{} `json:"photo,omitempty"`
Audio []string `json:"audio,omitempty"`
2020-10-06 17:07:48 +00:00
}
func (a *goBlog) convertMPMfToPost(mf *microformatItem) (*post, error) {
2020-10-06 17:07:48 +00:00
if len(mf.Type) != 1 || mf.Type[0] != "h-entry" {
return nil, errors.New("only entry type is supported so far")
}
2020-12-08 18:36:19 +00:00
entry := &post{
Parameters: map[string][]string{},
}
2020-10-06 17:07:48 +00:00
// Content
if mf.Properties != nil && len(mf.Properties.Content) == 1 && len(mf.Properties.Content[0]) > 0 {
entry.Content = mf.Properties.Content[0]
}
if len(mf.Properties.Published) == 1 {
entry.Published = mf.Properties.Published[0]
}
if len(mf.Properties.Updated) == 1 {
entry.Updated = mf.Properties.Updated[0]
}
2021-01-15 20:56:46 +00:00
if len(mf.Properties.PostStatus) == 1 {
entry.Status = postStatus(mf.Properties.PostStatus[0])
}
2020-10-06 17:07:48 +00:00
// Parameter
if len(mf.Properties.Name) == 1 {
entry.Parameters["title"] = mf.Properties.Name
}
if len(mf.Properties.Category) > 0 {
entry.Parameters[a.cfg.Micropub.CategoryParam] = mf.Properties.Category
2020-10-06 17:07:48 +00:00
}
if len(mf.Properties.InReplyTo) == 1 {
entry.Parameters[a.cfg.Micropub.ReplyParam] = mf.Properties.InReplyTo
2020-10-06 17:07:48 +00:00
}
if len(mf.Properties.LikeOf) == 1 {
entry.Parameters[a.cfg.Micropub.LikeParam] = mf.Properties.LikeOf
2020-10-06 17:07:48 +00:00
}
if len(mf.Properties.BookmarkOf) == 1 {
entry.Parameters[a.cfg.Micropub.BookmarkParam] = mf.Properties.BookmarkOf
2020-10-06 17:07:48 +00:00
}
if len(mf.Properties.Audio) > 0 {
entry.Parameters[a.cfg.Micropub.AudioParam] = mf.Properties.Audio
2020-10-06 17:07:48 +00:00
}
if len(mf.Properties.Photo) > 0 {
for _, photo := range mf.Properties.Photo {
if theString, justString := photo.(string); justString {
entry.Parameters[a.cfg.Micropub.PhotoParam] = append(entry.Parameters[a.cfg.Micropub.PhotoParam], theString)
entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = append(entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam], "")
2020-10-06 17:07:48 +00:00
} else if thePhoto, isPhoto := photo.(map[string]interface{}); isPhoto {
entry.Parameters[a.cfg.Micropub.PhotoParam] = append(entry.Parameters[a.cfg.Micropub.PhotoParam], cast.ToString(thePhoto["value"]))
entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam] = append(entry.Parameters[a.cfg.Micropub.PhotoDescriptionParam], cast.ToString(thePhoto["alt"]))
2020-10-06 17:07:48 +00:00
}
}
}
if len(mf.Properties.MpSlug) == 1 {
entry.Slug = mf.Properties.MpSlug[0]
}
err := a.computeExtraPostParameters(entry)
2020-10-06 17:07:48 +00:00
if err != nil {
return nil, err
}
return entry, nil
}
func (a *goBlog) computeExtraPostParameters(p *post) error {
2020-10-15 15:32:46 +00:00
p.Content = regexp.MustCompile("\r\n").ReplaceAllString(p.Content, "\n")
if split := strings.Split(p.Content, "---\n"); len(split) >= 3 && len(strings.TrimSpace(split[0])) == 0 {
2020-10-06 17:07:48 +00:00
// Contains frontmatter
fm := split[1]
meta := map[string]interface{}{}
err := yaml.Unmarshal([]byte(fm), &meta)
if err != nil {
return err
}
// Find section and copy frontmatter to params
for key, value := range meta {
// Delete existing content - replace
2020-10-15 15:32:46 +00:00
p.Parameters[key] = []string{}
2020-10-06 17:07:48 +00:00
if a, ok := value.([]interface{}); ok {
for _, ae := range a {
2020-10-15 15:32:46 +00:00
p.Parameters[key] = append(p.Parameters[key], cast.ToString(ae))
2020-10-06 17:07:48 +00:00
}
} else {
2020-10-15 15:32:46 +00:00
p.Parameters[key] = append(p.Parameters[key], cast.ToString(value))
2020-10-06 17:07:48 +00:00
}
}
// Remove frontmatter from content
2020-10-15 15:32:46 +00:00
p.Content = strings.Join(split[2:], "---\n")
2020-10-06 17:07:48 +00:00
}
// Check settings
2020-10-15 15:32:46 +00:00
if blog := p.Parameters["blog"]; len(blog) == 1 && blog[0] != "" {
p.Blog = blog[0]
delete(p.Parameters, "blog")
2020-10-06 17:07:48 +00:00
} else {
p.Blog = a.cfg.DefaultBlog
2020-10-06 17:07:48 +00:00
}
2021-01-15 20:56:46 +00:00
if path := p.Parameters["path"]; len(path) == 1 {
2020-10-15 15:32:46 +00:00
p.Path = path[0]
delete(p.Parameters, "path")
2020-10-06 17:07:48 +00:00
}
2021-01-15 20:56:46 +00:00
if section := p.Parameters["section"]; len(section) == 1 {
2020-10-15 15:32:46 +00:00
p.Section = section[0]
delete(p.Parameters, "section")
2020-10-06 17:07:48 +00:00
}
2021-01-15 20:56:46 +00:00
if slug := p.Parameters["slug"]; len(slug) == 1 {
2020-10-15 15:32:46 +00:00
p.Slug = slug[0]
delete(p.Parameters, "slug")
2020-10-06 17:07:48 +00:00
}
2021-01-15 20:56:46 +00:00
if published := p.Parameters["published"]; len(published) == 1 {
p.Published = published[0]
delete(p.Parameters, "published")
}
2021-01-15 20:56:46 +00:00
if updated := p.Parameters["updated"]; len(updated) == 1 {
p.Updated = updated[0]
delete(p.Parameters, "updated")
}
2021-01-15 20:56:46 +00:00
if status := p.Parameters["status"]; len(status) == 1 {
p.Status = postStatus(status[0])
delete(p.Parameters, "status")
}
2020-10-15 15:32:46 +00:00
if p.Path == "" && p.Section == "" {
// Has no path or section -> default section
p.Section = a.cfg.Blogs[p.Blog].DefaultSection
}
2020-10-15 15:32:46 +00:00
if p.Published == "" && p.Section != "" {
// Has no published date, but section -> published now
2020-12-16 20:24:53 +00:00
p.Published = time.Now().Local().String()
}
// Add images not in content
images := p.Parameters[a.cfg.Micropub.PhotoParam]
imageAlts := p.Parameters[a.cfg.Micropub.PhotoDescriptionParam]
useAlts := len(images) == len(imageAlts)
for i, image := range images {
2020-10-15 15:32:46 +00:00
if !strings.Contains(p.Content, image) {
if useAlts && len(imageAlts[i]) > 0 {
2020-10-15 15:32:46 +00:00
p.Content += "\n\n![" + imageAlts[i] + "](" + image + " \"" + imageAlts[i] + "\")"
} else {
2020-10-15 15:32:46 +00:00
p.Content += "\n\n![](" + image + ")"
}
}
2020-10-06 17:07:48 +00:00
}
return nil
}
func (a *goBlog) micropubDelete(w http.ResponseWriter, r *http.Request, u *url.URL) {
2021-02-08 17:51:07 +00:00
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "delete") {
a.serveError(w, r, "delete scope missing", http.StatusForbidden)
2020-11-22 19:30:02 +00:00
return
}
if err := a.deletePost(u.Path); err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
2020-11-22 19:30:02 +00:00
return
}
http.Redirect(w, r, u.String(), http.StatusNoContent)
}
func (a *goBlog) micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *microformatItem) {
2021-02-08 17:51:07 +00:00
if !strings.Contains(r.Context().Value(indieAuthScope).(string), "update") {
a.serveError(w, r, "update scope missing", http.StatusForbidden)
2020-11-22 19:30:02 +00:00
return
}
p, err := a.db.getPost(u.Path)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
2021-01-15 20:56:46 +00:00
oldPath := p.Path
oldStatus := p.Status
if mf.Replace != nil {
for key, value := range mf.Replace {
switch key {
case "content":
2020-10-15 15:32:46 +00:00
p.Content = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "published":
2020-10-15 15:32:46 +00:00
p.Published = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "updated":
2020-10-15 15:32:46 +00:00
p.Updated = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "name":
2020-10-15 15:32:46 +00:00
p.Parameters["title"] = cast.ToStringSlice(value)
case "category":
p.Parameters[a.cfg.Micropub.CategoryParam] = cast.ToStringSlice(value)
case "in-reply-to":
p.Parameters[a.cfg.Micropub.ReplyParam] = cast.ToStringSlice(value)
case "like-of":
p.Parameters[a.cfg.Micropub.LikeParam] = cast.ToStringSlice(value)
case "bookmark-of":
p.Parameters[a.cfg.Micropub.BookmarkParam] = cast.ToStringSlice(value)
case "audio":
p.Parameters[a.cfg.Micropub.AudioParam] = cast.ToStringSlice(value)
// TODO: photo
}
}
}
if mf.Add != nil {
for key, value := range mf.Add {
switch key {
case "content":
2020-10-15 15:32:46 +00:00
p.Content += strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "published":
2020-10-15 15:32:46 +00:00
p.Published = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "updated":
2020-10-15 15:32:46 +00:00
p.Updated = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
case "category":
category := p.Parameters[a.cfg.Micropub.CategoryParam]
if category == nil {
category = []string{}
}
p.Parameters[a.cfg.Micropub.CategoryParam] = append(category, cast.ToStringSlice(value)...)
case "in-reply-to":
p.Parameters[a.cfg.Micropub.ReplyParam] = cast.ToStringSlice(value)
case "like-of":
p.Parameters[a.cfg.Micropub.LikeParam] = cast.ToStringSlice(value)
case "bookmark-of":
p.Parameters[a.cfg.Micropub.BookmarkParam] = cast.ToStringSlice(value)
case "audio":
audio := p.Parameters[a.cfg.Micropub.CategoryParam]
if audio == nil {
audio = []string{}
}
p.Parameters[a.cfg.Micropub.AudioParam] = append(audio, cast.ToStringSlice(value)...)
// TODO: photo
}
}
}
if del := mf.Delete; del != nil {
if reflect.TypeOf(del).Kind() == reflect.Slice {
toDelete, ok := del.([]interface{})
if ok {
for _, key := range toDelete {
switch key {
case "content":
2020-10-15 15:32:46 +00:00
p.Content = ""
case "published":
2020-10-15 15:32:46 +00:00
p.Published = ""
case "updated":
2020-10-15 15:32:46 +00:00
p.Updated = ""
case "category":
delete(p.Parameters, a.cfg.Micropub.CategoryParam)
case "in-reply-to":
delete(p.Parameters, a.cfg.Micropub.ReplyParam)
case "like-of":
delete(p.Parameters, a.cfg.Micropub.LikeParam)
case "bookmark-of":
delete(p.Parameters, a.cfg.Micropub.BookmarkParam)
case "audio":
delete(p.Parameters, a.cfg.Micropub.AudioParam)
case "photo":
delete(p.Parameters, a.cfg.Micropub.PhotoParam)
delete(p.Parameters, a.cfg.Micropub.PhotoDescriptionParam)
}
}
}
} else {
toDelete, ok := del.(map[string]interface{})
if ok {
for key := range toDelete {
if ok {
switch key {
case "content":
2020-10-15 15:32:46 +00:00
p.Content = ""
case "published":
2020-10-15 15:32:46 +00:00
p.Published = ""
case "updated":
2020-10-15 15:32:46 +00:00
p.Updated = ""
case "in-reply-to":
delete(p.Parameters, a.cfg.Micropub.ReplyParam)
case "like-of":
delete(p.Parameters, a.cfg.Micropub.LikeParam)
case "bookmark-of":
delete(p.Parameters, a.cfg.Micropub.BookmarkParam)
// Use content to edit other parameters
}
}
}
}
}
}
err = a.computeExtraPostParameters(p)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
err = a.replacePost(p, oldPath, oldStatus)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, a.fullPostURL(p), http.StatusNoContent)
}