diff --git a/tests/Fixtures/divi/stage3/README.md b/tests/Fixtures/divi/stage3/README.md new file mode 100644 index 0000000..7a40732 --- /dev/null +++ b/tests/Fixtures/divi/stage3/README.md @@ -0,0 +1,114 @@ +# Divi-5-Fixtures für Etappe 3 (Library, globale Module, Theme Builder) + +Echte Daten aus Divi 5.2.1, erzeugt am 2026-09-26 auf der lokalen DDEV-Site (`https://wordpress.ddev.site:8443`). Alle Einträge wurden über **Divis eigene Speicherwege** angelegt – den echten Visual Builder und die echte Theme-Builder-Oberfläche in einem headless Chrome (Chrome 153, per DevTools-Protokoll gesteuert, angemeldet als Administrator). Weder Plugin-Code noch eigene Schreibwege in die Datenbank waren beteiligt; einzige Ausnahme ist `et_builder_enable_for_post()` für die neue Seite (siehe 1). + +Pfade zu Divi relativ zu `wp-content/themes/Divi/includes/`. Die Builder-JS-Dateien liegen in `builder-5/visual-builder/build/` und sind minifiziert – dort steht statt einer Zeilennummer ein Suchbegriff. + +## Dateien + +Je Post eine `.json` (Post-Zeile, **alle** Meta-Einträge roh in Speicherreihenfolge, alle Term-Beziehungen direkt aus den Tabellen) und, falls Inhalt vorhanden, `.html` mit `post_content` byte-genau. `manifest.json` ordnet Rolle → Post-ID zu, `theme-builder.json` enthält den Live-Theme-Builder-Post mit seiner Vorlagenliste, die `et_tb_*`-Optionen und die als ungenutzt markierten Posts. + +| Rolle | ID | Typ | Inhalt | +|---|---|---|---| +| `page--globale-instanzen` | 1053 | `page` (Entwurf) | eine Instanz des globalen Abschnitts, eine Instanz des globalen Moduls, normale Abschnitte | +| `library--layout-ganze-seite` | 1054 | `et_pb_layout` | ganzes Seitenlayout (vor dem Globalmachen gespeichert), Kategorie „Fixture“ | +| `library--library-abschnitt` | 1055 | `et_pb_layout` | Abschnitt, nicht global | +| `library--library-zeile` | 1056 | `et_pb_layout` | Zeile, nicht global | +| `library--library-modul` | 1057 | `et_pb_layout` | Blurb-Modul, nicht global, Schlagwort „fixture“ | +| `library-global--globaler-abschnitt` | 1058 | `et_pb_layout` | globaler Abschnitt | +| `library-global--globales-modul` | 1059 | `et_pb_layout` | globales Text-Modul | +| `tb-header--globaler-header` | 1061 | `et_header_layout` | globaler Header (Header der Standardvorlage) | +| `tb-body--body-mit-dynamic-content` | 1062 | `et_body_layout` | Dynamic Content: Titel, Meta-Feld, Auszug, Modul „Beitragsinhalt“ | +| `tb-footer--footer` | 1063 | `et_footer_layout` | Footer | +| `tb-template--default` | 1065 | `et_template` | „Standard-Website-Template“, **deaktiviert**, Header = 1061 | +| `tb-template--vorlage-globale-instanzen` | 1066 | `et_template` | **deaktiviert**, Header 1061 / Body 1062 / Footer 1063, Bedingung `singular:post_type:page:id:1053` | +| (theme-builder.json) | 1064 | `et_theme_builder` | Live-TB-Post, `_et_template` = [1065, 1066] | + +Nicht exportiert: die Revision 1060 der Seite. + +## Wie die Fixtures entstanden sind + +### 1. Seite „[Fixture] Globale Instanzen“ (1053) + +- Angelegt als leerer Entwurf (`wp post create`), dann `et_builder_enable_for_post(1053)` (`builder/core.php:4438-4452`) – das tut der Button „Divi Builder verwenden“; ohne `_et_pb_use_builder` öffnet `et_fb=1` den Builder nicht (`et_fb_is_enabled`, `builder/core.php:4195-4273`). +- Visual Builder geöffnet (`?page_id=1053&et_fb=1`). Die App läuft im iframe `app_window=1`; dort `divi.data`. +- Inhalt in den Builder-Store gesetzt: `dispatch('divi/edit-post').setContent(flat)` (Aktion `SET_CONTENT`, `edit-post.js`, Suche `Ht=e=>({type:"SET_CONTENT"`). Die Modul-Attribute (Admin-Labels, Texte, `builderVersion 5.2.1`, Spalte `flexType 24_24`) haben **wir** gewählt – wie bei den Etappe-2-Seiten; es wurden keine Module per Klick eingefügt, Divis Standardattribute beim Einfügen fehlen also. +- Gespeichert wie der Button „Entwurf speichern“: `dispatch('divi/serialized-post').syncToServer({syncType:'draft'})` (`app-ui.js`, Suche `syncToServer({syncType:"draft"`) → Divis Serialisierer (`serialized-post.js`, Suche `k=(t,e="root",r="global-module")`) → `POST divi/v1/sync-to-server` (`builder-5/server/VisualBuilder/REST/RESTRegistration.php:819-826`, `SyncToServerController::update`, `builder-5/server/VisualBuilder/REST/SyncToServer/SyncToServerController.php:56`). + +### 2. Library-Einträge (1054–1059) + +Über den echten Dialog „Zur Bibliothek hinzufügen“: `dispatch('divi/modal-library').open({name:'divi/add-to-library', owner:})` (so öffnet ihn das Rechtsklickmenü, `right-click-options.js`, Suche `name:"divi/add-to-library",owner:e`; für das ganze Layout `owner:""` wie das Tastenkürzel in `root.js`). Name, neue Kategorie/Schlagwort und der Schalter „Als Global abspeichern“ wurden im Dialog-DOM gesetzt, dann „Zur Bibliothek hinzufügen“ geklickt. Damit lief Divis eigener `onSaveClick` (`modal-library.js`, Suche `onSaveClick:({moduleNameOption`): + +- Serialisierung `serializeModuleFlatObjects` (ganzes Layout mit Kontext `"layout"`), FormData mit `layout_type`, `layout_scope`, `module_type`, `module_width`, `columns_layout`, `post_type`; +- `POST divi/v1/divi-library/create-item` (`RESTRegistration.php:743-749`, `DiviLibraryController::create_item`, `builder-5/server/VisualBuilder/REST/DiviLibrary/DiviLibraryController.php:606-757`) → `et_pb_submit_layout()` (`builder/shortcode-core.php:654-734`) → `et_pb_create_layout()` (`:738-800`); +- bei „global“ danach im Builder `Tn` (`modal-library.js`, Suche `Tn=({moduleId:e,templateId:t`): `setGlobalModuleAttribute(, )`, `setGlobalParentAttribute` für alle Nachkommen (`Nn`), `global-layouts.setLayoutContent`. Anschließend wurde die Seite gespeichert (siehe 1) – so entstanden die Instanzen in 1053. + +Reihenfolge: Layout 1054, Abschnitt 1055, Zeile 1056, Modul 1057, globaler Abschnitt 1058, globales Modul 1059, dann Seite speichern. + +### 3. Theme Builder (1061–1066) + +Auf der Seite `wp-admin/admin.php?page=et_theme_builder` mit den dort lokalisierten Nonces (`et_theme_builder_bundle.config.nonces`, `wp_localize_script` in `builder/frontend-builder/theme-builder/admin.php:129`) und jQuery, wie die TB-App (`builder/frontend-builder/build/theme-builder.js`, Suche `createLayout:function(e){return(0,l.post)` und `save:function(e,t)`): + +1. `et_theme_builder_api_create_layout` für `header`, `body`, `footer` (`theme-builder/api.php:12-46` → `et_theme_builder_insert_layout`, `theme-builder/theme-builder.php:1643-1667`) → 1061, 1062, 1063. +2. `et_theme_builder_api_save` mit `live=1` (`api.php:160-273` → `et_theme_builder_store_template`, `theme-builder.php:1463-1586`) mit zwei Vorlagen: Standardvorlage (Header 1061, **`enabled=0`**) und „[Fixture] Vorlage Globale Instanzen“ (Header 1061, Body 1062, Footer 1063, **`enabled=0`**, `use_on[]=singular:post_type:page:id:1053`). Divi hat dabei den Live-TB-Post 1064 angelegt (`et_theme_builder_get_theme_builder_post_id`, `theme-builder.php:378`), die Vorlagen 1065/1066 mit `publish` gespeichert und `_et_template` gesetzt. +3. `et_theme_builder_api_get_layout_url` (`api.php:116-147`) für die Builder-URLs; jede Layout-URL **ohne** `app_window=1` im Visual Builder geöffnet, Inhalt per `setContent` gesetzt, Titel „[Fixture] …“ über das Feld „Titel“ der Seiteneinstellungen eingetragen und mit `syncToServer({syncType:'publish'})` gespeichert (TB-Layouts sind immer `publish`) → `divi/v1/sync-to-server` wie oben. + +„Globaler Header“ heißt im Theme Builder: Header-Layout der Standardvorlage, das andere Vorlagen mit derselben ID mitbenutzen (1065 und 1066 zeigen beide auf 1061). Die Meta `_et_{header,body,footer}_layout_global` gibt es nur für TB-Library-Einträge, nicht für die Live-Vorlagen. + +### 4. Dynamic Content (Body-Layout 1062) + +Im Body-Layout: Überschrift = `post_title`, Text = `post_meta_key` mit manuellem Schlüssel `fixture_untertitel`, Text = `post_excerpt`, dazu das Modul `divi/post-content`. Die Werte haben die Form `$variable({"type":"content","value":{"name":"…","settings":{…}}})$` (`module.js`, Suche ``$variable(${JSON.stringify(s)})$``; Server: `builder-5/server/Packages/Module/Layout/Components/DynamicContent/`). + +## Was echt ist und was nachgebildet wurde + +**Echt aus Divi (JS-Serialisierer + Server):** gesamte Blockstruktur und Serialisierung aller `.html`, der `divi/placeholder`-Rahmen, die Form der globalen Instanzen, alle Library-Terme und -Meta, Post-Zeilen, alle Meta der Seite und der TB-Layouts, TB-Vorlagen-Meta, `_et_template` am TB-Post, der Umgang mit Kategorie/Schlagwort-Neuanlage. + +**Von uns vorgegeben bzw. aus JS nachgebildet:** + +- Die Modul-**Attribute** im Builder-Store (siehe 1). Divi hat sie unverändert serialisiert, aber nicht selbst erzeugt. +- Die Vorlagen-Nutzdaten für `et_theme_builder_api_save`: nachgebaut nach `formatTemplatesForStorage` (`theme-builder.js`, Suche `formatTemplatesForStorage=function`): `{id:, title, autogenerated_title:"0", default:"1"/"0", enabled:"1"/"0", layouts:{header|body|footer:{id, enabled:"1"}}, use_on:[…], exclude_from:[]}`, `hasDefault:"0"`, `first_request`/`last_request:"1"`. Der Server-Teil (Speichern, Zuordnung) ist echt. +- Die `settings` der Dynamic-Content-Werte: aus den `default`-Werten der Felder in `settings.dynamicContent.options` (so füllt der Einstellungsdialog vor) plus `meta_key`. Im echten Dialog können Schlüsselreihenfolge und ausgeblendete Felder (`custom_date_format`, `date_format` ohne Datumsfeld) abweichen. Die Namen (`post_title`, `post_meta_key`, `post_excerpt`) und die `$variable(…)$`-Hülle sind aus Divi belegt. + +## Format-Befunde (für Etappe 3 wichtig) + +1. **Der Visual Builder speichert anders als unsere bisherigen Seiten:** Zeilenumbrüche zwischen Blöcken, Module ohne Kinder **selbstschließend** (``, danach eine Leerzeile), `builderVersion` steht am Ende der Attribute; der `divi/placeholder`-Rahmen ohne Umbruch. Divis D4-Konvertierung und das Plugin schreiben dagegen gepaarte Kommentare ohne Umbrüche. Beide Formen kommen auf echten Seiten vor. Der Plugin-Parser liest alle zehn `.html` hier byte-identisch zurück (Parse → Serialize). +2. **Globale Instanz auf der Seite:** ``, darin die Kinder mit ihren **echten** Blocknamen und jeweils `"globalParent":"1058"`. Ein globales Modul ohne Kinder ist ein selbstschließender `divi/global-layout`-Block. `localAttrs` ist ein vollständiger Schnappschuss (kein leeres Objekt); `localAttrsMap`/`localChildren` fehlen, solange nichts entkoppelt ist. +3. **Globaler Library-Post** enthält normalen Inhalt ohne `globalModule`/`globalParent`; „global“ steckt nur im Term `scope=global` (beim Modul zusätzlich `_et_pb_module_type`, `_et_pb_excluded_global_options = "[]"`). +4. Library-Einträge bekommen **kein** `_et_pb_use_divi_5` und kein `_et_pb_use_builder` (bestätigt F3). Seiten und TB-Layouts nach VB-Speichern schon. +5. **Zeilen-Bug in Divi:** `_et_pb_row_layout` ist `"[object Object]"` (der Builder schickt `columns_layout` als stringifiziertes Objekt). Nicht als Wert verwenden. +6. **TB-Layouts haben keinen `layout_type`-Term**, obwohl `et_theme_builder_insert_layout` ihn setzen will (`theme-builder.php:1663`). In `wp_term_relationships` steht für 1061–1063 nichts; vermutlich ist die Taxonomie im admin-ajax-Aufruf nicht registriert (Registrierung über `ET_Builder_Library`, `builder/feature/Library.php:397`) – die Ursache ist nicht weiter geprüft. Nicht auf Terme bei TB-Layouts verlassen. +7. **TB-Bedingungen** stehen als mehrfaches Meta `_et_use_on` / `_et_exclude_from` mit IDs wie `singular:post_type:page:id:1053`. Die Vorlagenzuordnung ist `_et_template` (mehrfach) am `et_theme_builder`-Post; Vorlagen-Titel der Standardvorlage ist übersetzt („Standard-Website-Template“). +8. TB-Layouts heißen nach dem Anlegen alle „Theme Builder Layout“ mit Slug `theme-builder-layout` (in jedem Post-Typ gleich); erst der Titel im Builder benennt sie um. `_et_pb_post_settings_title` spiegelt den Titel. + +## Wirkung auf die Live-Site + +Vorher gab es keinen Theme-Builder-Post und keine Vorlagen. Jetzt existiert der Live-TB-Post 1064. Beide Vorlagen sind deaktiviert: `ET_Theme_Builder_Request::get_template()` überspringt deaktivierte Vorlagen (`theme-builder/ThemeBuilderRequest.php:393`) und nimmt die Standardvorlage nur, wenn sie aktiv ist (`:434`). Die Fixture-Vorlage wäre zudem nur für die Entwurfsseite 1053 zuständig. Geprüft: `get_template()` liefert für 1053 nichts, die Startseite enthält keinen Fixture-Header. Beim Live-Speichern hat Divi `et_theme_builder_trash_draft_and_unused_posts()` (`theme-builder.php:591`) ausgeführt; nichts wurde als ungenutzt markiert. **Achtung:** Wer im Theme Builder später speichert, speichert die komplette Vorlagenliste neu; Layouts, die dann nicht mehr zugeordnet sind, markiert Divi und löscht sie nach 7 Tagen. + +Neu angelegte Terme (vorher gab es keine Library-Terme): `layout_category` „Fixture“ (26), `layout_tag` „fixture“ (32) sowie Divis Systemterme `scope` (27, 34), `layout_type` (28, 30, 31, 33), `module_width` (29). + +## Neu exportieren + +``` +ddev wp eval-file wp-content/plugins/WP-LIT-MCP/tests/Integration/divi-export-stage3-fixtures.php +``` + +Exportiert werden nur Posts mit Titel „[Fixture] …“ und die Standardvorlage, wenn sie auf Fixture-Layouts zeigt. Das Skript löscht vorher alle `*.json`/`*.html` in diesem Ordner. + +## Aufräumen (lokal) + +Die Daten bleiben vorerst zum Ansehen stehen. Zum Entfernen (DDEV, WordPress-Wurzel; IDs vorher mit `manifest.json` abgleichen): + +``` +# Theme Builder: TB-Post (von den Fixtures neu angelegt), Vorlagen, Layouts +ddev wp post delete 1064 1065 1066 1061 1062 1063 --force +# Library-Einträge und Seite (Revision 1060 fällt mit weg) +ddev wp post delete 1054 1055 1056 1057 1058 1059 1053 --force +# Von den Fixtures angelegte Terme +ddev wp term delete layout_category 26 +ddev wp term delete layout_tag 32 +ddev wp eval 'foreach ([27,28,29,30,31,33,34] as $t) { wp_delete_term($t, get_term($t)->taxonomy); }' +# Divi-Caches +ddev wp eval 'et_theme_builder_clear_wp_cache("all"); ET_Core_PageResource::remove_static_resources("all", "all");' +``` + +`scope`, `layout_type` und `module_width` legt Divi beim nächsten Speichern in die Library wieder an. Gab es bis dahin eigene Library-Einträge, die diese Terme benutzen, die dritte Zeile weglassen. diff --git a/tests/Fixtures/divi/stage3/library--layout-ganze-seite.html b/tests/Fixtures/divi/stage3/library--layout-ganze-seite.html new file mode 100644 index 0000000..705efb4 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--layout-ganze-seite.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library--layout-ganze-seite.json b/tests/Fixtures/divi/stage3/library--layout-ganze-seite.json new file mode 100644 index 0000000..c995a88 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--layout-ganze-seite.json @@ -0,0 +1,51 @@ +{ + "post": { + "ID": 1054, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Layout ganze Seite", + "post_name": "fixture-layout-ganze-seite", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:10", + "post_modified_gmt": "2026-09-26 20:56:10", + "content_file": "library--layout-ganze-seite.html", + "content_sha256": "2fcb9fb909d698b97581531ac69c5a3973647a57a4970d7a45a51fbe4ae24968" + }, + "meta": { + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "layout" + ] + }, + "terms": { + "layout_category": [ + { + "slug": "fixture", + "name": "Fixture" + } + ], + "layout_type": [ + { + "slug": "layout", + "name": "layout" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "non_global", + "name": "non_global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/library--library-abschnitt.html b/tests/Fixtures/divi/stage3/library--library-abschnitt.html new file mode 100644 index 0000000..3ad44eb --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-abschnitt.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library--library-abschnitt.json b/tests/Fixtures/divi/stage3/library--library-abschnitt.json new file mode 100644 index 0000000..42539d3 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-abschnitt.json @@ -0,0 +1,45 @@ +{ + "post": { + "ID": 1055, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Library-Abschnitt", + "post_name": "fixture-library-abschnitt", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:14", + "post_modified_gmt": "2026-09-26 20:56:14", + "content_file": "library--library-abschnitt.html", + "content_sha256": "c26526fccd9a1dac416a91ddd1459059858783478921744698b77fe83dcb7e94" + }, + "meta": { + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "section" + ] + }, + "terms": { + "layout_type": [ + { + "slug": "section", + "name": "section" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "non_global", + "name": "non_global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/library--library-modul.html b/tests/Fixtures/divi/stage3/library--library-modul.html new file mode 100644 index 0000000..a627cf5 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-modul.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library--library-modul.json b/tests/Fixtures/divi/stage3/library--library-modul.json new file mode 100644 index 0000000..7c51c2b --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-modul.json @@ -0,0 +1,54 @@ +{ + "post": { + "ID": 1057, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Library-Modul", + "post_name": "fixture-library-modul", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:21", + "post_modified_gmt": "2026-09-26 20:56:21", + "content_file": "library--library-modul.html", + "content_sha256": "fd36cf6168b7492d26601fb4ed11accecc30fe1f8b7ec5a4d4091fc40d210ba4" + }, + "meta": { + "_et_pb_module_type": [ + "divi/blurb" + ], + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "module" + ] + }, + "terms": { + "layout_tag": [ + { + "slug": "fixture", + "name": "fixture" + } + ], + "layout_type": [ + { + "slug": "module", + "name": "module" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "non_global", + "name": "non_global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/library--library-zeile.html b/tests/Fixtures/divi/stage3/library--library-zeile.html new file mode 100644 index 0000000..34a05dc --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-zeile.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library--library-zeile.json b/tests/Fixtures/divi/stage3/library--library-zeile.json new file mode 100644 index 0000000..2ef95b8 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library--library-zeile.json @@ -0,0 +1,48 @@ +{ + "post": { + "ID": 1056, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Library-Zeile", + "post_name": "fixture-library-zeile", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:18", + "post_modified_gmt": "2026-09-26 20:56:18", + "content_file": "library--library-zeile.html", + "content_sha256": "0a47d5133a4ca10a5fd0ebb49908894a5c20a17798d5954e0897958ad4704dfe" + }, + "meta": { + "_et_pb_row_layout": [ + "[object Object]" + ], + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "row" + ] + }, + "terms": { + "layout_type": [ + { + "slug": "row", + "name": "row" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "non_global", + "name": "non_global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.html b/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.html new file mode 100644 index 0000000..e9c5312 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.json b/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.json new file mode 100644 index 0000000..0419e1f --- /dev/null +++ b/tests/Fixtures/divi/stage3/library-global--globaler-abschnitt.json @@ -0,0 +1,45 @@ +{ + "post": { + "ID": 1058, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Globaler Abschnitt", + "post_name": "fixture-globaler-abschnitt", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:25", + "post_modified_gmt": "2026-09-26 20:56:25", + "content_file": "library-global--globaler-abschnitt.html", + "content_sha256": "dd1549b879730de4d937c84236bb8e548f50024e47e9910561f24ae0e91b13a6" + }, + "meta": { + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "section" + ] + }, + "terms": { + "layout_type": [ + { + "slug": "section", + "name": "section" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "global", + "name": "global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/library-global--globales-modul.html b/tests/Fixtures/divi/stage3/library-global--globales-modul.html new file mode 100644 index 0000000..d6acb90 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library-global--globales-modul.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/library-global--globales-modul.json b/tests/Fixtures/divi/stage3/library-global--globales-modul.json new file mode 100644 index 0000000..8655bf7 --- /dev/null +++ b/tests/Fixtures/divi/stage3/library-global--globales-modul.json @@ -0,0 +1,51 @@ +{ + "post": { + "ID": 1059, + "post_type": "et_pb_layout", + "post_status": "publish", + "post_title": "[Fixture] Globales Modul", + "post_name": "fixture-globales-modul", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:56:29", + "post_modified_gmt": "2026-09-26 20:56:29", + "content_file": "library-global--globales-modul.html", + "content_sha256": "3fe0aa7eb9a0aad0e79cc23702c3082f7ff8fbbb80e35185f01ccfc751c1690b" + }, + "meta": { + "_et_pb_module_type": [ + "divi/text" + ], + "_et_pb_excluded_global_options": [ + "[]" + ], + "_et_pb_built_for_post_type": [ + "page" + ], + "_et_pb_template_type": [ + "module" + ] + }, + "terms": { + "layout_type": [ + { + "slug": "module", + "name": "module" + } + ], + "module_width": [ + { + "slug": "regular", + "name": "regular" + } + ], + "scope": [ + { + "slug": "global", + "name": "global" + } + ] + } +} diff --git a/tests/Fixtures/divi/stage3/manifest.json b/tests/Fixtures/divi/stage3/manifest.json new file mode 100644 index 0000000..cb38cc1 --- /dev/null +++ b/tests/Fixtures/divi/stage3/manifest.json @@ -0,0 +1,17 @@ +{ + "divi_version": "5.2.1", + "posts": { + "page--globale-instanzen": 1053, + "library--layout-ganze-seite": 1054, + "library--library-abschnitt": 1055, + "library--library-zeile": 1056, + "library--library-modul": 1057, + "library-global--globaler-abschnitt": 1058, + "library-global--globales-modul": 1059, + "tb-header--globaler-header": 1061, + "tb-body--body-mit-dynamic-content": 1062, + "tb-footer--footer": 1063, + "tb-template--vorlage-globale-instanzen": 1066, + "tb-template--default": 1065 + } +} diff --git a/tests/Fixtures/divi/stage3/page--globale-instanzen.html b/tests/Fixtures/divi/stage3/page--globale-instanzen.html new file mode 100644 index 0000000..9e32819 --- /dev/null +++ b/tests/Fixtures/divi/stage3/page--globale-instanzen.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/page--globale-instanzen.json b/tests/Fixtures/divi/stage3/page--globale-instanzen.json new file mode 100644 index 0000000..9e94d99 --- /dev/null +++ b/tests/Fixtures/divi/stage3/page--globale-instanzen.json @@ -0,0 +1,71 @@ +{ + "post": { + "ID": 1053, + "post_type": "page", + "post_status": "draft", + "post_title": "[Fixture] Globale Instanzen", + "post_name": "", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "0000-00-00 00:00:00", + "post_modified_gmt": "2026-09-26 20:56:33", + "content_file": "page--globale-instanzen.html", + "content_sha256": "e5691e17e77315c449c5bf536cb3944ea0bc0bee316c45aa26e3b04485253623" + }, + "meta": { + "_et_pb_use_builder": [ + "on" + ], + "_et_pb_show_page_creation": [ + "off" + ], + "_et_pb_ab_current_shortcode": [ + "[et_pb_split_track id=\"1053\" /]" + ], + "_et_pb_content_area_background_color": [ + "" + ], + "_et_pb_custom_css": [ + "" + ], + "_et_pb_enable_shortcode_tracking": [ + "off" + ], + "_et_pb_gutter_width": [ + "3" + ], + "_et_pb_page_z_index": [ + "auto" + ], + "_thumbnail_id": [ + "" + ], + "_et_pb_section_background_color": [ + "" + ], + "_et_pb_post_settings_title": [ + "[Fixture] Globale Instanzen" + ], + "_et_pb_post_settings_excerpt": [ + "" + ], + "_et_pb_first_image": [ + "" + ], + "_et_pb_truncate_post": [ + "" + ], + "_et_pb_truncate_post_date": [ + "" + ], + "_divi_off_canvas_data": [ + "a:2:{s:14:\"activeCanvasId\";s:36:\"4d4f306d-8e36-4b8f-9a7a-076eb1b075ac\";s:14:\"mainCanvasName\";s:11:\"Main Canvas\";}" + ], + "_et_pb_use_divi_5": [ + "on" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.html b/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.html new file mode 100644 index 0000000..a9ae55d --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.json b/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.json new file mode 100644 index 0000000..b1f4afe --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-body--body-mit-dynamic-content.json @@ -0,0 +1,71 @@ +{ + "post": { + "ID": 1062, + "post_type": "et_body_layout", + "post_status": "publish", + "post_title": "[Fixture] Body mit Dynamic Content", + "post_name": "theme-builder-layout", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:58:34", + "post_modified_gmt": "2026-09-26 21:00:07", + "content_file": "tb-body--body-mit-dynamic-content.html", + "content_sha256": "4f2830538ea84c8c490ee278be883f09d89816c9635c7571be294c8bdadce3a2" + }, + "meta": { + "_et_pb_use_builder": [ + "on" + ], + "_et_pb_show_page_creation": [ + "off" + ], + "_et_pb_ab_current_shortcode": [ + "[et_pb_split_track id=\"1062\" /]" + ], + "_et_pb_content_area_background_color": [ + "" + ], + "_et_pb_custom_css": [ + "" + ], + "_et_pb_enable_shortcode_tracking": [ + "off" + ], + "_et_pb_gutter_width": [ + "3" + ], + "_et_pb_page_z_index": [ + "auto" + ], + "_thumbnail_id": [ + "" + ], + "_et_pb_section_background_color": [ + "" + ], + "_et_pb_post_settings_title": [ + "[Fixture] Body mit Dynamic Content" + ], + "_et_pb_post_settings_excerpt": [ + "" + ], + "_et_pb_first_image": [ + "" + ], + "_et_pb_truncate_post": [ + "" + ], + "_et_pb_truncate_post_date": [ + "" + ], + "_divi_off_canvas_data": [ + "a:2:{s:14:\"activeCanvasId\";s:36:\"b773e1de-72ba-4640-85d6-249ce9e4a82d\";s:14:\"mainCanvasName\";s:11:\"Main Canvas\";}" + ], + "_et_pb_use_divi_5": [ + "on" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/tb-footer--footer.html b/tests/Fixtures/divi/stage3/tb-footer--footer.html new file mode 100644 index 0000000..dcd03c9 --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-footer--footer.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/tb-footer--footer.json b/tests/Fixtures/divi/stage3/tb-footer--footer.json new file mode 100644 index 0000000..5403923 --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-footer--footer.json @@ -0,0 +1,71 @@ +{ + "post": { + "ID": 1063, + "post_type": "et_footer_layout", + "post_status": "publish", + "post_title": "[Fixture] Footer", + "post_name": "theme-builder-layout", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:58:34", + "post_modified_gmt": "2026-09-26 21:00:21", + "content_file": "tb-footer--footer.html", + "content_sha256": "7e6f6744cbc55a903cf346328ab383e7ddda85292990cf93b49594fa6f3d7f53" + }, + "meta": { + "_et_pb_use_builder": [ + "on" + ], + "_et_pb_show_page_creation": [ + "off" + ], + "_et_pb_ab_current_shortcode": [ + "[et_pb_split_track id=\"1063\" /]" + ], + "_et_pb_content_area_background_color": [ + "" + ], + "_et_pb_custom_css": [ + "" + ], + "_et_pb_enable_shortcode_tracking": [ + "off" + ], + "_et_pb_gutter_width": [ + "3" + ], + "_et_pb_page_z_index": [ + "auto" + ], + "_thumbnail_id": [ + "" + ], + "_et_pb_section_background_color": [ + "" + ], + "_et_pb_post_settings_title": [ + "[Fixture] Footer" + ], + "_et_pb_post_settings_excerpt": [ + "" + ], + "_et_pb_first_image": [ + "" + ], + "_et_pb_truncate_post": [ + "" + ], + "_et_pb_truncate_post_date": [ + "" + ], + "_divi_off_canvas_data": [ + "a:2:{s:14:\"activeCanvasId\";s:36:\"7f8e1cce-7f40-4ac6-bc6d-239e0050ed4a\";s:14:\"mainCanvasName\";s:11:\"Main Canvas\";}" + ], + "_et_pb_use_divi_5": [ + "on" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/tb-header--globaler-header.html b/tests/Fixtures/divi/stage3/tb-header--globaler-header.html new file mode 100644 index 0000000..d93ffb6 --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-header--globaler-header.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/Fixtures/divi/stage3/tb-header--globaler-header.json b/tests/Fixtures/divi/stage3/tb-header--globaler-header.json new file mode 100644 index 0000000..e16320b --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-header--globaler-header.json @@ -0,0 +1,71 @@ +{ + "post": { + "ID": 1061, + "post_type": "et_header_layout", + "post_status": "publish", + "post_title": "[Fixture] Globaler Header", + "post_name": "theme-builder-layout", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:58:33", + "post_modified_gmt": "2026-09-26 20:59:52", + "content_file": "tb-header--globaler-header.html", + "content_sha256": "aa7c71a22cd8d7b3ecb7f6fbcbeaff289322b3797db3f3aac87763d29bb4b851" + }, + "meta": { + "_et_pb_use_builder": [ + "on" + ], + "_et_pb_show_page_creation": [ + "off" + ], + "_et_pb_ab_current_shortcode": [ + "[et_pb_split_track id=\"1061\" /]" + ], + "_et_pb_content_area_background_color": [ + "" + ], + "_et_pb_custom_css": [ + "" + ], + "_et_pb_enable_shortcode_tracking": [ + "off" + ], + "_et_pb_gutter_width": [ + "3" + ], + "_et_pb_page_z_index": [ + "auto" + ], + "_thumbnail_id": [ + "" + ], + "_et_pb_section_background_color": [ + "" + ], + "_et_pb_post_settings_title": [ + "[Fixture] Globaler Header" + ], + "_et_pb_post_settings_excerpt": [ + "" + ], + "_et_pb_first_image": [ + "" + ], + "_et_pb_truncate_post": [ + "" + ], + "_et_pb_truncate_post_date": [ + "" + ], + "_divi_off_canvas_data": [ + "a:2:{s:14:\"activeCanvasId\";s:36:\"b20ac30f-aeb9-4b8a-863f-44d36b514202\";s:14:\"mainCanvasName\";s:11:\"Main Canvas\";}" + ], + "_et_pb_use_divi_5": [ + "on" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/tb-template--default.json b/tests/Fixtures/divi/stage3/tb-template--default.json new file mode 100644 index 0000000..cc5e986 --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-template--default.json @@ -0,0 +1,47 @@ +{ + "post": { + "ID": 1065, + "post_type": "et_template", + "post_status": "publish", + "post_title": "Standard-Website-Template", + "post_name": "standard-website-template", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:58:34", + "post_modified_gmt": "2026-09-26 20:58:34", + "content_file": null, + "content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "meta": { + "_et_autogenerated_title": [ + "0" + ], + "_et_default": [ + "1" + ], + "_et_enabled": [ + "0" + ], + "_et_header_layout_id": [ + "1061" + ], + "_et_header_layout_enabled": [ + "1" + ], + "_et_body_layout_id": [ + "0" + ], + "_et_body_layout_enabled": [ + "1" + ], + "_et_footer_layout_id": [ + "0" + ], + "_et_footer_layout_enabled": [ + "1" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/tb-template--vorlage-globale-instanzen.json b/tests/Fixtures/divi/stage3/tb-template--vorlage-globale-instanzen.json new file mode 100644 index 0000000..6ddb0d6 --- /dev/null +++ b/tests/Fixtures/divi/stage3/tb-template--vorlage-globale-instanzen.json @@ -0,0 +1,50 @@ +{ + "post": { + "ID": 1066, + "post_type": "et_template", + "post_status": "publish", + "post_title": "[Fixture] Vorlage Globale Instanzen", + "post_name": "fixture-vorlage-globale-instanzen", + "post_author": 1, + "post_parent": 0, + "menu_order": 0, + "post_excerpt": "", + "post_date_gmt": "2026-09-26 20:58:34", + "post_modified_gmt": "2026-09-26 20:58:34", + "content_file": null, + "content_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "meta": { + "_et_autogenerated_title": [ + "0" + ], + "_et_default": [ + "0" + ], + "_et_enabled": [ + "0" + ], + "_et_header_layout_id": [ + "1061" + ], + "_et_header_layout_enabled": [ + "1" + ], + "_et_body_layout_id": [ + "1062" + ], + "_et_body_layout_enabled": [ + "1" + ], + "_et_footer_layout_id": [ + "1063" + ], + "_et_footer_layout_enabled": [ + "1" + ], + "_et_use_on": [ + "singular:post_type:page:id:1053" + ] + }, + "terms": {} +} diff --git a/tests/Fixtures/divi/stage3/theme-builder.json b/tests/Fixtures/divi/stage3/theme-builder.json new file mode 100644 index 0000000..2e76296 --- /dev/null +++ b/tests/Fixtures/divi/stage3/theme-builder.json @@ -0,0 +1,20 @@ +{ + "theme_builder_post": { + "ID": 1064, + "post_status": "publish", + "post_title": "Theme Builder", + "meta": { + "_et_template": [ + "1065", + "1066" + ] + } + }, + "template_ids_in_order": [ + 1065, + 1066 + ], + "draft_theme_builder_posts": [], + "options_et_tb": {}, + "marked_as_unused": [] +} diff --git a/tests/Integration/divi-export-stage3-fixtures.php b/tests/Integration/divi-export-stage3-fixtures.php new file mode 100644 index 0000000..c4b2939 --- /dev/null +++ b/tests/Integration/divi-export-stage3-fixtures.php @@ -0,0 +1,185 @@ +.json per post (post row, all meta, + * all term relationships), the raw post_content byte-exact as .html, the live Theme + * Builder post with its template list and the et_tb_* options as theme-builder.json, and a + * manifest.json (role → post ID). How the fixtures were created: tests/Fixtures/divi/stage3/README.md. + * + * Usage (DDEV, WordPress root): + * wp eval-file wp-content/plugins/WP-LIT-MCP/tests/Integration/divi-export-stage3-fixtures.php + * + * Only posts titled "[Fixture] …" are exported, plus the Default Website Template when its + * layouts are fixture layouts. Read-only. Fixtures are committed – no personal data. + * No `declare(strict_types=1);` – wp eval-file runs the file through eval(). + */ + +defined('WP_CLI') && WP_CLI || exit; + +$lit_prefix = '[Fixture] '; +$lit_dir = dirname(__DIR__) . '/Fixtures/divi/stage3'; +$lit_skip_meta = ['_edit_lock', '_edit_last']; + +/** Role name for a fixture post, derived from post type, title and scope. */ +$lit_role = static function (WP_Post $post, array $terms): string { + $slug = sanitize_title(preg_replace('/^\[Fixture\]\s*/', '', $post->post_title)); + $prefix = [ + 'page' => 'page', + 'et_pb_layout' => in_array('global', array_column($terms['scope'] ?? [], 'slug'), true) + ? 'library-global' + : 'library', + 'et_template' => 'tb-template', + 'et_header_layout' => 'tb-header', + 'et_body_layout' => 'tb-body', + 'et_footer_layout' => 'tb-footer', + ][$post->post_type] ?? $post->post_type; + + return $prefix . '--' . $slug; +}; + +/** All term relationships by taxonomy – read from the tables, some Divi taxonomies are not registered under WP-CLI. */ +$lit_terms = static function (int $postId): array { + global $wpdb; + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT tt.taxonomy, t.slug, t.name FROM {$wpdb->term_relationships} tr + JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id + JOIN {$wpdb->terms} t ON t.term_id = tt.term_id + WHERE tr.object_id = %d ORDER BY tt.taxonomy, t.slug", + $postId + )); + $terms = []; + foreach ($rows as $row) { + $terms[$row->taxonomy][] = ['slug' => $row->slug, 'name' => $row->name]; + } + + return $terms; +}; + +/** Raw meta rows (value as stored, multiple values kept in order). */ +$lit_meta = static function (int $postId) use ($lit_skip_meta): array { + global $wpdb; + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d ORDER BY meta_id", + $postId + )); + $meta = []; + foreach ($rows as $row) { + if (! in_array($row->meta_key, $lit_skip_meta, true)) { + $meta[$row->meta_key][] = $row->meta_value; + } + } + + return $meta; +}; + +$lit_write = static function (string $file, string $data): void { + if (file_put_contents($file, $data) === false) { + WP_CLI::error('Cannot write ' . $file); + } +}; + +$lit_json = static fn (array $data): string => wp_json_encode( + $data, + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE +) . "\n"; + +global $wpdb; +$lit_types = ['page', 'et_pb_layout', 'et_template', 'et_header_layout', 'et_body_layout', 'et_footer_layout']; +$lit_ids = array_map('intval', $wpdb->get_col($wpdb->prepare( + "SELECT ID FROM {$wpdb->posts} + WHERE post_type IN (" . implode(', ', array_fill(0, count($lit_types), '%s')) . ") + AND post_status NOT IN ('inherit', 'auto-draft') + AND post_title LIKE %s + ORDER BY ID", + ...[...$lit_types, $wpdb->esc_like($lit_prefix) . '%'] +))); + +// The live Theme Builder post and the Default Website Template, when it points to fixture layouts. +$lit_tb_id = (int) $wpdb->get_var( + "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'et_theme_builder' AND post_status = 'publish' ORDER BY ID LIMIT 1" +); +$lit_tb_templates = $lit_tb_id ? array_map('intval', get_post_meta($lit_tb_id, '_et_template', false)) : []; +foreach ($lit_tb_templates as $templateId) { + if (get_post_meta($templateId, '_et_default', true) !== '1') { + continue; + } + foreach (['header', 'body', 'footer'] as $area) { + $layoutId = (int) get_post_meta($templateId, "_et_{$area}_layout_id", true); + if ($layoutId > 0 && in_array($layoutId, $lit_ids, true) && ! in_array($templateId, $lit_ids, true)) { + $lit_ids[] = $templateId; + } + } +} + +if ($lit_ids === []) { + WP_CLI::error('No "[Fixture] " posts found – create them first (see tests/Fixtures/divi/stage3/README.md).'); +} + +if (! is_dir($lit_dir) && ! wp_mkdir_p($lit_dir)) { + WP_CLI::error('Cannot create ' . $lit_dir); +} +foreach (glob($lit_dir . '/*.{json,html}', GLOB_BRACE) ?: [] as $old) { + unlink($old); +} + +$lit_manifest = ['divi_version' => wp_get_theme('Divi')->get('Version'), 'posts' => []]; +foreach ($lit_ids as $postId) { + $post = get_post($postId); + $terms = $lit_terms($postId); + $role = $post->post_type === 'et_template' && get_post_meta($postId, '_et_default', true) === '1' + ? 'tb-template--default' + : $lit_role($post, $terms); + + $record = [ + 'post' => [ + 'ID' => $post->ID, + 'post_type' => $post->post_type, + 'post_status' => $post->post_status, + 'post_title' => $post->post_title, + 'post_name' => $post->post_name, + 'post_author' => (int) $post->post_author, + 'post_parent' => $post->post_parent, + 'menu_order' => $post->menu_order, + 'post_excerpt' => $post->post_excerpt, + 'post_date_gmt' => $post->post_date_gmt, + 'post_modified_gmt' => $post->post_modified_gmt, + 'content_file' => $post->post_content === '' ? null : $role . '.html', + 'content_sha256' => hash('sha256', $post->post_content), + ], + 'meta' => (object) $lit_meta($postId), + 'terms' => (object) $terms, + ]; + $lit_write($lit_dir . '/' . $role . '.json', $lit_json($record)); + if ($post->post_content !== '') { + $lit_write($lit_dir . '/' . $role . '.html', $post->post_content); + } + $lit_manifest['posts'][$role] = $postId; +} + +$lit_options = []; +$lit_option_rows = $wpdb->get_results( + "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE 'et\\_tb\\_%' ORDER BY option_name" +); +foreach ($lit_option_rows as $row) { + $lit_options[$row->option_name] = $row->option_value; +} +$lit_write($lit_dir . '/theme-builder.json', $lit_json([ + 'theme_builder_post' => $lit_tb_id ? [ + 'ID' => $lit_tb_id, + 'post_status' => get_post_status($lit_tb_id), + 'post_title' => get_the_title($lit_tb_id), + 'meta' => (object) $lit_meta($lit_tb_id), + ] : null, + 'template_ids_in_order' => $lit_tb_templates, + 'draft_theme_builder_posts' => array_map('intval', $wpdb->get_col( + "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'et_theme_builder' AND post_status = 'auto-draft'" + )), + 'options_et_tb' => (object) $lit_options, + 'marked_as_unused' => array_map('intval', $wpdb->get_col( + "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_et_theme_builder_marked_as_unused'" + )), +])); +$lit_write($lit_dir . '/manifest.json', $lit_json($lit_manifest)); + +WP_CLI::success(sprintf('%d posts exported to %s', count($lit_manifest['posts']), $lit_dir));