PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
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
metasync / admin / class-metasync-otto-debug.php

class-metasync-otto-debug.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.7.2, at admin/class-metasync-otto-debug.php

1,259 lines 47.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OTTO Debug Page for MetaSync Plugin
4 *
5 * This class provides comprehensive diagnostics for OTTO functionality
6 * to help developers troubleshoot why OTTO changes are not being applied.
7 *
8 * @package MetaSync
9 * @subpackage MetaSync/admin
10 * @since 1.0.0
11 */
12
13 // Prevent direct access
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 class Metasync_Otto_Debug {
19
20 /**
21 * The plugin name
22 */
23 private $plugin_name;
24
25 /**
26 * The plugin version
27 */
28 private $version;
29
30 /**
31 * Constructor
32 */
33 public function __construct($plugin_name, $version) {
34 $this->plugin_name = $plugin_name;
35 $this->version = $version;
36
37 // Add admin menu
38 add_action('admin_menu', array($this, 'add_debug_menu'));
39
40 // Add AJAX handlers
41 add_action('wp_ajax_metasync_otto_debug_test_api', array($this, 'ajax_test_otto_api'));
42 add_action('wp_ajax_metasync_otto_debug_test_notification', array($this, 'ajax_test_notification_endpoint'));
43 add_action('wp_ajax_metasync_otto_debug_clear_cache', array($this, 'ajax_clear_otto_cache'));
44 add_action('wp_ajax_metasync_otto_debug_simulate_crawl', array($this, 'ajax_simulate_crawl_notification'));
45 add_action('wp_ajax_metasync_otto_debug_test_url', array($this, 'ajax_test_specific_url'));
46 add_action('wp_ajax_metasync_otto_debug_emulate_changes', array($this, 'ajax_emulate_otto_changes'));
47 add_action('wp_ajax_metasync_otto_debug_simple_test', array($this, 'ajax_simple_test'));
48 add_action('wp_ajax_metasync_otto_debug_test_db_permissions', array($this, 'ajax_test_db_permissions'));
49 }
50
51
52 /**
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.
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 */
86 public function add_debug_menu() {
87 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
88 return;
89 }
90
91 $menu_slug = Metasync_Admin::$page_slug;
92
93 add_submenu_page(
94 $menu_slug,
95 Metasync::get_whitelabel_otto_name() . ' Debug',
96 Metasync::get_whitelabel_otto_name() . ' Debug',
97 'manage_options',
98 $menu_slug . '-otto-debug',
99 array($this, 'create_debug_page')
100 );
101 }
102
103 /**
104 * Create the debug page
105 */
106 public function create_debug_page() {
107 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
108 wp_die('Unauthorized');
109 }
110
111 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
112 ?>
113 <div class="wrap metasync-otto-debug">
114 <h1><?php echo esc_html($whitelabel_otto_name); ?> Debug & Diagnostics</h1>
115 <p class="description">Comprehensive diagnostics for <?php echo esc_html($whitelabel_otto_name); ?> functionality. This page helps identify why <?php echo esc_html($whitelabel_otto_name); ?> changes may not be applied.</p>
116
117 <div class="metasync-debug-container">
118 <?php $this->render_developer_access_status(); ?>
119 <?php $this->render_url_testing_section(); ?>
120 <?php $this->render_configuration_status(); ?>
121 <?php $this->render_notification_endpoint_status(); ?>
122 <?php $this->render_api_connectivity_status(); ?>
123 <?php $this->render_processing_status(); ?>
124 <?php $this->render_debug_tools(); ?>
125 </div>
126 </div>
127
128 <style>
129 .metasync-debug-container {
130 display: grid;
131 grid-template-columns: 1fr 1fr;
132 gap: 20px;
133 margin-top: 20px;
134 }
135
136 .debug-section {
137 background: #fff;
138 border: 1px solid #ccd0d4;
139 border-radius: 4px;
140 padding: 20px;
141 box-shadow: 0 1px 1px rgba(0,0,0,.04);
142 }
143
144 .debug-section h3 {
145 margin-top: 0;
146 color: #23282d;
147 border-bottom: 1px solid #eee;
148 padding-bottom: 10px;
149 }
150
151 .status-indicator {
152 display: inline-block;
153 width: 12px;
154 height: 12px;
155 border-radius: 50%;
156 margin-right: 8px;
157 }
158
159 .status-success { background-color: #46b450; }
160 .status-warning { background-color: #ffb900; }
161 .status-error { background-color: #dc3232; }
162 .status-info { background-color: #00a0d2; }
163
164 .debug-item {
165 margin: 10px 0;
166 padding: 8px;
167 background: #f9f9f9;
168 border-left: 4px solid #ddd;
169 }
170
171 .debug-item.success { border-left-color: #46b450; }
172 .debug-item.warning { border-left-color: #ffb900; }
173 .debug-item.error { border-left-color: #dc3232; }
174 .debug-item.info { border-left-color: #00a0d2; }
175
176 .debug-value {
177 font-family: monospace;
178 background: #fff;
179 padding: 4px 8px;
180 border: 1px solid #ddd;
181 border-radius: 3px;
182 word-break: break-all;
183 }
184
185 .debug-tools {
186 grid-column: 1 / -1;
187 }
188
189 .debug-button {
190 background: #0073aa;
191 color: white;
192 border: none;
193 padding: 8px 16px;
194 border-radius: 3px;
195 cursor: pointer;
196 margin: 5px;
197 }
198
199 .debug-button:hover {
200 background: #005a87;
201 }
202
203 .debug-button.danger {
204 background: #dc3232;
205 }
206
207 .debug-button.danger:hover {
208 background: #a00;
209 }
210
211 .debug-results {
212 margin-top: 15px;
213 padding: 15px;
214 background: #f1f1f1;
215 border-radius: 3px;
216 display: none;
217 }
218
219 .debug-results.show {
220 display: block;
221 }
222
223 .debug-results .error {
224 background: #f8d7da;
225 border: 1px solid #f5c6cb;
226 color: #721c24;
227 padding: 10px;
228 border-radius: 4px;
229 margin: 10px 0;
230 }
231
232 .json-output {
233 background: #fff;
234 border: 1px solid #ddd;
235 padding: 10px;
236 border-radius: 3px;
237 font-family: monospace;
238 white-space: pre-wrap;
239 max-height: 300px;
240 overflow-y: auto;
241 }
242 </style>
243 <?php
244 }
245
246 /**
247 * Render developer access status section
248 */
249 private function render_developer_access_status() {
250 $current_user = wp_get_current_user();
251 ?>
252 <div class="debug-section">
253 <h3><span class="status-indicator status-success"></span>Administrator Access</h3>
254
255 <div class="debug-item success">
256 <strong>Debug Access:</strong>
257 <span class="debug-value">Granted via manage_options</span>
258 </div>
259
260 <div class="debug-item info">
261 <strong>Current User:</strong>
262 <span class="debug-value"><?php echo esc_html($current_user->user_login); ?> (ID: <?php echo (int) $current_user->ID; ?>)</span>
263 </div>
264
265 <div class="debug-item info">
266 <strong>User Roles:</strong>
267 <span class="debug-value"><?php echo esc_html(implode(', ', $current_user->roles)); ?></span>
268 </div>
269
270 <div class="debug-item success">
271 <strong>Debug Access Active</strong>
272 <p>You have access to all <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> debug tools and diagnostics.</p>
273 </div>
274 </div>
275 <?php
276 }
277
278 /**
279 * Render URL testing section
280 */
281 private function render_url_testing_section() {
282 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
283 $site_url = get_site_url();
284 ?>
285 <div class="debug-section debug-tools">
286 <h3><span class="status-indicator status-info"></span>URL Testing & <?php echo esc_html($whitelabel_otto_name); ?> Emulation</h3>
287
288 <div class="debug-item info">
289 <strong>Test Specific URL:</strong>
290 <p>Enter any URL from your site to test <?php echo esc_html($whitelabel_otto_name); ?> functionality and check all diagnostic points.</p>
291 </div>
292
293 <div style="margin-bottom: 20px;">
294 <label for="test-url-input"><strong>URL to Test:</strong></label><br>
295 <input type="url" id="test-url-input" placeholder="<?php echo esc_attr($site_url); ?>/sample-page/" style="width: 100%; padding: 8px; margin: 5px 0;" />
296 <br>
297 <button id="test-specific-url" class="debug-button">Test URL & Check All Points</button>
298 <button id="emulate-otto-changes" class="debug-button">Emulate <?php echo esc_html($whitelabel_otto_name); ?> Changes</button>
299 <button id="simple-ajax-test" class="debug-button">Test AJAX Connection</button>
300 </div>
301
302 <div id="url-test-results" class="debug-results"></div>
303 <div id="emulation-results" class="debug-results"></div>
304
305 <div class="debug-item info">
306 <strong>What This Tests:</strong>
307 <ul style="margin-left: 20px;">
308 <li>URL accessibility and response</li>
309 <li><?php echo esc_html($whitelabel_otto_name); ?> API data for the specific URL</li>
310 <li>Crawl status and processing eligibility</li>
311 <li>Page type detection and exclusions</li>
312 <li><?php echo esc_html($whitelabel_otto_name); ?> recommendations and changes</li>
313 <li>Error simulation and validation</li>
314 </ul>
315 </div>
316
317 <div class="debug-item warning">
318 <strong>Database Permissions Check:</strong>
319 <p>Testing if Action Scheduler can schedule jobs...</p>
320 <button id="test-db-permissions" class="debug-button">Test Database Permissions</button>
321 <div id="db-permissions-results" class="debug-results"></div>
322 </div>
323 </div>
324 <?php
325 }
326
327 /**
328 * Render configuration status section
329 */
330 private function render_configuration_status() {
331 $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();
332 $general_options = Metasync::get_option('general');
333
334 // OTTO SSR is always enabled by default
335 $otto_enabled = true;
336 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
337 $otto_disable_loggedin = $general_options['otto_disable_on_loggedin'] ?? false;
338
339 ?>
340 <div class="debug-section">
341 <h3><span class="status-indicator <?php echo !empty($otto_uuid) ? 'status-success' : 'status-error'; ?>"></span>Configuration Status</h3>
342
343 <div class="debug-item success">
344 <strong><?php echo esc_html($whitelabel_otto_name); ?> SSR Enabled:</strong>
345 <span class="debug-value">Yes (Always Active)</span>
346 </div>
347
348 <div class="debug-item <?php echo !empty($otto_uuid) ? 'success' : 'error'; ?>">
349 <strong><?php echo esc_html($whitelabel_otto_name); ?> UUID:</strong>
350 <span class="debug-value"><?php echo !empty($otto_uuid) ? esc_html($otto_uuid) : 'Not Set'; ?></span>
351 </div>
352
353 <div class="debug-item <?php echo $otto_disable_loggedin ? 'warning' : 'info'; ?>">
354 <strong>Disable for Logged-in Users:</strong>
355 <span class="debug-value"><?php echo $otto_disable_loggedin ? 'Yes' : 'No'; ?></span>
356 </div>
357
358 <div class="debug-item info">
359 <strong>Current User Logged In:</strong>
360 <span class="debug-value"><?php echo is_user_logged_in() ? 'Yes' : 'No'; ?></span>
361 </div>
362
363 <div class="debug-item info">
364 <strong>Plugin Version:</strong>
365 <span class="debug-value"><?php echo esc_html(defined('METASYNC_VERSION') ? METASYNC_VERSION : 'Unknown'); ?></span>
366 </div>
367 </div>
368 <?php
369 }
370
371 /**
372 * Render notification endpoint status
373 */
374 private function render_notification_endpoint_status() {
375 $rest_url = rest_url('metasync/v1/otto_crawl_notify');
376 $site_url = get_site_url();
377
378 ?>
379 <div class="debug-section">
380 <h3><span class="status-indicator status-info"></span>Notification Endpoint Status</h3>
381
382 <div class="debug-item info">
383 <strong>REST API Base URL:</strong>
384 <span class="debug-value"><?php echo esc_html($site_url); ?></span>
385 </div>
386
387 <div class="debug-item info">
388 <strong><?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> Notification Endpoint:</strong>
389 <span class="debug-value"><?php echo esc_html($rest_url); ?></span>
390 </div>
391
392 <div class="debug-item <?php echo function_exists('rest_url') ? 'success' : 'error'; ?>">
393 <strong>REST API Available:</strong>
394 <span class="debug-value"><?php echo function_exists('rest_url') ? 'Yes' : 'No'; ?></span>
395 </div>
396
397 <div class="debug-item <?php echo $this->is_rest_endpoint_registered() ? 'success' : 'error'; ?>">
398 <strong>Endpoint Registered:</strong>
399 <span class="debug-value"><?php echo $this->is_rest_endpoint_registered() ? 'Yes' : 'No'; ?></span>
400 </div>
401
402 <div class="debug-item info">
403 <strong>Expected Method:</strong>
404 <span class="debug-value">POST</span>
405 </div>
406
407 <div class="debug-item info">
408 <strong>Expected JSON Fields:</strong>
409 <span class="debug-value">domain, urls</span>
410 </div>
411 </div>
412 <?php
413 }
414
415 /**
416 * Render API connectivity status
417 */
418 private function render_api_connectivity_status() {
419 $general_options = Metasync::get_option('general');
420 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
421
422 # Use endpoint manager to get the correct API URL
423 $api_url = class_exists('Metasync_Endpoint_Manager')
424 ? Metasync_Endpoint_Manager::get_endpoint('OTTO_URL_DETAILS')
425 : 'https://sa.searchatlas.com/api/v2/otto-url-details';
426 $test_url = add_query_arg(array(
427 'url' => get_site_url(),
428 'uuid' => $otto_uuid
429 ), $api_url);
430
431 ?>
432 <div class="debug-section">
433 <h3><span class="status-indicator status-info"></span>API Connectivity Status</h3>
434
435 <div class="debug-item info">
436 <strong><?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> API Endpoint:</strong>
437 <span class="debug-value"><?php echo esc_html($api_url); ?></span>
438 </div>
439
440 <div class="debug-item info">
441 <strong>Test URL:</strong>
442 <span class="debug-value"><?php echo esc_html($test_url); ?></span>
443 </div>
444
445 <div class="debug-item <?php echo $this->can_reach_otto_api() ? 'success' : 'error'; ?>">
446 <strong>API Reachable:</strong>
447 <span class="debug-value"><?php echo $this->can_reach_otto_api() ? 'Yes' : 'No'; ?></span>
448 </div>
449
450 <div class="debug-item info">
451 <strong>SSL Verification:</strong>
452 <span class="debug-value">Enabled</span>
453 </div>
454
455 <div class="debug-item info">
456 <strong>Timeout:</strong>
457 <span class="debug-value">30 seconds</span>
458 </div>
459
460 <div class="debug-item info">
461 <strong>User Agent:</strong>
462 <span class="debug-value">MetaSync-WordPress-Plugin/1.0</span>
463 </div>
464 </div>
465 <?php
466 }
467
468 /**
469 * Render processing status
470 */
471 private function render_processing_status() {
472 $current_url = $this->get_current_url();
473 $is_crawled = self::is_url_crawled($current_url);
474 $render_diagnostics = $this->get_render_strategy_diagnostics();
475
476 ?>
477 <div class="debug-section">
478 <h3><span class="status-indicator <?php echo $is_crawled ? 'status-success' : 'status-warning'; ?>"></span>Processing Status</h3>
479
480 <div class="debug-item info">
481 <strong>Current URL:</strong>
482 <span class="debug-value"><?php echo esc_html($current_url); ?></span>
483 </div>
484
485 <div class="debug-item <?php echo $is_crawled ? 'success' : 'warning'; ?>">
486 <strong>URL Crawled by <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?>:</strong>
487 <span class="debug-value"><?php echo $is_crawled ? 'Yes' : 'No'; ?></span>
488 </div>
489
490 <div class="debug-item <?php echo $this->is_otto_excluded() ? 'warning' : 'info'; ?>">
491 <strong><?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> Excluded:</strong>
492 <span class="debug-value"><?php echo $this->is_otto_excluded() ? 'Yes' : 'No'; ?></span>
493 </div>
494
495 <div class="debug-item info">
496 <strong>Page Type:</strong>
497 <span class="debug-value"><?php echo esc_html($this->get_page_type()); ?></span>
498 </div>
499
500 <div class="debug-item info">
501 <strong>Cache Status:</strong>
502 <span class="debug-value"><?php echo esc_html($this->get_cache_status()); ?></span>
503 </div>
504
505 <div class="debug-item info">
506 <strong>Processing Method:</strong>
507 <span class="debug-value"><?php echo esc_html($this->get_processing_method()); ?></span>
508 </div>
509
510 <?php if (!empty($render_diagnostics) && isset($render_diagnostics['available']) === false): ?>
511 <div class="debug-item info">
512 <strong>Render Strategy:</strong>
513 <div class="debug-value">
514 <ul style="margin: 5px 0 0 15px; padding: 0;">
515 <li><strong>PHP:</strong> <?php echo esc_html($render_diagnostics['php_version'] ?? 'Unknown'); ?></li>
516 <li><strong>WP:</strong> <?php echo esc_html($render_diagnostics['wp_version'] ?? 'Unknown'); ?></li>
517 <li><strong>Memory:</strong> <?php echo esc_html($render_diagnostics['memory_limit'] ?? 'Unknown'); ?> (used: <?php echo esc_html($render_diagnostics['memory_used'] ?? 'Unknown'); ?>)</li>
518 <li><strong>Buffer Level:</strong> <?php echo esc_html($render_diagnostics['buffer_level'] ?? 'Unknown'); ?></li>
519 <li><strong>Headers Sent:</strong> <?php echo ($render_diagnostics['headers_sent'] ?? false) ? 'Yes' : 'No'; ?></li>
520 </ul>
521 </div>
522 </div>
523
524 <div class="debug-item info">
525 <strong>Detected Plugins:</strong>
526 <div class="debug-value">
527 <?php
528 $plugins = $render_diagnostics['detected_plugins'] ?? [];
529 foreach ($plugins as $plugin => $active):
530 ?>
531 <span style="display: inline-block; margin: 2px 5px; padding: 2px 8px; background: <?php echo $active ? '#d4edda' : '#f8f9fa'; ?>; border-radius: 3px;">
532 <?php echo esc_html($plugin); ?>: <?php echo $active ? '✓' : '✗'; ?>
533 </span>
534 <?php endforeach; ?>
535 </div>
536 </div>
537
538 <div class="debug-item info">
539 <strong>Detected Hosts:</strong>
540 <div class="debug-value">
541 <?php
542 $hosts = $render_diagnostics['detected_hosts'] ?? [];
543 foreach ($hosts as $host => $detected):
544 ?>
545 <span style="display: inline-block; margin: 2px 5px; padding: 2px 8px; background: <?php echo $detected ? '#fff3cd' : '#f8f9fa'; ?>; border-radius: 3px;">
546 <?php echo esc_html($host); ?>: <?php echo $detected ? '✓' : '✗'; ?>
547 </span>
548 <?php endforeach; ?>
549 </div>
550 </div>
551 <?php endif; ?>
552 </div>
553 <?php
554 }
555
556 /**
557 * Render debug tools
558 */
559 private function render_debug_tools() {
560 ?>
561 <div class="debug-section debug-tools">
562 <h3><span class="status-indicator status-info"></span>Debug Tools</h3>
563
564 <div style="margin-bottom: 20px;">
565 <button id="test-otto-api" class="debug-button">Test API Connectivity</button>
566 <button id="test-notification-endpoint" class="debug-button">Test Notification Endpoint</button>
567 <button id="simulate-crawl" class="debug-button">Simulate Crawl Notification</button>
568 <button id="clear-otto-cache" class="debug-button danger">Clear <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> Cache</button>
569 </div>
570
571 <div id="api-test-results" class="debug-results"></div>
572 <div id="notification-test-results" class="debug-results"></div>
573 <div id="crawl-simulate-results" class="debug-results"></div>
574 <div id="cache-clear-results" class="debug-results"></div>
575
576 <div class="debug-item info">
577 <strong>Note:</strong> These tools help diagnose <?php echo esc_html(Metasync::get_whitelabel_otto_name()); ?> issues. Use with caution in production environments.
578 </div>
579 </div>
580 <?php
581 }
582
583 /**
584 * Check if REST endpoint is registered
585 */
586 private function is_rest_endpoint_registered() {
587 $routes = rest_get_server()->get_routes();
588 return isset($routes['/metasync/v1/otto_crawl_notify']);
589 }
590
591 /**
592 * Check if OTTO API is reachable
593 */
594 private function can_reach_otto_api() {
595 $general_options = Metasync::get_option('general');
596 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
597
598 if (empty($otto_uuid)) {
599 return false;
600 }
601
602 # Use endpoint manager to get the correct API URL
603 $api_endpoint = class_exists('Metasync_Endpoint_Manager')
604 ? Metasync_Endpoint_Manager::get_endpoint('OTTO_URL_DETAILS')
605 : 'https://sa.searchatlas.com/api/v2/otto-url-details';
606
607 $api_url = add_query_arg(array(
608 'url' => get_site_url(),
609 'uuid' => $otto_uuid
610 ), $api_endpoint);
611
612 $response = wp_remote_get($api_url, array(
613 'timeout' => 10,
614 'sslverify' => true
615 ));
616
617 return !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200;
618 }
619
620 /**
621 * Get current URL
622 */
623 private function get_current_url() {
624 $scheme = is_ssl() ? 'https' : 'http';
625 $host = $_SERVER['HTTP_HOST'] ?? '';
626 $uri = $_SERVER['REQUEST_URI'] ?? '';
627 return $scheme . '://' . $host . $uri;
628 }
629
630 /**
631 * Check if OTTO is excluded for current request
632 */
633 private function is_otto_excluded() {
634 // Check AJAX requests
635 if (wp_doing_ajax() || defined('DOING_AJAX') && DOING_AJAX) {
636 return true;
637 }
638
639 // Check WooCommerce pages
640 if (function_exists('is_woocommerce') && is_woocommerce()) {
641 return true;
642 }
643
644 // Check logged-in user exclusion
645 $general_options = Metasync::get_option('general');
646 if (!empty($general_options['otto_disable_on_loggedin']) &&
647 $general_options['otto_disable_on_loggedin'] === 'true' &&
648 is_user_logged_in()) {
649 return true;
650 }
651
652 return false;
653 }
654
655 /**
656 * Get page type
657 */
658 private function get_page_type() {
659 if (is_home()) return 'Home';
660 if (is_front_page()) return 'Front Page';
661 if (is_single()) return 'Single Post';
662 if (is_page()) return 'Page';
663 if (is_category()) return 'Category';
664 if (is_tag()) return 'Tag';
665 if (is_archive()) return 'Archive';
666 if (is_search()) return 'Search';
667 if (is_404()) return '404';
668 return 'Other';
669 }
670
671 /**
672 * Get cache status
673 */
674 private function get_cache_status() {
675 // Cache is disabled in current implementation
676 return 'Disabled (SSR Mode)';
677 }
678
679 /**
680 * Get processing method
681 */
682 private function get_processing_method() {
683 // OTTO SSR is always enabled by default
684 $otto_enabled = true;
685
686 if ($otto_enabled) {
687 // Check which render strategy would be used
688 if (class_exists('Metasync_Otto_Render_Strategy')) {
689 $method = Metasync_Otto_Render_Strategy::determine_method();
690 if ($method === Metasync_Otto_Render_Strategy::METHOD_BUFFER) {
691 return 'Server-Side Rendering (SSR) - Output Buffer (Fast)';
692 } elseif ($method === Metasync_Otto_Render_Strategy::METHOD_HTTP) {
693 return 'Server-Side Rendering (SSR) - HTTP Request (Fallback)';
694 }
695 }
696 return 'Server-Side Rendering (SSR)';
697 } else {
698 return 'Client-Side JavaScript';
699 }
700 }
701
702 /**
703 * Get render strategy diagnostics
704 */
705 public function get_render_strategy_diagnostics() {
706 if (!class_exists('Metasync_Otto_Render_Strategy')) {
707 return array(
708 'available' => false,
709 'message' => 'Render Strategy class not loaded'
710 );
711 }
712
713 return Metasync_Otto_Render_Strategy::get_diagnostics();
714 }
715
716 /**
717 * AJAX handler for testing OTTO API
718 */
719 public function ajax_test_otto_api() {
720 check_ajax_referer('metasync_otto_debug', 'nonce');
721
722 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
723 wp_die('Unauthorized');
724 }
725
726 $general_options = Metasync::get_option('general');
727 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
728
729 if (empty($otto_uuid)) {
730 wp_send_json_error(Metasync::get_whitelabel_otto_name() . ' UUID not configured');
731 }
732
733 $test_url = get_site_url();
734
735 # Use endpoint manager to get the correct API URL
736 $api_endpoint = class_exists('Metasync_Endpoint_Manager')
737 ? Metasync_Endpoint_Manager::get_endpoint('OTTO_URL_DETAILS')
738 : 'https://sa.searchatlas.com/api/v2/otto-url-details';
739
740 $api_url = add_query_arg(array(
741 'url' => $test_url,
742 'uuid' => $otto_uuid
743 ), $api_endpoint);
744
745 $response = wp_remote_get($api_url, array(
746 'timeout' => 30,
747 'sslverify' => true,
748 'headers' => array(
749 'User-Agent' => 'MetaSync-WordPress-Plugin/1.0'
750 )
751 ));
752
753 if (is_wp_error($response)) {
754 wp_send_json_error(array(
755 'error' => $response->get_error_message(),
756 'url' => $api_url
757 ));
758 }
759
760 $response_code = wp_remote_retrieve_response_code($response);
761 $body = wp_remote_retrieve_body($response);
762
763 wp_send_json_success(array(
764 'response_code' => $response_code,
765 'url' => $api_url,
766 'body' => $body,
767 'has_data' => !empty($body),
768 'data_valid' => json_decode($body, true) !== null
769 ));
770 }
771
772 /**
773 * AJAX handler for testing notification endpoint
774 */
775 public function ajax_test_notification_endpoint() {
776 check_ajax_referer('metasync_otto_debug', 'nonce');
777
778 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
779 wp_die('Unauthorized');
780 }
781
782 $endpoint_url = rest_url('metasync/v1/otto_crawl_notify');
783
784 $test_data = array(
785 'domain' => get_site_url(),
786 'urls' => array('/', '/about/', '/contact/')
787 );
788
789 $response = wp_remote_post($endpoint_url, array(
790 'headers' => array(
791 'Content-Type' => 'application/json'
792 ),
793 'body' => json_encode($test_data),
794 'timeout' => 30
795 ));
796
797 if (is_wp_error($response)) {
798 wp_send_json_error(array(
799 'error' => $response->get_error_message(),
800 'url' => $endpoint_url
801 ));
802 }
803
804 $response_code = wp_remote_retrieve_response_code($response);
805 $body = wp_remote_retrieve_body($response);
806
807 wp_send_json_success(array(
808 'response_code' => $response_code,
809 'url' => $endpoint_url,
810 'body' => $body,
811 'test_data' => $test_data
812 ));
813 }
814
815 /**
816 * AJAX handler for clearing OTTO cache
817 */
818 public function ajax_clear_otto_cache() {
819 check_ajax_referer('metasync_otto_debug', 'nonce');
820
821 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
822 wp_die('Unauthorized');
823 }
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.
829 delete_option('metasync_otto_crawldata');
830
831 // Clear any cached API responses
832 $general_options = Metasync::get_option('general');
833 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
834
835 if (!empty($otto_uuid)) {
836 delete_transient(Metasync_Heartbeat_Manager::public_hash_cache_key($otto_uuid));
837 }
838
839 delete_transient('metasync_otto_js_detected');
840
841 wp_send_json_success(array(
842 'message' => Metasync::get_whitelabel_otto_name() . ' cache cleared successfully',
843 'cleared_items' => array(
844 'crawl_data' => true,
845 'api_cache' => true
846 )
847 ));
848 }
849
850 /**
851 * AJAX handler for simulating crawl notification
852 */
853 public function ajax_simulate_crawl_notification() {
854 check_ajax_referer('metasync_otto_debug', 'nonce');
855
856 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
857 wp_die('Unauthorized');
858 }
859
860 $general_options = Metasync::get_option('general');
861 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
862
863 if (empty($otto_uuid)) {
864 wp_send_json_error(Metasync::get_whitelabel_otto_name() . ' UUID not configured');
865 }
866
867 // Simulate crawl notification
868 $test_data = array(
869 'domain' => get_site_url(),
870 'urls' => array('/', '/about/', '/contact/', '/blog/')
871 );
872
873 // Create a mock request object
874 $request = new WP_REST_Request('POST', '/metasync/v1/otto_crawl_notify');
875 $request->set_body(json_encode($test_data));
876
877 // Call the notification handler
878 $response = metasync_otto_crawl_notify($request);
879
880 wp_send_json_success(array(
881 'message' => 'Crawl notification simulated',
882 'test_data' => $test_data,
883 'response' => $response->get_data(),
884 'response_code' => $response->get_status()
885 ));
886 }
887
888 /**
889 * AJAX handler for testing specific URL
890 */
891 public function ajax_test_specific_url() {
892 try {
893 check_ajax_referer('metasync_otto_debug', 'nonce');
894
895 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
896 wp_die('Unauthorized');
897 }
898
899 $test_url = sanitize_url($_POST['test_url'] ?? '');
900
901 if (empty($test_url)) {
902 wp_send_json_error('No URL provided');
903 }
904
905 $results = array(
906 'test_url' => $test_url,
907 'timestamp' => current_time('mysql'),
908 'tests' => array()
909 );
910
911 // Test 1: URL Accessibility
912 $results['tests']['url_accessibility'] = $this->test_url_accessibility($test_url);
913
914 // Test 2: OTTO API Data
915 $results['tests']['otto_api_data'] = $this->test_otto_api_for_url($test_url);
916
917 // Test 3: Crawl Status
918 $results['tests']['crawl_status'] = $this->test_crawl_status($test_url);
919
920 // Test 4: Page Type Detection
921 $results['tests']['page_type_detection'] = $this->test_page_type_detection($test_url);
922
923 // Test 5: Processing Eligibility
924 $results['tests']['processing_eligibility'] = $this->test_processing_eligibility($test_url);
925
926 wp_send_json_success($results);
927
928 } catch (Exception $e) {
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
930 wp_send_json_error('Exception: ' . $e->getMessage());
931 }
932 }
933
934 /**
935 * AJAX handler for emulating OTTO changes
936 */
937 public function ajax_emulate_otto_changes() {
938 check_ajax_referer('metasync_otto_debug', 'nonce');
939
940 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
941 wp_die('Unauthorized');
942 }
943
944 $test_url = sanitize_url($_POST['test_url'] ?? '');
945
946 if (empty($test_url)) {
947 wp_send_json_error('No URL provided');
948 }
949
950 $results = array(
951 'test_url' => $test_url,
952 'timestamp' => current_time('mysql'),
953 'emulation' => array()
954 );
955
956 // Emulate OTTO processing
957 $results['emulation'] = $this->emulate_otto_processing($test_url);
958
959 wp_send_json_success($results);
960 }
961
962 /**
963 * Test URL accessibility
964 */
965 private function test_url_accessibility($url) {
966 $response = wp_remote_get($url, array(
967 'timeout' => 10,
968 'user-agent' => 'MetaSync Debug Tool'
969 ));
970
971 if (is_wp_error($response)) {
972 return array(
973 'status' => 'error',
974 'message' => $response->get_error_message(),
975 'accessible' => false
976 );
977 }
978
979 $status_code = wp_remote_retrieve_response_code($response);
980 $body = wp_remote_retrieve_body($response);
981
982 return array(
983 'status' => 'success',
984 'status_code' => $status_code,
985 'accessible' => $status_code === 200,
986 'content_length' => strlen($body),
987 'headers' => wp_remote_retrieve_headers($response)->getAll()
988 );
989 }
990
991 /**
992 * Test OTTO API data for specific URL
993 */
994 private function test_otto_api_for_url($url) {
995 $general_options = Metasync::get_option('general');
996 $otto_uuid = $general_options['otto_pixel_uuid'] ?? '';
997
998 if (empty($otto_uuid)) {
999 return array(
1000 'status' => 'error',
1001 'message' => Metasync::get_whitelabel_otto_name() . ' UUID not configured'
1002 );
1003 }
1004
1005 // Use the existing OTTO API function
1006 if (function_exists('metasync_fetch_otto_seo_data')) {
1007 $data = metasync_fetch_otto_seo_data($url, $otto_uuid);
1008
1009 if (is_wp_error($data)) {
1010 return array(
1011 'status' => 'error',
1012 'message' => $data->get_error_message(),
1013 'has_data' => false
1014 );
1015 }
1016
1017 return array(
1018 'status' => 'success',
1019 'has_data' => !empty($data),
1020 'data_keys' => array_keys($data ?? array()),
1021 'recommendations_count' => count($data['recommendations'] ?? array()),
1022 'sample_data' => array_slice($data ?? array(), 0, 3) // First 3 items for preview
1023 );
1024 }
1025
1026 return array(
1027 'status' => 'error',
1028 'message' => Metasync::get_whitelabel_otto_name() . ' API function not available'
1029 );
1030 }
1031
1032 /**
1033 * Test crawl status for URL
1034 */
1035 private function test_crawl_status($url) {
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
1042 return array(
1043 'status' => 'success',
1044 'is_crawled' => $is_crawled,
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
1049 );
1050 }
1051
1052 return array(
1053 'status' => 'error',
1054 'message' => Metasync::get_whitelabel_otto_name() . ' job status class not available'
1055 );
1056 }
1057
1058 /**
1059 * Test page type detection
1060 */
1061 private function test_page_type_detection($url) {
1062 $parsed_url = wp_parse_url($url);
1063 $path = $parsed_url['path'] ?? '/';
1064
1065 $detection = array(
1066 'url' => $url,
1067 'path' => $path,
1068 'is_ajax' => strpos($path, 'wp-admin/admin-ajax.php') !== false,
1069 'is_woocommerce' => function_exists('is_woocommerce') ? false : 'WooCommerce not available',
1070 'is_admin' => strpos($path, '/wp-admin/') !== false,
1071 'is_login' => strpos($path, '/wp-login.php') !== false,
1072 'is_cron' => strpos($path, '/wp-cron.php') !== false,
1073 'is_xmlrpc' => strpos($path, '/xmlrpc.php') !== false
1074 );
1075
1076 // Check if it's a WooCommerce page
1077 if (function_exists('is_woocommerce')) {
1078 // This would need to be tested with actual page context
1079 $detection['is_woocommerce'] = 'Requires page context to determine';
1080 }
1081
1082 return $detection;
1083 }
1084
1085 /**
1086 * Test processing eligibility
1087 */
1088 private function test_processing_eligibility($url) {
1089 $general_options = Metasync::get_option('general');
1090 // OTTO SSR is always enabled by default
1091 $otto_enabled = true;
1092 $disable_logged_in = $general_options['otto_disable_on_loggedin'] ?? false;
1093
1094 $eligibility = array(
1095 'otto_enabled' => $otto_enabled,
1096 'disable_logged_in' => $disable_logged_in,
1097 'user_logged_in' => is_user_logged_in(),
1098 'eligible_for_processing' => false,
1099 'exclusion_reasons' => array()
1100 );
1101
1102 // OTTO SSR is always enabled, so this check is no longer needed
1103
1104 if ($disable_logged_in && is_user_logged_in()) {
1105 $eligibility['exclusion_reasons'][] = 'User is logged in and ' . Metasync::get_whitelabel_otto_name() . ' disabled for logged-in users';
1106 }
1107
1108 $eligibility['eligible_for_processing'] = empty($eligibility['exclusion_reasons']);
1109
1110 return $eligibility;
1111 }
1112
1113 /**
1114 * Emulate OTTO processing
1115 */
1116 private function emulate_otto_processing($url) {
1117 $emulation = array(
1118 'url' => $url,
1119 'steps' => array(),
1120 'errors' => array(),
1121 'changes_applied' => array()
1122 );
1123
1124 // Step 1: Check if URL is crawled
1125 $emulation['steps']['check_crawled'] = $this->test_crawl_status($url);
1126
1127 // Step 2: Fetch OTTO data
1128 $emulation['steps']['fetch_otto_data'] = $this->test_otto_api_for_url($url);
1129
1130 // Step 3: Simulate HTML processing
1131 if (class_exists('Metasync_otto_html')) {
1132 try {
1133 $otto_html = new Metasync_otto_html(false);
1134 $emulation['steps']['html_processing'] = array(
1135 'status' => 'success',
1136 'message' => Metasync::get_whitelabel_otto_name() . ' HTML class available'
1137 );
1138
1139 // Simulate processing (without actually modifying files)
1140 $emulation['changes_applied'] = array(
1141 'header_changes' => 'Simulated header modifications',
1142 'body_changes' => 'Simulated body modifications',
1143 'footer_changes' => 'Simulated footer modifications'
1144 );
1145
1146 } catch (Exception $e) {
1147 $emulation['errors'][] = 'HTML processing error: ' . $e->getMessage();
1148 }
1149 } else {
1150 $emulation['errors'][] = Metasync::get_whitelabel_otto_name() . ' HTML class not available';
1151 }
1152
1153 return $emulation;
1154 }
1155
1156 /**
1157 * Simple AJAX test to verify basic functionality
1158 */
1159 public function ajax_simple_test() {
1160 try {
1161 check_ajax_referer('metasync_otto_debug', 'nonce');
1162
1163 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1164 wp_send_json_error('Unauthorized');
1165 }
1166
1167 wp_send_json_success(array(
1168 'message' => 'AJAX is working!',
1169 'timestamp' => current_time('mysql'),
1170 'user_id' => get_current_user_id(),
1171 'nonce_verified' => true
1172 ));
1173
1174 } catch (Exception $e) {
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
1176 wp_send_json_error('Exception: ' . $e->getMessage());
1177 }
1178 }
1179
1180 /**
1181 * AJAX handler for testing database permissions
1182 */
1183 public function ajax_test_db_permissions() {
1184 try {
1185 check_ajax_referer('metasync_otto_debug', 'nonce');
1186
1187 if (!current_user_can('manage_options') || !self::is_debug_tools_enabled()) {
1188 wp_send_json_error('Unauthorized');
1189 }
1190
1191 $results = array(
1192 'timestamp' => current_time('mysql'),
1193 'tests' => array()
1194 );
1195
1196 // Test 1: Try to schedule a simple action
1197 $test_action = 'metasync_debug_test_action';
1198 $scheduled = wp_schedule_single_event(time() + 60, $test_action, array('test' => 'data'));
1199
1200 $results['tests']['action_scheduler'] = array(
1201 'can_schedule' => $scheduled !== false,
1202 'scheduled' => $scheduled,
1203 'message' => $scheduled !== false ? 'Action Scheduler working' : 'Action Scheduler failed - likely database permissions issue'
1204 );
1205
1206 // Test 2: Check if we can access Action Scheduler tables
1207 global $wpdb;
1208 $table_name = $wpdb->prefix . 'actionscheduler_actions';
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
1210
1211 $results['tests']['table_access'] = array(
1212 'table_exists' => $table_exists,
1213 'table_name' => $table_name,
1214 'message' => $table_exists ? 'Action Scheduler table exists' : 'Action Scheduler table not found'
1215 );
1216
1217 // Test 3: Try to insert a test record
1218 if ($table_exists) {
1219 $insert_result = $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- admin debug tool — scratch diagnostics storage outside any WordPress API
1220 $table_name,
1221 array(
1222 'hook' => 'metasync_debug_test',
1223 'status' => 'pending',
1224 'scheduled_date_gmt' => current_time('mysql', 1),
1225 'args' => json_encode(array('test' => true)),
1226 'schedule' => 'once'
1227 ),
1228 array('%s', '%s', '%s', '%s', '%s')
1229 );
1230
1231 $results['tests']['insert_test'] = array(
1232 'can_insert' => $insert_result !== false,
1233 'insert_id' => $wpdb->insert_id,
1234 'last_error' => $wpdb->last_error,
1235 'message' => $insert_result !== false ? 'Can insert records' : 'Cannot insert records - ' . $wpdb->last_error
1236 );
1237
1238 // Clean up test record
1239 if ($insert_result !== false && $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
1241 }
1242 }
1243
1244 // Test 4: Check WordPress cron functionality
1245 $cron_disabled = defined('DISABLE_WP_CRON') && DISABLE_WP_CRON;
1246 $results['tests']['wp_cron'] = array(
1247 'enabled' => !$cron_disabled,
1248 'message' => $cron_disabled ? 'WordPress cron is disabled' : 'WordPress cron is enabled'
1249 );
1250
1251 wp_send_json_success($results);
1252
1253 } catch (Exception $e) {
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
1255 wp_send_json_error('Exception: ' . $e->getMessage());
1256 }
1257 }
1258 }
1259