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:
1 parent
0d61aa0f22
commit
ead47cc924
25 files changed
+437
-72
No files matched your search
@@ -23,6 +23,7 @@
|
||||
{%- endfor %}
|
||||
}}</script>
|
||||
<script src="/assets/color-field.js?v={{ kant_version() }}" defer></script>
|
||||
<script src="/assets/confirm.js?v={{ kant_version() }}" defer></script>
|
||||
{% block styles %}{% endblock %}
|
||||
{% block scripts %}{% endblock %}
|
||||
{# Eigener Block, nicht Teil von `scripts`: Bildschirme wie die Mediathek ersetzen `scripts`
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
{# The class is what the security test looks for: an editor must not even be
|
||||
offered this. #}
|
||||
<div class="delete-panel">
|
||||
<form action="{{ path('admin_delete', {id: page.id}) }}" method="post">
|
||||
<form action="{{ path('admin_delete', {id: page.id}) }}" method="post" data-confirm-title="„{{ page.title }}“ endgültig löschen?" data-confirm-text="Die Seite wird mit allen Fassungen gelöscht. Das lässt sich nicht rückgängig machen." data-confirm-label="Endgültig löschen" data-confirm-variant="danger">
|
||||
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
|
||||
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
{% if mail.protokoll is empty %}
|
||||
<span class="muted">var/log/mail.log</span>
|
||||
{% else %}
|
||||
<form action="{{ path('admin_mail_clear') }}" method="post">
|
||||
<form action="{{ path('admin_mail_clear') }}" method="post" data-confirm-title="Versandprotokoll leeren?" data-confirm-text="Alle Einträge in var/log/mail.log werden entfernt." data-confirm-label="Protokoll leeren" data-confirm-variant="danger">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('system') }}">
|
||||
<button class="text-button" type="submit">Protokoll leeren</button>
|
||||
</form>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<div class="sheet-part">
|
||||
<h4>Entfernen</h4>
|
||||
<p>Entfernt „{{ partial.name }}“.{% if partial.usedBy is not empty %} <strong>Noch eingebunden in {{ partial.usedBy|join(', ') }}</strong> – dort erscheint danach nichts mehr.{% endif %}</p>
|
||||
<form action="{{ path('admin_partial_delete', {id: partial.id}) }}" method="post">
|
||||
<form action="{{ path('admin_partial_delete', {id: partial.id}) }}" method="post" data-confirm-title="Sektion „{{ partial.name }}“ entfernen?" data-confirm-text="{% if partial.usedBy is not empty %}Sie ist noch eingebunden in {{ partial.usedBy|join(', ') }} – dort erscheint danach nichts mehr. {% endif %}Das lässt sich nicht rückgängig machen." data-confirm-label="Sektion entfernen" data-confirm-variant="danger">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('partials') }}">
|
||||
<button class="subtle danger-text" type="submit">Sektion entfernen</button>
|
||||
</form>
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<form action="{{ path('admin_settings_save') }}" method="post" class="card card-body">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||||
<input type="hidden" name="expected" value="{{ settingsStamp }}">
|
||||
<div class="fields">
|
||||
<div>
|
||||
<label for="legalName">Name des Unternehmens</label>
|
||||
@@ -390,7 +391,7 @@
|
||||
<td>{% if entry.to %}<code>/{{ entry.to }}</code>{% else %}<span class="muted">—</span>{% endif %}</td>
|
||||
<td>{{ entry.status }}</td>
|
||||
<td>
|
||||
<form action="{{ path('admin_redirect_save') }}" method="post">
|
||||
<form action="{{ path('admin_redirect_save') }}" method="post" data-confirm-title="Weiterleitung von /{{ entry.from }} entfernen?" data-confirm-text="Die alte Adresse antwortet danach mit „nicht gefunden“." data-confirm-label="Entfernen" data-confirm-variant="danger">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||||
<input type="hidden" name="remove" value="{{ entry.id }}">
|
||||
<button class="text-button danger-text" type="submit" aria-label="Weiterleitung von /{{ entry.from }} entfernen">Entfernen</button>
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
{% set emptyFont = {key: '', name: '', family: '', source: 'google', fallback: 'sans-serif', weights: [400, 700]} %}
|
||||
<form action="{{ path('admin_theme_save') }}" method="post" class="theme-form stack" data-theme-editor data-palettes="{{ palettes|json_encode|e('html_attr') }}" data-color-names="{{ colorNames|json_encode|e('html_attr') }}">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('theme') }}">
|
||||
<input type="hidden" name="expected" value="{{ themeStamp }}">
|
||||
|
||||
<section class="card">
|
||||
<div class="card-head">
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<div class="sheet-part">
|
||||
<h4>Konto löschen</h4>
|
||||
<p>Entfernt „{{ account.username }}“. Gespeicherte Inhalte bleiben unberührt – Seiten und Fassungen hängen nicht am Konto. Das eigene Konto lässt sich nicht löschen; deshalb bleibt immer mindestens ein Administrator übrig.</p>
|
||||
<form action="{{ path('admin_user_delete', {id: account.id}) }}" method="post">
|
||||
<form action="{{ path('admin_user_delete', {id: account.id}) }}" method="post" data-confirm-title="Konto „{{ account.username }}“ löschen?" data-confirm-text="Die Person kann sich danach nicht mehr anmelden. Seiten und Fassungen bleiben." data-confirm-label="Konto löschen" data-confirm-variant="danger">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('users') }}">
|
||||
<button class="subtle danger-text" type="submit">Konto löschen</button>
|
||||
</form>
|
||||
|
||||
Reference in new issue
Block a user