diff of a7047491ebdb799ca1a42164a76e65f5b6f78f7c

a7047491ebdb799ca1a42164a76e65f5b6f78f7c
diff --git a/aggressive-murja.asd b/aggressive-murja.asd
index 50567c3..ff2c8a0 100644
--- a/aggressive-murja.asd
+++ b/aggressive-murja.asd
@@ -91,6 +91,7 @@
 					       (:file "dnd-script")
 					       (:file "editor")))
 				 (:file "post-list")
+				 (:file "media-admin")
 				 (:file "new-post")))
 		   (:file "blog-root")
 		   ))
diff --git a/resources/css/murja.css b/resources/css/murja.css
index b09d7fa..38b37f0 100644
--- a/resources/css/murja.css
+++ b/resources/css/murja.css
@@ -479,6 +479,32 @@ input:required {
     display: none;
 }
 
+/* TODO: this file should probs be split among the spinneret files */
+
+.images {
+    display: flex;
+    flex-wrap: wrap;
+    flex-direction: row;
+}
+
+.image {
+    margin: 20px;
+    border-width: 3px;
+    border-style: inset;
+    list-style: none;
+}
+
+.image-header {
+    display: flex;
+    justify-content: right;
+}
+
+.mediamanager-header {
+    display: flex;
+    justify-content: space-between;
+    margin-right: 20px;
+}
+
 @media only screen and (max-device-width:480px)
 {
     body {
diff --git a/src/view/admin/components/tag-script.lisp b/src/view/admin/components/tag-script.lisp
index b0162ed..744669a 100644
--- a/src/view/admin/components/tag-script.lisp
+++ b/src/view/admin/components/tag-script.lisp
@@ -43,6 +43,7 @@
 
 				   (chain add-tag
 					  (add-event-listener "click" (lambda (e)
+									;; TODO: most of these .preventDefaults() could've been prevented by adding type="button" to all the <buttons>
 									(chain e (prevent-default))
 									(let* ((new-tag (chain
 											(prompt "New tag?")
diff --git a/src/view/admin/media-admin.lisp b/src/view/admin/media-admin.lisp
new file mode 100644
index 0000000..2b439c3
--- /dev/null
+++ b/src/view/admin/media-admin.lisp
@@ -0,0 +1,74 @@
+(defpackage murja.view.admin.media-admin
+  (:use :cl :binding-arrows :spinneret :ps :paren-async)
+  (:import-from :murja.media.media-db :select-referencing-posts*)
+  (:import-from :murja.view.components.tabs :deftab))
+
+(in-package :murja.view.admin.media-admin)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (named-readtables:in-readtable :murja.ps))
+
+(defun media-admin-script ()
+  (ps
+    (defun-async delete-selected (e)
+      (let* ((selected-nodes (chain document
+				    (query-selector-all ".marked-for-deletion:checked")))
+	     (delete-payload (create ids
+				     (chain Array
+					    (from selected-nodes)
+					    (map (lambda (node)
+						   (@ node dataset picid))))))
+	     (result (await 
+		      (fetch "/api/pictures"
+			     (create
+			      method "DELETE"
+			      body (chain JSON (stringify delete-payload))
+			      headers (create "Content-Type" "application/json"))))))
+	(if (= (@ result status) 204)
+	  (chain location (reload))
+	  (alert (+ "Error: " (@ result status) ": " (await (chain result (text))))))))
+    
+    (chain document
+	   (add-event-listener "DOMContentLoaded"
+			       (lambda (e)
+				 (let ((select-all-btn
+					 (chain document
+						(query-selector "#select-all")))
+				       (delete-selected-btn
+					 (chain document
+						(query-selector "#delete-selected"))))
+				   (chain delete-selected-btn
+					  (add-event-listener "click"
+							      #'delete-selected))
+				   (chain select-all-btn
+					  (add-event-listener "click"
+							      (lambda (e)
+								(dolist (element (chain document
+											(query-selector-all "input[type=checkbox]")))
+								  (setf (@ element checked) true)))))))))))
+					 
+
+
+(deftab blog/media-admin (:url "/blog/media-admin"
+			  :title "Manage media"
+			  :require-login t
+			  :needed-abilities ("create-post" "delete-post" "edit-post"))
+  (let ((all-pics (postmodern:query "SELECT id, name from blog.media")))
+    (:script (:raw (media-admin-script)))
+    (:div.mediamanager-header (:button :id "select-all" "Select all") (:button :id "delete-selected" "Delete selected images"))
+    (:ul.images
+     (dolist (pic all-pics)
+       (destructuring-bind (id name) pic
+	 (let ((referencing-posts (postmodern:query "SELECT post_id, post_title FROM blog.media_post_pairing WHERE media_id = $1" id)))
+	   
+	   (:li.image
+	    (:div.image-header (:label "Mark for deletion" (:input.marked-for-deletion :type :checkbox :data-picId id)))
+	    (:details (:summary name)
+		      (:img :src (format nil "/api/pictures/~a" id)))
+	    (:details (:summary ("Referencing posts (~d)" (length referencing-posts)))
+		      (if referencing-posts 
+			  (:ul 
+			   (dolist (referencer referencing-posts)
+			     (destructuring-bind (post-id post-title) referencer 
+			       (:li (:a :href (format nil "/blog/post/~a" post-id) post-title)))))
+			  (:div "No referencing posts"))))))))))