diff of 628f660967ce5b900fb8bb8f3d30d2c1539c7ec2

628f660967ce5b900fb8bb8f3d30d2c1539c7ec2
diff --git a/playwright-tests/tests/basic-tests.spec.ts b/playwright-tests/tests/basic-tests.spec.ts
index 473a5c9..43b28f5 100644
--- a/playwright-tests/tests/basic-tests.spec.ts
+++ b/playwright-tests/tests/basic-tests.spec.ts
@@ -217,5 +217,31 @@ test('basic testing', async ({ page, browser }) => {
 
     // test images
     await postPost(page, 'Image post', 'this is image content', tag, true);
+    await page.goto('http://localhost:3010');
+    await postPost(page, 'Image post2', 'this is image content2', tag, true);
+
+    // test media manager
+    await page.getByText('Manage media').click();
+    await expect(page.locator('details > img')).toHaveCount(2);
+
+    for(let cb in await page.locator('input[type="checkbox"]').all) {	
+	await expect(cb).not.toBeChecked();
+    }
+    await page.getByText('Select all').click();
+
+    for(let cb in await page.locator('input[type="checkbox"]').all) {	
+	await expect(cb).toBeChecked();
+    }
+
+    await page.evaluate(() => document.querySelectorAll('input[type="checkbox"]').forEach(input => input.checked = false));
+
+    let checkbox_labels = await page.getByText('Choose for deletion').all();
+    await checkbox_labels[0].click();
+    await page.getByText('Remove selected').click();
+    await expect(page.locator('details > img')).toHaveCount(1);
+
+    await page.getByText('Select all').click();
+    await page.getByText('Remove selected').click();
+    await expect(page.locator('details > img')).toHaveCount(0);
 });;
 
diff --git a/resources/sql/media-fns.sql b/resources/sql/media-fns.sql
index 47b7a22..69599e0 100644
--- a/resources/sql/media-fns.sql
+++ b/resources/sql/media-fns.sql
@@ -11,7 +11,7 @@ select id, name from blog.media;
 
 -- name: delete-picture* :!
 -- returns: :array-hash
-delete from blog.media where id = $1ยง;
+delete from blog.media where id = $1;
 
 -- name: select-referencing-posts* :?
 -- returns: :array-hash
diff --git a/src/routes/media-routes.lisp b/src/routes/media-routes.lisp
index bf3fe9a..9213d63 100644
--- a/src/routes/media-routes.lisp
+++ b/src/routes/media-routes.lisp
@@ -1,9 +1,9 @@
 (defpackage murja.routes.media-routes
   (:use :cl)
   (:import-from :lisp-fixup :slurp-bytes)
-  (:import-from :com.inuoe.jzon :stringify)
+  (:import-from :com.inuoe.jzon :stringify :parse)
   (:import-from :murja.middleware.db :@transaction)
-  (:import-from :murja.media.media-db :list-pictures :insert-media :select-referencing-posts* :get-media)
+  (:import-from :murja.media.media-db :delete-picture* :list-pictures :insert-media :select-referencing-posts* :get-media)
    
   (:import-from :murja.middleware.json :@json)
   (:import-from :murja.middleware.auth :@authenticated :@can? :*user*)
@@ -52,3 +52,16 @@
     (setf (hunchentoot:header-out "Content-Disposition")
 	  (format nil "inline; filename=~a" (gethash "name" pic)))
     (gethash "data" pic)))
+
+(defroute delete-pic ("/api/pictures" :method :delete
+				      :decorators (@transaction
+						   @authenticated
+						   @json
+						   (@can? "create-post"))) ()
+  (let* ((request-body (parse (hunchentoot:raw-post-data :force-text t)))
+	 (ids (coerce (gethash "ids" request-body) 'list)))
+    (log:info "Deleting pictures from ids ~a~%" ids)
+    (dolist (id ids)
+      (delete-picture* id))
+    (setf (hunchentoot:return-code*) 204)
+    ""))