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
@@ -196,6 +196,30 @@ final class BackupTest extends TestCase
|
||||
self::assertSame([$names[1], $names[0]], $removed);
|
||||
}
|
||||
|
||||
public function testADamagedBackupNeitherCountsAsIntactNorPushesOutACompleteOne(): void
|
||||
{
|
||||
$alt = $this->backup->create('Test', new \DateTimeImmutable('2026-09-10 10:00:00', new \DateTimeZone('UTC')));
|
||||
$neu = $this->backup->create('Test', new \DateTimeImmutable('2026-09-11 10:00:00', new \DateTimeZone('UTC')));
|
||||
// Fehlt eine der gezählten Mediendateien, ist die Sicherung nicht mehr unversehrt.
|
||||
unlink($neu['path'].'/medien/bild-480.webp');
|
||||
// Ein abgebrochener Lauf von früher: ein Ordner ohne Verzeichnis, der neueste von allen.
|
||||
$halb = $this->backup->directory().'/sicherung-20260912-100000';
|
||||
mkdir($halb);
|
||||
touch($halb, time() - 7200);
|
||||
// Und einer von eben - das kann eine Sicherung sein, die gerade geschrieben wird.
|
||||
$laufend = $this->backup->directory().'/sicherung-20260913-100000';
|
||||
mkdir($laufend);
|
||||
touch($neu['path'], time() - 7200);
|
||||
|
||||
$zustand = array_column($this->backup->all(), 'intact', 'name');
|
||||
self::assertSame([basename($laufend) => false, basename($halb) => false, $neu['name'] => false, $alt['name'] => true], $zustand);
|
||||
|
||||
// Vorher zählten die beschädigten als die neuesten, und die vollständige flog raus.
|
||||
$entfernt = $this->backup->rotate(1);
|
||||
self::assertEqualsCanonicalizing([basename($halb), $neu['name']], $entfernt);
|
||||
self::assertSame([basename($laufend), $alt['name']], array_column($this->backup->all(), 'name'));
|
||||
}
|
||||
|
||||
public function testTwoBackupsInTheSameSecondGetTwoDirectories(): void
|
||||
{
|
||||
// This is not academic: a restore takes a safety copy, and that lands in the same second
|
||||
|
||||
Reference in new issue
Block a user