2020-12-12 22:44:03 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-11-19 06:06:55 +00:00
|
|
|
"context"
|
2021-01-21 16:59:47 +00:00
|
|
|
"encoding/json"
|
2021-08-01 17:27:21 +00:00
|
|
|
"fmt"
|
2021-02-16 15:26:21 +00:00
|
|
|
"io"
|
2020-12-12 22:44:03 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2021-07-31 12:19:37 +00:00
|
|
|
"strings"
|
2021-11-19 06:06:55 +00:00
|
|
|
"time"
|
2021-06-18 12:32:03 +00:00
|
|
|
|
2021-06-28 20:17:18 +00:00
|
|
|
"go.goblog.app/app/pkgs/contenttype"
|
2021-07-31 12:19:37 +00:00
|
|
|
"gopkg.in/yaml.v3"
|
2021-11-19 06:06:55 +00:00
|
|
|
ws "nhooyr.io/websocket"
|
2020-12-12 22:44:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const editorPath = "/editor"
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) serveEditor(w http.ResponseWriter, r *http.Request) {
|
|
|
|
a.render(w, r, templateEditor, &renderData{
|
2021-12-20 13:00:11 +00:00
|
|
|
Data: map[string]interface{}{},
|
2021-03-22 07:20:56 +00:00
|
|
|
})
|
2020-12-12 22:44:03 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 17:39:08 +00:00
|
|
|
func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
|
2021-12-20 13:00:11 +00:00
|
|
|
blog, _ := a.getBlog(r)
|
2021-11-19 06:06:55 +00:00
|
|
|
c, err := ws.Accept(w, r, nil)
|
2021-11-01 17:39:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-11-19 06:06:55 +00:00
|
|
|
defer c.Close(ws.StatusNormalClosure, "")
|
|
|
|
ctx, cancel := context.WithTimeout(r.Context(), time.Minute*60)
|
|
|
|
defer cancel()
|
2021-11-01 17:39:08 +00:00
|
|
|
for {
|
|
|
|
// Retrieve content
|
2021-11-19 06:06:55 +00:00
|
|
|
mt, message, err := c.Read(ctx)
|
2021-11-01 17:39:08 +00:00
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
2021-11-19 06:06:55 +00:00
|
|
|
if mt != ws.MessageText {
|
2021-11-10 17:17:25 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-11-01 17:39:08 +00:00
|
|
|
// Create preview
|
2021-11-02 08:27:01 +00:00
|
|
|
preview, err := a.createMarkdownPreview(blog, message)
|
2021-11-01 17:39:08 +00:00
|
|
|
if err != nil {
|
2021-11-02 08:27:01 +00:00
|
|
|
preview = []byte(err.Error())
|
2021-11-01 17:39:08 +00:00
|
|
|
}
|
|
|
|
// Write preview to socket
|
2021-11-19 06:06:55 +00:00
|
|
|
err = c.Write(ctx, ws.MessageText, preview)
|
2021-11-01 17:39:08 +00:00
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-02 08:27:01 +00:00
|
|
|
func (a *goBlog) createMarkdownPreview(blog string, markdown []byte) (rendered []byte, err error) {
|
2022-01-10 18:23:20 +00:00
|
|
|
p := &post{
|
2021-11-02 08:27:01 +00:00
|
|
|
Content: string(markdown),
|
|
|
|
Blog: blog,
|
|
|
|
Path: "/editor/preview",
|
|
|
|
Published: localNowString(),
|
|
|
|
}
|
2022-01-10 18:23:20 +00:00
|
|
|
err = a.computeExtraPostParameters(p)
|
2021-11-02 08:27:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if t := p.Title(); t != "" {
|
|
|
|
p.RenderedTitle = a.renderMdTitle(t)
|
|
|
|
}
|
|
|
|
// Render post
|
2022-01-10 18:23:20 +00:00
|
|
|
var hb htmlBuilder
|
|
|
|
a.renderEditorPreview(&hb, a.cfg.Blogs[blog], p)
|
2022-01-20 17:22:10 +00:00
|
|
|
rendered = hb.Bytes()
|
2021-11-01 17:39:08 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
2021-03-22 07:20:56 +00:00
|
|
|
if action := r.FormValue("editoraction"); action != "" {
|
|
|
|
switch action {
|
|
|
|
case "loadupdate":
|
2021-10-24 16:02:07 +00:00
|
|
|
post, err := a.getPost(r.FormValue("path"))
|
2021-03-22 07:20:56 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-06 12:39:42 +00:00
|
|
|
a.render(w, r, templateEditor, &renderData{
|
2021-03-22 07:20:56 +00:00
|
|
|
Data: map[string]interface{}{
|
2021-10-24 16:02:07 +00:00
|
|
|
"UpdatePostURL": a.fullPostURL(post),
|
2021-06-23 17:20:50 +00:00
|
|
|
"UpdatePostContent": a.postToMfItem(post).Properties.Content[0],
|
2021-03-22 07:20:56 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
case "updatepost":
|
2021-05-08 12:09:42 +00:00
|
|
|
jsonBytes, err := json.Marshal(map[string]interface{}{
|
2021-03-22 07:20:56 +00:00
|
|
|
"action": actionUpdate,
|
2021-05-08 12:09:42 +00:00
|
|
|
"url": r.FormValue("url"),
|
2021-03-22 07:20:56 +00:00
|
|
|
"replace": map[string][]string{
|
|
|
|
"content": {
|
2021-05-08 12:09:42 +00:00
|
|
|
r.FormValue("content"),
|
2021-01-17 11:53:07 +00:00
|
|
|
},
|
2021-03-22 07:20:56 +00:00
|
|
|
},
|
2021-05-08 12:09:42 +00:00
|
|
|
})
|
2021-03-22 07:20:56 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
req, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(jsonBytes))
|
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-18 12:32:03 +00:00
|
|
|
req.Header.Set(contentType, contenttype.JSON)
|
2021-06-06 12:39:42 +00:00
|
|
|
a.editorMicropubPost(w, req, false)
|
2021-03-22 07:20:56 +00:00
|
|
|
case "upload":
|
2021-06-06 12:39:42 +00:00
|
|
|
a.editorMicropubPost(w, r, true)
|
2021-09-07 20:16:28 +00:00
|
|
|
case "tts":
|
|
|
|
parsedURL, err := url.Parse(r.FormValue("url"))
|
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
post, err := a.getPost(parsedURL.Path)
|
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = a.createPostTTSAudio(post); err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Redirect(w, r, post.Path, http.StatusFound)
|
2021-11-10 10:13:30 +00:00
|
|
|
case "helpgpx":
|
|
|
|
file, _, err := r.FormFile("file")
|
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2021-12-30 11:40:21 +00:00
|
|
|
originalGpx, err := io.ReadAll(file)
|
2021-11-10 10:13:30 +00:00
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2021-12-30 11:40:21 +00:00
|
|
|
minifiedGpx, err := a.min.MinifyString(contenttype.XML, string(originalGpx))
|
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
2021-11-10 10:13:30 +00:00
|
|
|
}
|
2021-12-30 11:40:21 +00:00
|
|
|
resultBytes, err := yaml.Marshal(map[string]string{
|
|
|
|
"gpx": minifiedGpx,
|
|
|
|
})
|
2021-11-10 10:13:30 +00:00
|
|
|
if err != nil {
|
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Header().Set(contentType, contenttype.TextUTF8)
|
|
|
|
_, _ = w.Write(resultBytes)
|
2021-03-22 07:20:56 +00:00
|
|
|
default:
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, "Unknown editoraction", http.StatusBadRequest)
|
2020-12-12 22:44:03 +00:00
|
|
|
}
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
2020-12-12 22:44:03 +00:00
|
|
|
}
|
2021-06-06 12:39:42 +00:00
|
|
|
a.editorMicropubPost(w, r, false)
|
2020-12-12 22:44:03 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) editorMicropubPost(w http.ResponseWriter, r *http.Request, media bool) {
|
2020-12-12 22:44:03 +00:00
|
|
|
recorder := httptest.NewRecorder()
|
2020-12-13 10:28:46 +00:00
|
|
|
if media {
|
2021-06-06 12:39:42 +00:00
|
|
|
addAllScopes(http.HandlerFunc(a.serveMicropubMedia)).ServeHTTP(recorder, r)
|
2020-12-13 10:28:46 +00:00
|
|
|
} else {
|
2021-06-06 12:39:42 +00:00
|
|
|
addAllScopes(http.HandlerFunc(a.serveMicropubPost)).ServeHTTP(recorder, r)
|
2020-12-13 10:28:46 +00:00
|
|
|
}
|
2020-12-12 22:44:03 +00:00
|
|
|
result := recorder.Result()
|
|
|
|
if location := result.Header.Get("Location"); location != "" {
|
2021-01-15 20:56:46 +00:00
|
|
|
http.Redirect(w, r, location, http.StatusFound)
|
2020-12-12 22:44:03 +00:00
|
|
|
return
|
|
|
|
}
|
2021-07-01 16:51:04 +00:00
|
|
|
if result.StatusCode >= 200 && result.StatusCode < 400 {
|
2020-12-12 22:44:03 +00:00
|
|
|
http.Redirect(w, r, editorPath, http.StatusFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(result.StatusCode)
|
2021-02-16 15:26:21 +00:00
|
|
|
_, _ = io.Copy(w, result.Body)
|
|
|
|
_ = result.Body.Close()
|
2020-12-12 22:44:03 +00:00
|
|
|
}
|
2021-07-31 12:19:37 +00:00
|
|
|
|
|
|
|
func (a *goBlog) editorPostTemplate(blog string) string {
|
|
|
|
var builder strings.Builder
|
|
|
|
marsh := func(param string, i interface{}) {
|
|
|
|
_ = yaml.NewEncoder(&builder).Encode(map[string]interface{}{
|
|
|
|
param: i,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
bc := a.cfg.Blogs[blog]
|
|
|
|
builder.WriteString("---\n")
|
|
|
|
marsh("blog", blog)
|
|
|
|
marsh("section", bc.DefaultSection)
|
|
|
|
marsh("status", statusDraft)
|
|
|
|
marsh("priority", 0)
|
|
|
|
marsh("slug", "")
|
|
|
|
marsh("title", "")
|
|
|
|
for _, t := range bc.Taxonomies {
|
|
|
|
marsh(t.Name, []string{""})
|
|
|
|
}
|
|
|
|
builder.WriteString("---\n")
|
|
|
|
return builder.String()
|
|
|
|
}
|
|
|
|
|
2021-08-01 17:27:21 +00:00
|
|
|
func (a *goBlog) editorPostDesc(blog string) string {
|
2021-07-31 12:24:58 +00:00
|
|
|
bc := a.cfg.Blogs[blog]
|
2021-08-01 17:27:21 +00:00
|
|
|
t := a.ts.GetTemplateStringVariant(bc.Lang, "editorpostdesc")
|
|
|
|
var paramBuilder, statusBuilder strings.Builder
|
2021-07-31 12:19:37 +00:00
|
|
|
for i, param := range []string{
|
2021-12-11 18:43:40 +00:00
|
|
|
"published",
|
|
|
|
"updated",
|
2021-07-31 12:19:37 +00:00
|
|
|
"summary",
|
|
|
|
"translationkey",
|
|
|
|
"original",
|
|
|
|
a.cfg.Micropub.AudioParam,
|
|
|
|
a.cfg.Micropub.BookmarkParam,
|
|
|
|
a.cfg.Micropub.LikeParam,
|
|
|
|
a.cfg.Micropub.LikeTitleParam,
|
|
|
|
a.cfg.Micropub.LocationParam,
|
|
|
|
a.cfg.Micropub.PhotoParam,
|
|
|
|
a.cfg.Micropub.PhotoDescriptionParam,
|
|
|
|
a.cfg.Micropub.ReplyParam,
|
|
|
|
a.cfg.Micropub.ReplyTitleParam,
|
2021-11-16 17:01:11 +00:00
|
|
|
gpxParameter,
|
2021-07-31 12:19:37 +00:00
|
|
|
} {
|
|
|
|
if param == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if i > 0 {
|
2021-08-01 17:27:21 +00:00
|
|
|
paramBuilder.WriteString(", ")
|
2021-07-31 12:19:37 +00:00
|
|
|
}
|
2021-08-01 17:27:21 +00:00
|
|
|
paramBuilder.WriteByte('`')
|
|
|
|
paramBuilder.WriteString(param)
|
|
|
|
paramBuilder.WriteByte('`')
|
2021-07-31 12:19:37 +00:00
|
|
|
}
|
2021-08-01 17:27:21 +00:00
|
|
|
for i, status := range []postStatus{
|
2021-12-11 18:43:40 +00:00
|
|
|
statusDraft, statusPublished, statusUnlisted, statusScheduled, statusPrivate,
|
2021-08-01 17:27:21 +00:00
|
|
|
} {
|
|
|
|
if i > 0 {
|
|
|
|
statusBuilder.WriteString(", ")
|
|
|
|
}
|
|
|
|
statusBuilder.WriteByte('`')
|
|
|
|
statusBuilder.WriteString(string(status))
|
|
|
|
statusBuilder.WriteByte('`')
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(t, paramBuilder.String(), "status", statusBuilder.String())
|
2021-07-31 12:19:37 +00:00
|
|
|
}
|