Files
WP-LIT-MCP/docs/superpowers/notes/2026-09-27-etappe-3-entscheidungen-entwurf.md
T
2026-09-26 23:05:34 +02:00

36 KiB
Raw Blame History

Etappe 3 – Entscheidungen der Planung (Entwurf)

Vorbereitung von Etappe 3 („Library & Theme Builder“), 2026-09-26/27. Grundlage: Spec §3, §4.3, §5, §7, §10, §12, §15; Entscheidungen Etappe 2 (D1–D12); Rulings 2b (S1–S52), Praxistest und Security-Audit 2b. Quelle: Divi 5.2.1 (wp-content/themes/Divi, style.css:4), Pfade relativ zu themes/Divi/includes/. Live geprüft nur lesend (DDEV, wp eval-file). Entwurf – noch nicht vom Controller freigegeben. Kosten, falls falsch, jeweils am Ende.

1. Verifizierte Fakten

1.1 Divi Library (et_pb_layout)

  • F1: Post-Typ et_pb_layout (ET_BUILDER_LAYOUT_POST_TYPE, builder/post/type/Layout.php:4), capability_type post, map_meta_cap, show_ui true, show_in_rest false, unterstützt revisions (Layout.php:79). Taxonomien layout_category, layout_tag, layout_pack, layout_type, module_width, scope, layout_location (Layout.php:83-90). Live: assign_terms = edit_posts, verwalten = manage_categories; layout_location ist lokal nicht registriert.
  • F2: Anlegen (Divi 4-Weg, von Divi 5 weiter benutzt): et_pb_submit_layout() (builder/shortcode-core.php:654-734) setzt Terme scope (global/not_global), layout_type (layout/section/row/module), module_width (regular/fullwidth), Kategorien/Tags und Meta _et_pb_row_layout (Zeilen), _et_pb_module_type (Module), _et_pb_excluded_global_options = "[]" (globale Module), _et_pb_built_for_post_type, _et_pb_template_type; et_pb_create_layout() (:738-800) speichert immer mit post_status publish über wp_insert_post (also mit kses). Im REST-/CLI-Kontext ist et_pb_submit_layout nicht geladen (live: function_exists = false; geladen nur über builder/shortcode-framework.php:6).
  • F3: Divi 5 legt Library-Einträge über die Builder-Route POST divi/v1/divi-library/create-item an (builder-5/server/VisualBuilder/REST/RESTRegistration.php:743-749, DiviLibraryController::create_item DiviLibraryController.php:606-757): D4-Konvertierung, Preset-/Farb-/Variablen-Import, dann et_pb_submit_layout (:715). Recht: edit_posts + et_pb_is_allowed('divi_library') + ('save_library') (:958-966). Inhalt wird vorher mit SavingUtility::prepare_content_for_db bereinigt (:980; VisualBuilder/Saving/SavingUtility.php:230-254: ohne unfiltered_html wird die Seite komplett geparst und neu serialisiert). Der Versuch, danach _et_pb_use_divi_5 zu setzen, liest $new_layout_meta['layout_id'] aus einem JSON-String (:715-720) – auf dieses Flag ist bei Library-Einträgen kein Verlass.
  • F4: Library-Einträge speichern ihren Inhalt in post_content in einem divi/placeholder-Wrapper (DiviLibraryController.php:2395, 2461-2466).
  • F5: „Global“ = Term scope=global; Rückfall für importierte D4-Module: _et_pb_module_type und _et_pb_excluded_global_options gesetzt (builder-5/server/Packages/GlobalLayout/GlobalLayout.php:34-61, GlobalLayout::is_global_layout_template()).
  • F6: Instanz auf einer Seite = Platzhalterblock divi/global-layout mit {"globalModule":"<ID>","blockName":"divi/<typ>","localAttrs":{…}} und den Kindern als innerBlocks (Schnappschuss). Belegt durch die D4→D5-Konvertierung (builder-5/server/Packages/Conversion/Conversion.php:1454-1479) und die Serialisierung des Builders (builder-5/visual-builder/build/serialized-post.js, Filter divi.serializedPost.parseFlatModuleObjectIntoBlockArray.nested: alle Attribute außer globalModule, localAttrsMap, localChildren, globalParent, blockName, globalModuleTemplate wandern nach localAttrs). Kinder innerhalb einer globalen Vorlage tragen globalParent (Conversion.php:1491-1494). Modul-Metadaten: divi/global-layout hat keine Attribute (builder-5/server/_all_modules_metadata.php:18550-18557).
  • F7: Ausgabe per Verweis. Beim Rendern ersetzt BlockParser jeden divi/global-layout durch den aktuellen Inhalt des Library-Posts (builder-5/server/FrontEnd/BlockParser/BlockParser.php:838-884; BlockParserStore::get_global_layout_content, BlockParserStore.php:1113-1330). Aus dem Schnappschuss localAttrs werden nur die Attribute übernommen, die die Vorlage in localAttrsMap als „nicht synchronisiert“ führt (apply_local_attrs_filtering, BlockParserStore.php:348); die Kinder aus der Seite nur, wenn die Vorlage localChildren: true hat (:1307-1311). Die Seitenkopie ist also ein Schnappschuss, maßgeblich ist der Library-Post.
  • F8: Keine Expansion bei REST-POST. BlockParser überspringt die Expansion bei MigrationContext, beim Speichern und bei jedem REST-Request mit POST/PUT/PATCH (BlockParser.php:827-831, _is_rest_update_request :690-701). Unsere MCP-Aufrufe sind REST-POST – lit/page-render-summary (D5, the_content im Prozess) sieht globale Module daher nicht.
  • F9: Bearbeitet man eine globale Instanz im Visual Builder, speichert der Builder sofort den Library-Post über POST divi/v1/divi-library/save (visual-builder/build/global-layouts.js; DiviLibraryController::save :1439-1447 = wp_update_post(wp_slash(…)), Inhalt vorher prepare_content_for_db). Recht: edit_posts + et_pb_is_allowed('edit_global_library') (:1494-1500) – ohne edit_post auf den Eintrag.
  • F10: Verschachtelte globale Module sind verboten: beim Öffnen einer globalen Vorlage im Builder werden innere divi/global-layout „entglobalisiert“ (VisualBuilder/SettingsData/SettingsDataCallbacks.php:739-743, Framework/Portability/PortabilityPost.php:3521-3605 maybe_deglobalize_nested_global_modules: Platzhalter → blockName mit localAttrs als Attribute, globalParent entfernt). Divi selbst liest globalModule aus Markup per String-Scan, weil parse_blocks() damit nicht zuverlässig umgeht (PortabilityPost.php:3510, BlockParserStore.php:1137-1184, FrontEnd/Assets/DetectFeature.php:349-359).
  • F11: Library-REST-Routen (RESTRegistration.php:671-775): divi-library (Liste), /item, /update-terms, /update-item, /convert-item, /split-item, /load, /create-item, /save, /upload-image, /item-location (löscht trotz Namen Posts, DiviLibraryController.php:1197-1221). Alle nonce-geschützt, nur für den Builder.
  • F12: Rechte im Divi-Rollen-Editor: et_pb_is_allowed() (builder/core.php:1128-1162); Library-Admin braucht divi_library + save_library (builder/functions.php:340-346), globale Einträge zusätzlich edit_global_library (functions.php:366-372; im Builder core.php:4248-4262). theme_builder und read_dynamic_content_custom_fields sind „gefährlich“: für andere Rollen als Administrator standardmäßig aus (core.php:1138-1156), Multisite-Super-Admins immer an. Live: keine Rollen-Einstellungen gespeichert (et_pb_role_settings leer) → Standardwerte.
  • F13: Live-Bestand: 0 et_pb_layout, 0 et_template, 0 et_theme_builder, 0 TB-Layouts, kein Inhalt mit globalModule (Probe s1.php). Echte Library-/TB-Daten fehlen vollständig.

