aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/handlers_design.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/web/handlers_design.go')
-rw-r--r--internal/web/handlers_design.go47
1 files changed, 44 insertions, 3 deletions
diff --git a/internal/web/handlers_design.go b/internal/web/handlers_design.go
index 601ba34..0db66c2 100644
--- a/internal/web/handlers_design.go
+++ b/internal/web/handlers_design.go
@@ -2,12 +2,14 @@ package web
import (
"bytes"
+ "encoding/json"
"errors"
"io"
"mime/multipart"
"net/http"
"path/filepath"
"strconv"
+ "strings"
"github.com/google/uuid"
"github.com/gramanas/blogspace/internal/store"
@@ -89,6 +91,24 @@ func (s *Server) storeUpload(r *http.Request, fh *multipart.FileHeader) (*store.
return s.st.CreateImage(r.Context(), currentBlog(r).ID, name, ct, buf.Bytes())
}
+// imageMarkdown is the line the editor inserts for an uploaded image.
+func imageMarkdown(img *store.Image) string {
+ return "![" + strings.NewReplacer("]", "", "\n", " ").Replace(img.Filename) + "](/media/" + img.ID.String() + ")"
+}
+
+// appendImageMD is the no-JavaScript path of "Insert image": the file arrives
+// with the form itself and is appended to the end of the text on save.
+func appendImageMD(md string, img *store.Image) string {
+ if img == nil {
+ return md
+ }
+ md = strings.TrimRight(md, "\n")
+ if md != "" {
+ md += "\n\n"
+ }
+ return md + imageMarkdown(img) + "\n"
+}
+
func kbString(n int64) string {
if n >= 1<<20 {
return strconv.FormatInt(n>>20, 10) + " MB"
@@ -107,24 +127,45 @@ func (s *Server) handleImages(w http.ResponseWriter, r *http.Request) {
s.render(w, r, "dashboard/images.html", map[string]any{"images": images})
}
+// handleImageUpload serves the Images page form and, when the client asks for
+// JSON, the editor's "Insert image" script.
func (s *Server) handleImageUpload(w http.ResponseWriter, r *http.Request) {
blog := currentBlog(r)
+ wantJSON := strings.Contains(r.Header.Get("Accept"), "application/json")
+ fail := func(msg string) {
+ if wantJSON {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": msg})
+ return
+ }
+ s.plainError(w, http.StatusBadRequest, msg)
+ }
if err := r.ParseMultipartForm(1 << 20); err != nil {
- s.plainError(w, http.StatusBadRequest, "Upload too large or malformed form.")
+ fail("Upload too large or malformed form.")
return
}
img, err := s.readUpload(r, "file")
if err != nil {
- s.plainError(w, http.StatusBadRequest, err.Error())
+ fail(err.Error())
return
}
if img == nil {
- s.plainError(w, http.StatusBadRequest, "Choose a file first.")
+ fail("Choose a file first.")
+ return
+ }
+ if wantJSON {
+ writeJSON(w, http.StatusOK, map[string]string{"id": img.ID.String(), "filename": img.Filename, "markdown": imageMarkdown(img)})
return
}
redirectOK(w, r, "/b/"+blog.Subdomain+"/images", "Uploaded "+img.Filename+".")
}
+func writeJSON(w http.ResponseWriter, status int, v any) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(status)
+ _ = json.NewEncoder(w).Encode(v)
+}
+
func (s *Server) handleImageDelete(w http.ResponseWriter, r *http.Request) {
blog := currentBlog(r)
id, err := uuid.Parse(r.PathValue("id"))