test: real Divi 5 stage 3 fixtures (library, global instances, Theme Builder)
Created through Divi's own save paths (Visual Builder add-to-library dialog, divi/v1/divi-library/create-item, divi/v1/sync-to-server, Theme Builder admin-ajax create_layout/save) and exported by divi-export-stage3-fixtures.php. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Exports the stage 3 Divi fixtures (library items, global instances, Theme Builder) from the
|
||||
* local site into tests/Fixtures/divi/stage3/: one <role>.json per post (post row, all meta,
|
||||
* all term relationships), the raw post_content byte-exact as <role>.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));
|
||||
Reference in New Issue
Block a user