1.2 Theme Builder

  • F14: Post-Typen et_theme_builder, et_template, et_header_layout, et_body_layout, et_footer_layout (builder/frontend-builder/theme-builder/theme-builder.php:7-23, 89-252), alle capability_type post, show_ui false, show_in_rest false; Layouts unterstützen revisions (:184, 216, 248), Vorlagen nicht (:152).
  • F15: Live- und Entwurfsstand: et_theme_builder_get_theme_builder_post_id($live) sucht den TB-Post mit Status publish (live) bzw. auto-draft (Entwurf des TB-Editors), ohne Meta _et_library_theme_builder (theme-builder.php:378-413). Die Vorlagen hängen als mehrfaches Meta _et_template am TB-Post, mit Sicherung in der Option et_tb_templates_backup_<id> während des Speicherns (:424-441; api.php:223-243).
  • F16: Vorlage (et_template) = Meta _et_default, _et_enabled, _et_autogenerated_title, _et_{header,body,footer}_layout_id, _et_{…}_layout_enabled, _et_{…}_layout_global, _et_library_item_id, mehrfach _et_use_on/_et_exclude_from (theme-builder.php:471-521). override = Layout-ID ≠ 0 oder Bereich deaktiviert (:502).
  • F17: et_theme_builder_store_template($tb_id, $template, $allow_default) (theme-builder.php:1463-1586): wp_die() ohne edit_others_posts (:1464-1466); neue Vorlagen sofort publish (:1515-1521); Layout-IDs nur übernommen, wenn der passende Typ und publish (:1486-1496); Bedingungen sanitize_text_field, dedupliziert (:1565-1583). Die Funktion hängt die Vorlage nicht an den TB-Post – das macht nur et_theme_builder_api_save (api.php:223-243).
  • F18: et_theme_builder_insert_layout($options) (theme-builder.php:1643-1667): wp_die() ohne edit_others_posts, wp_insert_post mit post_status publish (kses!), Term layout_type=layout, et_builder_enable_for_post() (setzt _et_pb_use_builder, core.php:4438-4449).
  • F19: Divis TB-Speichern läuft über admin-ajax (wp_ajax_et_theme_builder_api_save, api.php:160-273: et_builder_security_check('theme_builder', 'edit_others_posts', …)), ersetzt die gesamte _et_template-Liste des TB-Posts (:233-240), trasht beim Live-Speichern den Entwurf und markiert unreferenzierte Vorlagen/Layouts mit _et_theme_builder_marked_as_unused; nach 7 Tagen werden sie gelöscht (max. 50 je Lauf, theme-builder.php:591-678). Danach et_theme_builder_clear_wp_cache('all') und ET_Core_PageResource::remove_static_resources('all','all') (api.php:249-254).
  • F20: api.php wird nur bei is_admin() || wp_doing_ajax() geladen (theme-builder.php:50-61); im REST-Kontext fehlen die et_theme_builder_api_*-Funktionen. Vorhanden (live geprüft): et_theme_builder_insert_layout, et_theme_builder_store_template, et_theme_builder_get_theme_builder_templates, et_theme_builder_get_template_settings_options, et_theme_builder_get_flat_template_settings_options, et_theme_builder_clear_wp_cache. Divi 5 hat keine eigenen TB-REST-Routen (nur ThemeBuilder/Layout.php, ThemeBuilder.php für die Ausgabe).
  • F21: Bedingungs-IDs mit Trenner : (theme-builder.php:28): singular:post_type:<pt>:all (:703-710), archive:post_type:<pt> (nur post oder has_archive, :724-734), singular:post_type:<pt>:id:<id> (:805-813), singular:post_type:<pt>:children:id:<id> (:822-829), homepage (:717), search, 404 (:1030-1039), Archiv-, Taxonomie-, Autor-, Datumsvarianten (:873-987). Jede hat einen validate-Callback (template-setting-validations.php:13-325); et_theme_builder_get_flat_template_settings_options() (:1143) liefert die flache Liste.
  • F22: Die Layouts des Theme Builders werden bei der Ausgabe live gelesen (et_theme_builder_get_template_layouts, theme-builder.php:1295-1406); Änderungen an einem aktiven Layout sind sofort öffentlich. Divi-Module mit appearance.themeBuilderLayouts (z. B. divi/post-content, _all_modules_metadata.php:25503; fullwidth-post-content :14455) werden in anderen Bereichen nicht gerendert (Packages/ModuleLibrary/ModuleRegistration.php:509-527).
  • F23: Divi-Recht für TB-Layouts im Builder: et_pb_is_allowed('theme_builder') (functions.php:2353) plus die WP-Rechte; TB-Admin-AJAX zusätzlich edit_others_posts.

