Files
wordpress-theme-aksrt/tools/wp-cli-visual-fixtures.php

256 lines
9.2 KiB
PHP

<?php
/**
* Reversible WP-CLI visual fixtures for AKSRT/Zibll comparison.
*
* Usage:
* wp eval-file wp-content/themes/AKSRT-theme/tools/wp-cli-visual-fixtures.php create
* wp eval-file wp-content/themes/AKSRT-theme/tools/wp-cli-visual-fixtures.php cleanup
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!defined('WP_CLI') || !WP_CLI) {
return;
}
$fixture_action = sanitize_key((string) ($args[0] ?? ''));
$fixture_option = 'aksrt_visual_fixture_manifest_v1';
$fixture_prefix = 'aksrt_zibll_v1_';
$fixture_theme_dir = dirname(__DIR__);
$fixture_json = $fixture_theme_dir . '/tests/fixtures/widget-golden-v1.json';
if (!in_array($fixture_action, ['create', 'cleanup'], true)) {
WP_CLI::error('Expected create or cleanup.');
}
$cleanup = static function (array $manifest) use ($fixture_option): void {
$sidebars = wp_get_sidebars_widgets();
foreach ((array) ($manifest['widgets'] ?? []) as $widget) {
$id_base = sanitize_key((string) ($widget['id_base'] ?? ''));
$number = (int) ($widget['number'] ?? 0);
if ($id_base === '' || $number <= 0) {
continue;
}
$widget_id = $id_base . '-' . $number;
foreach ($sidebars as $area => $ids) {
if (!is_array($ids)) {
continue;
}
$sidebars[$area] = array_values(array_diff($ids, [$widget_id]));
}
$option_name = 'widget_' . $id_base;
$instances = get_option($option_name, []);
if (is_array($instances) && array_key_exists($number, $instances)) {
unset($instances[$number]);
update_option($option_name, $instances, false);
}
}
wp_set_sidebars_widgets($sidebars);
foreach (array_reverse((array) ($manifest['comments'] ?? [])) as $id) {
wp_delete_comment((int) $id, true);
}
foreach (array_reverse((array) ($manifest['posts'] ?? [])) as $id) {
wp_delete_post((int) $id, true);
}
foreach (array_reverse((array) ($manifest['attachments'] ?? [])) as $id) {
wp_delete_attachment((int) $id, true);
}
foreach (array_reverse((array) ($manifest['users'] ?? [])) as $id) {
if (function_exists('wp_delete_user')) {
wp_delete_user((int) $id);
}
}
foreach (array_reverse((array) ($manifest['terms'] ?? [])) as $term) {
wp_delete_term((int) ($term['id'] ?? 0), (string) ($term['taxonomy'] ?? 'category'));
}
delete_option($fixture_option);
};
$existing = get_option($fixture_option, []);
if ($fixture_action === 'cleanup') {
if (!is_array($existing) || empty($existing)) {
WP_CLI::success('No AKSRT visual fixtures found.');
return;
}
$cleanup($existing);
WP_CLI::success('AKSRT visual fixtures removed.');
return;
}
if (is_array($existing) && !empty($existing)) {
WP_CLI::error('Fixtures already exist. Run cleanup first.');
}
if (!is_file($fixture_json)) {
WP_CLI::error('Golden fixture JSON is missing.');
}
$golden = json_decode((string) file_get_contents($fixture_json), true);
if (!is_array($golden)) {
WP_CLI::error('Golden fixture JSON is invalid.');
}
$manifest = [
'prefix' => $fixture_prefix,
'created_at' => gmdate('c'),
'terms' => [],
'users' => [],
'attachments' => [],
'posts' => [],
'comments' => [],
'widgets' => [],
];
update_option($fixture_option, $manifest, false);
try {
$term_map = [];
foreach ((array) ($golden['dataset']['terms'] ?? []) as $source) {
$taxonomy = (string) ($source['taxonomy'] ?? 'category');
$result = wp_insert_term(
(string) ($source['name'] ?? 'Fixture'),
$taxonomy,
['slug' => $fixture_prefix . (int) ($source['id'] ?? 0)]
);
if (is_wp_error($result)) {
throw new RuntimeException($result->get_error_message());
}
$term_id = (int) $result['term_id'];
$term_map[(int) $source['id']] = $term_id;
$manifest['terms'][] = ['id' => $term_id, 'taxonomy' => $taxonomy];
}
$user_map = [];
foreach ((array) ($golden['dataset']['users'] ?? []) as $index => $source) {
$login = $fixture_prefix . 'user_' . ((int) $index + 1);
$user_id = wp_insert_user([
'user_login' => $login,
'user_pass' => 'AKSRT-Visual-2026!',
'user_email' => $login . '@example.test',
'display_name' => (string) ($source['name'] ?? $login),
'role' => 'subscriber',
]);
if (is_wp_error($user_id)) {
throw new RuntimeException($user_id->get_error_message());
}
$user_id = (int) $user_id;
$user_map[(int) ($source['id'] ?? 0)] = $user_id;
$manifest['users'][] = $user_id;
update_user_meta($user_id, 'aksrt_points', (int) ($source['points'] ?? 0));
if ($index === 0) {
update_user_meta($user_id, 'aksrt_vip_level', 2);
update_user_meta($user_id, 'aksrt_vip_expires', time() + YEAR_IN_SECONDS);
}
}
require_once ABSPATH . 'wp-admin/includes/image.php';
$media_sources = [
$fixture_theme_dir . '/assets/img/slider-bg.jpg',
$fixture_theme_dir . '/assets/img/user_t.jpg',
$fixture_theme_dir . '/assets/img/user-level-7.png',
];
foreach ($media_sources as $source_path) {
if (!is_file($source_path)) {
continue;
}
$upload = wp_upload_dir();
$filename = wp_unique_filename($upload['path'], $fixture_prefix . basename($source_path));
$target = trailingslashit($upload['path']) . $filename;
if (!copy($source_path, $target)) {
throw new RuntimeException('Unable to copy fixture media.');
}
$attachment_id = wp_insert_attachment([
'post_mime_type' => wp_check_filetype($target)['type'],
'post_title' => $fixture_prefix . pathinfo($filename, PATHINFO_FILENAME),
'post_status' => 'inherit',
], $target);
if (is_wp_error($attachment_id)) {
throw new RuntimeException($attachment_id->get_error_message());
}
$attachment_id = (int) $attachment_id;
wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $target));
$manifest['attachments'][] = $attachment_id;
}
$post_map = [];
foreach ((array) ($golden['dataset']['posts'] ?? []) as $index => $source) {
$post_id = wp_insert_post([
'post_title' => $fixture_prefix . (string) ($source['title'] ?? 'Fixture post'),
'post_name' => $fixture_prefix . (int) ($source['id'] ?? $index),
'post_status' => 'publish',
'post_type' => 'post',
'post_author' => (int) ($user_map[(int) ($source['author'] ?? 0)] ?? 1),
'post_excerpt' => '用于 AKSRT 与 Zibll 同内容视觉比较的固定摘要。',
'post_content' => '<h2>视觉基线标题</h2><p>固定正文内容,用于检查排版、目录、付费状态和响应式布局。</p>',
], true);
if (is_wp_error($post_id)) {
throw new RuntimeException($post_id->get_error_message());
}
$post_id = (int) $post_id;
$post_map[(int) ($source['id'] ?? 0)] = $post_id;
$manifest['posts'][] = $post_id;
$category_id = (int) ($term_map[(int) ($source['category'] ?? 0)] ?? 0);
if ($category_id > 0) {
wp_set_post_terms($post_id, [$category_id], 'category');
}
if (!empty($manifest['attachments'])) {
set_post_thumbnail($post_id, (int) $manifest['attachments'][$index % count($manifest['attachments'])]);
}
$pay_type = sanitize_key((string) ($source['pay_type'] ?? 'no'));
$pay = class_exists('AKSRT\\Core\\DomainModel') ? \AKSRT\Core\DomainModel::default_pay_meta() : [];
$pay['pay_type'] = $pay_type;
$pay['price'] = $pay_type === 'no' ? 0 : 19.9 + $index;
$pay['pay_limit'] = $pay_type === 'vip' ? 1 : 0;
update_post_meta($post_id, '_aksrt_pay', $pay);
update_post_meta($post_id, '_aksrt_pay_type', $pay_type);
update_post_meta($post_id, '_aksrt_pay_price', $pay['price']);
update_post_meta($post_id, 'aksrt_post_views', 1200 + ($index * 137));
}
$no_image = wp_insert_post([
'post_title' => $fixture_prefix . '无图长标题文章用于检查纯文字模式和两行截断行为',
'post_name' => $fixture_prefix . 'no-image',
'post_status' => 'publish',
'post_type' => 'post',
'post_content' => '<p>无缩略图场景。</p>',
], true);
if (!is_wp_error($no_image)) {
$manifest['posts'][] = (int) $no_image;
}
if (!empty($manifest['posts'])) {
stick_post((int) $manifest['posts'][0]);
}
$components = (array) ($golden['components'] ?? []);
$sidebars = wp_get_sidebars_widgets();
$sidebars['home_top_content'] = is_array($sidebars['home_top_content'] ?? null) ? $sidebars['home_top_content'] : [];
$sidebars['home_sidebar'] = is_array($sidebars['home_sidebar'] ?? null) ? $sidebars['home_sidebar'] : [];
$number = 9100;
foreach ($components as $slug => $component) {
$id_base = sanitize_key((string) ($component['id_base'] ?? ''));
if ($id_base === '') {
continue;
}
$number++;
$settings = (array) ($component['settings'] ?? []);
$settings['title'] = $fixture_prefix . (string) ($settings['title'] ?? $slug);
$option_name = 'widget_' . $id_base;
$instances = get_option($option_name, []);
$instances = is_array($instances) ? $instances : [];
$instances[$number] = $settings;
$instances['_multiwidget'] = 1;
update_option($option_name, $instances, false);
$widget_id = $id_base . '-' . $number;
$area = (($component['size'] ?? '') === 'sidebar') ? 'home_sidebar' : 'home_top_content';
$sidebars[$area][] = $widget_id;
$manifest['widgets'][] = ['id_base' => $id_base, 'number' => $number, 'area' => $area];
}
wp_set_sidebars_widgets($sidebars);
update_option($fixture_option, $manifest, false);
WP_CLI::success('Created reversible AKSRT visual fixtures. Login password: AKSRT-Visual-2026!');
} catch (Throwable $error) {
$cleanup($manifest);
WP_CLI::error('Fixture creation rolled back: ' . $error->getMessage());
}