Keep old pages readable, detect stale settings writes, make backups honest

- Own CSS is parsed on write only. A page whose stored CSS no longer
  passes a stricter rule answers 200 and opens in the editor; that one
  block loses its CSS. A document read several times in one request is
  validated once.
- Settings and theme refuse a write from an outdated form.
- A backup fails when a media file does not arrive completely and
  leaves no half directory behind; rotation counts intact backups only.
- The archive update takes its backup behind the maintenance page.
- A package import puts all media in place before removing the old
  ones, reports honestly when that fails and keeps a maintenance state
  that was already on.
- Restoring replaces the SQLite file in one move and points at pending
  migrations.
- Six irreversible actions ask first, in a dialog of the admin's own.
- Ctrl+S in the editor takes the field being typed in along; Delete
  no longer removes a block while a dialog is open.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Fable 5.2 committed 2026-11-10 11:41:05 +01:00
1 parent 0d61aa0f22
commit ead47cc924
25 files changed
+437 -72

No files matched your search

+14 -2
View File
@@ -1920,7 +1920,15 @@ function onKey(event) {
if (!history || busy) return;
const key = event.key.toLowerCase();
if (event.ctrlKey || event.metaKey) {
if (key === 's') { event.preventDefault(); save(); }
if (key === 's') {
event.preventDefault();
// Viele Felder schreiben erst beim Verlassen ins Dokument (eigenes CSS, Zahlen im Design).
// Ohne diesen Schritt sicherte Strg+S den Stand ohne das, was gerade getippt wurde - und
// meldete trotzdem „gespeichert“.
const field = document.activeElement;
if (field && field.ownerDocument === document && inField(field) && !field.isContentEditable) field.blur();
save();
}
// Keep native text-field undo intact; the toolbar always undoes the complete document.
if (inField(event.target)) return;
if (key === 'z' || key === 'y') { event.preventDefault(); (event.shiftKey || key === 'y') ? history.redo() : history.undo(); changed(); }
@@ -1935,7 +1943,11 @@ function onKey(event) {
}
// Delete removes the selected element - but never while a field or the preview text is
// being typed in, and never something the sidebar buttons would refuse.
if ((event.key === 'Delete' || event.key === 'Backspace') && selected && !inField(event.target) && !inlineEdit && !$('#delete').disabled) {
// Auch nicht, während ein Dialog offen ist oder der Fokus auf einem Bedienelement der
// Seitenleiste liegt: Entf auf einer Kachel der Mediathek löschte sonst den gewählten Baustein.
const where = event.target;
const onStage = !where?.closest || where.ownerDocument !== document || where === document.body || Boolean(where.closest('#structure, .canvas-scroll'));
if ((event.key === 'Delete' || event.key === 'Backspace') && selected && onStage && !document.querySelector('dialog[open]') && !inField(event.target) && !inlineEdit && !$('#delete').disabled) {
event.preventDefault(); actions.delete(selected);
}
}