1.3 Dynamic Content, Pods, Loop

  • F24: DynamicContentOptions::get_options($post_id, $context) sammelt alles über den Filter divi_module_dynamic_content_options (builder-5/server/Packages/Module/Layout/Components/DynamicContent/DynamicContentOptions.php:85-133). Live für Seite 831: 57 Optionen, davon 34 loop_*, post_meta_key (Felder before|after|select_meta_key|meta_key|date_format|custom_date_format|enable_html) und je Post-Typ post_link_url_<pt> mit Feld post_id.
  • F25: Meta-Schlüssel-Liste: „meistgenutzte“ Meta-Schlüssel der ganzen Website plus ACF-Felder – nur mit read_dynamic_content_custom_fields oder im Kontext display (DynamicContentOptionPostMetaKey.php:80-140). Pods kennt Divi nicht.
  • F26: Ausgabe von custom_meta_<key>: Wert über get_post_meta/ACF, Filter divi_module_dynamic_content_resolved_custom_meta_value (DynamicContentOptionPostMetaKey.php:404), dann esc_html ohne enable_html (:452-457). Arrays von Strings werden mit „, “ verbunden; Beziehungs-/Dateifelder ohne ACF bleiben rohe IDs.
  • F27: Interner Link als natives Dynamic Content: post_link_url_<post_type> mit settings.post_id, Ausgabe esc_url(get_permalink($id)) zur Renderzeit (DynamicContentOptionCustomPostLinkUrl.php:34, 186-217) – entwurfssicher, nach dem Veröffentlichen automatisch die schöne URL.
  • F28: Loop Builder: module.advanced.loop.desktop.value.{enable, queryType, …} (Packages/Module/Options/Loop/LoopUtils.php:135-145), Werte im Loop über loop_*-Optionen; die Expansion läuft nur beim Rendern (BlockParser.php:895-905).

1.4 Stand im Plugin

  • F29: ContentSchema::EXCLUDED schließt et_pb_layout, et_theme_builder, et_template und die drei TB-Layout-Typen aus (src/Integrations/WordPress/Schema/ContentSchema.php:19-24); PostGuard::editable liefert dafür not_found. Die Divi-Werkzeuge erreichen Library und TB heute nicht.
  • F30: Opak sind Blöcke mit globalModule, divi/global-layout oder nicht dekodierbaren Attributen (src/Integrations/Divi/BlockTree/Parser.php:56); Block::setAttrs wirft dort (Block.php:91-96).
  • F31: Parser-Kosten (live, 8 Divi-Posts, 28 KB, 20 Durchläufe): Parse + NodeIndex ≈ 2 ms je 100 KB Inhalt (Probe s3.php).
  • F32: Divi 5.2.1 registriert keine WordPress Abilities (grep wp_register_ability|wp_abilities_api_init im Theme: 0 Treffer) und enthält kein MCP.

