Make the entity repair a console command, not a script
scripts/ is on the exclusion list of build-release.php, so an installation that runs from the release archive - the usual case on shared hosting - would never have seen scripts/repair-doubled-entities.php. The 1.20.1 notes told everyone to run it, which was only true for a Git working copy. cms:repair:entities does the same work and ships with the application: a dry run by default, --schreiben to apply, each change validated the way saving validates it (pages with the heading rule, areas without, as in PartialRepository). The script is gone; there is one way to do this now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d08d6f648a
commit
f4ec0d9877
5 files changed
+262
-130
No files matched your search
@@ -6,7 +6,7 @@ Die übernommenen Originaldokumente sind `AGENTS.md` und `ENTWICKLUNGSPLAN.md`.
|
||||
|
||||
## Kaufmännisches Und und Zeilenumbrüche bleiben, wie sie sind (ab 1.20.1)
|
||||
|
||||
**Behoben.** Ein Text ohne Auszeichnung wurde beim Speichern doppelt kodiert: aus `&` wurde `&`, beim nächsten Speichern `&amp;` – irgendwann stand die Entity sichtbar auf der Seite. Ebenso wuchsen Zeilenumbrüche: jedes zweite Speichern machte aus einem `<br>` zwei. Beides ist behoben, und wiederholtes Speichern ändert einen Text jetzt nicht mehr. Was bereits falsch gespeichert ist, räumt `php scripts/repair-doubled-entities.php` auf (erst Trockenlauf, dann `--schreiben`).
|
||||
**Behoben.** Ein Text ohne Auszeichnung wurde beim Speichern doppelt kodiert: aus `&` wurde `&`, beim nächsten Speichern `&amp;` – irgendwann stand die Entity sichtbar auf der Seite. Ebenso wuchsen Zeilenumbrüche: jedes zweite Speichern machte aus einem `<br>` zwei. Beides ist behoben, und wiederholtes Speichern ändert einen Text jetzt nicht mehr. Was bereits falsch gespeichert ist, räumt `php bin/console cms:repair:entities` auf: ohne Option zeigt er nur, was sich ändern würde, mit `--schreiben` wendet er es an. Angefasst werden die veröffentlichte Fassung und der Entwurf jeder Seite sowie die Bereiche; ältere Fassungen bleiben Historie.
|
||||
|
||||
## Eigenes HTML und eingebettete Seiten (ab 1.20.0)
|
||||
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@
|
||||
|
||||
**Behoben.** Der Zweig dekodiert jetzt zuerst (`html_entity_decode`) und escaped danach, und er ersetzt den Umbruch, statt ihn zu ergänzen. Damit endet er bei derselben Darstellung wie der DOM-Zweig, und beide sind unveränderlich gegenüber wiederholtem Speichern.
|
||||
|
||||
**Bestand.** `scripts/repair-doubled-entities.php` räumt auf, was schon gespeichert ist: veröffentlichte Fassung und Entwurf jeder Seite sowie die Bereiche, mit Trockenlauf und `--schreiben`, jede Änderung gegen den Validator geprüft. Ältere Fassungen bleiben Historie. Auf DEEG angewandt (eine Stelle im Entwurf der Startseite). **Offen: kant-it.de** – dort stehen zwei dreifach kodierte Stellen im Entwurf der Startseite; das Skript gehört nach dem Update dort einmal ausgeführt, sonst steht der Murks beim nächsten Veröffentlichen live.
|
||||
**Bestand.** Der Konsolenbefehl `bin/console cms:repair:entities` räumt auf, was schon gespeichert ist: veröffentlichte Fassung und Entwurf jeder Seite sowie die Bereiche, mit Trockenlauf und `--schreiben`, jede Änderung gegen den Validator geprüft (Seiten wie beim Speichern, Bereiche ohne Überschriftspflicht). Als Befehl und nicht als Skript unter `scripts/`, weil dieser Ordner nicht ins Releasearchiv geht – eine Installation ohne Git hätte das Skript sonst nie gesehen. Ältere Fassungen bleiben Historie. Auf DEEG angewandt (eine Stelle im Entwurf der Startseite). **Offen: kant-it.de** – dort stehen zwei dreifach kodierte Stellen im Entwurf der Startseite; das Skript gehört nach dem Update dort einmal ausgeführt, sonst steht der Murks beim nächsten Veröffentlichen live.
|
||||
|
||||
**Geprüft:** neuer Test in `tests/CmsTest.php`: sechs Eingaben (Entity, nacktes Und, `<`, Zeilenumbruch, Markup, wörtliche Tags) dreimal hintereinander durch `sanitise()` – jedes Mal dasselbe Ergebnis; dazu eine Seite, die dreimal gespeichert und veröffentlicht wird und danach immer noch ein einziges `&` trägt.
|
||||
**Geprüft:** `tests/RepairEntitiesCommandTest.php` (Trockenlauf ändert nichts; Schreiben entschachtelt drei Stufen und lässt ein richtiges `&` stehen; ein zweiter Lauf findet nichts mehr) und ein neuer Test in `tests/CmsTest.php`: sechs Eingaben (Entity, nacktes Und, `<`, Zeilenumbruch, Markup, wörtliche Tags) dreimal hintereinander durch `sanitise()` – jedes Mal dasselbe Ergebnis; dazu eine Seite, die dreimal gespeichert und veröffentlicht wird und danach immer noch ein einziges `&` trägt.
|
||||
|
||||
**Nebenbei (DEEG):** Das Favicon zeigte den mittigen Ausschnitt des Logos, also „EE“ aus dem Schriftzug. Neu ist ein eigenes quadratisches Zeichen: der Dachgiebel aus dem Logo, weiß auf Logo-Blau, gerendert aus `logo-deeg.png` von `scripts/set-immobilien-deeg-favicon.php`. Vier Entwürfe standen zur Wahl (Doppeldach, Giebel, Dach blau auf weiß, Buchstabe D), Umut hat den Giebel gewählt.
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Repariert Texte, die durch das doppelte Kodieren in `RichText::sanitise()` verschachtelt wurden.
|
||||
*
|
||||
* Bis 1.20.0 hat der Zweig für Text ohne Tags die Eingabe nur escaped, statt sie vorher zu
|
||||
* dekodieren. Aus „Freudenstadt & Umgebung“ wurde bei jedem Speichern eine Stufe mehr
|
||||
* (`&amp;`, `&amp;amp;` …), bis die Entity sichtbar auf der Seite stand. Der Fehler ist
|
||||
* behoben; dieses Skript räumt auf, was schon gespeichert ist.
|
||||
*
|
||||
* Angefasst werden die Dokumente, die tatsächlich in Gebrauch sind: die veröffentlichte und die
|
||||
* Entwurfsfassung jeder Seite sowie die Bereiche. Ältere Fassungen bleiben, wie sie sind – sie
|
||||
* sind Historie; wer eine davon zurückholt, speichert sie danach einmal, und der reparierte Kern
|
||||
* schreibt sie richtig.
|
||||
*
|
||||
* Aufruf: php scripts/repair-doubled-entities.php [--schreiben]
|
||||
* Ohne `--schreiben` wird nur gezeigt, was sich ändern würde.
|
||||
*/
|
||||
require dirname(__DIR__).'/config/bootstrap.php';
|
||||
|
||||
use App\Content\DocumentValidator;
|
||||
use App\Infrastructure\ConnectionFactory;
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
$schreiben = in_array('--schreiben', $argv, true);
|
||||
$url = str_replace('%kernel.project_dir%', str_replace('\\', '/', $root), (string) $_SERVER['DATABASE_URL']);
|
||||
$db = ConnectionFactory::create($url);
|
||||
$validator = new DocumentValidator();
|
||||
|
||||
/** Entschachtelt eine doppelt kodierte Entity, so oft wie nötig: &amp;amp; wird zu &. */
|
||||
$entschachteln = static function (string $text): string {
|
||||
$vorher = null;
|
||||
// Nur eine Entity hinter einem & ist ein Fehler - ein einzelnes & ist richtig.
|
||||
while ($vorher !== $text) {
|
||||
$vorher = $text;
|
||||
$text = preg_replace('/&(amp|lt|gt|quot|apos|nbsp|#\d{1,6}|#x[0-9a-fA-F]{1,6});/', '&$1;', $text) ?? $text;
|
||||
}
|
||||
|
||||
return $text;
|
||||
};
|
||||
|
||||
/** Läuft durch die Bausteine und korrigiert die Texte; zählt die Änderungen. */
|
||||
$bausteine = function (array $liste, int &$zahl, array &$beispiele) use (&$bausteine, $entschachteln): array {
|
||||
foreach ($liste as $index => $block) {
|
||||
foreach (['text', 'caption', 'label', 'title', 'hint', 'provider'] as $feld) {
|
||||
$wert = $block['props'][$feld] ?? null;
|
||||
if (!is_string($wert) || '' === $wert) {
|
||||
continue;
|
||||
}
|
||||
$neu = $entschachteln($wert);
|
||||
if ($neu !== $wert) {
|
||||
$liste[$index]['props'][$feld] = $neu;
|
||||
++$zahl;
|
||||
if (count($beispiele) < 6) {
|
||||
$beispiele[] = mb_substr($wert, 0, 60).' → '.mb_substr($neu, 0, 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($block['children']) && is_array($block['children'])) {
|
||||
$liste[$index]['children'] = $bausteine($block['children'], $zahl, $beispiele);
|
||||
}
|
||||
}
|
||||
|
||||
return $liste;
|
||||
};
|
||||
|
||||
$gesamt = 0;
|
||||
$berichte = [];
|
||||
|
||||
// Die aktiven Fassungen jeder Seite: veröffentlicht und Entwurf.
|
||||
$aktive = [];
|
||||
foreach ($db->fetchAllAssociative('SELECT id, path, published_revision_id, draft_revision_id FROM page') as $seite) {
|
||||
foreach (['published_revision_id' => 'veröffentlicht', 'draft_revision_id' => 'Entwurf'] as $spalte => $was) {
|
||||
if (!empty($seite[$spalte])) {
|
||||
$aktive[(string) $seite[$spalte]] = '/'.$seite['path'].' ('.$was.')';
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($aktive as $revision => $bezeichnung) {
|
||||
$roh = $db->fetchOne('SELECT document FROM revision WHERE id = ?', [$revision]);
|
||||
if (!is_string($roh)) {
|
||||
continue;
|
||||
}
|
||||
$dokument = json_decode($roh, true, 512, JSON_THROW_ON_ERROR);
|
||||
$zahl = 0;
|
||||
$beispiele = [];
|
||||
$dokument['blocks'] = $bausteine($dokument['blocks'] ?? [], $zahl, $beispiele);
|
||||
if (0 === $zahl) {
|
||||
continue;
|
||||
}
|
||||
$gesamt += $zahl;
|
||||
$berichte[] = ['was' => $bezeichnung, 'zahl' => $zahl, 'beispiele' => $beispiele];
|
||||
if ($schreiben) {
|
||||
// Geprüft wird wie beim Speichern - eine Korrektur darf kein ungültiges Dokument hinterlassen.
|
||||
$validator->validate($dokument, writing: true);
|
||||
$db->executeStatement('UPDATE revision SET document = ? WHERE id = ?', [json_encode($dokument, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), $revision]);
|
||||
}
|
||||
}
|
||||
|
||||
// Und die Bereiche (Kopf, Fuß, verknüpfte Sektionen).
|
||||
foreach ($db->fetchAllAssociative('SELECT id, kind, name, document FROM partial') as $bereich) {
|
||||
$dokument = json_decode((string) $bereich['document'], true, 512, JSON_THROW_ON_ERROR);
|
||||
$zahl = 0;
|
||||
$beispiele = [];
|
||||
$dokument['blocks'] = $bausteine($dokument['blocks'] ?? [], $zahl, $beispiele);
|
||||
if (0 === $zahl) {
|
||||
continue;
|
||||
}
|
||||
$gesamt += $zahl;
|
||||
$berichte[] = ['was' => 'Bereich '.$bereich['kind'].' „'.$bereich['name'].'“', 'zahl' => $zahl, 'beispiele' => $beispiele];
|
||||
if ($schreiben) {
|
||||
$validator->validate($dokument, writing: true);
|
||||
$db->executeStatement('UPDATE partial SET document = ? WHERE id = ?', [json_encode($dokument, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), $bereich['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($berichte as $bericht) {
|
||||
echo $bericht['was'], ': ', $bericht['zahl'], ' Stelle(n)', "\n";
|
||||
foreach ($bericht['beispiele'] as $beispiel) {
|
||||
echo ' ', $beispiel, "\n";
|
||||
}
|
||||
}
|
||||
if (0 === $gesamt) {
|
||||
echo "Nichts zu reparieren.\n";
|
||||
exit(0);
|
||||
}
|
||||
echo $schreiben ? "$gesamt Stelle(n) repariert.\n" : "$gesamt Stelle(n) gefunden. Mit --schreiben anwenden.\n";
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Content\DocumentValidator;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Räumt Texte auf, die vor 1.20.1 bei jedem Speichern eine Kodierungsstufe dazubekommen haben.
|
||||
*
|
||||
* `RichText::sanitise()` hat Text ohne ein einziges Tag nur escaped, statt ihn vorher zu
|
||||
* dekodieren. Aus „Freudenstadt & Umgebung“ wurde deshalb `&amp;`, dann `&amp;amp;`
|
||||
* – bis die Entity sichtbar auf der Seite stand. Der Fehler ist behoben; dieser Befehl bringt in
|
||||
* Ordnung, was schon in der Datenbank liegt.
|
||||
*
|
||||
* Angefasst wird, was in Gebrauch ist: die veröffentlichte und die Entwurfsfassung jeder Seite
|
||||
* und die Bereiche. Ältere Fassungen bleiben Historie – wer eine zurückholt, speichert sie
|
||||
* danach einmal, und der reparierte Kern schreibt sie richtig.
|
||||
*/
|
||||
#[AsCommand(name: 'cms:repair:entities', description: 'Repariert doppelt kodierte Texte (&amp;) aus Fassungen vor 1.20.1.')]
|
||||
final class RepairEntitiesCommand extends Command
|
||||
{
|
||||
public function __construct(private Connection $db, private DocumentValidator $validator)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('schreiben', null, InputOption::VALUE_NONE, 'Die Korrekturen wirklich speichern. Ohne diese Option wird nur gezeigt, was sich ändern würde.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$schreiben = (bool) $input->getOption('schreiben');
|
||||
$gesamt = 0;
|
||||
$zeilen = [];
|
||||
|
||||
// Die aktiven Fassungen jeder Seite: veröffentlicht und Entwurf.
|
||||
$aktive = [];
|
||||
foreach ($this->db->fetchAllAssociative('SELECT id, path, published_revision_id, draft_revision_id FROM page') as $seite) {
|
||||
foreach (['published_revision_id' => 'veröffentlicht', 'draft_revision_id' => 'Entwurf'] as $spalte => $was) {
|
||||
if (!empty($seite[$spalte])) {
|
||||
$aktive[(string) $seite[$spalte]] = '/'.$seite['path'].' ('.$was.')';
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($aktive as $revision => $bezeichnung) {
|
||||
$roh = $this->db->fetchOne('SELECT document FROM revision WHERE id = ?', [$revision]);
|
||||
if (!is_string($roh)) {
|
||||
continue;
|
||||
}
|
||||
$dokument = json_decode($roh, true, 512, JSON_THROW_ON_ERROR);
|
||||
$zahl = 0;
|
||||
$beispiele = [];
|
||||
$dokument['blocks'] = $this->bausteine($dokument['blocks'] ?? [], $zahl, $beispiele);
|
||||
if (0 === $zahl) {
|
||||
continue;
|
||||
}
|
||||
$gesamt += $zahl;
|
||||
$zeilen[] = [$bezeichnung, $zahl, implode(' · ', $beispiele)];
|
||||
if ($schreiben) {
|
||||
// Geprüft wie beim Speichern: eine Korrektur darf kein ungültiges Dokument hinterlassen.
|
||||
$this->validator->validate($dokument, writing: true);
|
||||
$this->db->executeStatement('UPDATE revision SET document = ? WHERE id = ?', [$this->json($dokument), $revision]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->db->fetchAllAssociative('SELECT id, kind, name, document FROM partial') as $bereich) {
|
||||
$dokument = json_decode((string) $bereich['document'], true, 512, JSON_THROW_ON_ERROR);
|
||||
$zahl = 0;
|
||||
$beispiele = [];
|
||||
$dokument['blocks'] = $this->bausteine($dokument['blocks'] ?? [], $zahl, $beispiele);
|
||||
if (0 === $zahl) {
|
||||
continue;
|
||||
}
|
||||
$gesamt += $zahl;
|
||||
$zeilen[] = ['Bereich '.$bereich['kind'].' „'.$bereich['name'].'“', $zahl, implode(' · ', $beispiele)];
|
||||
if ($schreiben) {
|
||||
// Ein Bereich traegt keine Ueberschrift - genau wie im PartialRepository.
|
||||
$this->validator->validate($dokument, requireHeading: false, writing: true);
|
||||
$this->db->executeStatement('UPDATE partial SET document = ? WHERE id = ?', [$this->json($dokument), $bereich['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === $gesamt) {
|
||||
$io->success('Nichts zu reparieren: kein Text trägt eine doppelt kodierte Entity.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
$io->table(['Wo', 'Stellen', 'Beispiel'], $zeilen);
|
||||
if ($schreiben) {
|
||||
$io->success(sprintf('%d Stelle(n) repariert.', $gesamt));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
$io->warning(sprintf('%d Stelle(n) gefunden. Mit --schreiben anwenden.', $gesamt));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/** @param array<int, array<string, mixed>> $liste */
|
||||
private function bausteine(array $liste, int &$zahl, array &$beispiele): array
|
||||
{
|
||||
foreach ($liste as $index => $block) {
|
||||
foreach (['text', 'caption', 'label', 'title', 'hint', 'provider'] as $feld) {
|
||||
$wert = $block['props'][$feld] ?? null;
|
||||
if (!is_string($wert) || '' === $wert) {
|
||||
continue;
|
||||
}
|
||||
$neu = $this->entschachteln($wert);
|
||||
if ($neu !== $wert) {
|
||||
$liste[$index]['props'][$feld] = $neu;
|
||||
++$zahl;
|
||||
if (count($beispiele) < 3) {
|
||||
$beispiele[] = mb_substr($wert, 0, 40).' → '.mb_substr($neu, 0, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($block['children']) && is_array($block['children'])) {
|
||||
$liste[$index]['children'] = $this->bausteine($block['children'], $zahl, $beispiele);
|
||||
}
|
||||
}
|
||||
|
||||
return $liste;
|
||||
}
|
||||
|
||||
/** Entschachtelt so oft wie nötig: aus &amp;amp; wird &. Ein einzelnes & ist richtig und bleibt. */
|
||||
private function entschachteln(string $text): string
|
||||
{
|
||||
$vorher = null;
|
||||
while ($vorher !== $text) {
|
||||
$vorher = $text;
|
||||
$text = preg_replace('/&(amp|lt|gt|quot|apos|nbsp|#\d{1,6}|#x[0-9a-fA-F]{1,6});/', '&$1;', $text) ?? $text;
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $dokument */
|
||||
private function json(array $dokument): string
|
||||
{
|
||||
return json_encode($dokument, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests;
|
||||
|
||||
use App\Command\RepairEntitiesCommand;
|
||||
use App\Content\DocumentValidator;
|
||||
use App\Infrastructure\ConnectionFactory;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* `cms:repair:entities` räumt auf, was das doppelte Kodieren vor 1.20.1 hinterlassen hat.
|
||||
*
|
||||
* Geprüft wird beides: dass der Trockenlauf nichts anfasst, und dass das Schreiben genau die
|
||||
* verschachtelten Entities entschachtelt – ein einzelnes `&` ist richtig und muss bleiben.
|
||||
*/
|
||||
final class RepairEntitiesCommandTest extends TestCase
|
||||
{
|
||||
private string $path;
|
||||
private Connection $db;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->path = tempnam(sys_get_temp_dir(), 'kant-repair-');
|
||||
$this->db = ConnectionFactory::create('sqlite:///'.str_replace('\\', '/', $this->path));
|
||||
TestSchema::build($this->db);
|
||||
$this->db->insert('website', ['id' => 'default', 'name' => 'KantSites', 'default_locale' => 'de', 'theme' => '{}']);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->db->close();
|
||||
gc_collect_cycles();
|
||||
@unlink($this->path);
|
||||
}
|
||||
|
||||
/** Legt eine Seite mit einem Text an und gibt die Kennung der Fassung zurück. */
|
||||
private function seite(string $text): string
|
||||
{
|
||||
$dokument = [
|
||||
'schemaVersion' => 17,
|
||||
'title' => 'Start',
|
||||
'description' => 'Eine Seite zum Reparieren.',
|
||||
'blocks' => [['id' => 's1', 'type' => 'section', 'children' => [
|
||||
['id' => 'h1', 'type' => 'heading', 'props' => ['text' => 'Start', 'level' => 1]],
|
||||
['id' => 't1', 'type' => 'text', 'props' => ['text' => $text]],
|
||||
]]],
|
||||
];
|
||||
// Erst die Seite, dann ihre Fassung: die Fassung zeigt per Fremdschluessel auf die Seite.
|
||||
$revision = bin2hex(random_bytes(16));
|
||||
$this->db->insert('page', ['id' => 'page-1', 'slug' => 'home', 'path' => 'home', 'type' => 'page', 'locale' => 'de', 'website_id' => 'default', 'version' => 1, 'created_at' => gmdate('Y-m-d H:i:s')]);
|
||||
$this->db->insert('revision', ['id' => $revision, 'page_id' => 'page-1', 'document' => json_encode($dokument, JSON_THROW_ON_ERROR), 'created_at' => gmdate('Y-m-d H:i:s')]);
|
||||
$this->db->update('page', ['published_revision_id' => $revision, 'draft_revision_id' => $revision], ['id' => 'page-1']);
|
||||
|
||||
return $revision;
|
||||
}
|
||||
|
||||
private function text(string $revision): string
|
||||
{
|
||||
$dokument = json_decode((string) $this->db->fetchOne('SELECT document FROM revision WHERE id = ?', [$revision]), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
return $dokument['blocks'][0]['children'][1]['props']['text'];
|
||||
}
|
||||
|
||||
private function tester(): CommandTester
|
||||
{
|
||||
return new CommandTester(new RepairEntitiesCommand($this->db, new DocumentValidator()));
|
||||
}
|
||||
|
||||
public function testTheDryRunReportsWithoutChangingAnything(): void
|
||||
{
|
||||
$revision = $this->seite('Freudenstadt &amp; Umgebung');
|
||||
$tester = $this->tester();
|
||||
$tester->execute([]);
|
||||
|
||||
self::assertStringContainsString('1 Stelle', $tester->getDisplay());
|
||||
self::assertSame('Freudenstadt &amp; Umgebung', $this->text($revision), 'Ohne --schreiben bleibt alles stehen.');
|
||||
}
|
||||
|
||||
public function testWritingUnnestsEveryLevelAndLeavesASingleEntityAlone(): void
|
||||
{
|
||||
// Drei Stufen, wie sie auf einer Seite standen, die dreimal gespeichert wurde.
|
||||
$revision = $this->seite('Projekte &amp;amp; Lösungen, „Zitat&quot;, und ein richtiges & dazu');
|
||||
$tester = $this->tester();
|
||||
$tester->execute(['--schreiben' => true]);
|
||||
|
||||
self::assertStringContainsString('repariert', $tester->getDisplay());
|
||||
self::assertSame('Projekte & Lösungen, „Zitat", und ein richtiges & dazu', $this->text($revision));
|
||||
|
||||
// Ein zweiter Lauf findet nichts mehr - und macht aus dem richtigen & kein nacktes &.
|
||||
$zweiter = $this->tester();
|
||||
$zweiter->execute(['--schreiben' => true]);
|
||||
self::assertStringContainsString('Nichts zu reparieren', $zweiter->getDisplay());
|
||||
self::assertSame('Projekte & Lösungen, „Zitat", und ein richtiges & dazu', $this->text($revision));
|
||||
}
|
||||
|
||||
public function testAnUntouchedInstallationSaysSoAndKeepsQuiet(): void
|
||||
{
|
||||
$revision = $this->seite('Alles gut & sauber');
|
||||
$tester = $this->tester();
|
||||
$tester->execute(['--schreiben' => true]);
|
||||
|
||||
self::assertStringContainsString('Nichts zu reparieren', $tester->getDisplay());
|
||||
self::assertSame('Alles gut & sauber', $this->text($revision));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user