/
home
/
altere25
/
.trash
/
wp-to-sanity-migration.11
/
/home/altere25/.trash/wp-to-sanity-migration.11
mkdir
upload
Name
Size
Mode
Actions
acf-json\.gitkeep
0
0644
edit
dl
rm
assets\css\admin.css
12113
0644
edit
dl
rm
assets\js\admin.js
52894
0644
edit
dl
rm
includes\class-ad-admin.php
10358
0644
edit
dl
rm
includes\class-ad-ai-client.php
10724
0644
edit
dl
rm
includes\class-ad-auth.php
9189
0644
edit
dl
rm
includes\class-ad-cli.php
8671
0644
edit
dl
rm
includes\class-ad-extractor.php
17237
0644
edit
dl
rm
includes\class-ad-mcp.php
6429
0644
edit
dl
rm
includes\class-ad-plugin-core.php
10252
0644
edit
dl
rm
includes\class-ad-progress.php
8022
0644
edit
dl
rm
includes\class-ad-rest.php
43443
0644
edit
dl
rm
includes\class-ad-uninstall.php
1966
0644
edit
dl
rm
includes\class-ad-webhook.php
24172
0644
edit
dl
rm
languages\.gitkeep
0
0644
edit
dl
rm
templates\admin\wizard.php
29414
0644
edit
dl
rm
wp-to-sanity-migration\acf-json\.gitkeep
0
0644
edit
dl
rm
wp-to-sanity-migration\assets\
0
0644
edit
dl
rm
wp-to-sanity-migration\assets\css\admin.css
12113
0644
edit
dl
rm
wp-to-sanity-migration\assets\js\admin.js
52894
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-admin.php
10358
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-ai-client.php
10724
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-auth.php
9189
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-cli.php
8671
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-extractor.php
17237
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-mcp.php
6429
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-plugin-core.php
10252
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-progress.php
8022
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-rest.php
43443
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-uninstall.php
1966
0644
edit
dl
rm
wp-to-sanity-migration\includes\class-ad-webhook.php
24172
0644
edit
dl
rm
wp-to-sanity-migration\languages\.gitkeep
0
0644
edit
dl
rm
wp-to-sanity-migration\package.json
220
0644
edit
dl
rm
wp-to-sanity-migration\readme.txt
8174
0644
edit
dl
rm
wp-to-sanity-migration\templates\
0
0644
edit
dl
rm
wp-to-sanity-migration\templates\admin\wizard.php
29414
0644
edit
dl
rm
wp-to-sanity-migration\uninstall.php
365
0644
edit
dl
rm
wp-to-sanity-migration\wp-to-sanity-migration.php
4418
0644
edit
dl
rm
Edit:
/home/altere25/.trash/wp-to-sanity-migration.11/includes\class-ad-extractor.php
(17237B)
<?php /** * Altered Digital Migration — Page Builder Extractor * * Detects which page builder authored a post and extracts the raw builder * payload so the Worker can dispatch to the right parser without re-detecting. * * Detection order (per post): * 1. Plugin hint (`_ad_builder_hint`) already set on the post meta — fast path. * 2. Elementor: presence of `_elementor_data` meta + Elementor plugin active. * 3. Beaver Builder: presence of `_fl_builder_data` meta. * 4. Divi: `_et_pb_use_builder` meta === 'on' (Divi 4 shortcode mode) OR * `et_pb_` block-comment signature in `post_content` (Divi 5 block mode). * 5. WPBakery: `vc_` shortcode signature in `post_content`. * 6. Fallback: `gutenberg` (block editor / classic editor HTML). * * @package AlteredDigitalMigration */ if (!defined('ABSPATH')) { exit; } class AD_Extractor { /** @var AD_Plugin_Core */ private $core; /** @var array<string,bool>|null Cache of active builder plugins. */ private $active_builders_cache; public function __construct(AD_Plugin_Core $core) { $this->core = $core; } // ----------------------------------------------------------------- // Active-builder scan (cheap, cached per request) // ----------------------------------------------------------------- /** * Scan active plugins for known page builders. Cached on the instance. * * @return array<string,bool> Map of builder => active. */ public function detect_active_builders(): array { if (is_array($this->active_builders_cache)) { return $this->active_builders_cache; } $map = [ 'elementor' => 'elementor/elementor.php', 'wpbakery' => 'js_composer/js_composer.php', 'divi' => 'divi-builder/divi-builder.php', 'beaver' => 'bb-plugin/fl-builder.php', ]; // Divi theme also ships the builder (no plugin). Treat a Divi/Extra // theme as a Divi builder install. $theme = strtolower(get_template()); $divi_theme = in_array($theme, ['divi', 'extra'], true); $active = []; if (!function_exists('is_plugin_active')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } foreach ($map as $builder => $plugin_path) { $active[$builder] = is_plugin_active($plugin_path); } $active['divi'] = $active['divi'] || $divi_theme; $this->active_builders_cache = $active; return $active; } // ----------------------------------------------------------------- // Per-post detection (used by AD_Webhook::build_payload) // ----------------------------------------------------------------- /** * Detect the builder that authored a post and return its raw payload. * * @param WP_Post $post * @return array{builder:string,raw:mixed} `builder` is one of * 'gutenberg'|'elementor'|'wpbakery'|'divi'|'beaver'; `raw` is the * builder-specific data (JSON array, shortcode string, or null). */ public function detect_builder_for_post(WP_Post $post): array { $post_id = (int) $post->ID; // 1. Elementor — `_elementor_data` is a JSON array of widget trees. $elementor_data = get_post_meta($post_id, '_elementor_data', true); if (!empty($elementor_data) && is_string($elementor_data)) { $decoded = json_decode($elementor_data, true); if (is_array($decoded)) { return ['builder' => 'elementor', 'raw' => $decoded]; } } // 2. Beaver Builder — `_fl_builder_data` is a serialized array tree. $beaver_data = get_post_meta($post_id, '_fl_builder_data', true); if (!empty($beaver_data)) { $unserialized = maybe_unserialize($beaver_data); if (is_array($unserialized)) { return ['builder' => 'beaver', 'raw' => $unserialized]; } // Some Beaver versions store already-arrays. if (is_array($beaver_data)) { return ['builder' => 'beaver', 'raw' => $beaver_data]; } } // 3. Divi 4 — `_et_pb_use_builder` flag + shortcode body. $divi_flag = get_post_meta($post_id, '_et_pb_use_builder', true); if ($divi_flag === 'on') { return ['builder' => 'divi', 'raw' => $post->post_content]; } // 4. Divi 5 — block-comment `<!-- wp:et_pb/... -->` signature. if (has_blocks($post->post_content) && stripos($post->post_content, 'et_pb') !== false) { return ['builder' => 'divi', 'raw' => $post->post_content]; } // 5. WPBakery — `[vc_row]` / `[vc_column]` shortcodes in body. if (stripos($post->post_content, '[vc_') !== false) { return ['builder' => 'wpbakery', 'raw' => $post->post_content]; } // 6. Default — Gutenberg block editor or classic editor HTML. return ['builder' => 'gutenberg', 'raw' => null]; } /** * Extract raw builder data for a post by ID. Thin wrapper used by the * analyze-data REST route and the plugin admin UI. * * @param int $post_id * @return array{builder:string,raw:mixed} */ public function extract_builder_data(int $post_id): array { $post = get_post($post_id); if (!$post) { return ['builder' => 'gutenberg', 'raw' => null]; } return $this->detect_builder_for_post($post); } // ----------------------------------------------------------------- // Full-site analysis (for the AI service /analyze route) // ----------------------------------------------------------------- /** * Build the aggregated analysis payload the AI service consumes: * post-type counts, builder distribution, sample posts per builder, * ACF field inventory, media + term counts, theme info. * * Called by AD_REST::handle_analyze_data (altered-digital/v1/analyze-data). * * @return array<string,mixed> */ public function build_analysis_payload(): array { $settings = $this->core->settings(); $post_types = $settings['post_types'] ?? ['post', 'page']; $active_builders = $this->detect_active_builders(); $per_type_counts = []; $builder_distribution = [ 'gutenberg' => 0, 'elementor' => 0, 'wpbakery' => 0, 'divi' => 0, 'beaver' => 0, 'unknown' => 0, ]; $samples = [ 'gutenberg' => [], 'elementor' => [], 'wpbakery' => [], 'divi' => [], 'beaver' => [], ]; foreach ($post_types as $pt) { $count_query = new WP_Query([ 'post_type' => $pt, 'post_status' => 'publish', 'fields' => 'ids', 'posts_per_page' => 1, 'no_found_rows' => false, ]); $per_type_counts[$pt] = (int) $count_query->found_posts; // Builder-distribution + samples. Batch 200 at a time to avoid // loading the whole site into memory. $offset = 0; $batch = 200; $sample_target = 10; while (true) { $batch_ids = get_posts([ 'post_type' => $pt, 'post_status' => 'publish', 'posts_per_page' => $batch, 'offset' => $offset, 'fields' => 'ids', 'no_found_rows' => true, 'orderby' => 'ID', 'order' => 'ASC', ]); if (empty($batch_ids)) { break; } foreach ($batch_ids as $pid) { $post = get_post($pid); if (!$post) { continue; } $detected = $this->detect_builder_for_post($post); $builder = $detected['builder']; if (!isset($builder_distribution[$builder])) { $builder = 'unknown'; } $builder_distribution[$builder]++; if (isset($samples[$builder]) && count($samples[$builder]) < $sample_target) { $samples[$builder][] = [ 'id' => (int) $post->ID, 'type' => $post->post_type, 'title' => $post->post_title, 'builder' => $detected['builder'], 'raw' => $this->truncate_raw($detected['raw'], 8192), ]; } } $offset += $batch; } } $redirect_count = $this->count_redirects(); return [ 'site_url' => home_url(), 'site_name' => get_bloginfo('name'), 'theme' => [ 'name' => wp_get_theme()->get('Name'), 'version' => wp_get_theme()->get('Version'), 'template' => get_template(), ], 'post_types' => $post_types, 'per_type_counts' => $per_type_counts, 'total_posts' => array_sum($per_type_counts), 'active_builders' => $active_builders, 'builder_distribution' => $builder_distribution, 'samples' => $samples, 'acf_field_groups' => $this->acf_field_inventory(), 'media_count' => $this->count_media(), 'term_counts' => $this->term_counts($settings['taxonomies'] ?? ['category', 'post_tag']), 'redirect_count' => $redirect_count, 'wp_version' => get_bloginfo('version'), 'php_version' => PHP_VERSION, ]; } // ----------------------------------------------------------------- // Redirect rules (Redirection plugin extraction) // ----------------------------------------------------------------- /** * Check whether the Redirection plugin (John Godley) is active and its * table exists. The Redirection plugin stores rules in * `{prefix}redirection_items` + groups in `{prefix}redirection_groups`. * * @return bool */ public function redirection_available(): bool { if (!is_plugin_active('redirection/redirection.php')) { return false; } global $wpdb; $table = $wpdb->prefix . 'redirection_items'; // phpcs:ignore WordPress.DB $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)); return $exists === $table; } /** * Count active redirect rules from the Redirection plugin. * * @return int */ public function count_redirects(): int { if (!$this->redirection_available()) { return 0; } global $wpdb; $items_table = $wpdb->prefix . 'redirection_items'; $groups_table = $wpdb->prefix . 'redirection_groups'; // phpcs:ignore WordPress.DB $count = $wpdb->get_var( "SELECT COUNT(i.id) FROM {$items_table} i INNER JOIN {$groups_table} g ON i.group_id = g.id WHERE i.status = 'active' AND g.status = 'enabled'" ); return (int) $count; } /** * Extract all active redirect rules from the Redirection plugin. * * Returns an array of normalised rules: * [{from, to, statusCode, permanent, legacyWpId}] * * `from` is the matched URL path (relative or absolute), `to` is the * destination URL, `statusCode` is the HTTP code (default 301), * `permanent` is true for 301/308, and `legacyWpId` is the WP post id * the redirect was associated with (from the `action_data` column when * the action type is 'url' with an explicit id). * * @return array<int,array<string,mixed>> */ public function extract_redirects(): array { if (!$this->redirection_available()) { return []; } global $wpdb; $items_table = $wpdb->prefix . 'redirection_items'; $groups_table = $wpdb->prefix . 'redirection_groups'; // phpcs:ignore WordPress.DB $rows = $wpdb->get_results( "SELECT i.id, i.url, i.action_type, i.action_code, i.action_data, i.match_type, i.title FROM {$items_table} i INNER JOIN {$groups_table} g ON i.group_id = g.id WHERE i.status = 'active' AND g.status = 'enabled' ORDER BY i.id ASC", ARRAY_A ); if (empty($rows) || !is_array($rows)) { return []; } $out = []; foreach ($rows as $row) { $from = isset($row['url']) ? trim($row['url']) : ''; $action_type = isset($row['action_type']) ? $row['action_type'] : ''; $action_code = isset($row['action_code']) ? (int) $row['action_code'] : 301; // Only handle 'url' action type (the most common — redirect to a URL). // Other types ('pass', 'error', 'url_from_match') are skipped. if ($action_type !== 'url' || $from === '') { continue; } // Parse action_data — it's JSON or a plain URL depending on version. $to = ''; $action_data = isset($row['action_data']) ? $row['action_data'] : ''; if (!empty($action_data)) { $decoded = json_decode($action_data, true); if (is_array($decoded) && isset($decoded['url'])) { $to = trim((string) $decoded['url']); } elseif (is_string($action_data)) { $to = trim($action_data); } } if ($to === '') { continue; } $statusCode = $action_code > 0 ? $action_code : 301; $out[] = [ 'from' => $from, 'to' => $to, 'statusCode' => $statusCode, 'permanent' => ($statusCode === 301 || $statusCode === 308), 'legacyWpId' => isset($row['id']) ? (int) $row['id'] : null, ]; } return $out; } /** * Count inherited (published) attachments. wp_count_attachments() * returns an stdClass with status properties (inherit, trash, etc.), * so we use property access, not array access. * * @return int */ private function count_media(): int { $counts = wp_count_attachments(); if (!is_object($counts)) { return 0; } return (int) ($counts->inherit ?? 0); } /** * Truncate raw builder data to a byte budget so samples stay small. * * @param mixed $raw * @param int $max_bytes * @return mixed */ private function truncate_raw($raw, int $max_bytes) { if (is_string($raw)) { return strlen($raw) > $max_bytes ? substr($raw, 0, $max_bytes) . '…[truncated]' : $raw; } $json = wp_json_encode($raw); if (is_string($json) && strlen($json) > $max_bytes) { return substr($json, 0, $max_bytes) . '…[truncated]'; } return $raw; } /** * Inventory of ACF field groups + their fields (if ACF is active). * * @return array<int,array<string,mixed>> */ private function acf_field_inventory(): array { if (!function_exists('acf_get_field_groups')) { return []; } $out = []; foreach (acf_get_field_groups() as $group) { $fields = function_exists('acf_get_fields') ? acf_get_fields($group) : []; $out[] = [ 'key' => $group['key'] ?? '', 'title' => $group['title'] ?? '', 'fields' => array_map(function ($f) { return [ 'key' => $f['key'] ?? '', 'label' => $f['label'] ?? '', 'name' => $f['name'] ?? '', 'type' => $f['type'] ?? '', ]; }, $fields), ]; } return $out; } /** * Counts per taxonomy. * * @param array<int,string> $taxonomies * @return array<string,int> */ private function term_counts(array $taxonomies): array { $counts = []; foreach ($taxonomies as $tax) { $terms = get_terms([ 'taxonomy' => $tax, 'hide_empty' => false, 'fields' => 'count', ]); $counts[$tax] = is_wp_error($terms) ? 0 : (int) $terms; } return $counts; } }
Save
cmd:
run