diff --git a/docs/guides/divi-design-system.md b/docs/guides/divi-design-system.md index c48ac3d..8f3d746 100644 --- a/docs/guides/divi-design-system.md +++ b/docs/guides/divi-design-system.md @@ -231,14 +231,14 @@ Website: den Nutzer ausdrücklich fragen. Jede Änderung legt einen Snapshot an. ## Snapshots und Wiederherstellen Vor jeder Änderung an Presets, Farben, Variablen und Theme-Optionen sichert LIT MCP den Bereich (je Bereich bleiben -die letzten 20 Snapshots); die Antwort nennt die `snapshot_id`. Wäre der Bereich zu groß für eine Sicherung (über +die letzten 20 Snapshots; eine Gesamtgrenze von 8 MB entfernt ältere früher); die Antwort nennt die `snapshot_id`. Wäre der Bereich zu groß für eine Sicherung (über 2 MB), antwortet das Werkzeug mit `snapshot_too_large` und schreibt nichts. - `lit/divi-design-snapshots` mit `area` (`presets`, `colors`, `variables` oder `theme_options`; Standard `presets`) listet die Snapshots eines Bereichs, neueste zuerst, mit Zeitpunkt, Werkzeug und Protokolleintrag. - `lit/divi-design-restore` mit `snapshot_id`: Der Probelauf zeigt den Unterschied zum aktuellen Stand (`diff`, `design_rev`); nach Rückfrage `confirm: true`. Vorher sichert LIT MCP den aktuellen Stand selbst – auch eine - Wiederherstellung lässt sich zurücknehmen. Ist die ID unbekannt oder verdrängt, kommt `snapshot_not_found`. + Wiederherstellung lässt sich zurücknehmen. Ist die ID unbekannt oder verdrängt, kommt `not_found`. - Was ein Restore macht, hängt vom Bereich ab: **Farben**, die es im Snapshot nicht gibt, entfallen. **Variablen**, die erst nach dem Snapshot entstanden, bleiben archiviert erhalten (Divi löscht keine). **Theme-Optionen**, die im Snapshot nicht gesetzt waren, kehren zum Divi-Standard zurück. **Presets**: Rechte-Regel `unfiltered_html` diff --git a/docs/guides/divi-placeholders.md b/docs/guides/divi-placeholders.md index 1059774..2c35368 100644 --- a/docs/guides/divi-placeholders.md +++ b/docs/guides/divi-placeholders.md @@ -88,7 +88,9 @@ Regeln und Fehlermeldungen: 3. Passt die Option nicht zur Art der Schleife, steht in der Meldung: `… passt nicht zur Schleife dieses Elements (Typ: Terme).` (Typ: Beiträge, Terme, Benutzer oder Menü). Eine Option der passenden Art wählen. Das ist eine Sicherheitsgrenze, kein Schönheitsfehler: Divi liest die Feld-Optionen in einer Term-, Benutzer- oder - Menü-Schleife sonst bei dem Objekt mit derselben ID und gäbe fremde Daten aus. Nie umgehen. + Menü-Schleife sonst bei dem Objekt mit derselben ID und gäbe fremde Daten aus. Nie umgehen. Bei einer nicht + unterstützten Schleife (ACF-Wiederholungsfeld, Alias oder leere Art) nennt die Meldung statt des Typs die rohe + Abfrageart der Schleife; in solchen Schleifen ist jede `loop_*`-Option abgelehnt. 4. Die Feld-Optionen (`loop_*_meta_key_manual_custom_field`) gibt es nur in der Vollform; für den Schlüssel gelten dieselben Regeln wie bei `{{field:}}` (kein Schlüssel mit `_` am Anfang). 5. Vor dem Setzen lesen: `lit/divi-dynamic-sources` mit `post_type`, `post_id` und `node` (Pfad-ID des Moduls aus @@ -98,11 +100,11 @@ Regeln und Fehlermeldungen: 6. `lit-divi-move` erlaubt das Verschieben von Schleifen-Inhalt aus der Schleife heraus oder in eine Schleife anderer Art (Divi lässt es zu), antwortet aber mit einer Warnung: der Inhalt zeigt dort nichts oder falsche Daten. Warnung dem Nutzer nennen, Modul zurückschieben oder den Inhalt neu setzen. -7. `lit-divi-update` warnt ebenso, wenn ein Update die Art der Schleife eines Elements ändert und darunter - unveränderte Schleifen-Inhalte stehen. -8. Ein neues Element mit eigener Schleife und Schleifen-Inhalten darunter geht nicht in einem `lit-divi-insert`- - Aufruf (die Schleife gilt erst nach dem Speichern): erst das Element mit Schleife einfügen, dann die Inhalte - mit `parent` = dieses Element. +7. `lit-divi-update` warnt ebenso, wenn ein Update die Art der Schleife eines Elements ändert oder die Schleife + ausschaltet (Art wird „keine“) und darunter unveränderte Schleifen-Inhalte stehen. +8. `lit-divi-insert` lehnt einen Aufruf ab, in dem irgendwo ein neues Element mit eigener Schleife und irgendwo + Schleifen-Inhalt vorkommt (die Schleife gilt erst nach dem Speichern): erst das Element mit Schleife einfügen, + dann die Inhalte mit `parent` = dieses Element in einem zweiten Aufruf. Grenzen (bewusst nicht gelöst; dem Nutzer sagen, statt zu umgehen): @@ -111,8 +113,9 @@ Grenzen (bewusst nicht gelöst; dem Nutzer sagen, statt zu umgehen): Schleife am Element selbst ein. - Der Zwei-Aufruf-Weg wird nicht verhindert, nur gemeldet: Schleifen-Inhalte (z. B. ein Beitrags-Feld) in einer Beitrags-Schleife einfügen und danach die Art der Schleife ändern oder das Modul in eine Schleife anderer Art - verschieben. Das Werkzeug antwortet nur mit einer Warnung (Divis eigener Builder erlaubt dasselbe); sie dem - Nutzer nennen und nicht ignorieren. + verschieben. Das Werkzeug antwortet nur mit einer Warnung (Divis eigener Builder erlaubt dasselbe), obwohl Divi + dann Felder eines anderen Objekts mit derselben ID ausgeben kann; die Warnung dem Nutzer nennen und nicht + ignorieren. - **Globale Module** (`[global ]`) haben keinen Schleifen-Kontext; `loop_*` dort ist abgelehnt. - `lit-content-create` und Theme-Builder-Vorlagen kennen keinen Schleifen-Kontext: erst die Seite mit Schleife anlegen (Nutzer im Divi-Builder), dann die Inhalte mit `lit-divi-insert` setzen. diff --git a/docs/guides/divi-shortform.md b/docs/guides/divi-shortform.md index 834f3f1..b2e5203 100644 --- a/docs/guides/divi-shortform.md +++ b/docs/guides/divi-shortform.md @@ -131,7 +131,7 @@ Module in einem Element mit eingeschalteter Divi-Schleife zeigen Daten des jewei `{"type": "text", "content": "{{loop:post_date}}"}` mit `parent` = die Spalte (oder das Element) der Schleife. Das gilt in `lit-divi-insert` und `lit-divi-update` (`set`); außerhalb einer Schleife und in einer Schleife anderer Art lehnt das Werkzeug `loop_*`-Inhalte ab (Meldungen, Optionen und Grenzen: `divi-placeholders`, Abschnitt -„Schleifen-Inhalte“). `lit-divi-move` warnt, wenn ein Modul mit Schleifen-Inhalt die Schleife verlässt. +„Schleifen-Inhalte“). `lit-divi-move` warnt, wenn ein Modul mit Schleifen-Inhalt die Schleife verlässt oder in eine Schleife anderer Art kommt. `lit-content-create` kennt keinen Schleifen-Kontext. ## Einfügen, ändern, verschieben, löschen diff --git a/docs/guides/overview.md b/docs/guides/overview.md index 2f43229..72b6e8f 100644 --- a/docs/guides/overview.md +++ b/docs/guides/overview.md @@ -68,7 +68,7 @@ Für den Einstieg liefert `lit-guide` mit dem Thema „all“ alle Themen in ein `message`, `hint` (und ggf. `fields`). Dem `hint` folgen. Weitere Codes: `forbidden` (ein Recht fehlt, die Meldung nennt es), `in_use` (ein Preset oder Eintrag wird noch verwendet; `usages` nennt die Fundstellen), `conflict` (eine andere Änderung am Designsystem läuft noch, oder der Name einer Schrift ist vergeben), - `snapshot_not_found`, `snapshot_too_large`, `font_fetch_failed` (Schriftdatei nicht ladbar) und `font_invalid` + `not_found` (z. B. eine unbekannte oder verdrängte `snapshot_id`), `snapshot_too_large`, `font_fetch_failed` (Schriftdatei nicht ladbar) und `font_invalid` (Datei passt nicht zum Format). - `confirm_required`: Nutzer fragen, dann mit `confirm: true` wiederholen. Beim Designsystem enthält die Antwort die `design_rev`, die mit `confirm: true` zurückgegeben wird; `rev_mismatch` heißt dort: der Bestand hat sich diff --git a/tests/Unit/Guides/LoopGuideTest.php b/tests/Unit/Guides/LoopGuideTest.php index b1250a2..d4e37ff 100644 --- a/tests/Unit/Guides/LoopGuideTest.php +++ b/tests/Unit/Guides/LoopGuideTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Guides; +use LIT\MCP\Integrations\Divi\Compose\Placeholders; use PHPUnit\Framework\TestCase; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; @@ -53,16 +54,22 @@ final class LoopGuideTest extends TestCase public function testRefusalPhrasesAreTheOnesTheCodeSays(): void { + // Only string literals count (no comments), matched exactly including case. $code = ''; $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::root() . '/src')); foreach ($files as $file) { - if (str_ends_with((string) $file, '.php')) { - $code .= (string) file_get_contents((string) $file); + if (! str_ends_with((string) $file, '.php')) { + continue; + } + foreach (token_get_all((string) file_get_contents((string) $file)) as $token) { + if (is_array($token) && in_array($token[0], [T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE], true)) { + $code .= $token[1] . "\n"; + } } } foreach (self::PHRASES as $phrase) { - self::assertNotFalse(mb_stripos($code, $phrase), 'no source says "' . $phrase . '"'); + self::assertNotFalse(mb_strpos($code, $phrase), 'no source string says "' . $phrase . '"'); } } @@ -72,6 +79,7 @@ final class LoopGuideTest extends TestCase self::assertNotEmpty($examples[1], 'the guides show no {{loop:…}} example'); foreach (array_unique($examples[1]) as $name) { self::assertContains('loop_' . $name, self::DIVI_LOOP_OPTIONS, '{{loop:' . $name . '}} is no Divi option'); + self::assertSame('loop_' . $name, Placeholders::LOOP[$name] ?? null, '{{loop:' . $name . '}} is not in Placeholders::LOOP'); } } }