diff --git a/handlers.py b/handlers.py
index 6a03142..c3240ce 100644
--- a/handlers.py
+++ b/handlers.py
@@ -174,6 +174,11 @@ def _set_page_tags(page_id, tag_string, db=None):
return_db(db)
+def _cleanup_orphaned_tags(db):
+ """Delete tags that have no page associations."""
+ db.execute("DELETE FROM tags WHERE id NOT IN (SELECT DISTINCT tag_id FROM page_tags)")
+
+
# --- Route handlers ---
@@ -499,7 +504,8 @@ def handle_pages(query=None):
tag_links = " ".join(f'[{esc(t)}] ' for t in tags)
tags_html = f' {tag_links}'
items += (
- f'
{esc(r["title"])}{note_html}{tags_html} '
+ f' '
+ f'{esc(r["title"])} {note_html}{tags_html} '
f'({esc(r["url"])} ) '
f'edit '
f'remove '
@@ -509,13 +515,71 @@ def handle_pages(query=None):
return _respond(
f"indexed pages ({total}) "
f"{msg_html}"
+ f''
+ f''
f'export | import
'
f'back '
)
+def handle_bulk_action(body):
+ ids = body.get("ids", [])
+ action = body.get("action", [""])[0]
+ if not ids:
+ return _redirect("/pages")
+ # Validate all ids are integers
+ try:
+ page_ids = [int(i) for i in ids]
+ except ValueError:
+ return _error(400)
+ db = get_db()
+ try:
+ if action == "delete":
+ for pid in page_ids:
+ db.execute("DELETE FROM page_tags WHERE page_id = ?", (pid,))
+ db.execute("DELETE FROM links WHERE page_id = ?", (pid,))
+ db.execute("DELETE FROM pages WHERE id = ?", (pid,))
+ _cleanup_orphaned_tags(db)
+ db.commit()
+ elif action == "retag":
+ bulk_tags = body.get("bulk_tags", [""])[0].strip()
+ tag_mode = body.get("tag_mode", ["add"])[0]
+ if bulk_tags:
+ for pid in page_ids:
+ if tag_mode == "add":
+ existing = _get_page_tags(pid, db)
+ new_tags = [t.strip().lower() for t in bulk_tags.split(",") if t.strip()]
+ merged = ", ".join(sorted(set(existing + new_tags)))
+ _set_page_tags(pid, merged, db)
+ else:
+ _set_page_tags(pid, bulk_tags, db)
+ _cleanup_orphaned_tags(db)
+ db.commit()
+ finally:
+ return_db(db)
+ return _redirect("/pages")
+
+
def handle_edit_form(page_id, msg=""):
db = get_db()
try:
@@ -561,6 +625,7 @@ def handle_edit_submit(page_id, body):
)
_set_page_tags(page_id, tags, db)
+ _cleanup_orphaned_tags(db)
db.commit()
@@ -596,6 +661,7 @@ def handle_delete(page_id):
db.execute("DELETE FROM page_tags WHERE page_id = ?", (page_id,))
db.execute("DELETE FROM links WHERE page_id = ?", (page_id,))
db.execute("DELETE FROM pages WHERE id = ?", (page_id,))
+ _cleanup_orphaned_tags(db)
db.commit()
finally:
return_db(db)
@@ -1344,6 +1410,8 @@ def _dispatch_inner(data):
return _respond("403 Forbidden Invalid or missing CSRF token.
", status=403)
if path == "/add":
return handle_add_submit(body)
+ elif path == "/pages/bulk":
+ return handle_bulk_action(body)
elif path == "/add/manual":
return handle_add_manual_submit(body)
elif path.startswith("/edit/"):