| @@ -63,8 +63,25 @@ | ||
| 63 | 63 | return false; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | /** |
| 67 | + * Whether a URL's crawl pipeline has completed, per the bounded | |
| 68 | + * Metasync_Otto_Job_Status store. Replaces the legacy unbounded | |
| 69 | + * metasync_otto_crawldata option lookup: state history is pruned | |
| 70 | + * 48 hours after completion, so a URL finished longer ago reads as | |
| 71 | + * "not crawled" here without implying Otto never touched it. | |
| 72 | + */ | |
| 73 | + private static function is_url_crawled($url) { | |
| 74 | + if (!class_exists('Metasync_Otto_Job_Status')) { | |
| 75 | + return false; | |
| 76 | + } | |
| 77 | + $entry = Metasync_Otto_Job_Status::get($url); | |
| 78 | + return is_array($entry) | |
| 79 | + && isset($entry['state']) | |
| 80 | + && $entry['state'] === Metasync_Otto_Job_Status::STATE_COMPLETED; | |
| 81 | + } | |
| 82 | + | |
| 83 | + /** | |
| 67 | 84 | * Add debug menu (internal only - hidden on customer production sites) |
| 68 | 85 | */ |
| 69 | 86 | public function add_debug_menu() { |
| 70 | 87 | if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) { |
| @@ -102,9 +119,8 @@ | ||
| 102 | 119 | <?php $this->render_url_testing_section(); ?> |
| 103 | 120 | <?php $this->render_configuration_status(); ?> |
| 104 | 121 | <?php $this->render_notification_endpoint_status(); ?> |
| 105 | 122 | <?php $this->render_api_connectivity_status(); ?> |
| 106 | - <?php $this->render_crawl_data_status(); ?> | |
| 107 | 123 | <?php $this->render_processing_status(); ?> |
| 108 | 124 | <?php $this->render_debug_tools(); ?> |
| 109 | 125 | </div> |
| 110 | 126 | </div> |
| @@ -242,14 +258,14 @@ | ||
| 242 | 258 | </div> |
| 243 | 259 | |
| 244 | 260 | <div class="debug-item info"> |
| 245 | 261 | <strong>Current User:</strong> |
| 246 | - <span class="debug-value"><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo $current_user->ID; ?>)</span> | |
| 262 | + <span class="debug-value"><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo (int) $current_user->ID; ?>)</span> | |
| 247 | 263 | </div> |
| 248 | 264 | |
| 249 | 265 | <div class="debug-item info"> |
| 250 | 266 | <strong>User Roles:</strong> |
| 251 | - <span class="debug-value"><?php echo implode(', ', $current_user->roles); ?></span> | |
| 267 | + <span class="debug-value"><?php echo esc_html(implode(', ', $current_user->roles)); ?></span> | |
| 252 | 268 | </div> |
| 253 | 269 | |
| 254 | 270 | <div class="debug-item success"> |
| 255 | 271 | <strong>Debug Access Active</strong> |
| @@ -345,9 +361,9 @@ | ||
| 345 | 361 | </div> |
| 346 | 362 | |
| 347 | 363 | <div class="debug-item info"> |
| 348 | 364 | <strong>Plugin Version:</strong> |
| 349 | - <span class="debug-value"><?php echo defined('METASYNC_VERSION') ? METASYNC_VERSION : 'Unknown'; ?></span> | |
| 365 | + <span class="debug-value"><?php echo esc_html(defined('METASYNC_VERSION') ? METASYNC_VERSION : 'Unknown'); ?></span> | |
| 350 | 366 | </div> |
| 351 | 367 | </div> |
| 352 | 368 | <?php |
| 353 | 369 | } |
| @@ -449,64 +465,13 @@ | ||
| 449 | 465 | <?php |
| 450 | 466 | } |
| 451 | 467 | |
| 452 | 468 | /** |
| 453 | - * Render crawl data status | |
| 454 | - */ | |
| 455 | - private function render_crawl_data_status() { | |
| 456 | - $crawl_data = get_option('metasync_otto_crawldata'); | |
| 457 | - | |
| 458 | - ?> | |
| 459 | - <div class="debug-section"> | |
| 460 | - <h3><span class="status-indicator <?php echo !empty($crawl_data) ? 'status-success' : 'status-warning'; ?>"></span>Crawl Data Status</h3> | |
| 461 | - | |
| 462 | - <div class="debug-item <?php echo !empty($crawl_data) ? 'success' : 'warning'; ?>"> | |
| 463 | - <strong>Crawl Data Available:</strong> | |
| 464 | - <span class="debug-value"><?php echo !empty($crawl_data) ? 'Yes' : 'No'; ?></span> | |
| 465 | - </div> | |
| 466 | - | |
| 467 | - <?php if (!empty($crawl_data)): ?> | |
| 468 | - <div class="debug-item info"> | |
| 469 | - <strong>Domain:</strong> | |
| 470 | - <span class="debug-value"><?php echo esc_html($crawl_data['domain'] ?? 'Not Set'); ?></span> | |
| 471 | - </div> | |
| 472 | - | |
| 473 | - <div class="debug-item info"> | |
| 474 | - <strong>Total URLs Crawled:</strong> | |
| 475 | - <span class="debug-value"><?php echo count($crawl_data['urls'] ?? []); ?></span> | |
| 476 | - </div> | |
| 477 | - | |
| 478 | - <div class="debug-item info"> | |
| 479 | - <strong>Last Updated:</strong> | |
| 480 | - <span class="debug-value"><?php echo $this->get_option_last_updated('metasync_otto_crawldata'); ?></span> | |
| 481 | - </div> | |
| 482 | - | |
| 483 | - <div class="debug-item info"> | |
| 484 | - <strong>Sample URLs:</strong> | |
| 485 | - <div class="debug-value"> | |
| 486 | - <?php | |
| 487 | - $sample_urls = array_slice($crawl_data['urls'] ?? [], 0, 5); | |
| 488 | - foreach ($sample_urls as $url) { | |
| 489 | - echo esc_html($url) . '<br>'; | |
| 490 | - } | |
| 491 | - if (count($crawl_data['urls'] ?? []) > 5) { | |
| 492 | - echo '... and ' . (count($crawl_data['urls']) - 5) . ' more'; | |
| 493 | - } | |
| 494 | - ?> | |
| 495 | - </div> | |
| 496 | - </div> | |
| 497 | - <?php endif; ?> | |
| 498 | - </div> | |
| 499 | - <?php | |
| 500 | - } | |
| 501 | - | |
| 502 | - /** | |
| 503 | 469 | * Render processing status |
| 504 | 470 | */ |
| 505 | 471 | private function render_processing_status() { |
| 506 | 472 | $current_url = $this->get_current_url(); |
| 507 | - $otto_pixel = new Metasync_otto_pixel(Metasync::get_option('general')['otto_pixel_uuid'] ?? ''); | |
| 508 | - $is_crawled = $otto_pixel->is_url_crawled($current_url); | |
| 473 | + $is_crawled = self::is_url_crawled($current_url); | |
| 509 | 474 | $render_diagnostics = $this->get_render_strategy_diagnostics(); |
| 510 | 475 | |
| 511 | 476 | ?> |
| 512 | 477 | <div class="debug-section"> |
| @@ -528,19 +493,19 @@ | ||
| 528 | 493 | </div> |
| 529 | 494 | |
| 530 | 495 | <div class="debug-item info"> |
| 531 | 496 | <strong>Page Type:</strong> |
| 532 | - <span class="debug-value"><?php echo $this->get_page_type(); ?></span> | |
| 497 | + <span class="debug-value"><?php echo esc_html($this->get_page_type()); ?></span> | |
| 533 | 498 | </div> |
| 534 | 499 | |
| 535 | 500 | <div class="debug-item info"> |
| 536 | 501 | <strong>Cache Status:</strong> |
| 537 | - <span class="debug-value"><?php echo $this->get_cache_status(); ?></span> | |
| 502 | + <span class="debug-value"><?php echo esc_html($this->get_cache_status()); ?></span> | |
| 538 | 503 | </div> |
| 539 | 504 | |
| 540 | 505 | <div class="debug-item info"> |
| 541 | 506 | <strong>Processing Method:</strong> |
| 542 | - <span class="debug-value"><?php echo $this->get_processing_method(); ?></span> | |
| 507 | + <span class="debug-value"><?php echo esc_html($this->get_processing_method()); ?></span> | |
| 543 | 508 | </div> |
| 544 | 509 | |
| 545 | 510 | <?php if (!empty($render_diagnostics) && isset($render_diagnostics['available']) === false): ?> |
| 546 | 511 | <div class="debug-item info"> |
| @@ -652,29 +617,8 @@ | ||
| 652 | 617 | return !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200; |
| 653 | 618 | } |
| 654 | 619 | |
| 655 | 620 | /** |
| 656 | - * Get option last updated time | |
| 657 | - */ | |
| 658 | - private function get_option_last_updated($option_name) { | |
| 659 | - global $wpdb; | |
| 660 | - | |
| 661 | - $result = $wpdb->get_var($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API | |
| 662 | - "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s", | |
| 663 | - $option_name | |
| 664 | - )); | |
| 665 | - | |
| 666 | - if ($result) { | |
| 667 | - $data = maybe_unserialize($result); | |
| 668 | - if (isset($data['last_updated'])) { | |
| 669 | - return gmdate('Y-m-d H:i:s', $data['last_updated']); | |
| 670 | - } | |
| 671 | - } | |
| 672 | - | |
| 673 | - return 'Unknown'; | |
| 674 | - } | |
| 675 | - | |
| 676 | - /** | |
| 677 | 621 | * Get current URL |
| 678 | 622 | */ |
| 679 | 623 | private function get_current_url() { |
| 680 | 624 | $scheme = is_ssl() ? 'https' : 'http'; |
| @@ -872,20 +816,23 @@ | ||
| 872 | 816 | * AJAX handler for clearing OTTO cache |
| 873 | 817 | */ |
| 874 | 818 | public function ajax_clear_otto_cache() { |
| 875 | 819 | check_ajax_referer('metasync_otto_debug', 'nonce'); |
| 876 | - | |
| 820 | + | |
| 877 | 821 | if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) { |
| 878 | 822 | wp_die('Unauthorized'); |
| 879 | 823 | } |
| 880 | - | |
| 881 | - // Clear crawl data | |
| 824 | + | |
| 825 | + // Clear the retired crawl-data option if an internal debug tool is | |
| 826 | + // ever re-enabled. This explicit action is harmless and preserves | |
| 827 | + // the old clear-cache contract; production never reaches this | |
| 828 | + // handler because debug tools are hard-disabled. | |
| 882 | 829 | delete_option('metasync_otto_crawldata'); |
| 883 | - | |
| 830 | + | |
| 884 | 831 | // Clear any cached API responses |
| 885 | 832 | $general_options = Metasync::get_option('general'); |
| 886 | 833 | $otto_uuid = $general_options['otto_pixel_uuid'] ?? ''; |
| 887 | - | |
| 834 | + | |
| 888 | 835 | if (!empty($otto_uuid)) { |
| 889 | 836 | delete_transient(Metasync_Heartbeat_Manager::public_hash_cache_key($otto_uuid)); |
| 890 | 837 | } |
| 891 | 838 | |
| @@ -1085,27 +1032,27 @@ | ||
| 1085 | 1032 | /** |
| 1086 | 1033 | * Test crawl status for URL |
| 1087 | 1034 | */ |
| 1088 | 1035 | private function test_crawl_status($url) { |
| 1089 | - if (class_exists('Metasync_otto_pixel')) { | |
| 1090 | - $otto_pixel = new Metasync_otto_pixel(false); | |
| 1091 | - $is_crawled = $otto_pixel->is_url_crawled($url); | |
| 1092 | - | |
| 1093 | - // Get crawl data from options | |
| 1094 | - $crawl_data = get_option('metasync_otto_crawldata'); | |
| 1095 | - | |
| 1036 | + if (class_exists('Metasync_Otto_Job_Status')) { | |
| 1037 | + $entry = Metasync_Otto_Job_Status::get($url); | |
| 1038 | + $is_crawled = is_array($entry) | |
| 1039 | + && isset($entry['state']) | |
| 1040 | + && $entry['state'] === Metasync_Otto_Job_Status::STATE_COMPLETED; | |
| 1041 | + | |
| 1096 | 1042 | return array( |
| 1097 | 1043 | 'status' => 'success', |
| 1098 | 1044 | 'is_crawled' => $is_crawled, |
| 1099 | - 'crawl_data' => $crawl_data, | |
| 1100 | - 'total_crawled_urls' => count($crawl_data['urls'] ?? array()), | |
| 1101 | - 'domain' => $crawl_data['domain'] ?? 'Not set' | |
| 1045 | + 'job_state' => is_array($entry) && isset($entry['state']) ? $entry['state'] : 'unknown', | |
| 1046 | + 'job_stage' => is_array($entry) && isset($entry['stage']) ? $entry['stage'] : '', | |
| 1047 | + 'job_attempts' => is_array($entry) && isset($entry['attempts']) ? (int) $entry['attempts'] : 0, | |
| 1048 | + 'job_updated' => is_array($entry) && isset($entry['updated']) ? $entry['updated'] : null | |
| 1102 | 1049 | ); |
| 1103 | 1050 | } |
| 1104 | - | |
| 1051 | + | |
| 1105 | 1052 | return array( |
| 1106 | 1053 | 'status' => 'error', |
| 1107 | - 'message' => Metasync::get_whitelabel_otto_name() . ' pixel class not available' | |
| 1054 | + 'message' => Metasync::get_whitelabel_otto_name() . ' job status class not available' | |
| 1108 | 1055 | ); |
| 1109 | 1056 | } |
| 1110 | 1057 | |
| 1111 | 1058 | /** |