2. Empfohlene Entscheidungen

  • E1 – Ein Zugang für alle Divi-Posts. Die bestehenden Divi-Werkzeuge (lit/divi-outline, -node-get, -insert, -update, -move, -delete, -duplicate-node) akzeptieren zusätzlich IDs von et_pb_layout und et_header/body/footer_layout (Spec §5.1). Umsetzung: eine DiviTarget-Richtlinie neben PostGuard (Typ, Recht, Status-Regel je Typ), die DiviPageLoader verwendet; ContentSchema::EXCLUDED bleibt (die Content-Tools sehen diese Typen weiter nicht). – Grund: keine zweite Werkzeugfamilie, gleiche Pipeline (D7, S39). – Kosten, falls falsch: Rechteprüfung pro Typ an einer Stelle falsch → Zugriff auf Library/TB ohne Divi-Recht; abgefangen durch eigene Tests je Typ.
  • E2 – Globale Module werden im Library-Post bearbeitet, nie in der Seitenkopie. Auf Seiten bleiben divi/global-layout-Knoten opak (D3); lit/divi-node-get/-outline liefern für sie global_id, block_name und einen Hinweis „mit post_id=<global_id> bearbeiten“ (S19-Teil 2). Schreiben in den Library-Post wirkt auf alle Instanzen (F7); die Antwort nennt die betroffenen Seiten (Verwendungssuche über "globalModule":"<id>" in post_content, wie Divi F10) und verlangt bei mehr als einer veröffentlichten Instanz confirm: true. localAttrs/Kinder-Schnappschüsse in Seiten werden nie angefasst; das Bearbeiten nicht synchronisierter Attribute einer einzelnen Instanz ist nicht Teil von Etappe 3. – Grund: Divis Ausgabe ist per Verweis (F7), der Builder macht es genauso (F9). – Kosten: Instanz-spezifische Abweichungen nur im Builder.
  • E3 – Rechte (strenger als Divi, nie lockerer). Library lesen: edit_posts + divi_library; speichern/anlegen: + save_library; einfügen aus der Library: + add_library; globalen Eintrag bearbeiten: + edit_global_library und edit_post auf den Eintrag (Divi prüft das nicht, F9). Theme Builder lesen und schreiben: edit_others_posts + et_pb_is_allowed('theme_builder') (F19, F23; Spec §4.5 um die Divi-Rolle ergänzt). Dynamic-Content-Meta-Liste nur mit read_dynamic_content_custom_fields (F25). Weil et_theme_builder_store_template/insert_layout bei fehlendem Recht wp_die() aufrufen (F17, F18), prüft das Plugin vorher selbst. et_pb_is_allowed steckt in einem Adapter (E15). – Kosten: ein Redakteur mit WP-Rechten, aber ohne Divi-Rolle, bekommt forbidden (gewollt, wie im Builder).
  • E4 – Library-Einträge legt das Plugin selbst an (kein et_pb_submit_layout, keine Builder-Route): wp_insert_post mit leerem Inhalt und Status publish (Divi kennt keine Library-Entwürfe, F2), Terme/Meta wie F2, danach Inhalt über DiviSaver::saveNew im kses-Fenster (D7, D11-Nachtrag), eingewickelt in divi/placeholder (F4), Flags wie DiviFlags. Ist ein Schritt fehlerhaft, wird der Entwurf endgültig gelöscht (wie lit/content-create). – Grund: et_pb_submit_layout ist im REST-Kontext nicht geladen und speichert mit kses (F2); die Builder-Route braucht Nonce und importiert Presets/Farben (F3). – Kosten: Divi ändert Terme/Meta → Einträge fehlen in der Library-Ansicht; abgesichert durch Fixture-Vergleich mit echten Builder-Einträgen (E13) und Kompatibilitätsprüfung (E15).
  • E5 – lit/library-insert als Kopie oder globale Instanz. Kopie: Inhalt ohne divi/placeholder, verschachtelte globale Platzhalter entglobalisiert wie Divi (F10), eingefügt wie lit/divi-insert (StructureRules, AdminLabel). Globale Instanz: das Plugin erzeugt <!-- wp:divi/global-layout {"globalModule":"<id>","blockName":"<Wurzeltyp>"} --><!-- /wp:divi/global-layout --> ohne localAttrs (voll synchronisiert). Prüfungen: Eintrag existiert, publish, scope=global, read_post, Wurzeltyp passt an die Zielposition, kein globales Modul in einem globalen Library-Post (F10). – Kosten: falls der Builder leere localAttrs/fehlende innerBlocks anders erwartet, sieht der Mensch im Builder evtl. eine andere Darstellung – vor dem Bau mit einer Builder-Fixture verifizieren (E13).
  • E6 – Werte aus gespeicherten Inhalten (Library → Seite, Seite → Seite, Seite → Library) gelten als „gespeichert, nicht vom Client“ wie bei lit/divi-duplicate-node, werden aber beim Wechsel des Posts erneut geprüft: für Benutzer ohne unfiltered_html werden Knoten mit Builder-only-Feldern (S7/S26: *.decoration.attributes, *.advanced.html, *.css.*) und Code-Modul abgelehnt („nur im Divi-Builder“). Alles andere geht unverändert durch den BlockTree (byte-identisch). – Grund: Audit M1 – der Weg über eine andere Post-ID darf die Builder-only-Sperre nicht umgehen; Autor und damit Divis before/after-Filterung ändern sich mit dem Ziel-Post (R2-Befund 1). – Kosten: Redakteure können admin-gebaute Abschnitte mit eigenem CSS nicht kopieren.
  • E7 – Theme Builder: Vorlagen und Layouts über Divis vorhandene Funktionen (et_theme_builder_insert_layout mit leerem Inhalt, Inhalt danach über DiviSaver::saveNew; et_theme_builder_store_template), Zuordnung zum Live-TB-Post selbst (add_post_meta($live_id, '_et_template', …), gleiche Reihenfolge wie F19 inkl. Backup-Option), danach Cache wie F19. Nie der Entwurfs-TB-Post, nie et_theme_builder_trash_draft_and_unused_posts(). Existiert ein TB-Entwurf (auto-draft, F15), wird abgelehnt mit Hinweis „Theme Builder ist in Bearbeitung – dort speichern oder verwerfen“ (sonst überschreibt ein späteres Speichern im TB-Editor die Zuordnung und die neue Vorlage wird nach 7 Tagen gelöscht, F19). Bedingungen nur aus et_theme_builder_get_flat_template_settings_options() plus deren validate-Callback, Post-IDs mit read_post. – Kosten: Divi ändert die TB-Speicherung → Vorlage fehlt im TB-Editor; Kompatibilitätsprüfung + E2E.
  • E8 – Neue TB-Vorlagen werden deaktiviert angelegt (_et_enabled=0); aktivieren ist eine explizite Aktion in lit/tb-assign (enabled: true, confirm: true, Liste der betroffenen Bedingungen). Standardvorlage (_et_default) nie über MCP. – Grund: Spec §2 Prinzip 5 (Status bleibt, Veröffentlichen explizit); TB-Posts kennen keinen Entwurf (F17, F18, F22). – Kosten: ein Aufruf mehr. Offen als Frage F-1.
  • E9 – Werkzeugliste Etappe 3 und Exposition. Direkt: nur lit/library-insert (Spec §4.3) → 22 direkte Tools, unter der Warnschwelle 25 des Tools-Tabs. Indirekt: lit/library-list (mit id → Detail; ersetzt lit/library-get), lit/library-save, lit/divi-node-copy, lit/library-make-global, lit/library-find-similar, lit/divi-dynamic-sources, lit/tb-list, lit/tb-create-template, lit/tb-assign. Gruppen: Library und Theme Builder als eigene Gruppen („nur lesen“ wirkt wie bei Divi, Audit M2: lit/library-insert und lit/divi-node-copy folgen zusätzlich dem Modus von lit/divi-insert). Destruktiv-Hinweis: lit/library-make-global (ersetzt Knoten), lit/tb-assign (öffentliche Wirkung). – Kosten: lit/library-get aus der Spec entfällt als eigener Name (Spec §5.7 anpassen).
  • E10 – lit/library-make-global: legt einen globalen Eintrag aus dem Knoten an (E4) und ersetzt den Knoten durch eine Instanz (E5), in einem Aufruf, alles oder nichts (Library-Post wird bei Fehlschlag der Seite gelöscht). „Gleichartige Vorkommen ersetzen“ nur für inhaltsgleiche Knoten (Inhalts-Fingerabdruck, E11), mit Probelauf (Standard) und confirm: true je Seite mit tree_rev (Muster D10). Strukturgleiche Knoten mit anderen Texten werden nur gemeldet, nie ersetzt (Spec §3.3 „fast gleich“ = später). – Kosten: Kunde muss ähnliche Abschnitte vorher angleichen.
  • E11 – lit/library-find-similar: zwei Fingerabdrücke je Abschnitt/Zeile: Struktur (Blocknamen und Tiefe, Spalten-flexType, Preset-IDs) und Inhalt (kanonische Attribute ohne Admin-Label, Bytes der Kinder). Suche seitenweise mit cursor, Post-Typ-Filter, höchstens 200 Posts je Aufruf, nur Posts, die der Benutzer bearbeiten darf; kein Index/Meta (F31: ≈1 s Parse für 1000 Seiten à 50 KB, Datenbanklast dominiert). Die Schreibwarnung „ähnliche Abschnitte“ (Spec §5.5, D6) vergleicht nur mit Library-Einträgen (klein) und verweist sonst auf das Tool. – Kosten: auf sehr großen Websites mehrere Aufrufe; bei Bedarf später ein Cache nach tree_rev.
  • E12 – Dynamic Content. lit/divi-dynamic-sources(post_type) liefert DynamicContentOptions::get_options für einen Beispiel-Post des Typs (sonst 0), ohne loop_* (S36), dazu Pods-Felder des Typs aus PodsFieldSource mit Speicherart und Eignung (Text-ähnlich = {{field:<name>}}; Beziehung/Datei = „zeigt IDs“, F26). Beim Schreiben in einen TB-Body-Layout gilt als Kontext-Post-Typ der Typ aus den Bedingungen der zugeordneten Vorlage (singular:post_type:<pt>:…); ohne Zuordnung Warnung. Loop Builder bleibt lesbar/opak (Werte unverändert), wird nicht erzeugt. – Kosten: CPT-Archive mit Pods-Feldern in Schleifen erst später.
  • E13 – Übernahmen aus Etappe 2: (a) {{link:<post_id>}} → post_link_url_<post_type> mit settings.post_id (F27), Prüfung read_post, kein Papierkorb; nur in URL-Feldern; (b) lit/guide mit topic: "all" (alle Themen in einem Aufruf, Größenlimit); (c) S19: lit/divi-design-system mit Filter (section: colors/variables/presets/fonts, module für Presets) und global_id in node-get; (d) lit/page-render-summary meldet globale Platzhalter als Hinweis „globales Modul – im Auszug nicht enthalten“ (F8) und erhält für Library-/TB-IDs keinen Zugang. – Kosten: gering.
  • E14 – Reihenfolge und Aufteilung. 3a „Library“: Fixtures (E16) → Übernahmen E13 → DiviTarget/Rechte (E1, E3) → lit/library-list → Divi-Werkzeuge auf Library-Posts inkl. globaler (E2) → lit/library-save (E4) → lit/library-insert (E5) → lit/divi-node-copy (E6) → lit/divi-dynamic-sources (E12) → Guides/Prompt globale-module-vorschlagen. 3b „Theme Builder & Konsolidierung“: lit/tb-list → TB-Layouts in den Divi-Werkzeugen (inkl. themeBuilderLayouts-Regel F22 in StructureRules) → lit/tb-create-template (E7, E8) → lit/tb-assign → lit/library-find-similar + Warnung (E11) → lit/library-make-global (E10) → Guide theme-builder, Prompt custom-post-type-vorlage. Abschluss jeder Teiletappe wie 2b (Review je Task, Security-Audit, Praxistest). – Kosten: find-similar/make-global kommen spät; sie hängen an stabilen Library-Grundlagen.
  • E15 – Divi-Zugriffe über Adapter mit Live-Kompatibilitätsprüfung (siehe Abschnitt 5): je Divi-Bereich eine Klasse (DiviModules und DesignSystemReader gibt es schon; neu DiviLibraryStore, DiviThemeBuilder, DiviRoles), die Funktions-/Klassen-Existenz und Signaturen prüft und bei Abweichung unsupported statt Fatal liefert; lit/site-status zeigt den Befund. – Kosten: etwas mehr Code; spart Ausfälle nach Divi-Updates.
  • E16 – Teststrategie. (1) Unit: Platzhalter-Erkennung, Fingerabdrücke, Bedingungs-Validierung, Instanz-Markup, Entglobalisieren gegen Divis maybe_deglobalize_nested_global_modules (gleiche Eingaben, gleiches Ergebnis). (2) Fixtures aus echten Builder-Daten: Jan-Niclas baut vor Beginn in DDEV im Visual Builder nach Checkliste globales Modul, globale Zeile mit einem nicht synchronisierten Attribut, globaler Abschnitt, normaler Library-Abschnitt, je eine Seite mit Instanzen, eine TB-Vorlage „Person“ mit Header/Body/Footer und Bedingungen; ein neues Lese-Skript tests/Integration/divi-export-library.php exportiert post_content, Terme, Meta und TB-Meta als builder-library-*.html / builder-tb-*.json (wie D12). Roundtrip-Test läuft automatisch mit. (3) DDEV-Smoke divi-library-smoke.sh / tb-smoke.sh (mit Aufräumen). (4) Manuelle Abnahme: vom Plugin erzeugte Einträge/Instanzen/Vorlagen im Builder und TB-Editor öffnen und speichern. – Kosten: ohne Fixtures (F13) bleiben E4/E5/E7 Vermutung; daher Fixtures als erster Schritt und Bedingung für den Bau.
  • E17 – Sicherheit neuer Schreibwege: jeder Weg nach post_content von Library- und TB-Posts geht durch Composer/AttrWriter/TreeEditor → DiviSaver (D7, S39); Titel, Terme und Meta über WordPress mit üblicher Filterung (sanitize_text_field, wp_set_object_terms mit bestehenden Term-IDs; neue Terme nur mit manage_categories); keine Builder-Route und kein et_pb_create_layout mit Client-Inhalt; TB-Bedingungen nur aus Divis Liste; Revision vor jeder Änderung (Library und TB-Layouts unterstützen Revisionen, F1/F14), Protokolleintrag je Post; nach Library-/TB-Änderungen remove_static_resources('all','all',true) und et_theme_builder_clear_wp_cache('all') (Spec §5.6 Schritt 5). – Kosten: Review-Pflicht wie D7.

