feat: add soft-delete functionality for library items with confirmation dialog
This commit is contained in:
+73
@@ -6035,6 +6035,79 @@ def delete_item(id):
|
||||
return redirect(url_for('home_admin'))
|
||||
|
||||
|
||||
@app.route('/delete_library_item/<id>', 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."""
|
||||
|
||||
@@ -720,6 +720,7 @@
|
||||
${actionLabel}
|
||||
</button>
|
||||
${canEditLibraryItems ? `<button class="button" style="background:#0ea5e9;color:#fff;" onclick="openEditLibraryItem('${item._id}')">Bearbeiten</button>` : ''}
|
||||
${canEditLibraryItems ? `<button class="button" style="background:#dc2626;color:#fff; margin-left:6px;" onclick="confirmDeleteLibraryItem('${item._id}')">Löschen</button>` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user