diff --git a/docs/guides/divi-library.md b/docs/guides/divi-library.md index 6fb9c54..87a09c2 100644 --- a/docs/guides/divi-library.md +++ b/docs/guides/divi-library.md @@ -17,7 +17,11 @@ Die Divi-Bibliothek speichert Layouts, Abschnitte, Zeilen und Module zur Wiederv und Divi-4-Shortcodes mit `global_module` – auch in Texten von Divi-5-Modulen. Im Zweifel zählt eine Seite mit: ein Verweis, der sich nicht eindeutig lesen lässt, und ein `global_module`, das keine gültige Zahl ist (Divi zeigt dann den neuesten Eintrag), gelten als Verweis auf jeden Eintrag. Sehr ungewöhnliche Schreibweisen - können trotzdem unerkannt bleiben. Steckt der Eintrag in einem wiederverwendbaren Block (`wp_block`), erscheint + können trotzdem unerkannt bleiben; dazu gehört ein maskiert in einem Modulwert gespeicherter + `divi/global-layout`-Block: Divi wertet Blöcke in der Ausgabe von Shortcode-Modulen und in + WooCommerce-Textmodulen (Warenkorb, Kasse) erneut aus, sodass er dort als globales Modul erscheinen kann + (bekanntes Restrisiko, wie die übrigen ungewöhnlichen Schreibweisen; es braucht Bearbeitungsrechte auf der + verweisenden Seite). Steckt der Eintrag in einem wiederverwendbaren Block (`wp_block`), erscheint dieser Block in der Liste, nicht die Seite, die ihn einbettet – wie bei Divi selbst. - `lit-library-insert`: einen Eintrag in eine Divi-Seite einfügen. `mode: "global"` (Standard bei globalen Einträgen) fügt eine verknüpfte Instanz ein, `mode: "copy"` (Standard sonst) eine unabhängige Kopie. @@ -29,8 +33,26 @@ Die Divi-Bibliothek speichert Layouts, Abschnitte, Zeilen und Module zur Wiederv - `lit/library-save`: einen Abschnitt, eine Zeile oder ein Modul als neuen Eintrag speichern (`post_id`, `tree_rev`, Pfad-ID `id`, `title`, `scope: "normal"` oder `"global"`, vorhandene `categories`/`tags`). Der Knoten auf der Seite bleibt eine eigenständige Kopie – um ihn zu verknüpfen, ihn durch eine Instanz - ersetzen (siehe Ablauf unten). Ein Knoten, der selbst ein globales Modul ist oder eines enthält, lässt sich - nicht als globaler Eintrag speichern. + ersetzen (siehe Ablauf unten). Ein Knoten, der selbst ein globales Modul ist oder auf einen Eintrag verweist + (auch als Divi-4-Shortcode mit `global_module` in einem Text), lässt sich nicht als globaler Eintrag speichern. +- `lit/library-find-similar`: Knoten finden, die wie ein Knoten (`post_id` und `id`) oder ein Eintrag + (`library_id`) aufgebaut sind. `match: "identical"` = gleicher Inhalt einschließlich aller Texte (nur + Admin-Labels ausgenommen), `"structure"` = gleicher Aufbau (Modultypen, Spalten, Presets) mit anderen Inhalten. + Durchsucht werden Einträge der Divi-Inhaltstypen (optional nur `post_type`), nie Bibliothekseinträge oder + Theme-Builder-Layouts; nur Einträge, die das Konto bearbeiten darf. Höchstens 200 Einträge je Aufruf + (`limit`), weiter mit `next_cursor`; bei sehr vielen Treffern endet ein Aufruf früher und sagt das in `note`. + Jeder Treffer nennt `tree_rev` und `replaceable` (`true`: inhaltsgleich und in einer Seite oder einem Beitrag – + nur solche Treffer ersetzt `lit/library-make-global`). +- `lit/library-make-global`: einen Knoten einer Seite oder eines Beitrags in ein globales Modul umwandeln + (`post_id`, `tree_rev`, `id`, `title`, optional `categories`/`tags`): neuer globaler Eintrag mit genau diesem + Inhalt, der Knoten wird durch die Instanz ersetzt – alles oder nichts. Weitere, inhaltsgleiche Vorkommen + ersetzen: `library_id` und `targets` (je `post_id`, `tree_rev`, `ids` der Treffer `"identical"`; höchstens 20 + Seiten). Alle Ziele werden vorher geprüft; ohne `confirm` ist das ein Probelauf (`confirm_required` mit + `affected`), dann `confirm: true`. Jede Seite bekommt eine Revision (`pages`). Nur Seiten und Beiträge + (Inhaltstypen `page` und `post`), die das Konto bearbeiten darf – keine Bibliothekseinträge und keine + Theme-Builder-Layouts, auch nicht als Quelle. Knoten, die auf einen Eintrag verweisen, werden kein globales + Modul. Schlägt eine Seite fehl, bleiben die vorher ersetzten Seiten ersetzt (je mit Revision), die übrigen + unverändert; der Fehler nennt alle drei Gruppen (`changed_posts`, `failed_post`, `unchanged_posts`). - `lit/divi-node-copy`: einen Knoten von einem Eintrag an eine Position eines anderen kopieren (`source_post_id`, `source_id`, `source_tree_rev` aus `lit-divi-outline` der Quelle – Pflicht, weil Pfad-IDs nur für diesen Stand gelten; Ziel mit `post_id`, `tree_rev`, Position). Quelle und Ziel dürfen @@ -54,15 +76,17 @@ Die Divi-Bibliothek speichert Layouts, Abschnitte, Zeilen und Module zur Wiederv 4. Dem Nutzer die betroffenen Seiten nennen und fragen. Erst nach Zustimmung dieselbe Änderung mit `confirm: true` wiederholen (bei `lit-divi-delete` gilt dasselbe `confirm`). 5. Vorher entsteht eine Revision des Eintrags (`revision_id`); sind Revisionen abgeschaltet, eine - Sicherungskopie im Papierkorb (`backup_id`). Schlägt das Speichern danach fehl, nennt der Fehler die + Sicherungskopie im Papierkorb (`backup_id`). `lit/revision-restore` gilt nicht für Bibliothekseinträge: + Zurück geht es über `restore_url` in der Antwort (Revisionsvergleich in WordPress, dort wiederherstellen). Schlägt das Speichern danach fehl, nennt der Fehler die `backup_id`. Nach dem Speichern wird Divis CSS-Cache für alle Seiten geleert. Die Bestätigung wird abgelehnt (`forbidden`), wenn das Konto einen veröffentlichten, privaten oder geplanten Eintrag, der das Modul zeigt, nicht bearbeiten darf – schon der Probelauf warnt davor und nennt diese IDs in `blocked_ids`, nicht lesbare nur als Anzahl in `blocked_hidden_count` (Entwürfe blockieren nicht). -Theme-Builder-Layouts (Header, Body, Footer, Vorlagen) blockieren außerdem, wenn die Divi-Rolle des Kontos -„Theme Builder“ nicht erlaubt. Zeigen -mehr als 2000 veröffentlichte, private oder geplante Einträge das Modul, lehnt LIT MCP die Bestätigung ab +Theme-Builder-Layouts (Header, Body, Footer, Vorlagen) blockieren außerdem, wenn das Konto den Theme Builder +nicht benutzen darf (Divi-Rolle „Theme Builder“, `edit_others_posts`, `edit_theme_options`) oder die Gruppe +„Divi Theme Builder“ auf „nur lesen“ steht; ohne Zugang zum Theme Builder erscheinen sie nur als Anzahl +(`hidden_count`, `blocked_hidden_count`), nie mit ID oder Titel. Zeigen mehr als 2000 veröffentlichte, private oder geplante Einträge das Modul, lehnt LIT MCP die Bestätigung ab (`unsupported`); solche Einträge im Divi-Builder bearbeiten. Sind Revisionen für die Bibliothek abgeschaltet, lehnt das Werkzeug die Änderung außerdem ab (`unsupported`), wenn der Papierkorb deaktiviert ist oder das Konto kein `unfiltered_html` hat (die Sicherungskopie wäre dann nicht verlustfrei). @@ -80,6 +104,9 @@ wird leer; ein globaler Eintrag enthält keine globalen Module. Nur aktive Eintr `lit-page-render-summary` zeigt globale Module nicht (über die Schnittstelle rendert Divi sie nicht) und nennt sie in `notes`; ihren Inhalt mit `lit-divi-outline` und der ID lesen. +Fügt eine Divi-Änderung einen Abschnitt ein, der wie ein Abschnitt der Bibliothek aufgebaut ist, nennt +`warnings` den Eintrag – dann `lit-library-insert` erwägen. + ## Rechte - WordPress: die Bibliothek nutzen = Beiträge bearbeiten dürfen; einen Eintrag lesen oder ändern = diesen @@ -92,10 +119,8 @@ sie in `notes`; ihren Inhalt mit `lit-divi-outline` und der ID lesen. Hinweis. - Einstellungen → LIT MCP → Tools: Gruppe „Divi-Bibliothek“ auf „nur lesen“ sperrt jeden Schreibzugriff auf Bibliothekseinträge, auch über die Divi-Werkzeuge; deaktiviert gilt jeder Eintrag als nicht vorhanden. - Grenze: Die Einträge selbst sind dann nicht lesbar und nicht änderbar, aber `lit-divi-outline`, - `lit-divi-node-get` und `lit-page-render-summary` einer Seite nennen die ID eines dort verwendeten globalen - Moduls weiterhin (auch ohne das Recht „Divi-Bibliothek“). Diese ID mit den Divi-Werkzeugen zu öffnen, - antwortet `not_found`; dann nicht weiter versuchen. + Ohne das Recht „Divi-Bibliothek“ oder mit deaktivierter Gruppe nennen `lit-divi-outline`, `lit-divi-node-get` + und `lit-page-render-summary` globale Module nur als solche, ohne ihre ID. `lit-library-insert` und `lit/divi-node-copy` sind außerdem gesperrt, wenn `lit-divi-insert` gesperrt ist; `lit/divi-node-copy` mit einem Bibliothekseintrag als Quelle auch, wenn `lit-library-insert` gesperrt ist. - Ohne das Recht `unfiltered_html` überträgt LIT MCP beim Kopieren, Einfügen und Speichern (`lit/divi-node-copy`, @@ -109,13 +134,13 @@ sie in `notes`; ihren Inhalt mit `lit-divi-outline` und der ID lesen. ## Ablauf: globale Module vorschlagen -1. Die Seiten lesen (`lit-divi-outline`) und wiederkehrende Abschnitte suchen (gleiche Admin-Labels oder - Texte, z. B. Kontakt, Öffnungszeiten, Call-to-Action). -2. Dem Nutzer vorschlagen, welche davon globale Module werden sollen, und erklären: eine Änderung wirkt - danach auf allen Seiten. -3. Nach Zustimmung den Abschnitt einer Seite mit `lit/library-save` und `scope: "global"` speichern. -4. Auf jeder Seite (auch der ersten) die Instanz mit `lit-library-insert` direkt vor dem bisherigen Abschnitt - einfügen und den bisherigen Abschnitt nach Rückfrage mit `lit-divi-delete` entfernen. +1. Wiederkehrende Abschnitte suchen: `lit/library-find-similar` mit einem typischen Abschnitt (`post_id`, `id`) + – z. B. Kontakt, Öffnungszeiten, Call-to-Action. +2. Dem Nutzer die Treffer zeigen und erklären: eine Änderung wirkt danach auf allen Seiten mit dem Modul. + Treffer mit `match: "structure"` unterscheiden sich im Inhalt – vorher klären, welche Fassung gilt, und die + übrigen angleichen (`lit-divi-update`) oder weglassen. +3. Nach Zustimmung `lit/library-make-global` mit dem gewählten Abschnitt. +4. Die Treffer mit `replaceable: true` mit `lit/library-make-global` (`library_id`, `targets` mit der `tree_rev` + aus `lit/library-find-similar`) ersetzen: Probelauf zeigen, dann `confirm: true`. Inhaltsgleiche Treffer in + anderen Inhaltstypen (`replaceable: false`) im Divi-Builder durch das globale Modul ersetzen. 5. Den Nutzer bitten, das Ergebnis im Divi-Builder anzusehen. - -Unterscheiden sich die Abschnitte in Details, vorher mit dem Nutzer klären, welche Fassung gilt. diff --git a/docs/guides/divi-placeholders.md b/docs/guides/divi-placeholders.md index 8145596..9ec277d 100644 --- a/docs/guides/divi-placeholders.md +++ b/docs/guides/divi-placeholders.md @@ -33,12 +33,16 @@ beginnt (geschützte Meta-Daten), sind nicht erlaubt. In Link-Feldern (`url`, `button_url` …) für interne Links immer `{{link:}}` verwenden statt fester Adressen wie `/?page_id=…`. Der Eintrag muss existieren und für das Konto lesbar sein; Einträge im Papierkorb, -automatische Entwürfe und Medien (Anhänge – dafür die Adresse aus der Mediathek) lehnt das Werkzeug ab, -ebenso `{{link:…}}` außerhalb von Link-Feldern. Solange das Ziel ein Entwurf oder privat ist, zeigt Divi +automatische Entwürfe, Einträge der Divi-Bibliothek und Medien (Anhänge – dafür die Adresse aus der Mediathek) +lehnt das Werkzeug ab – wie eine unbekannte ID –, ebenso `{{link:…}}` außerhalb von Link-Feldern. Solange das Ziel ein Entwurf oder privat ist, zeigt Divi Besuchern nur die Adresse `?page_id=` (kein Titel); nach dem Veröffentlichen die normale Adresse. Sonst nimmt ein Link-Feld jeden dynamischen Inhalt an; sinnvoll sind nur Werte, die eine Adresse ergeben (`{{post:url}}`, ein Feld mit einer URL, eine Link-Variable). +Dieselben Regeln gelten für die Vollform `post_link_url_` mit `post_id`: das Ziel muss lesbar sein, darf +nicht im Papierkorb liegen, kein Anhang und kein Bibliothekseintrag sein und muss genau den Typ im Namen haben +(`post_link_url_page` nur für Seiten). `{{link:}}` wählt den passenden Namen selbst. + Welche dynamischen Quellen und Felder es für einen Inhaltstyp gibt, liefert `lit/divi-dynamic-sources` (über `mcp-adapter-execute-ability`): Divis Quellen mit Platzhalter und die Felder, die sich als `{{field:…}}` eignen. Gelistet werden nur textartige Felder; Beziehungs- und Dateifelder (Divi zeigt sie nur diff --git a/docs/guides/overview.md b/docs/guides/overview.md index a41a34b..e974322 100644 --- a/docs/guides/overview.md +++ b/docs/guides/overview.md @@ -23,9 +23,9 @@ Direkt sichtbar: `lit-guide`, `lit-site-status`, `lit-content-types`, `lit-conte `lit-divi-outline`, `lit-divi-node-get`, `lit-divi-catalog`, `lit-divi-design-system`, `lit-divi-insert`, `lit-divi-update`, `lit-divi-move`, `lit-divi-delete`, `lit-library-insert`. -Die Tools sind nach Integration gruppiert (Grundlagen, WordPress, Medien, Divi, Divi-Bibliothek). Jede Gruppe hat unter -Einstellungen → LIT MCP → Tools eine eigene Einstellung (direkt/indirekt/deaktiviert), die ihre Tools übernehmen -(„wie Gruppe“). Eine Einstellung am einzelnen Tool geht vor; die Gruppe zeigt dann „gemischt“. „nur lesen“ +Die Tools sind nach Integration gruppiert (Grundlagen, WordPress, Medien, Divi, Divi-Bibliothek, +Divi Theme Builder). Jede Gruppe hat unter Einstellungen → LIT MCP → Tools eine eigene Einstellung +(direkt/indirekt/deaktiviert), die ihre Tools übernehmen („wie Gruppe“). Eine Einstellung am einzelnen Tool geht vor; die Gruppe zeigt dann „gemischt“. „nur lesen“ schaltet alle schreibenden Tools der Gruppe ab – auch solche mit eigener Einstellung. `lit-guide` bleibt immer direkt; `lit-media-preview`/`lit-media-download` bleiben bei Gruppe „indirekt“ direkt. Ab mehr als 25 direkten Tools warnt die Seite (Kontextkosten im Assistenten). Änderungen gelten ab der nächsten Verbindung. @@ -37,13 +37,17 @@ Status (`lit/post-unpublish`, `lit/post-schedule`, `lit/post-trash`), Kopien (`l (`lit/menu-list`, `lit/menu-get`, `lit/menu-create`, `lit/menu-set-items`, `lit/menu-assign-location`), Einstellungen (`lit/wp-settings-get`, `lit/wp-settings-update`), Medien (`lit/media-get`, `lit/media-upload`, `lit/media-update`, `lit/media-delete`), Divi (`lit/divi-duplicate-node`, `lit/divi-replace-text`, -`lit/divi-node-copy`, `lit/divi-dynamic-sources`), Divi-Bibliothek (`lit/library-list`, `lit/library-save`). +`lit/divi-node-copy`, `lit/divi-dynamic-sources`), Divi-Bibliothek (`lit/library-list`, `lit/library-save`, +`lit/library-find-similar`, `lit/library-make-global`), Divi Theme Builder (`lit/tb-list`, +`lit/tb-create-template`, `lit/tb-assign`, `lit/tb-activate`). Die `lit-divi-…`-Werkzeuge gibt es nur, wenn der Divi-5-Builder aktiv ist. Sie arbeiten auch auf Einträgen -der Divi-Bibliothek (ID als `post_id`); globale Module: `lit-guide` topic `divi-library`. +der Divi-Bibliothek und auf Layouts des Theme Builders (ID als `post_id`); globale Module: `lit-guide` topic +`divi-library`, Vorlagen: `lit-guide` topic `theme-builder`. Weitere Themen: `lit-guide` mit `topic` = `content-types`, `workflows`, `media`, `divi-structure`, -`divi-shortform`, `divi-attrs`, `divi-placeholders`, `divi-design-system` oder `divi-library`. +`divi-shortform`, `divi-attrs`, `divi-placeholders`, `divi-design-system`, `divi-library` oder +`theme-builder`. Für den Einstieg liefert `lit-guide` mit dem Thema „all“ alle Themen in einem Aufruf. @@ -52,7 +56,9 @@ Für den Einstieg liefert `lit-guide` mit dem Thema „all“ alle Themen in ein - Erfolg: `ok`, `summary`, `changed`, `warnings`, `rev`, `revision_id` (Revision mit dem Stand vorher, nur bei Tools, die Inhalt überschreiben, sonst `null`), `log_id`, URLs. Divi-Werkzeuge zusätzlich `tree_rev` (Stand des Seiteninhalts, Pflicht für die nächste Divi-Änderung) und `outline` (betroffener Abschnitt); - `lit/divi-replace-text` nennt `tree_rev` je Seite in `results` (Probelauf: in `matches`). + `lit/divi-replace-text` nennt `tree_rev` je Seite in `results` (Probelauf: in `matches`). Bei + Bibliothekseinträgen und Theme-Builder-Layouts kommt `restore_url` hinzu (Revisionsvergleich in WordPress) – + `lit/revision-restore` gilt nur für Inhaltstypen. - Fehler: JSON mit `code` (z. B. `validation_failed`, `rev_mismatch`, `write_failed`, `partial_write`), `message`, `hint` (und ggf. `fields`). Dem `hint` folgen. - `confirm_required`: Nutzer fragen, dann mit `confirm: true` wiederholen. diff --git a/docs/guides/theme-builder.md b/docs/guides/theme-builder.md new file mode 100644 index 0000000..533e5f5 --- /dev/null +++ b/docs/guides/theme-builder.md @@ -0,0 +1,130 @@ +# Divi Theme Builder + +Der Theme Builder legt fest, wie Kopfzeile, Inhalt und Fußzeile für bestimmte Seiten aussehen – z. B. für alle +Einträge eines eigenen Inhaltstyps. Eine **Vorlage** hat Bedingungen („gilt für alle Personen“) und bis zu drei +**Layouts** (Kopfzeile, Inhalt, Fußzeile). Die **Standardvorlage** gilt überall, wo keine andere greift; ihre +Kopf- und Fußzeile heißen „global“, weil andere Vorlagen sie mitbenutzen. + +## Grundregel: nichts wird ohne Freigabe sichtbar + +- Neue Vorlagen legt LIT MCP immer **deaktiviert** an – Besucher sehen nichts davon. +- Aktivieren und Deaktivieren gibt es nur mit `lit/tb-activate`, und immer so: Probelauf (Bedingungen, Layouts, + andere aktive Vorlagen mit denselben oder verwandten Bedingungen unter `overlaps`/`possible_overlaps`) → + Nutzer fragen → dieselbe Anfrage mit `confirm: true`. +- Auch alles andere, was Besucher sofort sähen, ist zuerst ein Probelauf (`confirm_required`) und braucht + `confirm: true`: Bedingungen einer aktiven Vorlage ändern (`lit/tb-assign`) und ein Layout ändern, das eine + aktive Vorlage zeigt (die `lit-divi-…`-Werkzeuge; die Antwort nennt die Vorlagen unter `templates`, solche + ohne Berechtigung nur als Anzahl `hidden_templates`). Vor dem + Speichern eines sichtbaren Layouts entsteht eine Revision (wiederherstellbar über `restore_url`, siehe + „Rückgängig machen“); sind Revisionen abgeschaltet, eine Sicherungskopie im Papierkorb (`backup_id`). +- `lit/divi-replace-text` ändert sichtbare Layouts nicht; solche Änderungen mit `lit-divi-update` und + `confirm: true` vornehmen. +- Die Standardvorlage ändert LIT MCP nicht (nur im Theme Builder). + +## Werkzeuge (über `mcp-adapter-execute-ability`) + +- `lit/tb-list`: alle Vorlagen mit `enabled`, `live` (für Besucher wirksam), `status`, Bedingungen (`use_on`, + `exclude_from` und Beschriftungen), Layouts (`id`, Titel, `shared_with` = andere Vorlagen mit demselben + Layout) und `template_rev`. `template_id`: nur diese Vorlage. `conditions: true` liefert alle Bedingungen, + die Divi anbietet; eine ID, die auf „:“ endet, braucht ein Objekt am Ende, z. B. + `singular:post_type:page:id:42`. `detached` zeigt Vorlagen, die ein Speichern im Theme Builder entfernt hat + (siehe unten, höchstens 50 je Aufruf); `draft_pending` zeigt ungespeicherte Änderungen im Theme Builder. Eine + aktive Vorlage, die nicht veröffentlicht ist (z. B. im Papierkorb), lässt sich nur deaktivieren. +- `lit/tb-create-template`: `title`, je Bereich (`header`, `body`, `footer`) `source`: + `"new"` (neues Layout; optional `nodes` wie bei `lit-divi-insert` und `title`), `"global"` (Kopf-/Fußzeile + der Standardvorlage mitbenutzen; nicht für `body`), `"default"` (nichts Eigenes, Standard bei fehlender + Angabe) oder `"hidden"` (Bereich ausblenden). Optional `use_on`/`exclude_from`. Alles oder nichts: schlägt + ein Schritt fehl, entfernt LIT MCP alles wieder. Antwort: `template_id`, `template_rev`, `enabled: false`, + `layouts`. +- `lit/tb-assign`: Bedingungen ersetzen (`template_id`, `template_rev`, `use_on`). Fehlt `exclude_from`, + bleiben die bisherigen Ausnahmen; `[]` entfernt sie. Der Probelauf einer aktiven Vorlage zeigt vorher/nachher + (`before`, `after`) und Überschneidungen. Die Antwort nennt die bisherigen Bedingungen unter `previous`. +- `lit/tb-activate`: `enabled: true` oder `false` (`template_id`, `template_rev`). Deaktivieren ist der + schnelle Weg zurück. Zum Aktivieren müssen alle Layouts der Vorlage noch aktiv sein. +- Layouts bearbeiten: `lit-divi-outline`, `lit-divi-node-get`, `lit-divi-insert`, `lit-divi-update`, + `lit-divi-move`, `lit-divi-delete` … mit der Layout-ID als `post_id` (aus `lit/tb-list`). Benutzt eine + andere Vorlage dasselbe Layout (`shared_with`), wirkt die Änderung auch dort. Das Modul „Beitragsinhalt“ + (`post-content`) gibt es nur im Inhalt-Layout, nicht in Kopf- oder Fußzeile. + +## Bedingungen + +Erlaubt sind nur die Bedingungen, die Divi selbst anbietet (`lit/tb-list` mit `conditions: true`), höchstens 50 +je Liste. Ein Objekt am Ende (Seite, Beitrag, Begriff, Person) muss existieren und für das Konto lesbar bzw. +zuweisbar sein; sonst lautet die Antwort wie bei einer unbekannten ID. Bedingungen für Autoren +(`archive:user:id:…`) verlangen das Recht, Benutzer aufzulisten (`list_users`) – strenger als Divi. Bedingungs-IDs +bestehen nur aus Buchstaben, Ziffern, `_`, `:` und `-`; eine Rolle, deren Kennung andere Zeichen enthält (manche +Rollen-Plugins), dann im Divi Theme Builder setzen. + +## Felder im Inhalt-Layout (Dynamic Content) + +Im Inhalt-Layout zeigen Platzhalter die Daten des jeweiligen Eintrags: `{{post:title}}`, `{{post:excerpt}}`, +`{{post:date}}` und Felder als `{{field:}}`. Welche Felder ein Inhaltstyp hat und welche Divi als Text +zeigt, liefert `lit/divi-dynamic-sources` mit `post_type`. Nur textartige Felder eignen sich: Bei Auswahlfeldern +zeigt Divi den gespeicherten Wert, nicht die Beschriftung; Beziehungs- und Dateifelder zeigen nur IDs, Pods mit +Tabellen-Speicherung gar nichts. + +LIT MCP prüft die Felder eines geänderten Inhalt-Layouts gegen die Inhaltstypen aus den Bedingungen +(`singular:post_type::…`) der Vorlagen, die das Layout benutzen, und warnt (`warnings`) – gespeichert wird +trotzdem. Das gilt für die Kurzform `{{field:…}}` und für Felder, die im Divi-Builder über „Benutzerdefiniertes +Feld“ gewählt wurden. Bei einem sichtbaren Layout stehen die Warnungen schon im Probelauf. Ohne solche Bedingung +bleiben die Felder ungeprüft (die Antwort sagt das). Kopf- und Fußzeile werden nicht geprüft. + +## Wenn der Theme Builder gerade offen ist + +- Hat der Theme Builder ungespeicherte Änderungen (Entwurf, `draft_pending`), lehnen `lit/tb-create-template`, + `lit/tb-assign` und das Aktivieren ab: erst dort speichern oder verwerfen. +- Deaktivieren (`lit/tb-activate` mit `enabled: false`) geht immer – auch dann, und auch bei einer aktiven + Vorlage im Papierkorb. Die Antwort warnt: Speichert jemand ein offenes Theme-Builder-Fenster, kann Divi + seinen eigenen Stand wiederherstellen; danach mit `lit/tb-list` prüfen. +- Speichert jemand in einem Theme-Builder-Fenster, das vor dem Anlegen einer Vorlage geöffnet wurde, entfernt + Divi die neue Vorlage wieder aus der Liste und löscht sie 7 Tage später. `lit/tb-list` zeigt sie dann unter + `detached` (mit `marked_unused`). Nur auf ausdrücklichen Wunsch: `lit/tb-assign` mit `reattach: true` ordnet + sie wieder zu – immer deaktiviert, nur Vorlagen unter `detached` (keine, die eine Theme-Builder-Vorlage der + Divi-Bibliothek nutzt) und nur, wenn ihre Layouts noch aktiv sind. + +## Rechte + +- WordPress: „Beiträge anderer bearbeiten“ (`edit_others_posts`) und „Design bearbeiten“ + (`edit_theme_options`); je Vorlage und Layout das Recht, sie zu bearbeiten; zum Anlegen zusätzlich + „veröffentlichen“ (Divi legt Vorlagen und Layouts sofort aktiv an). +- Divi → Rollen-Editor: „Theme Builder“ – standardmäßig nur für Administratoren. +- Fehlt eines davon, gibt es für dieses Konto keine Vorlagen und Layouts (`not_found` wie bei einer unbekannten + ID bzw. `forbidden`). Vorlagen, die das Konto nicht bearbeiten darf, erscheinen nirgends; aktive davon nur als + Anzahl (`hidden_live` in `lit/tb-list`, `hidden_overlaps` im Probelauf, `hidden_templates` beim Ändern eines + Layouts). +- Einstellungen → LIT MCP → Tools: Gruppe „Divi Theme Builder“ auf „nur lesen“ sperrt jedes Schreiben, auch über + die `lit-divi-…`-Werkzeuge und über globale Module in Layouts; deaktiviert gibt es keine Vorlagen und Layouts. + Nur dieser Gruppenschalter hält die `lit-divi-…`-Werkzeuge von Theme-Builder-Layouts fern – einzelne + `lit/tb-…`-Werkzeuge abzuschalten reicht dafür nicht. + +## Globale Module im Theme Builder + +`lit/library-make-global` wandelt keine Knoten in Theme-Builder-Layouts um. Stattdessen den Abschnitt mit +`lit/library-save` (`scope: "global"`) speichern und mit `lit-library-insert` (`mode: "global"`) einfügen +(`lit-guide` topic `divi-library`). + +## Rückgängig machen + +Vorlagen haben keine Revisionen: `lit/tb-assign` nennt die bisherigen Bedingungen (`previous`), `lit/tb-activate` +schaltet zurück; jede Änderung steht im Protokoll. Layouts erhalten wie Divi-Seiten eine Revision (`revision_id`), +aber `lit/revisions-list` und `lit/revision-restore` gelten nur für Seiten, Beiträge und andere Inhaltstypen, +nicht für Theme-Builder-Layouts: Die Antwort nennt deshalb `restore_url`, den Revisionsvergleich in WordPress, +in dem der Nutzer die Revision wiederherstellt. Danach die Seite im Divi-Builder bzw. auf der Website prüfen – +Divi zwischenspeichert Layouts und CSS; zeigt sie noch den alten Stand, „Statische CSS-Dateien leeren“ (Divi → +Theme-Optionen → Builder). Schneller Weg zurück bei einem sichtbaren Layout: die Vorlage mit +`lit/tb-activate` (`enabled: false`) ausblenden. Neu angelegte Vorlagen im Divi Theme Builder löschen. + +## Ablauf: Vorlage für einen Inhaltstyp (custom-post-type-vorlage) + +1. Inhaltstyp und Felder klären: `lit-content-types`, dann `lit/divi-dynamic-sources` mit `post_type`. +2. `lit/tb-list` lesen: gibt es schon eine Vorlage für den Typ? Gibt es eine globale Kopf-/Fußzeile? +3. Mit dem Nutzer den Aufbau abstimmen (welche Felder, welche Reihenfolge, Kopf-/Fußzeile global oder eigen). +4. `lit/tb-create-template` mit `use_on: ["singular:post_type::all"]`, `header`/`footer` meist + `{"source": "global"}`, `body` mit `source: "new"` und `nodes` (Überschrift `{{post:title}}`, Texte mit + `{{field:…}}`, das Modul `post-content`, wenn der Eintrag eigenen Inhalt hat). Warnungen zu Feldern beachten. +5. Das Inhalt-Layout bei Bedarf mit `lit-divi-outline`/`lit-divi-update` verfeinern (Layout-ID als `post_id`; + solange die Vorlage deaktiviert ist, ohne Probelauf). +6. Den Nutzer bitten, die Vorlage im Theme Builder anzusehen (Vorschau eines Eintrags ist erst nach dem + Aktivieren möglich). +7. Erst auf ausdrücklichen Wunsch: `lit/tb-activate` mit `enabled: true` – Probelauf zeigen, dann + `confirm: true`. diff --git a/docs/guides/workflows.md b/docs/guides/workflows.md index 87734f8..d13faf8 100644 --- a/docs/guides/workflows.md +++ b/docs/guides/workflows.md @@ -26,8 +26,9 @@ Vorhandene Abschnitte aus der Divi-Bibliothek zuerst suchen (`lit/library-list`) und mit `lit-library-insert` einfügen, statt sie neu zu bauen. Einen gelungenen Abschnitt für später speichern: `lit/library-save`. -Abschnitte zwischen Seiten kopieren: `lit/divi-node-copy`. Globale Module ändern und neue vorschlagen: -`lit-guide` topic `divi-library`. +Abschnitte zwischen Seiten kopieren: `lit/divi-node-copy`. Wiederkehrende Abschnitte finden und zu globalen +Modulen machen: `lit/library-find-similar`, `lit/library-make-global`. Globale Module ändern und neue +vorschlagen: `lit-guide` topic `divi-library`. ## Text auf vielen Seiten ersetzen @@ -68,3 +69,9 @@ geändert, sondern in `skipped_nodes` genannt – sie im Bibliothekseintrag änd 1. `lit-content-types` mit `post_type` → Pflichtfelder, Auswahlwerte, Beziehungsziele. 2. `lit-content-create` mit allen Pflichtfeldern in `fields`. 3. Beziehungen über IDs (vorher mit `lit-content-query` suchen). + +## Vorlage für einen Inhaltstyp (Theme Builder) + +Wie Einträge eines Inhaltstyps (z. B. Personen) auf der Website aussehen, legt eine Theme-Builder-Vorlage fest. +Ablauf und Regeln: `lit-guide` topic `theme-builder`. Neue Vorlagen sind deaktiviert; aktiviert wird nur auf +ausdrücklichen Wunsch mit `lit/tb-activate`. diff --git a/docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md b/docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md index 9ff14d8..ce11d67 100644 --- a/docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md +++ b/docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md @@ -220,3 +220,49 @@ Quellen der Web-Recherche: [Divi AI Agent – Ankündigung](https://www.elegantt Divi-Katalog beim Standard von 128M nicht mehr in den Speicher passt. - Server-Instructions: Zeile „Divi-Bibliothek: …“ nur bei aktiver, beschreibbarer Gruppe; MCP-Prompts gibt es noch nicht, der Ablauf „globale Module vorschlagen“ steht im Guide `divi-library` (E14). + +## 7. Umsetzung Etappe 3b (Plan 2026-09-27-etappe-3b-theme-builder.md) + +Rulings Q1–Q25: `.superpowers/sdd/2026-09-27-etappe-3b-theme-builder/progress.md`. + +- E7 präzisiert: Layouts und neue Vorlagen über `et_theme_builder_insert_layout`/`et_theme_builder_store_template` + mit Rechteprüfung davor und `wp_die`-Sperre währenddessen; Zuordnung zur Live-Liste, Bedingungen und + `_et_enabled` als Nachbildung von Divis Meta-Schreibweise (store_template würde bei bestehenden Vorlagen auch + Standard-Kennzeichen, Titel und Onboarding-Meta neu schreiben). Nach dem Anlegen Rücklesen; Abweichung = + alles zurücknehmen, auch Posts, die Divi vor einem Fehler schon eingefügt hatte (Q19). +- E8/Antwort F1 umgesetzt als eigenes Tool `lit/tb-activate` (statt `enabled` in `lit/tb-assign`); neue Vorlagen + sind immer deaktiviert. Jede andere Änderung, die Besucher sofort sähen (Bedingungen aktiver Vorlagen, Layouts + aktiver Vorlagen), ist zuerst ein Probelauf und braucht `confirm`; Layouts mit der Sicherung aus + `VisibleEdits` (Q2, Q9). Aktiv = zugeordnet und `_et_enabled`, unabhängig vom Post-Status (Q8). +- Der Ausschalter (`lit/tb-activate` mit `enabled: false`) geht immer: auch bei TB-Entwurf, dann mit Warnung + (Q20), und bei einer aktiven Vorlage im Papierkorb (Q16). +- TB-Editor: Ablehnung bei TB-Entwurf; neue Vorlagen hängen sofort an der Live-Liste; `lit/tb-list` zeigt vom + Theme Builder entfernte Vorlagen (`detached`), `lit/tb-assign` mit `reattach` ordnet sie deaktiviert wieder zu + (nur mit aktiven Layouts, Q8/B10). Befunde 10–12: `tests/Fixtures/divi/stage3b/README.md`. +- E3 für den Theme Builder: `edit_others_posts`, `edit_theme_options` (Q15) und Divis Rollen-Recht + „Theme Builder“, je Vorlage/Layout `edit_post`, zum Anlegen die Veröffentlichen-Rechte. Nicht bearbeitbare + Vorlagen antworten wie fehlende (Q18); aktive davon nur als Anzahl in Liste und Probeläufen + von `lit/tb-assign`/`lit/tb-activate` (Q21) und der Layout-Änderungen (`hidden_templates`; die Rückfrage + gilt trotzdem für alle aktiven Vorlagen, Q22). Globale Bibliotheksänderungen: Theme-Builder-Layouts in der + Verwendungsliste nur mit Theme-Builder-Zugang, sonst nur gezählt; bestätigen nur mit den Theme-Builder-Rechten + und schreibbarer Gruppe (Q17 M1). Revisionen von Layouts und Bibliothekseinträgen: `restore_url` zum + WordPress-Revisionsvergleich, `lit/revision-restore` bleibt bei den Inhaltstypen (Audit L3, Etappe U). +- Bedingungen nur aus Divis Katalog; Objekte lesbar (Begriffe: `assign_term`); strenger als Divi: + Autoren-Bedingungen (`archive:user:id:`) nur mit `list_users` (Q10). +- E9: eigene Gruppe „Divi Theme Builder“ (`divi-theme-builder`); alle neuen Tools indirekt, 22 direkte Tools. +- E10/E11 umgesetzt mit `Fingerprint` (Struktur/Inhalt). „Inhaltsgleich“ heißt gleiche Bytes einschließlich + Textteilen zwischen Blöcken und Inhalt von Core-Blöcken, nur Admin-Labels ausgenommen (Q25); ersetzt wird nur + auf Seiten und Beiträgen (`page`, `post`), die das Konto bearbeiten darf – Theme-Builder-Layouts auch nicht als + Quelle (Q24). Globale Einträge enthalten keinen Verweis auf einen Eintrag in irgendeiner Schreibweise (Q7). + D10-Semantik beim Speichern mehrerer Seiten: Abbruch bei der ersten gescheiterten Seite, Fehler nennt + geänderte, gescheiterte und unveränderte Seiten. +- E12: Feldprüfung im Inhalt-Layout als Warnung gegen die Inhaltstypen der Vorlagen-Bedingungen, beide + Feldformen (Q11). +- P13 (vollständiges Ausblenden der Bibliotheks-IDs, auch im Suchen-und-Ersetzen) und P10 (Zielregeln der + Vollform `post_link_url_*`, Bibliothekseinträge und Typen ohne Divi-Linkoption wie fehlende IDs, Q14) + umgesetzt. +- Restrisiken: P25 (ungewöhnliche Schreibweisen globaler Verweise), dazu Divis erneutes Auswerten von Blöcken in + Shortcode-Modulen und WooCommerce-Textmodulen (dokumentiert im Guide `divi-library`); das Präfilter-Orakel von + `lit/divi-replace-text` (Q13) und die Race-Bedingungen zwischen Prüfen und Schreiben bleiben für Etappe U. +- Server-Instructions: Zeile „Divi Theme Builder: …“ nur bei aktiver, beschreibbarer Gruppe; der Ablauf + „custom-post-type-vorlage“ steht im Guide `theme-builder`. diff --git a/docs/superpowers/notes/2026-09-27-etappe-3b-abnahme.md b/docs/superpowers/notes/2026-09-27-etappe-3b-abnahme.md new file mode 100644 index 0000000..f0eb17e --- /dev/null +++ b/docs/superpowers/notes/2026-09-27-etappe-3b-abnahme.md @@ -0,0 +1,145 @@ +# Etappe 3b – Manuelle Abnahme (Theme Builder, ähnliche Abschnitte, globale Module umwandeln) + +Automatisch geprüft sind bereits: + +- alle Unit-Tests (`composer test`) und Lint (`composer lint`); +- `tb-smoke.sh` (neu) und die sechs bisherigen Integrationsskripte `divi-library-smoke.sh`, + `divi-write-smoke.sh`, `divi-read-smoke.sh`, `mcp-smoke.sh`, `wp-basis-smoke.sh` und `media-admin-smoke.sh` + (alle in `tests/Integration/`). + +`tb-smoke.sh` prüft über den OAuth-Endpunkt, nur mit temporären Daten, die es danach wieder löscht. Die einzige +Vorlage, die es aktiviert, gilt nur für eine temporäre Entwurfsseite; Besucher sehen davon nichts. Geprüft werden: + +- Werkzeugliste: weiterhin 22 direkte Tools; `lit/tb-list`, `lit/tb-create-template`, `lit/tb-assign`, + `lit/tb-activate`, `lit/library-find-similar` und `lit/library-make-global` indirekt und erreichbar; + `lit-site-status` ohne Befund bei Divi; +- neue Vorlage: deaktiviert, in der Liste des Theme Builders, Inhalt-Layout über Divis eigene Funktionen mit dem + Inhalt des Plugins, wird nicht angezeigt; die Divi-Werkzeuge ändern das Layout einer deaktivierten Vorlage + sofort, „Inhalt posten“ (`post-content`) ist im Inhalt-Layout erlaubt; +- `lit/tb-assign`: bei einer deaktivierten Vorlage sofort gespeichert, fehlendes `exclude_from` lässt die + Ausnahmen stehen, `[]` entfernt sie, eine veraltete `template_rev` wird abgelehnt; +- `lit/tb-activate`: erst Probelauf, dann Aktivieren; die Entwurfsseite zeigt die Vorlage in der Vorschau, die + Startseite und Besucher sehen nichts; Änderungen am Layout (auch Löschen) und an den Bedingungen einer aktiven + Vorlage erst nach Probelauf; Deaktivieren; die Standardvorlage wird abgelehnt; +- ein ungespeicherter Entwurf im Theme Builder sperrt Anlegen, Zuordnen und Aktivieren; Deaktivieren geht + trotzdem, mit Warnung; +- eine Vorlage, die ein veraltetes Theme-Builder-Fenster aus der Liste entfernt hat: erscheint unter + `detached`, Aktivieren und Zuordnen ohne `reattach` werden abgelehnt, mit `reattach: true` ist sie wieder in + der Liste, deaktiviert und ohne „ungenutzt“-Markierung; +- ein Redakteur ohne Theme-Builder-Recht: `lit/tb-list` und Anlegen abgelehnt, Vorlagen und Layouts gelten als + nicht vorhanden; Gruppe „Divi Theme Builder“ auf „nur lesen“ bzw. deaktiviert wirkt auch auf die + Divi-Werkzeuge; +- Pods, mit einem eigenen temporären Pod: Divi bietet die Bedingung „Alle …“ für den neuen Inhaltstyp an; im + Inhalt-Layout erzeugt das Beziehungsfeld eine Warnung, das Auswahlfeld einen Hinweis, das Textfeld nichts; +- `lit/library-find-similar` (gleich und nur gleich aufgebaut) und `lit/library-make-global` (Umwandeln, + dann Ersetzen nach Probelauf; ein nur ähnlicher Abschnitt wird nicht ersetzt); +- P13 (keine Bibliotheks-IDs ohne Bibliothekszugang) und P10 (`post_link_url_page` nicht auf Beiträge); +- am Ende: Liste des Theme Builders und Aktiv-Zustand jeder Vorlage wie vorher, Optionen wiederhergestellt, + keine Beiträge, Metadaten, Begriffe, Protokollzeilen, Pods, Konten oder Rollen des Laufs übrig. + +Die folgenden Punkte brauchen den echten Theme Builder bzw. Visual Builder und einen verbundenen KI-Assistenten. +Abweichungen als kurze Notiz zurückmelden; sie werden Folgeaufgaben. + +Wichtig: Eine aktive Vorlage ändert sofort, was Besucher sehen. Für die Abnahme Vorlagen nur für Entwurfsseiten +oder für die „[Fixture] Personen“ aktivieren und im selben Schritt wieder deaktivieren. Die „[Fixture]“- und +„[Test]“-Inhalte nicht ändern. + +## Vorbereitung + +- [ ] DDEV läuft, Divi 5.2.1 ist aktiv, Pods ist aktiv, LIT MCP zeigt Version 0.5.0 (Plugins-Seite). +- [ ] Divi → Theme Builder öffnen und den Stand notieren: welche Vorlagen es gibt und welche aktiv sind + (Schalter in der Vorlage). Ist dort etwas ungespeichert, speichern oder verwerfen. Danach das Fenster + schließen – ein offenes Theme-Builder-Fenster kann beim späteren Speichern Vorlagen des Plugins wieder + entfernen. +- [ ] Unter Einstellungen → LIT MCP → Tools stehen die Gruppen „Divi“, „Divi-Bibliothek“ und „Divi Theme Builder“ + auf ihrem Standard (nicht „nur lesen“, nicht deaktiviert); in der Gruppe „Divi Theme Builder“ stehen + `lit/tb-list`, `lit/tb-create-template`, `lit/tb-assign` und `lit/tb-activate`. +- [ ] Nach einem Lauf von `tb-smoke.sh` ist nichts übrig: unter Seiten, Divi → Divi-Bibliothek und Divi → Theme + Builder nach „LIT Smoke“ suchen – keine Treffer; unter Pods Admin → Pods verwalten kein Pod „lit_smk_…“. +- [ ] Den KI-Assistenten neu verbinden, damit er die neuen Tools und Hinweise lädt. In einer neuen Unterhaltung + nennt der Hinweis des Servers den Theme Builder. +- [ ] Eine Entwurfsseite „Abnahme 3b – Vorlage“ anlegen (nicht veröffentlichen). Sie ist das Ziel der + Seiten-Vorlage unten. + +## 1. Vom Plugin angelegte Vorlage im Theme Builder und Visual Builder + +- [ ] Den KI-Assistenten bitten: „Lege im Theme Builder eine Vorlage ‚Abnahme 3b – Seite‘ für die Seite + ‚Abnahme 3b – Vorlage‘ an: globale Kopfzeile, im Inhalt eine Überschrift mit dem Seitentitel, ein Text und + der Seiteninhalt, Fußzeile ausgeblendet.“ Erwartet: er nimmt `lit/tb-create-template` und sagt, dass die + Vorlage deaktiviert ist und erst mit `lit/tb-activate` wirkt. +- [ ] Divi → Theme Builder öffnen: die Vorlage steht in der Liste, deaktiviert, Bedingung „Seiten: Abnahme 3b – + Vorlage“ (bzw. die Seiten-ID), Kopfzeile = globale Kopfzeile, Fußzeile ausgeblendet. +- [ ] Das Inhalt-Layout im Visual Builder öffnen: Überschrift mit dynamischem Inhalt „Titel“, Text und das + Modul „Inhalt posten“ sind vorhanden und bearbeitbar; der Builder meldet keine Fehler. Ohne Speichern + schließen. +- [ ] Im Theme Builder eine harmlose Änderung an einer anderen, deaktivierten Vorlage machen (z. B. Titel) und + „Änderungen speichern“: die neue Vorlage bleibt in der Liste. Die Änderung danach zurücknehmen. +- [ ] Den Assistenten bitten, die Vorlage aufzulisten: `lit/tb-list` zeigt sie deaktiviert, mit Layout-IDs und + `template_rev`. + +## 2. Aktivieren mit Probelauf + +- [ ] Den Assistenten bitten, die Vorlage zu aktivieren. Erwartet: er zeigt zuerst den Probelauf (Bedingungen, + Layouts, Überschneidungen mit anderen aktiven Vorlagen) und fragt nach, bevor er etwas speichert. +- [ ] Nach der Nachfrage ablehnen: im Theme Builder ist die Vorlage weiter deaktiviert. +- [ ] Erneut bitten und zustimmen: im Theme Builder ist sie aktiv. Die Vorschau der Entwurfsseite zeigt die + Vorlage (Überschrift mit dem Seitentitel, Text, Seiteninhalt, keine Fußzeile). Die Startseite und andere + Seiten sehen aus wie vorher. +- [ ] Den Assistenten bitten, im Inhalt-Layout den Text zu ändern: er meldet, dass die Vorlage aktiv ist, zeigt + den Probelauf und fragt nach. Nach Zustimmung zeigt die Vorschau den neuen Text; unter „Revisionen“ des + Layouts steht der alte Stand. +- [ ] Den Assistenten bitten, die Bedingungen der aktiven Vorlage zu ändern (z. B. eine Ausnahme hinzufügen): + Probelauf mit vorher/nachher, erst nach Zustimmung gespeichert. +- [ ] Den Assistenten bitten, die Vorlage wieder zu deaktivieren (Probelauf, Zustimmung): im Theme Builder ist + sie deaktiviert, die Vorschau der Entwurfsseite zeigt wieder das normale Seitenlayout. +- [ ] Unter Einstellungen → LIT MCP → Protokoll stehen Anlegen, Aktivieren, Layout-Änderung, Bedingungen und + Deaktivieren. + +## 3. Bedingungen für einen Pods-Inhaltstyp + +- [ ] Den KI-Assistenten bitten: „Lege eine Vorlage für alle [Fixture] Personen an: globale Kopfzeile, im Inhalt + Name, Funktion, Stufe und Mentor, Fußzeile wie bisher.“ Erwartet: er liest vorher die Felder + (`lit/divi-dynamic-sources` für `lit_fixture_person`), legt die Vorlage deaktiviert an und weist darauf hin, + dass Divi beim Mentor nur die ID zeigt und bei der Stufe den gespeicherten Wert statt der Beschriftung. +- [ ] Divi → Theme Builder: die Vorlage hat die Bedingung „Alle [Fixture] Personen“ und ist deaktiviert. +- [ ] Das Inhalt-Layout im Visual Builder öffnen: die Module zeigen dynamischen Inhalt „Benutzerdefiniertes + Feld“ mit „role“, „level“ und „mentor“ und lassen sich im Dialog bearbeiten. Ohne Speichern schließen. +- [ ] Den Assistenten bitten, die Bedingung auf genau eine Person zu ändern (Erika Muster), dann zu aktivieren + (Probelauf, Zustimmung): die Seite dieser Person zeigt Name und Funktion („Geschäftsführung“), die Stufe als + „senior“; die Seite von Max Beispiel sieht aus wie vorher. Danach sofort wieder deaktivieren lassen. + +## 4. Ähnliche Abschnitte zu globalen Modulen machen + +- [ ] Zwei Entwurfsseiten „Abnahme 3b – A“ und „Abnahme 3b – B“ mit demselben Abschnitt bauen (im Visual Builder + den Abschnitt von A kopieren und in B einfügen), dazu eine Seite „Abnahme 3b – C“ mit demselben Abschnitt, + aber einem geänderten Text. +- [ ] Den Assistenten bitten, wiederkehrende Abschnitte zu finden und zu globalen Modulen zu machen. Erwartet: + `lit/library-find-similar` nennt B als gleich und C als nur gleich aufgebaut; er schlägt vor, den Abschnitt + von A umzuwandeln, und fragt nach. +- [ ] Nach Zustimmung: `lit/library-make-global` legt einen globalen Eintrag an und setzt ihn auf Seite A als + Instanz ein; für Seite B zeigt er zuerst den Probelauf und ersetzt erst nach Zustimmung. Seite C bleibt + unverändert. +- [ ] Divi → Divi-Bibliothek: der Eintrag ist ein globaler Abschnitt. +- [ ] Seiten A und B im Visual Builder öffnen: der Abschnitt ist als globales Modul gekennzeichnet; eine + Änderung daran im Builder erscheint nach dem Speichern auf beiden Seiten. Unter „Revisionen“ von B steht der + Stand vor dem Ersetzen. +- [ ] Den Assistenten bitten, in eine andere Entwurfsseite einen neuen Abschnitt einzufügen, der wie der + Bibliotheksabschnitt aufgebaut ist: die Antwort weist auf den Bibliothekseintrag hin. + +## 5. Rechte + +- [ ] Optional mit einem Redakteurs-Konto (Rolle unter Einstellungen → LIT MCP → Zugriff freigeben, den + Assistenten in einem privaten Browserfenster mit diesem Konto verbinden): der Assistent sieht keine Vorlagen + und erklärt, dass die Berechtigung für den Theme Builder fehlt. Danach die Freigabe zurücknehmen und wieder + als Administrator verbinden. +- [ ] Gruppe „Divi Theme Builder“ auf „nur lesen“: `lit/tb-list` geht, Anlegen und Aktivieren fehlen, Änderungen + an Theme-Builder-Layouts mit den Divi-Werkzeugen werden abgelehnt. Danach auf den Standard zurückstellen. + +## Aufräumen + +- [ ] Divi → Theme Builder: die Vorlagen „Abnahme 3b …“ und die Personen-Vorlage löschen und „Änderungen + speichern“. Danach dieselben Vorlagen aktiv wie in der Vorbereitung notiert. +- [ ] Die Seiten „Abnahme 3b …“ und den globalen Eintrag in der Divi-Bibliothek löschen (auch aus dem Papierkorb). +- [ ] Optional: die Einträge der Abnahme im Protokoll-Tab über „Protokoll leeren“ entfernen. +- [ ] Ob Pods wieder deaktiviert werden soll, entscheidet der Benutzer; die Fixture-Daten („[Fixture] Personen“) + bleiben stehen, bis ihr Aufräumen laut `tests/Fixtures/divi/stage3b/README.md` gewünscht ist. diff --git a/docs/superpowers/notes/2026-09-27-etappe-3b-rulings.md b/docs/superpowers/notes/2026-09-27-etappe-3b-rulings.md new file mode 100644 index 0000000..13ecaef --- /dev/null +++ b/docs/superpowers/notes/2026-09-27-etappe-3b-rulings.md @@ -0,0 +1,111 @@ +# Etappe 3b – Entscheidungen (Rulings), Befunde und offene Punkte + +Aus dem SDD-Ledger übernommen, 2026-09-27. Security-Audit: 2026-09-27-etappe-3b-security-audit.md. Abnahme: 2026-09-27-etappe-3b-abnahme.md. + +## Offen für spätere Etappen +- Etappe U: Revisionen von Theme-Builder-Layouts und Bibliothekseinträgen über MCP wiederherstellen (jetzt nur restore_url zum WordPress-Revisionsbildschirm, Audit L3); Oracle über Vorfilter/next_cursor von replace-text (Q13); Race beim Anhängen von Vorlagen; try/catch um Divi-Aufrufe; tree_rev-Race; DNS-Pinning; Build-Hygiene. +- Etappe 4: Presets können Lottie-Quelle/Interactions tragen – Preset-Schreiben braucht dieselben Builder-Sperren. +- Bewusst belassen: {{field:}} im Body-Layout warnt nur (Anzeige-Parität mit Divi, Audit I1); Bestätigung ist nicht an den Probelauf gebunden (wie 3a, Audit I3); gemeinsamer confirm-Helfer verworfen (Q17 M4). + +## Ledger + +Spec: docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md; decisions: docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md (§5 user answers binding, F1: TB templates disabled by default + explicit activation tool); rulings 1a/1b/2a/2b/3a in docs/superpowers/notes/. +Branch: etappe-3 (base 0edb474; 3a merged to main 4f05388), in place. Pods 3.3.9.2 activated locally by user permission (2026-09-27), no pods defined yet. +Process rules carried over: opus reviewers for security-relevant tasks; controller verifies live with benign probes if reviewer agents stop; smoke guard; commits by explicit path; final review + security audit + one fix wave (breaker after round 2). + +## Pre-flight rulings +Pre-flight (opus): 1 Critical, 6 Important, 16 Minor, 7 Info; all anchors match once; full tables in preflight.md (B1–B23). +- Ruling Q1 (B1, Critical): 3b fixtures use their own pod `lit_fixture_person` („[Fixture] Personen“) that no smoke deletes; wp-basis-smoke's shared lit_person stays as is — cost if wrong: none +- Ruling Q2 (B2): extract GlobalEdits' backup + confirm-merge into a shared helper; live TB layout edits reuse it (backup when revisions off, P9 refusals) — cost if wrong: none +- Ruling Q3 (B3): lit/tb-assign leaves omitted exclude_from unchanged; dry run shows both lists (before/after) — cost if wrong: none +- Ruling Q4 (B4): TemplateBuildTest uses assertPartials — cost if wrong: none +- Ruling Q5 (B5, USER-APPROVED 2026-09-27): Task 1 Step 8 may enable the fixture template briefly, conditions limited to exactly one „[Fixture]“ person, disabled again in the same step (try/finally), verified off afterwards; Step 6 does not change Divi's post-type setting — cost if wrong: momentary visible change on one fixture URL +- Ruling Q6 (B6): tb-smoke creates/deletes its own temporary pod, never touches lit_person or lit_fixture_person — cost if wrong: none +- Ruling Q7 (B7): UsageMatcher::referencesAny() refuses any global reference in every spelling Divi renders; used by lit/library-make-global and lit/library-save scope global (no self-reference, no nested refs in shortcode text) — cost if wrong: none +- Ruling Q8 (B8–B21 minors): adopt as proposed in preflight.md (template live = attached+enabled regardless of post status; layouts must be published before reattach/activate; tb-list hides IDs of templates the user can't edit; test/count/doc fixes; smoke library-id check via jq) — cost if wrong: small +- Ruling Q9 (B22 / plan open point): live-visible TB edits use dry run + confirm (with Q2 backup), not refusal — matches F2 — cost if wrong: none +- Ruling Q10 (B23 / plan open point): author conditions require list_users — only admins hold Divi's theme_builder right by default — cost if wrong: editors with theme_builder can't set author conditions + +## Progress +- Task 1: dispatched (opus, BASE 0edb474; Q1 own pod, Q5 user-approved brief activation) +- Task 1: done (c615522; fixtures: pod lit_fixture_person 2381, persons 2385/2386, body layout 2391, template 2394 disabled, global header 1061; brief activation only for Erika, off again; live site unchanged). Finding 10: stale TB editor save detaches our template silently + marks unused (7-day trash) → confirms plan mitigation (detached/reattach). Finding 12: Divi dialog writes Pods fields as post_meta_key + select_meta_key "custom_meta_" (empty meta_key); plugin writes custom_meta_; choice shows stored value; relation shows raw ID +- Ruling Q11: Task 11 (Pods field check) and any usage/field detection recognize both forms (custom_meta_ and post_meta_key/select_meta_key); S29/S30 meta-key rules already cover both on write — cost if wrong: missed warnings +- Ruling Q12: Task 1 (fixtures/test data only) verified by controller from report + commit instead of a separate review; fixtures are re-checked by later task reviews — cost if wrong: none +- Task 1: complete (0edb474..c615522) +- Tasks 2–3: dispatched (opus, BASE c615522, one commit each) +- Tasks 2–3: implementer done (06f1379 P10 PostIdParser + type-exact post_link_url; c7ce924 P13 revealsIds + replace-text skipped_nodes ID-oracle fix; 1923 tests); concern: replace-text prefilter/next_cursor partial oracle → U (Ruling Q13) +- Tasks 2–3: review (opus): both Approved, live-verified (P10 nothing widened; P13 no ID in any path, digit oracle fixed, nur lesen reveals). Important (pre-existing): {{link:}} type oracle ("Unbekannter Name post_link_url_et_pb_layout" vs "gibt es nicht") +- Ruling Q14: WpLinkTargets treats et_pb_layout (and any post type without a Divi post_link_url option, incl. internal _-types) as not found → uniform message; minors: merge stacked docblocks WordPressIntegration.php:52-56, DiviTextSearch withoutLibraryIds doc (hides more for undecodable opaque openers) — fix round 1 now — cost if wrong: none +- Ruling Q13 (confirmed): replace-text prefilter/next_cursor oracle deferred to U, documented as parity with WordPress' own content search +- Tasks 2–3: fix round 1 (Q14; 82ef69a; 1926 tests): WpLinkTargets linkable = Divi's et_builder_get_public_post_types() minus attachment/_-types/et_pb_layout; identical answers library vs missing ID (test); DiviTextSearch strips IDs only in global nodes — verified by controller against report/tests +- Tasks 2, 3: complete (c615522..82ef69a) +- Task 4: dispatched (opus, BASE 82ef69a; wp_die guard, ThemeBuilderAccess, Q8 minors) +- Task 4: implementer done (d04a508; 1958 tests); helpers for T7/T9/T10: ThemeBuilderAccess::canEditTemplate(), layoutsRefusal(); attach race → U +- Task 4: review (opus): Approved; wp_die guard live-verified (CLI/AJAX/JSON, nested, no output/status leak, handlers restored, nothing written); ThemeBuilderAccess = Divi's save gate; P12 no oracle; isLive matches Divi +- Ruling Q15: ThemeBuilderAccess::canUse additionally requires edit_theme_options (Divi's TB admin page gate) — never laxer than Divi's UI — cost if wrong: roles with theme_builder but without edit_theme_options can't use TB tools +- Ruling Q16 (T4 minors): T10 must be able to deactivate an attached+enabled template that sits in the trash (raw lookup for the off-switch, fail-safe); docblock note on third-party wp_die inside hooks; canEditTemplate docblock (IDs from Divi's list); dedupe GlobalUsages::THEME_BUILDER_TYPES/DiviSaver cache clear/compat row → final wave +- Task 4: fix round 1 (Q15 + docblocks) dispatched +- Task 4: fix round 1 (Q15; 4ff1c1e; 1959 tests) — verified by controller +- Task 4: complete (82ef69a..4ff1c1e) +- Task 5: dispatched (opus, BASE 4ff1c1e; Q2 shared backup/confirm helper, Q9, Q8) +- Task 5: implementer done (b1dd35a; 1980 tests; smoke ok); VisibleEdits shared helper (GlobalEdits/ThemeBuilderEdits/DiviWriter); TB layouts get Divi flags (as Divi writes); T11 adds ?DynamicFields after VisibleEdits param +- Task 5: review (opus): Approved; live: 9 write paths on live layout → confirm_required/merged confirm/replace-text refused, confirmed save with revision, backup when revisions off; access P12 no oracle; post-content body-only on all paths; GlobalEdits byte-identical after Q2 +- Ruling Q17 (T5 minors): M1 GlobalUsages P8a TB rows reuse ThemeBuilderAccess::canUse (not only theme_builder) → final wave (security, must); M2 confirm description mentions live TB layouts → T16; M3 library-path P9/P14 wording test → final wave; M4 optional VisibleEdits::confirmed → final wave +- Task 5: complete (4ff1c1e..b1dd35a) +- Tasks 6–7: dispatched (opus, BASE b1dd35a, one commit each; Q10, Q8, group wiring) +- Tasks 6–7: implementer done (ac69fdf TemplateConditions: 34 Divi IDs, 27 round-trip live, assign_term for terms, list_users for authors, uniform missing/unreadable; 54922a9 tb-list + group live: nur lesen blocks TB writes, disabled → not_found; 1998 tests) +- Tasks 6–7: review (opus): both Approved; live: 34 Divi condition IDs covered by construction, parsing safe, uniform missing/unreadable, tb-list leaks nothing, detached shown, group matrix verified, never enabled a template +- Ruling Q18: ThemeBuilderAccess::template() answers not_found (identical to missing) for templates the user can't edit (canEditTemplate), consistent with tb-list (Q8/B11) — implement in T8 (first caller) incl. test; minors → final wave: DETACHED_LIMIT applied after edit filter + truncation note, invalid id message, role-slug regex doc +- Tasks 6, 7: complete (b1dd35a..54922a9) +- Task 8: dispatched (opus, BASE 54922a9; F1, Q4, Q8, Q18, rollback reuse) +- Task 8: implementer done (42b9b38; 2020 tests; live create: disabled+attached, tb-list shows, fixture page byte-identical, cleanup complete); controller deleted 3 leftover change-log rows of the live check (1248–1250) +- Task 8: review (opus): never-enabled verified on all paths incl. forced read-back; rollback per step live; unsaved-editor detection = Divi's hasDraft; rights live. Important: post inserted by Divi before it fails (throw inside store_template/insert_layout after wp_insert_post) not tracked → orphan published et_template (disabled/detached, no visible effect); minors: orphan layout_type term relationships after delete, partial() names deleted IDs, refuse() loses details, default template editability +- Ruling Q19: while guarded() runs, record IDs of posts inserted of the expected types (wp_insert_post action) and hand them over (exception subclass carrying IDs) so TemplateBuild rolls them back; undo() also removes layout_type term relationships; partial() lists only IDs whose delete failed; refuse() keeps all failure details; defaultTemplate() respects canEditTemplate — fix round 1 — cost if wrong: none +- Task 8: fix round 1 (Q19; 2827fd4; 2030 tests; live wp_die-in-hook on template and layout save → write_failed, nothing left) — verified by controller; residual (throw between DB insert and first cache clean) documented, accepted +- Task 8: complete (54922a9..2827fd4) +- Tasks 9–10: dispatched (opus, BASE 2827fd4, one commit each; Q3, Q9, Q8, Q16, Q18) +- Tasks 9–10: implementer done (0975b2f tb-assign; 64fc439 tb-activate; 2056 tests; live on temp draft only, visible site unchanged, cleanup verified) +- Ruling Q20: the off-switch (tb-activate enabled:false) is exempt from the unsaved-editor refusal (E7) — reducing visibility must always be possible; result warns that saving an open Theme Builder editor may restore its own state — cost if wrong: a stale editor may re-enable (warned) +- Tasks 9–10: review (opus): spec ✅ both; live: no path makes a template visible except confirmed activate (reattach disables first), Q3/Q9 dry runs, Q16 trash off-switch no oracle, rights, change log; visible site unchanged at every step; changes requested: overlap lists leak uneditable live templates (Q8/B11), Q20 not yet implemented +- Ruling Q21: one shared ThemeBuilderAccess::editableLiveTemplates() used by tb-list, tb-assign and tb-activate dry runs; uneditable live templates only as a count („weitere aktive Vorlagen ohne Berechtigung“); Q20 implemented (off-switch skips draft refusal + warning); minors: dedupe overlaps vs possible_overlaps, reattach records previous enabled state in previous/log, stricter exclude_from null handling, live from $after->isLive() — fix round 1 +- Tasks 9–10: first fix-round implementer stopped by user mid-way; user said 'weiter' → fresh opus implementer continues from uncommitted partial work (Q20, Q21, minors) +- Tasks 9–10: fix round 1 (Q20, Q21, minors; a31d434; 2068 tests); tb-list hidden_live count; re-review running +- Tasks 9–10: re-review clean (Q21 no leak, counts only; Q20 live with a real Divi draft; minors fixed) +- Ruling Q22 (must, final wave): ThemeBuilderEdits::showing()/listing() use editableLiveTemplates() (count for hidden) — same leak class as Q21, in layout-edit confirm details — cost if wrong: none +- Tasks 9, 10: complete (2827fd4..a31d434) +- Task 11: dispatched (opus, BASE a31d434; Q11 both forms, Q21 pattern) +- Task 11: implementer done (6be2e65; 2084 tests; live read-only on 2391); DynamicFields::metaKeyOf reads both forms (Q11), choice/relation notes, editable templates only, dry run shows field warnings +- Ruling Q23: the plan's review checkpoint after Task 11 is covered by per-task reviews + the final whole-branch review (every task so far reviewed with live checks) — no separate checkpoint review — cost if wrong: late discovery of cross-task drift (final review) +- Task 11: review (sonnet): Approved; metaKeyOf matches Divi's DynamicContentOptionPostMetaKey branch for branch, live fixture 2391 unchanged, no leak, named-arg wiring correct +- Task 11: complete (a31d434..6be2e65) +- Tasks 12–13: dispatched (opus, BASE 6be2e65, one commit each; bounded, no leaks, cheap write-time warning) +- Tasks 12–13: implementer done (f82964f find-similar: caps 200 posts/200 matches/5000 nodes, batch loads; 4f70886 write-time warning: ≤200 library sections cached per request; 2102 tests; smoke ok) +- Tasks 12–13: review (sonnet): both Approved; live find-similar as admin + temp author: no oracle (P12 forbidden for foreign reference), pagination exact, excluded types uniform; minors → final wave: excluded-type message names "" type; multi-type own-posts restriction documented +- Tasks 12, 13: complete (6be2e65..4f70886) +- Tasks 14–15: dispatched (opus, BASE 4f70886, one commit each; Q7 referencesAny, reuse library-save/GlobalInstance/VisibleEdits, per-page results) +- Tasks 14–15: implementer done (44c1804 make-global + NewLibraryEntry shared + referencesAny; e733065 replace identical occurrences: pre-check all targets, per-page commit+revision, stop at first failure with changed/failed/unchanged lists; 2160 tests; smokes ok) +- Ruling Q24: make-global refuses Theme Builder layouts also as SOURCE (stricter than brief; keeps TB edits on the TB tools) — cost if wrong: a TB section can't be made global directly (copy to a page first) +- Tasks 14–15: review (opus, SEC): T14 Approved (referencesAny 27 spellings live, rights, rollback); T15 Important: Fingerprint::body ignores text parts / core-block inner HTML → "identical" target with different visible content replaced (live, silent content swap); minors: revision note on page-N failure, "pages and posts only" wording vs Divi-enabled types; Low: Divi re-runs do_blocks in shortcode module/Woo checkout text → escaped global-layout inside JSON value expands (document with P25) +- Ruling Q25: Fingerprint content hash includes non-whitespace text parts and full bytes of opaque blocks (structure hash unchanged); plans() additionally compares normalized serialized bytes of the node; regression tests for both variants; find-similar "identical" uses the same fix; failed-page message keeps "Revision X bleibt erhalten"; scope check matches wording (only page/post types, or wording = Divi-enabled types) — decide pages+posts only; Low item documented with P25 — fix round 1 — cost if wrong: none +- Tasks 14–15: fix round 1 (Q25; 4e95983; 2170 tests; smokes ok; live: altered copies no longer identical, forced confirm refused, pages unchanged) — verified by controller against the reviewer's repro scenario +- Tasks 14, 15: complete (4f70886..4e95983) +- Tasks 16 (docs, opus) and 17 (tb-smoke + acceptance, opus) dispatched in parallel, BASE 4e95983 (disjoint files) +- Task 16: implementer done (3db96a3; 2173 tests; v0.5.0); docs honest about open Q22/Q17-M1 (final wave) +- Task 16: review approved (spec ✅, quality approved; 2173 tests re-run green; only cosmetic line-wrap nit) +Task 16: complete +- Task 17: implementer done (0b7a1c2; 2173 tests; all 7 smokes green); deviations: enabled template on temp draft page via preview, temp pod never enabled, read-only → not found. Review dispatched (sonnet) +- Task 17: review approved (spec ✅, quality approved; smoke re-run live green, no leftovers, TB baseline 1065=0 1066=0 2394=0) +Ruling: Claude-Session trailer after Co-Authored-By is correct — the session's current attribution instruction requires both lines; common.md predates it — cost if wrong: cosmetic +Task 17: complete +- Final whole-branch review + security audit dispatched in parallel (opus), range 0edb474..0b7a1c2 +- Final review done: 0 critical, 3 important (I-1 Q22, I-2 Q17M1 + TB-ID leak to library-only editors, I-3 Q17M3), 12 minor; report final-review.md. Ruling: drop Q17 M4 (shared confirm helper) — 13 call sites project-wide, out of scope — cost: minor duplication stays. Waiting for security audit before fix wave. +- Security audit done: 0 C/H/M, 4 Low (L1=Q17M1, L2=Q22, L3 TB/library revisions not restorable via MCP, L4 reattach accepts TB-library templates), 3 Info; report security-audit.md +Ruling: fix wave covers I-1..I-3, L1..L4, all final-review minors except Q17 M4 — both reports agree, all small — cost if wrong: extra churn +Ruling: L3 gets the minimal fix (restore_url to WP revision screen + corrected docs), not MCP restore for TB/library types — the full route needs new access wiring, belongs to Etappe U — cost: restore stays a manual step in wp-admin +Ruling: I1 stays a warning (display parity, preflight ruling), I2 documented in guide, I3 unchanged like 3a — cost: none beyond 3a status quo +- Final fix wave dispatched (opus), BASE 0b7a1c2 +- Fix wave done: 7590da4 43dc3fb 79f2fdb dcae2d4 e64d32b 5290a9d e7bbe1e 14bc954 6288acf 50cfa42 beb8706; 2199 tests, lint clean, tb/library/write smokes green, TB baseline off +Ruling: accept stricter I-2 (read-only TB group also blocks global edits reaching TB layouts) — matches the guide and group semantics — cost: a global edit may be blocked where the owner expected it to pass; one-line revert noted in fixwave-report +Ruling: accept M-9 forbidden for accounts without any TB rights (tool-level, ID-independent, same as tb-list) — no existence oracle since answer does not depend on the ID; Q18 not_found stays for per-template refusals — cost: answer-code change for clients +- Scoped re-review dispatched (opus), range 0b7a1c2..beb8706 +- Re-review: Approved (all in-scope findings addressed; N-1 minor docblock, N-2/N-3 info). Ruling: fix N-1, partial-write restore_url (completes L3) and make-global backup hint now via resumed fix-wave agent; N-2 as guide sentence; N-3 left — all tiny, closes L3 cleanly — cost: one more small commit set diff --git a/docs/superpowers/notes/2026-09-27-etappe-3b-security-audit.md b/docs/superpowers/notes/2026-09-27-etappe-3b-security-audit.md new file mode 100644 index 0000000..c4ad1dd --- /dev/null +++ b/docs/superpowers/notes/2026-09-27-etappe-3b-security-audit.md @@ -0,0 +1,275 @@ +# Etappe 3b (Divi Theme Builder): security audit + +- **Scope:** branch etappe-3, `0edb474..0b7a1c2` (HEAD). New: `src/Integrations/Divi/ThemeBuilder/*`; library find-similar and make-global (`Library/LibraryFindSimilarAbility`, `LibraryMakeGlobalAbility`, `NewLibraryEntry`, `SimilarSections`, `UsageMatcher::referencesAny`); `Pages/VisibleEdits`, `Pages/DynamicFields`, `Pages/LayoutBuilder`, `BlockTree/Fingerprint`, `Compose/PostIdParser`, `WpLinkTargets`, and the loader, writer, saver, exposure and wiring changes (`DiviPageLoader`, `DiviWriter`, `SaveScope`, `DiviIntegration`, `Plugin`). +- **Environment:** DDEV (WordPress 7.1.x, Divi 5.2.1, Pods 3.3.9.2), single site, only user `admin` (no editor or author accounts), `DISALLOW_UNFILTERED_HTML` off, `et_pb_role_settings` empty (Divi defaults), `EMPTY_TRASH_DAYS` 30. Live Theme Builder post 1064 lists templates 1065 (default), 1066 and 2394, all with `enabled: false`, and there is no Theme Builder draft. +- **Method:** + - Read every new or changed `src/` file in the range and traced each write path to its checks. + - Read the Divi code the plugin mirrors or relies on: `theme-builder.php` (`et_theme_builder_get_template`, `_get_template_layouts`, `_trash_draft_and_unused_posts`, `store_template`), `ThemeBuilderRequest::get_template`, `local-library.php`, `core.php` `et_pb_is_allowed`, `DynamicContentOptionsController`, and `DynamicContentOptionPostMetaKey`. + - Ran four read-only `wp eval-file` probes. The probe files were temporary files in the web container's `/tmp`, deleted afterwards. They covered the live template state, the post type caps of the TB types, `wp_revisions_enabled` on the TB layouts, and Divi's flat condition list. + - Simulated the editor and author roles with an in-memory `WP_User` (ID 987654, never saved), because the site has no such accounts. + - Nothing was written to the site. `git status` is clean. `vendor/bin/phpunit`: 2173 tests, OK. + +**Result:** no Critical, High or Medium findings. +- The known items hold as recorded: Q22 and Q17 M1 are confirmed, both Low, with exact fixes below. +- The deferred items (Q13 prefilter oracle, attach race, try/catch around Divi calls, DNS pinning, tree_rev race, preset gates) are not more severe than recorded. None of them reaches Theme Builder objects through a new path; see "Deferred items re-checked". +- Findings: four Low (two of them Q17 M1 and Q22) and three Info notes. + +--- + +## Findings (most severe first) + +### L1 – Low (authorization, disclosure): P8a treats Theme Builder rows with Divi's `theme_builder` option alone, and the dry run names Theme Builder layouts to accounts that ThemeBuilderAccess hides them from (ruling Q17 M1, confirmed) + +**Where** +- `src/Integrations/Divi/Library/GlobalUsages.php:218-225` (`editable()`): a TB row counts as editable when `roles->allows('theme_builder')` and `edit_post` both hold. It does not check `edit_others_posts`, `edit_theme_options` (ruling Q15), or the group "Divi Theme Builder" (disabled or "nur lesen"). +- `GlobalUsages.php:98-106` (`report()`): a row is listed with `post_id`, `title`, `status` and `type` whenever `read_post` holds. For the TB layout types (capability type `post`, published, `map_meta_cap`) `read_post` is plain `read`. Checked in-memory: author `read_post(1061) = true`. +- Wiring: `src/Integrations/Divi/DiviIntegration.php:197` (GlobalEdits) and `src/Integrations/Divi/Library/LibraryIntegration.php:64` (LibraryListAbility) both build `new GlobalUsages(null, $roles)` without the Theme Builder access. +- Compare `ThemeBuilderAccess::canUse()` (`ThemeBuilderAccess.php:51-61`), and `layout()`/`visible()` (`:63-67`, `:87-98`). Every direct TB path answers a TB ID like a missing one without these rights (P12). + +**Scenario** +1. The site owner enables the Divi role option `theme_builder` for editors in Divi's role editor. Editors still lack `edit_theme_options`, so they cannot open Divi's Theme Builder page, and every `lit/tb-*` tool and every Divi tool on a TB layout refuses them (Q15). +2. A global module sits in the Theme Builder header of a live template. +3. The editor has `edit_global_library` and `edit_post` on the entry. They edit the entry with any Divi tool and confirm. +4. P8a passes the header row (`theme_builder` on, `edit_post` via `edit_others_posts`). The site-wide header changes, which Q15 says this account may not do. +5. The same holds when the group "Divi Theme Builder" is set to "nur lesen" or disabled for the connection. +6. Disclosure, any account with library rights (authors too): the dry run's `affected` lists the TB layout's ID, title and type. `blocked_ids` names it by ID. For the same account, `lit-divi-outline post_id=` answers "nicht gefunden". +7. Mitigation: Divi's own Visual Builder allows the same global edit from any page (parity with Divi, as audit 3a L6). The impact is the plugin's own stricter rule P8a/Q15, not an escalation beyond Divi. + +**Evidence:** by reading. Post type caps and `read_post` for editor and author were checked live, in-memory. The DDEV data has no global instance inside a TB layout, so no end-to-end probe was run. + +**Fix** +- Give GlobalUsages the Theme Builder decision as a closure, so the Library namespace keeps no dependency on ThemeBuilder: + ```php + // GlobalUsages + /** @param (Closure(): bool)|null $themeBuilder Whether TB objects are open to this account (ThemeBuilderAccess). */ + public function __construct(private ?\wpdb $db = null, ?DiviRoles $roles = null, private ?Closure $themeBuilder = null) + … + private function themeBuilderOpen(): bool + { + return $this->themeBuilder !== null + ? ($this->themeBuilder)() + : $this->roles->allows('theme_builder') // fail-safe default = ThemeBuilderAccess::canUse() + && current_user_can('edit_others_posts') + && current_user_can('edit_theme_options'); + } + + private function isThemeBuilder(array $row): bool + { + return in_array($row['type'], self::THEME_BUILDER_TYPES, true); + } + + private function editable(array $row): bool + { + if ($this->isThemeBuilder($row) && ! $this->themeBuilderOpen()) { + return false; + } + + return current_user_can('edit_post', $row['id']); + } + ``` +- In `report()`, replace `:98` with `$readable = (! $this->isThemeBuilder($row) || $this->themeBuilderOpen()) && current_user_can('read_post', $id);`. TB rows the account cannot use then only count: `hidden_count`, `blocked_hidden`. They are still blocked, never named (P12, as ThemeBuilderAccess). +- Wiring, at both call sites (`DiviIntegration.php:197`, and through `DiviIntegration::library()` → `LibraryIntegration.php:64`): + ```php + new GlobalUsages(null, $this->access->roles(), fn (): bool => $this->themeBuilderAccess->visible() + && $this->themeBuilderAccess->writableGate() === null) + ``` + `visible()` already contains `canUse()`. The `writableGate()` part makes "nur lesen"/disabled for the TB group also block global edits that would change a live TB layout. If the controller wants only the Q17 M1 scope, use `canUse() === null` for `editable()` and `visible()` for the listing. +- Tests: + - A TB row with `theme_builder` on but without `edit_theme_options` is blocked (hidden count, no ID). + - A TB row with the group "nur lesen" is blocked. + - An author sees no TB row in `affected`. + - A page row is unchanged. + +### L2 – Low (disclosure): the confirm details of a live TB layout list every live template, including templates the user may not edit (ruling Q22, confirmed) + +**Where** +- `src/Integrations/Divi/ThemeBuilder/ThemeBuilderEdits.php:43-52` (`showing()`: all live templates that show the layout). +- `:59-62` (`prepare()`: `details['templates'] = self::listing($templates)`). +- `:96-108` (`notice()`: the same for lit-divi-delete's own confirm). +- `:274-285` (`listing()`: `id`, `title`, `use_on` with object IDs). +- Passed on unchanged by `src/Integrations/Divi/Pages/DiviReplaceTextAbility.php:402-408` (`templates` in the per-page refusal). +- Compare `ThemeBuilderAccess::editableLiveTemplates()` (`ThemeBuilderAccess.php:193-201`). tb-list, tb-assign, tb-activate and `fieldWarnings()` (`ThemeBuilderEdits.php:117-160`) already use it (Q21). + +**Scenario** +1. A TB user may edit layout L (`edit_post` on L) but not template T. T is hidden from them by Q8/B11/Q18: tb-list omits it, and tb-assign/tb-activate answer "gibt es nicht". +2. Any Divi write on L without `confirm` answers `confirm_required` with `templates: [{id: T, title, use_on: [...]}]`. +3. That discloses T's ID, title and conditions, including object IDs such as `singular:post_type:page:id:`. +4. In practice this is narrow: the account already holds `edit_others_posts` and the TB rights, so `edit_post` on a published template fails only under a capability plugin that restricts it (per-post or per-type rules). The class of leak is the one Q21 closed elsewhere. + +**Evidence:** by reading (the code paths above). No second account exists on DDEV. + +**Fix** (the decision stays on *all* live templates; only the answer is filtered): +```php +// ThemeBuilderEdits +public function prepare(DiviPage $page, array $input): array|WP_Error +{ + $templates = $this->showing($page); // all live ones – decides whether confirm is needed + $details = $this->templateDetails($templates); + if ($templates === []) { … unchanged … } + … message keeps self::count($templates) (the total) … +} + +public function notice(DiviPage $page, WP_Error $error): WP_Error +{ + … VisibleEdits::merged($error, …self::count($templates)…, $this->templateDetails($templates)) … +} + +/** + * Ruling Q21/Q22: live templates the user may edit by id/title/use_on, the others only as a count. + * @param list