PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / trunk
Search Atlas SEO – OTTO AI SEO Automation for WordPress vtrunk
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | admin/class-metasync-otto-debug.php +80 -410 2.6.22 → trunk View file →
@@ -36,11 +36,8 @@
36 36
37 37 // Add admin menu
38 38 add_action('admin_menu', array($this, 'add_debug_menu'));
39 39
40 - // Add magic word handler
41 - add_action('admin_init', array($this, 'handle_magic_word_access'));
42 -
43 40 // Add AJAX handlers
44 41 add_action('wp_ajax_metasync_otto_debug_test_api', array($this, 'ajax_test_otto_api'));
45 42 add_action('wp_ajax_metasync_otto_debug_test_notification', array($this, 'ajax_test_notification_endpoint'));
46 43 add_action('wp_ajax_metasync_otto_debug_clear_cache', array($this, 'ajax_clear_otto_cache'));
@@ -52,21 +49,46 @@
52 49 }
53 50
54 51
55 52 /**
56 - * Add debug menu (developer only - hidden by default)
53 + * Whether debug tooling is allowed to run at all right now.
54 + *
55 + * `manage_options` alone isn't a real restriction — every site admin has
56 + * it, so this page and its AJAX handlers were reachable by anyone on any
57 + * site. Hard-disabled: the menu item, the page, and every AJAX handler
58 + * are unreachable for all users on all sites, no toggle or capability
59 + * bypasses this. The class and its methods are left in place; only
60 + * access is blocked.
57 61 */
62 + private static function is_debug_tools_enabled() {
63 + return false;
64 + }
65 +
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 + /**
84 + * Add debug menu (internal only - hidden on customer production sites)
85 + */
58 86 public function add_debug_menu() {
59 - // Check if user has developer capabilities
60 - if (!Metasync::current_user_has_plugin_access()) {
87 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
61 88 return;
62 89 }
63 90
64 - // Check if debug access is enabled via magic word
65 - if (!$this->is_debug_access_enabled()) {
66 - return;
67 - }
68 -
69 91 $menu_slug = Metasync_Admin::$page_slug;
70 92
71 93 add_submenu_page(
72 94 $menu_slug,
@@ -78,169 +100,13 @@
78 100 );
79 101 }
80 102
81 103 /**
82 - * Add magic word handler for direct access
83 - */
84 - public function handle_magic_word_access() {
85 - // Check if magic word is provided
86 - if (isset($_GET['metasync_debug']) && $_GET['metasync_debug'] === 'abracadabra@2020') {
87 - // Enable debug access via user meta (persistent, no sessions needed)
88 - $current_user = wp_get_current_user();
89 - if ($current_user && $current_user->ID) {
90 - update_user_meta($current_user->ID, 'metasync_debug_enabled', 'true');
91 - }
92 -
93 - // Redirect to admin with debug access enabled
94 - $redirect_url = admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-otto-debug');
95 - wp_redirect($redirect_url);
96 - exit;
97 - }
98 - }
99 -
100 - /**
101 - * Check if debug access is enabled via magic word system
102 - * Hidden from regular users, only accessible with secret key
103 - */
104 - private function is_debug_access_enabled() {
105 - // Magic word for developer access
106 - $magic_word = 'abracadabra@2020';
107 -
108 - // Check if magic word is provided in URL parameter
109 - if (isset($_GET['metasync_debug']) && $_GET['metasync_debug'] === $magic_word) {
110 - // Enable debug access via user meta (persistent, no sessions needed)
111 - $current_user = wp_get_current_user();
112 - if ($current_user && $current_user->ID) {
113 - update_user_meta($current_user->ID, 'metasync_debug_enabled', 'true');
114 - }
115 - return true;
116 - }
117 -
118 - // Check if debug access is enabled via user meta (for persistent access)
119 - $current_user = wp_get_current_user();
120 - if ($current_user && $current_user->ID) {
121 - $debug_enabled = get_user_meta($current_user->ID, 'metasync_debug_enabled', true);
122 - if ($debug_enabled === 'true') {
123 - return true;
124 - }
125 - }
126 -
127 - return false;
128 - }
129 -
130 - /**
131 - * Check if current user is a developer
132 - * Multiple methods to identify developers without requiring WP_DEBUG
133 - */
134 - private function is_developer_user($user) {
135 - // Method 1: Check for specific user meta
136 - $is_developer = get_user_meta($user->ID, 'metasync_developer', true);
137 - if ($is_developer === 'true') {
138 - return true;
139 - }
140 -
141 - // Method 2: Check for specific user roles
142 - $developer_roles = array('administrator', 'developer', 'super_admin');
143 - $user_roles = $user->roles;
144 -
145 - foreach ($developer_roles as $role) {
146 - if (in_array($role, $user_roles)) {
147 - return true;
148 - }
149 - }
150 -
151 - // Method 3: Check for specific capabilities
152 - if ($user->has_cap('manage_options') && $user->has_cap('edit_plugins')) {
153 - return true;
154 - }
155 -
156 - // Method 4: Check for specific email domains (optional)
157 - $email_domain = substr(strrchr($user->user_email, "@"), 1);
158 - $developer_domains = array('searchatlas.com', 'yourcompany.com'); // Add your company domains
159 -
160 - if (in_array($email_domain, $developer_domains)) {
161 - return true;
162 - }
163 -
164 - // Method 5: Check for specific username patterns
165 - $username_patterns = array('/^dev_/', '/^admin_/', '/^support_/');
166 - foreach ($username_patterns as $pattern) {
167 - if (preg_match($pattern, $user->user_login)) {
168 - return true;
169 - }
170 - }
171 -
172 - return false;
173 - }
174 -
175 - /**
176 - * Helper function to enable developer access for a specific user
177 - * Call this function to grant debug access to a user
178 - *
179 - * Usage: Metasync_Otto_Debug::enable_developer_access($user_id);
180 - */
181 - public static function enable_developer_access($user_id) {
182 - update_user_meta($user_id, 'metasync_debug_enabled', 'true');
183 - }
184 -
185 - /**
186 - * Helper function to disable developer access for a specific user
187 - *
188 - * Usage: Metasync_Otto_Debug::disable_developer_access($user_id);
189 - */
190 - public static function disable_developer_access($user_id) {
191 - delete_user_meta($user_id, 'metasync_debug_enabled');
192 - }
193 -
194 - /**
195 - * Get the magic word for debug access
196 - *
197 - * Usage: Metasync_Otto_Debug::get_magic_word();
198 - */
199 - public static function get_magic_word() {
200 - return 'abracadabra@2020';
201 - }
202 -
203 - /**
204 - * Generate debug access URL
205 - *
206 - * Usage: Metasync_Otto_Debug::get_debug_access_url();
207 - */
208 - public static function get_debug_access_url() {
209 - $admin_url = admin_url('admin.php');
210 - $magic_word = self::get_magic_word();
211 - return add_query_arg('metasync_debug', $magic_word, $admin_url);
212 - }
213 -
214 - /**
215 - * Quick enable debug access for current user
216 - *
217 - * Usage: Metasync_Otto_Debug::quick_enable_debug();
218 - */
219 - public static function quick_enable_debug() {
220 - $current_user = wp_get_current_user();
221 - if ($current_user && $current_user->ID) {
222 - update_user_meta($current_user->ID, 'metasync_debug_enabled', 'true');
223 - return true;
224 - }
225 - return false;
226 - }
227 -
228 - /**
229 104 * Create the debug page
230 105 */
231 106 public function create_debug_page() {
232 - // Check if this is a magic word access request
233 - if (isset($_GET['metasync_debug']) && $_GET['metasync_debug'] === 'abracadabra@2020') {
234 - // Enable debug access via user meta (persistent, no sessions needed)
235 - $current_user = wp_get_current_user();
236 - if ($current_user && $current_user->ID) {
237 - update_user_meta($current_user->ID, 'metasync_debug_enabled', 'true');
238 - }
239 -
240 - // Show access granted message
241 - $this->show_access_granted_page();
242 - return;
107 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
108 + wp_die('Unauthorized');
243 109 }
244 110
245 111 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
246 112 ?>
@@ -253,9 +119,8 @@
253 119 <?php $this->render_url_testing_section(); ?>
254 120 <?php $this->render_configuration_status(); ?>
255 121 <?php $this->render_notification_endpoint_status(); ?>
256 122 <?php $this->render_api_connectivity_status(); ?>
257 - <?php $this->render_crawl_data_status(); ?>
258 123 <?php $this->render_processing_status(); ?>
259 124 <?php $this->render_debug_tools(); ?>
260 125 </div>
261 126 </div>
@@ -378,161 +243,35 @@
378 243 <?php
379 244 }
380 245
381 246 /**
382 - * Show access granted page
383 - */
384 - private function show_access_granted_page() {
385 - $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
386 - $current_user = wp_get_current_user();
387 - ?>
388 - <div class="wrap">
389 - <h1><?php echo esc_html($whitelabel_otto_name); ?> Debug Access Granted</h1>
390 -
391 - <div class="notice notice-success">
392 - <p><strong>Debug Access Enabled!</strong></p>
393 - <p>You now have access to the <?php echo esc_html($whitelabel_otto_name); ?> Debug page.</p>
394 - </div>
395 -
396 - <div class="card">
397 - <h2>Next Steps</h2>
398 - <ol>
399 - <li><strong>Access Debug Page:</strong> The "<?php echo esc_html($whitelabel_otto_name); ?> Debug" menu should now be visible in the <?php echo esc_html(Metasync::get_effective_plugin_name()); ?> plugin menu</li>
400 - <li><strong>Use Diagnostics:</strong> Click on "<?php echo esc_html($whitelabel_otto_name); ?> Debug" to access comprehensive diagnostic tools</li>
401 - <li><strong>Troubleshoot Issues:</strong> Use the debug tools to identify why <?php echo esc_html($whitelabel_otto_name); ?> changes may not be applied</li>
402 - </ol>
403 - </div>
404 -
405 - <div class="card">
406 - <h2>Access Information</h2>
407 - <table class="form-table">
408 - <tr>
409 - <th scope="row">Magic Word</th>
410 - <td><code>abracadabra@2020</code></td>
411 - </tr>
412 - <tr>
413 - <th scope="row">Current User</th>
414 - <td><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo $current_user->ID; ?>)</td>
415 - </tr>
416 - <tr>
417 - <th scope="row">Access Type</th>
418 - <td>User Meta (persistent)</td>
419 - </tr>
420 - <tr>
421 - <th scope="row">Debug Page URL</th>
422 - <td><code><?php echo esc_html(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-otto-debug')); ?></code></td>
423 - </tr>
424 - </table>
425 - </div>
426 -
427 - <div class="card">
428 - <h2>Security Notes</h2>
429 - <ul>
430 - <li><strong>Persistent Access:</strong> This access is stored in your user profile and persists across sessions</li>
431 - <li><strong>Magic Word:</strong> Keep the magic word <code>abracadabra@2020</code> confidential</li>
432 - <li><strong>Developer Only:</strong> This debug page is intended for plugin developers only</li>
433 - <li><strong>To Revoke Access:</strong> Use <code>Metasync_Otto_Debug::disable_developer_access(<?php echo $current_user->ID; ?>)</code></li>
434 - </ul>
435 - </div>
436 -
437 - <div class="card">
438 - <h2>Quick Access</h2>
439 - <p>To access the debug page directly, use this URL:</p>
440 - <p><a href="<?php echo esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-otto-debug')); ?>" class="button button-primary">Open <?php echo esc_html($whitelabel_otto_name); ?> Debug Page</a></p>
441 -
442 - <p>Or add the magic word to any WordPress admin URL:</p>
443 - <p><code>?metasync_debug=abracadabra@2020</code></p>
444 - </div>
445 -
446 - <style>
447 - .card {
448 - background: #fff;
449 - border: 1px solid #ccd0d4;
450 - border-radius: 4px;
451 - padding: 20px;
452 - margin: 20px 0;
453 - box-shadow: 0 1px 1px rgba(0,0,0,.04);
454 - }
455 - .card h2 {
456 - margin-top: 0;
457 - color: #23282d;
458 - border-bottom: 1px solid #eee;
459 - padding-bottom: 10px;
460 - }
461 - .form-table th {
462 - width: 200px;
463 - }
464 - </style>
465 - </div>
466 - <?php
467 - }
468 -
469 - /**
470 247 * Render developer access status section
471 248 */
472 249 private function render_developer_access_status() {
473 250 $current_user = wp_get_current_user();
474 - $is_debug_enabled = $this->is_debug_access_enabled();
475 - $debug_meta = get_user_meta($current_user->ID, 'metasync_debug_enabled', true);
476 -
477 251 ?>
478 252 <div class="debug-section">
479 - <h3><span class="status-indicator <?php echo $is_debug_enabled ? 'status-success' : 'status-warning'; ?>"></span>Developer Access Status</h3>
253 + <h3><span class="status-indicator status-success"></span>Administrator Access</h3>
480 254
481 - <div class="debug-item <?php echo $is_debug_enabled ? 'success' : 'warning'; ?>">
255 + <div class="debug-item success">
482 256 <strong>Debug Access:</strong>
483 - <span class="debug-value"><?php echo $is_debug_enabled ? 'Granted' : 'Not Granted'; ?></span>
257 + <span class="debug-value">Granted via manage_options</span>
484 258 </div>
485 259
486 260 <div class="debug-item info">
487 261 <strong>Current User:</strong>
488 - <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>
489 263 </div>
490 264
491 265 <div class="debug-item info">
492 266 <strong>User Roles:</strong>
493 - <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>
494 268 </div>
495 269
496 - <div class="debug-item info">
497 - <strong>Email Domain:</strong>
498 - <span class="debug-value"><?php echo esc_html(substr(strrchr($current_user->user_email, "@"), 1)); ?></span>
499 - </div>
500 -
501 - <div class="debug-item info">
502 - <strong>Debug Meta:</strong>
503 - <span class="debug-value"><?php echo $debug_meta ? esc_html($debug_meta) : 'Not Set'; ?></span>
504 - </div>
505 -
506 - <?php if (!$is_debug_enabled): ?>
507 - <div class="debug-item warning">
508 - <strong>Enable Debug Access:</strong>
509 - <p><strong>Method 1 - Magic Word (Recommended):</strong></p>
510 - <p>Add this parameter to any WordPress admin URL:</p>
511 - <div class="debug-value">
512 - <code>?metasync_debug=abracadabra@2020</code>
513 - </div>
514 - <p><strong>Example:</strong> <code><?php echo esc_html(admin_url('admin.php?metasync_debug=abracadabra@2020')); ?></code></p>
515 -
516 - <p><strong>Method 2 - User Meta:</strong></p>
517 - <p>Run this code in WordPress:</p>
518 - <div class="debug-value">
519 - <code>Metasync_Otto_Debug::enable_developer_access(<?php echo $current_user->ID; ?>);</code>
520 - </div>
521 -
522 - <p><strong>Method 3 - WordPress CLI:</strong></p>
523 - <div class="debug-value">
524 - <code>wp user meta update <?php echo $current_user->ID; ?> metasync_debug_enabled true</code>
525 - </div>
526 - </div>
527 - <?php else: ?>
528 270 <div class="debug-item success">
529 271 <strong>Debug Access Active</strong>
530 272 <p>You have access to all <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> debug tools and diagnostics.</p>
531 - <p><strong>Magic Word:</strong> <code>abracadabra@2020</code></p>
532 - <p><strong>Access URL:</strong> <code><?php echo esc_html(self::get_debug_access_url()); ?></code></p>
533 273 </div>
534 - <?php endif; ?>
535 274 </div>
536 275 <?php
537 276 }
538 277
@@ -622,9 +361,9 @@
622 361 </div>
623 362
624 363 <div class="debug-item info">
625 364 <strong>Plugin Version:</strong>
626 - <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>
627 366 </div>
628 367 </div>
629 368 <?php
630 369 }
@@ -726,64 +465,13 @@
726 465 <?php
727 466 }
728 467
729 468 /**
730 - * Render crawl data status
731 - */
732 - private function render_crawl_data_status() {
733 - $crawl_data = get_option('metasync_otto_crawldata');
734 -
735 - ?>
736 - <div class="debug-section">
737 - <h3><span class="status-indicator <?php echo !empty($crawl_data) ? 'status-success' : 'status-warning'; ?>"></span>Crawl Data Status</h3>
738 -
739 - <div class="debug-item <?php echo !empty($crawl_data) ? 'success' : 'warning'; ?>">
740 - <strong>Crawl Data Available:</strong>
741 - <span class="debug-value"><?php echo !empty($crawl_data) ? 'Yes' : 'No'; ?></span>
742 - </div>
743 -
744 - <?php if (!empty($crawl_data)): ?>
745 - <div class="debug-item info">
746 - <strong>Domain:</strong>
747 - <span class="debug-value"><?php echo esc_html($crawl_data['domain'] ?? 'Not Set'); ?></span>
748 - </div>
749 -
750 - <div class="debug-item info">
751 - <strong>Total URLs Crawled:</strong>
752 - <span class="debug-value"><?php echo count($crawl_data['urls'] ?? []); ?></span>
753 - </div>
754 -
755 - <div class="debug-item info">
756 - <strong>Last Updated:</strong>
757 - <span class="debug-value"><?php echo $this->get_option_last_updated('metasync_otto_crawldata'); ?></span>
758 - </div>
759 -
760 - <div class="debug-item info">
761 - <strong>Sample URLs:</strong>
762 - <div class="debug-value">
763 - <?php
764 - $sample_urls = array_slice($crawl_data['urls'] ?? [], 0, 5);
765 - foreach ($sample_urls as $url) {
766 - echo esc_html($url) . '<br>';
767 - }
768 - if (count($crawl_data['urls'] ?? []) > 5) {
769 - echo '... and ' . (count($crawl_data['urls']) - 5) . ' more';
770 - }
771 - ?>
772 - </div>
773 - </div>
774 - <?php endif; ?>
775 - </div>
776 - <?php
777 - }
778 -
779 - /**
780 469 * Render processing status
781 470 */
782 471 private function render_processing_status() {
783 472 $current_url = $this->get_current_url();
784 - $otto_pixel = new Metasync_otto_pixel(Metasync::get_option('general')['otto_pixel_uuid'] ?? '');
785 - $is_crawled = $otto_pixel->is_url_crawled($current_url);
473 + $is_crawled = self::is_url_crawled($current_url);
786 474 $render_diagnostics = $this->get_render_strategy_diagnostics();
787 475
788 476 ?>
789 477 <div class="debug-section">
@@ -805,19 +493,19 @@
805 493 </div>
806 494
807 495 <div class="debug-item info">
808 496 <strong>Page Type:</strong>
809 - <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>
810 498 </div>
811 499
812 500 <div class="debug-item info">
813 501 <strong>Cache Status:</strong>
814 - <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>
815 503 </div>
816 504
817 505 <div class="debug-item info">
818 506 <strong>Processing Method:</strong>
819 - <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>
820 508 </div>
821 509
822 510 <?php if (!empty($render_diagnostics) && isset($render_diagnostics['available']) === false): ?>
823 511 <div class="debug-item info">
@@ -929,29 +617,8 @@
929 617 return !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200;
930 618 }
931 619
932 620 /**
933 - * Get option last updated time
934 - */
935 - private function get_option_last_updated($option_name) {
936 - global $wpdb;
937 -
938 - $result = $wpdb->get_var($wpdb->prepare(
939 - "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
940 - $option_name
941 - ));
942 -
943 - if ($result) {
944 - $data = maybe_unserialize($result);
945 - if (isset($data['last_updated'])) {
946 - return date('Y-m-d H:i:s', $data['last_updated']);
947 - }
948 - }
949 -
950 - return 'Unknown';
951 - }
952 -
953 - /**
954 621 * Get current URL
955 622 */
956 623 private function get_current_url() {
957 624 $scheme = is_ssl() ? 'https' : 'http';
@@ -1051,9 +718,9 @@
1051 718 */
1052 719 public function ajax_test_otto_api() {
1053 720 check_ajax_referer('metasync_otto_debug', 'nonce');
1054 721
1055 - if (!Metasync::current_user_has_plugin_access()) {
722 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1056 723 wp_die('Unauthorized');
1057 724 }
1058 725
1059 726 $general_options = Metasync::get_option('general');
@@ -1107,9 +774,9 @@
1107 774 */
1108 775 public function ajax_test_notification_endpoint() {
1109 776 check_ajax_referer('metasync_otto_debug', 'nonce');
1110 777
1111 - if (!Metasync::current_user_has_plugin_access()) {
778 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1112 779 wp_die('Unauthorized');
1113 780 }
1114 781
1115 782 $endpoint_url = rest_url('metasync/v1/otto_crawl_notify');
@@ -1149,20 +816,23 @@
1149 816 * AJAX handler for clearing OTTO cache
1150 817 */
1151 818 public function ajax_clear_otto_cache() {
1152 819 check_ajax_referer('metasync_otto_debug', 'nonce');
1153 -
1154 - if (!Metasync::current_user_has_plugin_access()) {
820 +
821 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1155 822 wp_die('Unauthorized');
1156 823 }
1157 -
1158 - // 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.
1159 829 delete_option('metasync_otto_crawldata');
1160 -
830 +
1161 831 // Clear any cached API responses
1162 832 $general_options = Metasync::get_option('general');
1163 833 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
1164 -
834 +
1165 835 if (!empty($otto_uuid)) {
1166 836 delete_transient(Metasync_Heartbeat_Manager::public_hash_cache_key($otto_uuid));
1167 837 }
1168 838
@@ -1182,9 +852,9 @@
1182 852 */
1183 853 public function ajax_simulate_crawl_notification() {
1184 854 check_ajax_referer('metasync_otto_debug', 'nonce');
1185 855
1186 - if (!Metasync::current_user_has_plugin_access()) {
856 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1187 857 wp_die('Unauthorized');
1188 858 }
1189 859
1190 860 $general_options = Metasync::get_option('general');
@@ -1221,9 +891,9 @@
1221 891 public function ajax_test_specific_url() {
1222 892 try {
1223 893 check_ajax_referer('metasync_otto_debug', 'nonce');
1224 894
1225 - if (!Metasync::current_user_has_plugin_access()) {
895 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1226 896 wp_die('Unauthorized');
1227 897 }
1228 898
1229 899 $test_url = sanitize_url($_POST['test_url'] ?? '');
@@ -1255,9 +925,9 @@
1255 925
1256 926 wp_send_json_success($results);
1257 927
1258 928 } catch (Exception $e) {
1259 - error_log('MetaSync OTTO Debug: Exception in ajax_test_specific_url: ' . $e->getMessage());
929 + error_log('MetaSync OTTO Debug: Exception in ajax_test_specific_url: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
1260 930 wp_send_json_error('Exception: ' . $e->getMessage());
1261 931 }
1262 932 }
1263 933
@@ -1266,9 +936,9 @@
1266 936 */
1267 937 public function ajax_emulate_otto_changes() {
1268 938 check_ajax_referer('metasync_otto_debug', 'nonce');
1269 939
1270 - if (!Metasync::current_user_has_plugin_access()) {
940 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1271 941 wp_die('Unauthorized');
1272 942 }
1273 943
1274 944 $test_url = sanitize_url($_POST['test_url'] ?? '');
@@ -1362,27 +1032,27 @@
1362 1032 /**
1363 1033 * Test crawl status for URL
1364 1034 */
1365 1035 private function test_crawl_status($url) {
1366 - if (class_exists('Metasync_otto_pixel')) {
1367 - $otto_pixel = new Metasync_otto_pixel(false);
1368 - $is_crawled = $otto_pixel->is_url_crawled($url);
1369 -
1370 - // Get crawl data from options
1371 - $crawl_data = get_option('metasync_otto_crawldata');
1372 -
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 +
1373 1042 return array(
1374 1043 'status' => 'success',
1375 1044 'is_crawled' => $is_crawled,
1376 - 'crawl_data' => $crawl_data,
1377 - 'total_crawled_urls' => count($crawl_data['urls'] ?? array()),
1378 - '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
1379 1049 );
1380 1050 }
1381 -
1051 +
1382 1052 return array(
1383 1053 'status' => 'error',
1384 - 'message' => Metasync::get_whitelabel_otto_name() . ' pixel class not available'
1054 + 'message' => Metasync::get_whitelabel_otto_name() . ' job status class not available'
1385 1055 );
1386 1056 }
1387 1057
1388 1058 /**
@@ -1388,9 +1058,9 @@
1388 1058 /**
1389 1059 * Test page type detection
1390 1060 */
1391 1061 private function test_page_type_detection($url) {
1392 - $parsed_url = parse_url($url);
1062 + $parsed_url = wp_parse_url($url);
1393 1063 $path = $parsed_url['path'] ?? '/';
1394 1064
1395 1065 $detection = array(
1396 1066 'url' => $url,
@@ -1489,9 +1159,9 @@
1489 1159 public function ajax_simple_test() {
1490 1160 try {
1491 1161 check_ajax_referer('metasync_otto_debug', 'nonce');
1492 1162
1493 - if (!Metasync::current_user_has_plugin_access()) {
1163 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1494 1164 wp_send_json_error('Unauthorized');
1495 1165 }
1496 1166
1497 1167 wp_send_json_success(array(
@@ -1501,9 +1171,9 @@
1501 1171 'nonce_verified' => true
1502 1172 ));
1503 1173
1504 1174 } catch (Exception $e) {
1505 - error_log('MetaSync OTTO Debug: Exception in ajax_simple_test: ' . $e->getMessage());
1175 + error_log('MetaSync OTTO Debug: Exception in ajax_simple_test: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
1506 1176 wp_send_json_error('Exception: ' . $e->getMessage());
1507 1177 }
1508 1178 }
1509 1179
@@ -1513,9 +1183,9 @@
1513 1183 public function ajax_test_db_permissions() {
1514 1184 try {
1515 1185 check_ajax_referer('metasync_otto_debug', 'nonce');
1516 1186
1517 - if (!Metasync::current_user_has_plugin_access()) {
1187 + if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1518 1188 wp_send_json_error('Unauthorized');
1519 1189 }
1520 1190
1521 1191 $results = array(
@@ -1535,9 +1205,9 @@
1535 1205
1536 1206 // Test 2: Check if we can access Action Scheduler tables
1537 1207 global $wpdb;
1538 1208 $table_name = $wpdb->prefix . 'actionscheduler_actions';
1539 - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name;
1209 + $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table_name))) == $table_name; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1540 1210
1541 1211 $results['tests']['table_access'] = array(
1542 1212 'table_exists' => $table_exists,
1543 1213 'table_name' => $table_name,
@@ -1545,9 +1215,9 @@
1545 1215 );
1546 1216
1547 1217 // Test 3: Try to insert a test record
1548 1218 if ($table_exists) {
1549 - $insert_result = $wpdb->insert(
1219 + $insert_result = $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1550 1220 $table_name,
1551 1221 array(
1552 1222 'hook' => 'metasync_debug_test',
1553 1223 'status' => 'pending',
@@ -1566,9 +1236,9 @@
1566 1236 );
1567 1237
1568 1238 // Clean up test record
1569 1239 if ($insert_result !== false && $wpdb->insert_id) {
1570 - $wpdb->delete($table_name, array('ID' => $wpdb->insert_id));
1240 + $wpdb->delete($table_name, array('ID' => $wpdb->insert_id)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1571 1241 }
1572 1242 }
1573 1243
1574 1244 // Test 4: Check WordPress cron functionality
@@ -1580,9 +1250,9 @@
1580 1250
1581 1251 wp_send_json_success($results);
1582 1252
1583 1253 } catch (Exception $e) {
1584 - error_log('MetaSync OTTO Debug: Exception in ajax_test_db_permissions: ' . $e->getMessage());
1254 + error_log('MetaSync OTTO Debug: Exception in ajax_test_db_permissions: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- unreachable in production
1585 1255 wp_send_json_error('Exception: ' . $e->getMessage());
1586 1256 }
1587 1257 }
1588 1258 }