| @@ -297,17 +297,8 @@ | ||
| 297 | 297 | // `metasync_enable_seo_suite` filter. |
| 298 | 298 | require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-seo-suite.php'; |
| 299 | 299 | new Metasync_Seo_Suite(); |
| 300 | 300 | |
| 301 | - // Classic-editor save path for the three fields that exist only as | |
| 302 | - // Gutenberg sidebar (REST) meta otherwise: breadcrumb title, language | |
| 303 | - // alternates, primary category. The Suite renders the first two and | |
| 304 | - // emits the nonce; this handler persists them from the classic form. | |
| 305 | - // It guards itself against block-editor and managed-page context, so | |
| 306 | - // it is safe to register unconditionally. | |
| 307 | - require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-metasync-classic-seo-fields.php'; | |
| 308 | - new Metasync_Classic_Seo_Fields(); | |
| 309 | - | |
| 310 | 301 | // SEO Health CSV export: must run on admin_init (before output). |
| 311 | 302 | // Cheap $_GET check avoids loading the class on every admin page. |
| 312 | 303 | if ( |
| 313 | 304 | isset($_GET['page'], $_GET['export'], $_GET['_wpnonce']) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- routing check, the handler verifies nonce and capability |
| @@ -352,8 +343,13 @@ | ||
| 352 | 343 | if (class_exists('Metasync_Breadcrumbs_Schema')) { |
| 353 | 344 | new Metasync_Breadcrumbs_Schema($this->get_plugin_name(), $this->get_version()); |
| 354 | 345 | } |
| 355 | 346 | |
| 347 | + // Initialize Developer Panel (for endpoint switching) | |
| 348 | + if (class_exists('Metasync_Dev_Panel')) { | |
| 349 | + $dev_panel = new Metasync_Dev_Panel($this->get_plugin_name(), $this->get_version()); | |
| 350 | + } | |
| 351 | + | |
| 356 | 352 | // Initialize Site Health integration |
| 357 | 353 | if (class_exists('Metasync_Site_Health')) { |
| 358 | 354 | $site_health = new Metasync_Site_Health(); |
| 359 | 355 | $site_health->register_tests(); |
| @@ -572,20 +568,8 @@ | ||
| 572 | 568 | // One-time upgrade: regenerate sitemap to remove any Beaver Builder template entries |
| 573 | 569 | if ( ! get_option( 'metasync_sitemap_bb_exclusion_applied' ) ) { |
| 574 | 570 | $this->loader->add_action('init', $this, 'maybe_regenerate_sitemap_after_upgrade'); |
| 575 | 571 | } |
| 576 | - | |
| 577 | - // One-time upgrade: delete the legacy Otto crawl-data option. Nothing | |
| 578 | - // reads or writes it anymore (the webhook and SEO sync job stopped | |
| 579 | - // persisting crawl data), and on sites where OTTO crawled many URLs | |
| 580 | - // the row could grow to megabytes while being autoloaded on every | |
| 581 | - // request before WordPress 6.6. The guard below runs on every request, | |
| 582 | - // so the flag must be autoloaded (like the sitemap flag above): a | |
| 583 | - // boolean true costs ~2 bytes inside the alloptions query WordPress | |
| 584 | - // already always runs, keeping the steady-state cost a cache hit. | |
| 585 | - if ( ! get_option( 'metasync_otto_crawldata_removed' ) ) { | |
| 586 | - $this->loader->add_action('init', $this, 'maybe_delete_legacy_otto_crawl_data'); | |
| 587 | - } | |
| 588 | 572 | } |
| 589 | 573 | |
| 590 | 574 | /** |
| 591 | 575 | * One-time upgrade routine: regenerate the XML sitemap so that Beaver Builder |
| @@ -615,40 +599,8 @@ | ||
| 615 | 599 | // so that enabling sitemaps later will still clean up BB templates. |
| 616 | 600 | } |
| 617 | 601 | |
| 618 | 602 | /** |
| 619 | - * One-time upgrade routine: delete the legacy Otto crawl-data option. | |
| 620 | - * | |
| 621 | - * The option stored every URL Otto ever sent and grew without bound; | |
| 622 | - * nothing writes it anymore and nothing in production reads it (per-URL | |
| 623 | - * state lives in Metasync_Otto_Job_Status, delivery receipts in | |
| 624 | - * Metasync_Otto_Webhook_Audit, both bounded). This drops the leftover | |
| 625 | - * row — which on long-running sites can be megabytes of autoloaded data. | |
| 626 | - * | |
| 627 | - * Runs once on 'init' and sets a flag so it never runs again. The flag | |
| 628 | - * is autoloaded deliberately: the registration guard reads it on every | |
| 629 | - * request, and an autoloaded boolean is a free cache hit inside the | |
| 630 | - * alloptions query WordPress always performs — a non-autoloaded flag | |
| 631 | - * would cost a dedicated SELECT per request forever, the exact waste | |
| 632 | - * this routine exists to remove. delete_option() is called directly | |
| 633 | - * without reading the option first: loading a multi-megabyte value into | |
| 634 | - * memory just to delete it would be the one way this cleanup could hurt | |
| 635 | - * a site, and the SQL DELETE never needs the value. | |
| 636 | - */ | |
| 637 | - public function maybe_delete_legacy_otto_crawl_data() { | |
| 638 | - $done_key = 'metasync_otto_crawldata_removed'; | |
| 639 | - if ( get_option( $done_key ) ) { | |
| 640 | - return; | |
| 641 | - } | |
| 642 | - | |
| 643 | - // No "feature in use" conditional here, unlike the sitemap routine: | |
| 644 | - // the row is dead weight whether or not Otto is configured, and | |
| 645 | - // delete_option() on an absent row is a cheap no-op. | |
| 646 | - delete_option( 'metasync_otto_crawldata' ); | |
| 647 | - update_option( $done_key, true ); | |
| 648 | - } | |
| 649 | - | |
| 650 | - /** | |
| 651 | 603 | * Initialize endpoint URL filtering for staging mode |
| 652 | 604 | * Intercepts HTTP requests and replaces production URLs with staging URLs |
| 653 | 605 | */ |
| 654 | 606 | private function init_endpoint_filtering() { |
| @@ -951,52 +903,8 @@ | ||
| 951 | 903 | return is_string($value) && strncmp($value, self::API_KEY_ENC_PREFIX, strlen(self::API_KEY_ENC_PREFIX)) === 0; |
| 952 | 904 | } |
| 953 | 905 | |
| 954 | 906 | /** |
| 955 | - * Whether a value is a strictly valid UUID (versions 1-8). | |
| 956 | - * | |
| 957 | - * The single shared validator for every write path that persists | |
| 958 | - * `general.otto_pixel_uuid` (SSO push callback, pull-based connect, | |
| 959 | - * heartbeat writes, settings sanitizer). The OTTO Pixel UUID must never | |
| 960 | - * hold arbitrary text — usernames such as `raza.imran` landing in it | |
| 961 | - * break OTTO SSR authentication — so every write path funnels through | |
| 962 | - * this check instead of a scattered one-off regex. | |
| 963 | - * | |
| 964 | - * Deliberately stricter than wp_is_uuid(), which only checks the | |
| 965 | - * 8-4-4-4-12 hex shape and would accept placeholder values such as | |
| 966 | - * `11111111-2222-3333-4444-555555555555`. The version nibble (first hex | |
| 967 | - * of the third group) must be 1-8 — RFC 4122 defined 1-5 and RFC 9562 | |
| 968 | - * added 6/7/8, which modern generators increasingly emit — and the | |
| 969 | - * variant nibble (first hex of the fourth group) must be 8/9/a/b. That | |
| 970 | - * keeps behaviour identical with and without the WP helper loaded (unit | |
| 971 | - * tests run without it). | |
| 972 | - * | |
| 973 | - * An empty string is NOT valid: clearing the field is a separate concern | |
| 974 | - * owned by the disconnect flow (unset + write_options_without_stored_merge), | |
| 975 | - * so '' must not masquerade as a usable UUID here. | |
| 976 | - * | |
| 977 | - * @param mixed $value | |
| 978 | - * @return bool True only for a canonical 8-4-4-4-12 hex UUID of version 1-8 | |
| 979 | - * with an RFC-4122 variant (8/9/a/b) in the fourth group. | |
| 980 | - */ | |
| 981 | - public static function is_valid_uuid($value) | |
| 982 | - { | |
| 983 | - if (!is_string($value)) { | |
| 984 | - return false; | |
| 985 | - } | |
| 986 | - | |
| 987 | - $value = trim($value); | |
| 988 | - if ($value === '') { | |
| 989 | - return false; | |
| 990 | - } | |
| 991 | - | |
| 992 | - return (bool) preg_match( | |
| 993 | - '/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', | |
| 994 | - $value | |
| 995 | - ); | |
| 996 | - } | |
| 997 | - | |
| 998 | - /** | |
| 999 | 907 | * Encrypt a plaintext Search Atlas API key for storage at rest. |
| 1000 | 908 | * |
| 1001 | 909 | * Uses AES-256-GCM (authenticated) with a random 12-byte IV. The IV, the |
| 1002 | 910 | * 16-byte GCM tag and the ciphertext are concatenated and base64-encoded |
| @@ -1586,9 +1494,8 @@ | ||
| 1586 | 1494 | * @param string $text Plain-English help text (escaped internally). |
| 1587 | 1495 | */ |
| 1588 | 1496 | public static function render_tooltip_icon($tooltip_id, $text) |
| 1589 | 1497 | { |
| 1590 | - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_tooltip_icon_html() escapes every interpolated value internally (esc_attr / esc_html) | |
| 1591 | 1498 | echo self::get_tooltip_icon_html($tooltip_id, $text); |
| 1592 | 1499 | } |
| 1593 | 1500 | |
| 1594 | 1501 | /** |