3. Fragen an den Benutzer (nach Wichtigkeit)

  1. Sollen neue Theme-Builder-Vorlagen sofort wirken? Divi kennt für Vorlagen keinen Entwurf.
    • (a) Deaktiviert anlegen, Aktivieren nur ausdrücklich mit lit/tb-assign und Bestätigung. Empfehlung.
    • (b) Sofort aktiv, wie im TB-Editor.
    • (c) Aktiv nur auf Bedingungen für einzelne IDs, alles Breitere (ganzer Post-Typ, Archiv) deaktiviert.
  2. Wie frei darf der KI-Assistent globale Module ändern? Jede Änderung wirkt sofort auf allen Seiten, auch veröffentlichten.
    • (a) Erlaubt mit Rechten wie im Builder, confirm: true und Liste der betroffenen Seiten. Empfehlung.
    • (b) Nur Administratoren (unfiltered_html/manage_options).
    • (c) Globale Einträge nur lesen, Änderungen nur im Builder.
  3. Was zuerst: Library oder Theme Builder?
    • (a) 3a Library (globale Module, Einfügen, Kopieren), dann 3b Theme Builder. Empfehlung (technisch sauberere Grundlage).
    • (b) Theme Builder zuerst, weil ein Kunde CPT-Vorlagen braucht (Library-Teil danach).
  4. Pods-Beziehungs- und Dateifelder im Theme Builder: Divi zeigt davon nur IDs.
    • (a) In Etappe 3 nur Text-ähnliche Pods-Felder anbieten, andere als „nicht geeignet“ melden. Empfehlung.
    • (b) Das Plugin formatiert Pods-Werte beim Rendern (Filter divi_module_dynamic_content_resolved_custom_meta_value) – wirkt dann auf der Website, auch ohne MCP.
    • (c) Ganz auf später verschieben.
  5. Woher kommen die echten Fixtures?
    • (a) Du baust sie lokal in DDEV nach Checkliste, ein Skript exportiert sie. Empfehlung.
    • (b) Export von einer Kundenseite (anonymisiert).
    • (c) Nur aus Divi-Code abgeleitet (riskant, F13).

