Direktbearbeitung, Kontrastwarnung und Gestaltungsvorlagen
Inline-Textbearbeitung ohne Skript in der Vorschau: der Iframe führt weiterhin kein eigenes JavaScript aus, der Editor greift in sein Dokument und setzt das Element auf contenteditable. Während der Bearbeitung wird die Vorschau nicht neu geladen, sonst wäre der Cursor weg; Eingaben werden zu einem Undo-Schritt zusammengefasst. Neben dem Doppelklick startet eine Schaltfläche dasselbe ohne Maus. Kontrast wird nach WCAG 2.1 berechnet und als Warnung in drei Stufen gemeldet, nicht als Verbot - der Plan verlangt Prüfung, nicht Bevormundung. Gestaltungsvorlagen liegen im Theme und gelten seitenübergreifend. Sie werden mit demselben Compiler geprüft wie ein Blockstil, also kann über diesen Weg kein Wert hereinkommen, für den ein Block abgewiesen würde. Dazu Kopieren und Einfügen zwischen Blöcken und Bildauswahl für Hintergrundbilder über die Mediathek. Geprüft: 155 PHP-Tests / 579 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, Lint/Format/Container. Erstmals wurde der neue Editor im laufenden Betrieb bedient: Direktbearbeitung und Kontrastwarnung mit echten Werten ausgelöst, Teständerungen verworfen. Weiterhin keine axe- oder Lighthouse-Messung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
abf0bd2c6b
commit
270cbda9ac
11 files changed
+388
-10
No files matched your search
@@ -122,7 +122,7 @@ final class EditorSecurityTest extends WebTestCase
|
||||
$this->post($client, $action, []);
|
||||
self::assertResponseRedirects('http://localhost/admin/login');
|
||||
}
|
||||
foreach ([['/admin/pages', ['title' => 'Geschmuggelt', 'slug' => 'geschmuggelt']], ['/admin/pages/p1/delete', ['expectedVersion' => 2]], ['/admin/menus/header', ['name' => 'X', 'expectedVersion' => 0]]] as [$url, $body]) {
|
||||
foreach ([['/admin/pages', ['title' => 'Geschmuggelt', 'slug' => 'geschmuggelt']], ['/admin/pages/p1/delete', ['expectedVersion' => 2]], ['/admin/menus/header', ['name' => 'X', 'expectedVersion' => 0]], ['/admin/api/style-presets', ['name' => 'X']]] as [$url, $body]) {
|
||||
$client->request('POST', $url, $body);
|
||||
self::assertResponseRedirects('http://localhost/admin/login');
|
||||
}
|
||||
@@ -403,6 +403,41 @@ final class EditorSecurityTest extends WebTestCase
|
||||
self::assertSame('Kontakt', $this->db->fetchOne('SELECT name FROM form'));
|
||||
}
|
||||
|
||||
public function testStylePresetsNeedAnEditorRoleACsrfTokenAndAValidStyle(): void
|
||||
{
|
||||
$client = $this->login('editor');
|
||||
$client->request('GET', '/admin/api/design');
|
||||
self::assertResponseIsSuccessful();
|
||||
$design = json_decode($client->getResponse()->getContent(), true);
|
||||
// The editor receives the vocabulary, the palette and the presets from one place.
|
||||
self::assertArrayHasKey('fontSize', $design['properties']);
|
||||
self::assertSame([], $design['presets']);
|
||||
$token = $design['csrfToken'];
|
||||
|
||||
$post = function (array $body, string $csrf) use ($client): void {
|
||||
$client->request('POST', '/admin/api/style-presets', server: ['CONTENT_TYPE' => 'application/json', 'HTTP_X_CSRF_TOKEN' => $csrf], content: json_encode($body));
|
||||
};
|
||||
$post(['name' => 'Heller Abschnitt', 'style' => ['desktop' => ['fontSize' => '32px']]], 'wrong');
|
||||
self::assertResponseStatusCodeSame(403);
|
||||
|
||||
// A style the documents would refuse cannot arrive through a preset either.
|
||||
$post(['name' => 'Boese', 'style' => ['desktop' => ['fontSize' => '32px;position:fixed']]], $token);
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
$post(['name' => ' ', 'style' => ['desktop' => ['fontSize' => '32px']]], $token);
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
self::assertStringNotContainsString('position:fixed', (string) $this->db->fetchOne('SELECT theme FROM website WHERE id = ?', ['default']));
|
||||
|
||||
$post(['name' => 'Heller Abschnitt', 'style' => ['desktop' => ['fontSize' => '32px', 'padding' => ['top' => '40px']]]], $token);
|
||||
self::assertResponseIsSuccessful();
|
||||
$presets = json_decode($client->getResponse()->getContent(), true)['presets'];
|
||||
self::assertSame('heller-abschnitt', $presets[0]['key']);
|
||||
self::assertSame('32px', $presets[0]['style']['desktop']['fontSize']);
|
||||
|
||||
// Removing works by sending the same name without a style.
|
||||
$post(['name' => 'Heller Abschnitt', 'style' => null], $token);
|
||||
self::assertSame([], json_decode($client->getResponse()->getContent(), true)['presets']);
|
||||
}
|
||||
|
||||
public function testMediaUploadReencodingCsrfMetadataConflictAndArchive(): void
|
||||
{
|
||||
$client = $this->login('editor');
|
||||
|
||||
Reference in new issue
Block a user