/
home
/
altere25
/
.trash
/
wp-to-sanity-companion
/
/home/altere25/.trash/wp-to-sanity-companion
mkdir
upload
Name
Size
Mode
Actions
assets\css\admin.css
7611
0644
edit
dl
rm
assets\js\admin.js
8534
0644
edit
dl
rm
assets\js\claude-connect.js
6502
0644
edit
dl
rm
includes\class-ad-admin.php
26374
0644
edit
dl
rm
includes\class-ad-ai-client.php
8153
0644
edit
dl
rm
includes\class-ad-auth.php
9189
0644
edit
dl
rm
includes\class-ad-cli.php
8669
0644
edit
dl
rm
includes\class-ad-extractor.php
12210
0644
edit
dl
rm
includes\class-ad-mcp.php
6429
0644
edit
dl
rm
includes\class-ad-plugin-core.php
7947
0644
edit
dl
rm
includes\class-ad-progress.php
8022
0644
edit
dl
rm
includes\class-ad-rest.php
31008
0644
edit
dl
rm
includes\class-ad-uninstall.php
1924
0644
edit
dl
rm
includes\class-ad-webhook.php
14854
0644
edit
dl
rm
package.json
220
0644
edit
dl
rm
templates\admin\claude-connect.php
5152
0644
edit
dl
rm
templates\admin\dashboard.php
10457
0644
edit
dl
rm
templates\admin\export.php
16570
0644
edit
dl
rm
templates\admin\log.php
10849
0644
edit
dl
rm
templates\admin\onboarding-v2.php
3827
0644
edit
dl
rm
templates\admin\onboarding.php
10714
0644
edit
dl
rm
templates\admin\progress.php
2443
0644
edit
dl
rm
templates\admin\schema-preview.php
5213
0644
edit
dl
rm
uninstall.php
365
0644
edit
dl
rm
wp-to-sanity-companion.php
4171
0644
edit
dl
rm
Edit:
/home/altere25/.trash/wp-to-sanity-companion/includes\class-ad-extractor.php
(12210B)
<?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; } } 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' => (int) wp_count_attachments()['inherit'] ?? 0, 'term_counts' => $this->term_counts($settings['taxonomies'] ?? ['category', 'post_tag']), 'wp_version' => get_bloginfo('version'), 'php_version' => PHP_VERSION, ]; } /** * 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