4. Risiken

Risiko Gegenmaßnahme
Keine echten Library-/TB-Daten lokal (F13); Instanz-Format (localAttrs, innerBlocks, globalModule an der Vorlagenwurzel) nur aus Code/minifiziertem JS abgeleitet Fixtures zuerst (E16, Frage 5), Builder-Abnahme
Späteres Speichern im TB-Editor überschreibt unsere _et_template-Zuordnung, Vorlage wird nach 7 Tagen gelöscht (F19) Ablehnung bei TB-Entwurf (E7), lit/tb-list zeigt unzugeordnete Vorlagen
Globale Änderung wirkt sofort auf veröffentlichte Seiten confirm, Seitenliste, Revision des Library-Posts (E2, Frage 2)
page-render-summary sieht globale Module nicht (F8) Hinweis im Auszug (E13d)
et_theme_builder_* rufen wp_die() statt Fehler zu liefern (F17, F18) Vorabprüfung der Rechte, Adapter (E3, E15)
Divi-Interna ändern sich (Library-Meta, TB-Meta, Instanz-Format); ET kündigt ein offizielles MCP an Adapter + Kompatibilitätsprüfung (E15), Fixture-Tests je Divi-Update
Builder-only-Werte gelangen über Kopie in fremde Posts (Audit M1-Muster) E6
Pods mit Tabellen-Speicherung: get_post_meta findet nichts lit/divi-dynamic-sources meldet solche Felder als ungeeignet (E12)
Rollen-Editor: „gefährliche“ Divi-Rechte standardmäßig nur für Administratoren E3, klare forbidden-Meldung mit Hinweis auf Divi → Rollen-Editor
Große Websites: find-similar lädt viel post_content Cursor, 200 Posts je Aufruf (E11)
Loop Builder / Archive nicht abgedeckt bewusst später (E12)

