2020-10-06 17:07:48 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2020-10-14 16:23:56 +00:00
|
|
|
"fmt"
|
2020-10-06 17:07:48 +00:00
|
|
|
"net/http"
|
2020-10-14 16:23:56 +00:00
|
|
|
"net/url"
|
|
|
|
"reflect"
|
|
|
|
"regexp"
|
2020-10-06 17:07:48 +00:00
|
|
|
"strings"
|
2020-10-14 16:23:56 +00:00
|
|
|
"time"
|
2020-10-06 17:07:48 +00:00
|
|
|
|
|
|
|
"github.com/spf13/cast"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
)
|
|
|
|
|
2020-10-14 16:23:56 +00:00
|
|
|
const micropubPath = "/micropub"
|
2020-10-06 17:07:48 +00:00
|
|
|
|
|
|
|
type micropubConfig struct {
|
|
|
|
MediaEndpoint string `json:"media-endpoint,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
2020-10-14 16:23:56 +00:00
|
|
|
switch r.URL.Query().Get("q") {
|
|
|
|
case "config":
|
2020-10-06 17:07:48 +00:00
|
|
|
w.Header().Add(contentType, contentTypeJSON)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2020-10-14 19:20:17 +00:00
|
|
|
mc := µpubConfig{}
|
|
|
|
if appConfig.Micropub.MediaStorage != nil {
|
|
|
|
mc.MediaEndpoint = appConfig.Server.PublicAddress + micropubPath + micropubMediaSubPath
|
|
|
|
}
|
|
|
|
_ = json.NewEncoder(w).Encode(mc)
|
2020-10-14 16:23:56 +00:00
|
|
|
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 {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2020-10-19 18:25:30 +00:00
|
|
|
p, err := getPost(u.Path)
|
2020-10-14 16:23:56 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
mf = p.toMfItem()
|
2020-10-14 16:23:56 +00:00
|
|
|
} else {
|
2020-10-19 18:25:30 +00:00
|
|
|
posts, err := getPosts(&postsRequestConfig{})
|
2020-10-14 16:23:56 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, 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"], p.toMfItem())
|
2020-10-14 16:23:56 +00:00
|
|
|
}
|
|
|
|
mf = list
|
|
|
|
}
|
2020-10-06 17:07:48 +00:00
|
|
|
w.Header().Add(contentType, contentTypeJSON)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2020-10-14 16:23:56 +00:00
|
|
|
_ = json.NewEncoder(w).Encode(mf)
|
|
|
|
default:
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:32:46 +00:00
|
|
|
func (p *post) toMfItem() *microformatItem {
|
|
|
|
params := p.Parameters
|
|
|
|
params["path"] = []string{p.Path}
|
|
|
|
params["section"] = []string{p.Section}
|
|
|
|
params["blog"] = []string{p.Blog}
|
|
|
|
pb, _ := yaml.Marshal(p.Parameters)
|
|
|
|
content := fmt.Sprintf("---\n%s---\n%s", string(pb), p.Content)
|
2020-10-14 16:23:56 +00:00
|
|
|
return µformatItem{
|
|
|
|
Type: []string{"h-entry"},
|
|
|
|
Properties: µformatProperties{
|
2020-10-15 15:32:46 +00:00
|
|
|
Name: p.Parameters["title"],
|
|
|
|
Published: []string{p.Published},
|
|
|
|
Updated: []string{p.Updated},
|
2020-10-14 16:23:56 +00:00
|
|
|
Content: []string{content},
|
2020-10-15 15:32:46 +00:00
|
|
|
MpSlug: []string{p.Slug},
|
|
|
|
Category: p.Parameters[appConfig.Micropub.CategoryParam],
|
2020-10-14 16:23:56 +00:00
|
|
|
},
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func serveMicropubPost(w http.ResponseWriter, r *http.Request) {
|
|
|
|
defer r.Body.Close()
|
2020-10-15 15:32:46 +00:00
|
|
|
var p *post
|
2020-10-14 16:23:56 +00:00
|
|
|
if ct := r.Header.Get(contentType); strings.Contains(ct, contentTypeWWWForm) || strings.Contains(ct, contentTypeMultipartForm) {
|
|
|
|
var err error
|
|
|
|
r.ParseForm()
|
|
|
|
if strings.Contains(ct, contentTypeMultipartForm) {
|
|
|
|
err := r.ParseMultipartForm(0)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
2020-10-14 16:23:56 +00:00
|
|
|
if action := micropubAction(r.Form.Get("action")); action != "" {
|
|
|
|
u, err := url.Parse(r.Form.Get("url"))
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
if action == actionDelete {
|
|
|
|
micropubDelete(w, r, u)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Error(w, "Action not supported", http.StatusNotImplemented)
|
2020-10-06 17:07:48 +00:00
|
|
|
return
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
p, err = convertMPValueMapToPost(r.Form)
|
2020-10-06 17:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else if strings.Contains(ct, contentTypeJSON) {
|
|
|
|
parsedMfItem := µformatItem{}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(parsedMfItem)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2020-10-14 16:23:56 +00:00
|
|
|
if parsedMfItem.Action != "" {
|
|
|
|
u, err := url.Parse(parsedMfItem.URL)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
if parsedMfItem.Action == actionDelete {
|
|
|
|
micropubDelete(w, r, u)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if parsedMfItem.Action == actionUpdate {
|
|
|
|
micropubUpdate(w, r, u, parsedMfItem)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Error(w, "Action not supported", http.StatusNotImplemented)
|
|
|
|
return
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
p, err = convertMPMfToPost(parsedMfItem)
|
2020-10-06 17:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
http.Error(w, "wrong content type", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2020-10-14 16:23:56 +00:00
|
|
|
if !strings.Contains(r.Context().Value("scope").(string), "create") {
|
|
|
|
http.Error(w, "create scope missing", http.StatusForbidden)
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
err := p.create()
|
2020-10-06 17:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
w.Header().Add("Location", appConfig.Server.PublicAddress+p.Path)
|
2020-10-06 17:07:48 +00:00
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:32:46 +00:00
|
|
|
func 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")
|
|
|
|
}
|
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]
|
|
|
|
}
|
2020-10-12 17:54:22 +00:00
|
|
|
if published, ok := values["published"]; ok {
|
|
|
|
entry.Published = published[0]
|
|
|
|
}
|
|
|
|
if updated, ok := values["updated"]; ok {
|
|
|
|
entry.Updated = updated[0]
|
|
|
|
}
|
2020-10-06 17:07:48 +00:00
|
|
|
// Parameter
|
|
|
|
if name, ok := values["name"]; ok {
|
|
|
|
entry.Parameters["title"] = name
|
|
|
|
}
|
|
|
|
if category, ok := values["category"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.CategoryParam] = category
|
|
|
|
} else if categories, ok := values["category[]"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.CategoryParam] = categories
|
|
|
|
}
|
|
|
|
if inReplyTo, ok := values["in-reply-to"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.ReplyParam] = inReplyTo
|
|
|
|
}
|
|
|
|
if likeOf, ok := values["like-of"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.LikeParam] = likeOf
|
|
|
|
}
|
|
|
|
if bookmarkOf, ok := values["bookmark-of"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.BookmarkParam] = bookmarkOf
|
|
|
|
}
|
|
|
|
if audio, ok := values["audio"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.AudioParam] = audio
|
|
|
|
} else if audio, ok := values["audio[]"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.AudioParam] = audio
|
|
|
|
}
|
|
|
|
if photo, ok := values["photo"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoParam] = photo
|
|
|
|
} else if photos, ok := values["photo[]"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoParam] = photos
|
|
|
|
}
|
|
|
|
if photoAlt, ok := values["mp-photo-alt"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoDescriptionParam] = photoAlt
|
|
|
|
} else if photoAlts, ok := values["mp-photo-alt[]"]; ok {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoDescriptionParam] = photoAlts
|
|
|
|
}
|
|
|
|
if slug, ok := values["mp-slug"]; ok {
|
|
|
|
entry.Slug = slug[0]
|
|
|
|
}
|
2020-10-14 16:23:56 +00:00
|
|
|
err := entry.computeExtraPostParameters()
|
2020-10-06 17:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
|
2020-10-14 16:23:56 +00:00
|
|
|
type micropubAction string
|
|
|
|
|
|
|
|
const (
|
|
|
|
actionUpdate micropubAction = "update"
|
|
|
|
actionDelete = "delete"
|
|
|
|
)
|
|
|
|
|
2020-10-06 17:07:48 +00:00
|
|
|
type microformatItem struct {
|
2020-10-14 16:23:56 +00:00
|
|
|
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 {
|
2020-10-14 16:23:56 +00:00
|
|
|
Name []string `json:"name,omitempty"`
|
|
|
|
Published []string `json:"published,omitempty"`
|
|
|
|
Updated []string `json:"updated,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
|
|
|
}
|
|
|
|
|
2020-10-15 15:32:46 +00:00
|
|
|
func 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-10-15 15:32:46 +00:00
|
|
|
entry := &post{}
|
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]
|
|
|
|
}
|
2020-10-12 17:54:22 +00:00
|
|
|
if len(mf.Properties.Published) == 1 {
|
|
|
|
entry.Published = mf.Properties.Published[0]
|
|
|
|
}
|
|
|
|
if len(mf.Properties.Updated) == 1 {
|
|
|
|
entry.Updated = mf.Properties.Updated[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[appConfig.Micropub.CategoryParam] = mf.Properties.Category
|
|
|
|
}
|
|
|
|
if len(mf.Properties.InReplyTo) == 1 {
|
|
|
|
entry.Parameters[appConfig.Micropub.ReplyParam] = mf.Properties.InReplyTo
|
|
|
|
}
|
|
|
|
if len(mf.Properties.LikeOf) == 1 {
|
|
|
|
entry.Parameters[appConfig.Micropub.LikeParam] = mf.Properties.LikeOf
|
|
|
|
}
|
|
|
|
if len(mf.Properties.BookmarkOf) == 1 {
|
|
|
|
entry.Parameters[appConfig.Micropub.BookmarkParam] = mf.Properties.BookmarkOf
|
|
|
|
}
|
|
|
|
if len(mf.Properties.Audio) > 0 {
|
|
|
|
entry.Parameters[appConfig.Micropub.AudioParam] = mf.Properties.Audio
|
|
|
|
}
|
|
|
|
if len(mf.Properties.Photo) > 0 {
|
|
|
|
for _, photo := range mf.Properties.Photo {
|
|
|
|
if theString, justString := photo.(string); justString {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoParam] = append(entry.Parameters[appConfig.Micropub.PhotoParam], theString)
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoDescriptionParam] = append(entry.Parameters[appConfig.Micropub.PhotoDescriptionParam], "")
|
|
|
|
} else if thePhoto, isPhoto := photo.(map[string]interface{}); isPhoto {
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoParam] = append(entry.Parameters[appConfig.Micropub.PhotoParam], cast.ToString(thePhoto["value"]))
|
|
|
|
entry.Parameters[appConfig.Micropub.PhotoDescriptionParam] = append(entry.Parameters[appConfig.Micropub.PhotoDescriptionParam], cast.ToString(thePhoto["alt"]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(mf.Properties.MpSlug) == 1 {
|
|
|
|
entry.Slug = mf.Properties.MpSlug[0]
|
|
|
|
}
|
2020-10-14 16:23:56 +00:00
|
|
|
err := entry.computeExtraPostParameters()
|
2020-10-06 17:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return entry, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-15 15:32:46 +00:00
|
|
|
func (p *post) computeExtraPostParameters() error {
|
|
|
|
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 {
|
2020-10-14 16:23:56 +00:00
|
|
|
// 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 {
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Blog = appConfig.DefaultBlog
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
if path := p.Parameters["path"]; len(path) == 1 && path[0] != "" {
|
|
|
|
p.Path = path[0]
|
|
|
|
delete(p.Parameters, "path")
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
if section := p.Parameters["section"]; len(section) == 1 && section[0] != "" {
|
|
|
|
p.Section = section[0]
|
|
|
|
delete(p.Parameters, "section")
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
if slug := p.Parameters["slug"]; len(slug) == 1 && slug[0] != "" {
|
|
|
|
p.Slug = slug[0]
|
|
|
|
delete(p.Parameters, "slug")
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
if p.Path == "" && p.Section == "" {
|
2020-10-14 16:23:56 +00:00
|
|
|
// Has no path or section -> default section
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Section = appConfig.Blogs[p.Blog].DefaultSection
|
2020-10-14 16:23:56 +00:00
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
if p.Published == "" && p.Section != "" {
|
2020-10-14 16:23:56 +00:00
|
|
|
// Has no published date, but section -> published now
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Published = time.Now().String()
|
2020-10-14 16:23:56 +00:00
|
|
|
}
|
|
|
|
// Add images not in content
|
2020-10-15 15:32:46 +00:00
|
|
|
images := p.Parameters[appConfig.Micropub.PhotoParam]
|
|
|
|
imageAlts := p.Parameters[appConfig.Micropub.PhotoDescriptionParam]
|
2020-10-14 16:23:56 +00:00
|
|
|
useAlts := len(images) == len(imageAlts)
|
|
|
|
for i, image := range images {
|
2020-10-15 15:32:46 +00:00
|
|
|
if !strings.Contains(p.Content, image) {
|
2020-10-14 16:23:56 +00:00
|
|
|
if useAlts && len(imageAlts[i]) > 0 {
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Content += "\n\n![" + imageAlts[i] + "](" + image + " \"" + imageAlts[i] + "\")"
|
2020-10-14 16:23:56 +00:00
|
|
|
} else {
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Content += "\n\n![](" + image + ")"
|
2020-10-14 16:23:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-06 17:07:48 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-14 16:23:56 +00:00
|
|
|
func micropubDelete(w http.ResponseWriter, r *http.Request, u *url.URL) {
|
|
|
|
if !strings.Contains(r.Context().Value("scope").(string), "delete") {
|
|
|
|
http.Error(w, "delete scope missing", http.StatusForbidden)
|
|
|
|
}
|
|
|
|
err := deletePost(u.Path)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *microformatItem) {
|
|
|
|
if !strings.Contains(r.Context().Value("scope").(string), "update") {
|
|
|
|
http.Error(w, "update scope missing", http.StatusForbidden)
|
|
|
|
}
|
2020-10-19 18:25:30 +00:00
|
|
|
p, err := getPost(u.Path)
|
2020-10-14 16:23:56 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
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), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "published":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Published = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "updated":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Updated = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "name":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters["title"] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "category":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.CategoryParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "in-reply-to":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.ReplyParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "like-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.LikeParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "bookmark-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.BookmarkParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "audio":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.AudioParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
// 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), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "published":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Published = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "updated":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Updated = strings.TrimSpace(strings.Join(cast.ToStringSlice(value), " "))
|
2020-10-14 16:23:56 +00:00
|
|
|
case "category":
|
2020-10-15 15:32:46 +00:00
|
|
|
category := p.Parameters[appConfig.Micropub.CategoryParam]
|
2020-10-14 16:23:56 +00:00
|
|
|
if category == nil {
|
|
|
|
category = []string{}
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.CategoryParam] = append(category, cast.ToStringSlice(value)...)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "in-reply-to":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.ReplyParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "like-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.LikeParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "bookmark-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.BookmarkParam] = cast.ToStringSlice(value)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "audio":
|
2020-10-15 15:32:46 +00:00
|
|
|
audio := p.Parameters[appConfig.Micropub.CategoryParam]
|
2020-10-14 16:23:56 +00:00
|
|
|
if audio == nil {
|
|
|
|
audio = []string{}
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Parameters[appConfig.Micropub.AudioParam] = append(audio, cast.ToStringSlice(value)...)
|
2020-10-14 16:23:56 +00:00
|
|
|
// 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 = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "published":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Published = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "updated":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Updated = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "category":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.CategoryParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "in-reply-to":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.ReplyParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "like-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.LikeParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "bookmark-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.BookmarkParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "audio":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.AudioParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "photo":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.PhotoParam)
|
|
|
|
delete(p.Parameters, appConfig.Micropub.PhotoDescriptionParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} 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 = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "published":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Published = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "updated":
|
2020-10-15 15:32:46 +00:00
|
|
|
p.Updated = ""
|
2020-10-14 16:23:56 +00:00
|
|
|
case "in-reply-to":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.ReplyParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "like-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.LikeParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
case "bookmark-of":
|
2020-10-15 15:32:46 +00:00
|
|
|
delete(p.Parameters, appConfig.Micropub.BookmarkParam)
|
2020-10-14 16:23:56 +00:00
|
|
|
// Use content to edit other parameters
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
err = p.computeExtraPostParameters()
|
2020-10-14 16:23:56 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2020-10-15 15:32:46 +00:00
|
|
|
err = p.replace()
|
2020-10-14 16:23:56 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|