From 74fa0d8a687f2744b532c97722d21d5779335133 Mon Sep 17 00:00:00 2001 From: Aiirondev Date: Thu, 28 May 2026 18:51:55 +0200 Subject: [PATCH] feat: add soft-delete functionality for library items with confirmation dialog --- Web/app.py | 73 ++++++++++++++++++++++++++++++++ Web/templates/library_table.html | 41 ++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/Web/app.py b/Web/app.py index f859d86..c57c922 100755 --- a/Web/app.py +++ b/Web/app.py @@ -6035,6 +6035,79 @@ def delete_item(id): return redirect(url_for('home_admin')) +@app.route('/delete_library_item/', methods=['POST']) +def delete_library_item(id): + """ + Route to soft-delete library items (books/media) revisionssicher. + Requires admin and library module enabled. + """ + if 'username' not in session: + flash('Nicht angemeldet.', 'error') + return redirect(url_for('login')) + if not us.check_admin(session['username']): + flash('Administratorrechte erforderlich.', 'error') + return redirect(url_for('login')) + if not cfg.MODULES.is_enabled('library'): + flash('Bibliotheks-Modul ist deaktiviert.', 'error') + return redirect(url_for('home_admin')) + + # Verify item exists and is a library item + try: + item_doc = it.get_item(id) + except Exception: + item_doc = None + + if not item_doc: + flash('Element nicht gefunden.', 'error') + return redirect(url_for('library_admin')) + + if item_doc.get('ItemType') not in LIBRARY_ITEM_TYPES: + flash('Element ist kein Bibliotheksmedium.', 'error') + return redirect(url_for('library_admin')) + + group_item_ids = it.get_group_item_ids(id) + if not group_item_ids: + flash('Element nicht gefunden.', 'error') + return redirect(url_for('library_admin')) + + client = None + try: + client = MongoClient(MONGODB_HOST, MONGODB_PORT) + db = client[MONGODB_DB] + result = _soft_delete_item_groups(db, [id], session.get('username', '')) + success = bool(result.get('success')) + soft_deleted = int(result.get('soft_deleted_items', 0)) + archived_files = int((result.get('archive') or {}).get('archived_files', 0)) + + try: + _append_audit_event_standalone( + event_type='library_item_deleted', + payload={ + 'root_item_id': id, + 'group_item_ids': result.get('group_item_ids', []), + 'soft_deleted_items': soft_deleted, + 'archived_files': archived_files, + } + ) + except Exception: + app.logger.warning('Audit write failed for library_item_deleted') + + except Exception as e: + app.logger.error(f"Error deleting library item {id}: {e}") + success = False + archived_files = 0 + finally: + if client: + client.close() + + if success: + flash(f'Bibliotheksmedium revisionssicher deaktiviert ({soft_deleted} Versionen, {archived_files} Mediendateien archiviert).', 'success') + else: + flash('Fehler beim Löschen des Bibliotheksmediums.', 'error') + + return redirect(url_for('library_admin')) + + @app.route('/bulk_delete_items', methods=['POST']) def bulk_delete_items(): """Soft-delete multiple selected item groups in one request.""" diff --git a/Web/templates/library_table.html b/Web/templates/library_table.html index 7db390a..efdb90a 100644 --- a/Web/templates/library_table.html +++ b/Web/templates/library_table.html @@ -720,6 +720,7 @@ ${actionLabel} ${canEditLibraryItems ? `` : ''} + ${canEditLibraryItems ? `` : ''} `; @@ -739,6 +740,46 @@ } } + // Confirm and call library item delete endpoint + function confirmDeleteLibraryItem(itemId) { + if (!itemId) return; + if (!confirm('Dieses Bibliotheksmedium revisionssicher löschen? Diese Aktion deaktiviert alle zugehörigen Exemplare und archiviert Medien.')) return; + deleteLibraryItem(itemId); + } + + async function deleteLibraryItem(itemId) { + if (!itemId) return; + document.body.style.cursor = 'wait'; + try { + const resp = await fetch(`/delete_library_item/${itemId}`, { + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}' + }, + credentials: 'same-origin' + }); + + const ct = resp.headers.get('Content-Type') || ''; + if (ct.includes('application/json')) { + const j = await resp.json(); + if (!resp.ok || j.ok === false) { + alert(j.message || 'Fehler beim Löschen'); + } else { + alert(j.message || 'Gelöscht'); + } + loadLibraryItems(); + } else { + // Server redirected to HTML (flash messages). Reload to show state. + location.reload(); + } + } catch (e) { + console.error('Delete failed', e); + alert('Fehler beim Löschen des Elements.'); + } finally { + document.body.style.cursor = ''; + } + } + // Filter toggle document.getElementById('filterToggleBtn').addEventListener('click', () => { filterPanelOpen = !filterPanelOpen;