5. Schnittstellen-Einordnung

Stufen: (a) öffentliche/dokumentierte API (WordPress-Kern, dokumentierte Divi-Hooks), (b) Divis Builder-REST/AJAX (divi/v1, admin-ajax, nonce-geschützt), (c) interne PHP-Klassen/Funktionen/Meta-Konventionen, (d) selbst nachgebildete interne Formate. Empfehlung: B = behalten, W = wechseln auf offizielleren Weg, A = in Adapter mit Live-Kompatibilitätsprüfung (E15).

Zur Offizialität: Elegant Themes dokumentiert Hooks und die Modul-API (dev.elegantthemes.com, u. a. divi_module_dynamic_content_options, divi_module_dynamic_content_resolved_custom_meta_value, divi_module_dynamic_content_custom_meta_options, divi_module_library_register_module_attrs), aber keine API für externen oder KI-Zugriff. Der Divi AI Agent läuft im Builder (im Browser, AI-Wallet-Abrechnung); in den Kommentaren zur Ankündigung schreibt ET, ein „official MCP is on the way“. Divi registriert keine WordPress Abilities (F32).

5.1 Bereits genutzt (Etappe 2a/2b)

Schnittstelle Stelle im Plugin Stufe Risiko bei Divi-Updates Empfehlung
WordPress-Kern: wp_update_post, Revisionen, kses an/aus, Meta-API DiviSaver, RevisionPoint a gering B
Block-Delimiter-Format (<!-- wp:… -->, WP_Block_Parser-Muster) BlockTree/Parser a (WP-Format) gering B
Divi-Blocknamen und Attribut-JSON (module.decoration…, flexType, divi/placeholder) Composer, AttrWriter, ColumnWidths d (Schema aus c) mittel B – Pfade werden gegen den Live-Katalog validiert; Builder-Fixtures
ModuleRegistration::get_all_core_modules_metadata(), get_default_attrs() Schema/DiviModules c mittel A (Adapter existiert; Prüfung in lit/site-status ergänzen)
GlobalData::get_global_colors/-variables, GlobalPreset::get_data, Option et_divi Design/DesignSystemReader, MediaUsageFinder c (REST-Pendant divi/v1/global-data/* = b) mittel A; kein Wechsel auf b (nonce, Builder-Kontext)
DynamicContentOptions::get_options() Pages/DynamicContentMap c; Füllweg über dokumentierten Filter divi_module_dynamic_content_options (a); REST divi/v1/dynamic-content/options (b) mittel A (Rückfall auf Standard-Map existiert)
$variable(...)$-Syntax, gcid/gvid, Dynamic-Content-JSON Compose/VariableRef, Placeholders d mittel B + Prüfung gegen Divis Regex ET_THEME_BUILDER_DYNAMIC_CONTENT_REGEX_D5 (theme-builder.php:36) im Kompatibilitätstest
Meta _et_pb_use_divi_5, _et_pb_use_builder, _et_pb_show_page_creation Pages/DiviFlags, PostPresenter d (nachgebildet aus SyncToServerController) mittel B; _et_pb_use_builder könnte über et_builder_enable_for_post() (c) laufen – kein Gewinn
ET_Core_PageResource::remove_static_resources() DiviSaver c (dokumentierter Hook et_core_page_resource_post_id_before_clear deutet auf stabile API) gering B
et_builder_enabled_for_post_type() ContentSchema c gering B
ET_BUILDER_PRODUCT_VERSION DiviModules c gering B
Divis wp_insert_post_data-Filter (AttributeSecurity, HtmlSecurity) als Verteidigung in der Tiefe Verhalten, kein Aufruf c mittel B (nie entfernen, S7)

5.2 Geplant (Etappe 3)

Schnittstelle Zweck Stufe Risiko Empfehlung
Post-Typen, Taxonomien, Meta der Library (F1, F2) Liste, Anlegen (E4) c/d mittel A (DiviLibraryStore), Vergleich mit Builder-Fixtures
GlobalLayout::is_global_layout_template() „global?“ c gering B (Divis eigene Definition statt Term-Logik)
divi/global-layout-Platzhalter (globalModule, blockName, localAttrs) Instanzen (E5, E10) d hoch A + Fixtures; nur minimale Form ohne localAttrs erzeugen
Entglobalisieren wie PortabilityPost::maybe_deglobalize_nested_global_modules Kopie einfügen (E5) c mittel Eigene Umsetzung (d), per Test gegen Divis Methode geprüft (Aufruf direkt wäre möglich, arbeitet aber auf Strings ohne unsere Byte-Garantie)
Builder-Routen divi/v1/divi-library/* (F11) – b mittel nicht nutzen: Nonce, fehlende Pro-Eintrag-Rechte (F9), create-item hängt an nicht geladenem Code (F2, F3)
et_pb_is_allowed() Divi-Rollen (E3) c gering A (DiviRoles)
et_theme_builder_insert_layout, et_theme_builder_store_template TB schreiben (E7) c (in Spec genannt) mittel B über Adapter DiviThemeBuilder, Rechte vorab (wp_die)
et_theme_builder_get_theme_builder_post_id, …_get_theme_builder_templates, …_get_template TB lesen c gering B/A
_et_template-Zuordnung + Backup-Option (F15, F19) Vorlage aktivieren d (admin-ajax-Weg et_theme_builder_api_save = b, im REST-Kontext nicht geladen) mittel A; bei Divi-Update prüfen, ob et_theme_builder_api_save die Logik ändert
et_theme_builder_get_flat_template_settings_options() + validate-Callbacks Bedingungen (E7) c gering B
et_theme_builder_clear_wp_cache, remove_static_resources('all','all',true) Cache c gering B
DynamicContentOptions::get_options() je Post-Typ lit/divi-dynamic-sources c (Filter a) mittel A (vorhandener DynamicContentMap)
post_link_url_<pt> + post_id {{link:ID}} (E13a) d (Format), Registry c gering B – Name über die Registry prüfen, nicht fest verdrahten
divi_module_dynamic_content_resolved_custom_meta_value nur falls Frage 4 = (b) a gering B (dokumentiert)
appearance.themeBuilderLayouts aus den Modul-Metadaten StructureRules für TB-Bereiche c gering B (aus Live-Katalog)
Loop Builder (module.advanced.loop) – d hoch nicht erzeugen (E12)
WordPress Abilities von Divi – – – gibt es nicht (F32); beobachten, ob ETs angekündigtes MCP eine offizielle Schnittstelle bringt, die c/d-Teile ersetzen kann

Quellen der Web-Recherche: Divi AI Agent – Ankündigung, Divi Dev Docs – Filter, Divi Developers.

5. Antworten des Benutzers (2026-09-27)

  • F1 Theme-Builder-Vorlagen: standardmäßig deaktiviert angelegt; Aktivieren über ein eigenes Tool (explizit).
  • F2 Globale Module bearbeiten: erlaubt mit Trockenlauf (Liste der betroffenen Seiten) und confirm, vorher Revision/Sicherung des Bibliothekseintrags.
  • F3 Reihenfolge: erst 3a Bibliothek, dann 3b Theme Builder.
  • F4 Pods-Beziehungs-/Dateifelder: als dynamische Quelle nur textartige Felder anbieten; andere mit Hinweis auslassen.
  • F5 Testdaten: selbst erzeugen („Probier es selbst“) – über Divis eigene Speicherwege (Builder-REST-Controller/Services im Prozess), als „[Fixture] …“ markiert, danach als Fixtures ins Plugin exportiert. Pods ist lokal deaktiviert; Aktivierung für 3b beim Benutzer anfragen.