PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.5.23
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.5.23
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 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / includes / class-metasync-debug-manager.php

class-metasync-debug-manager.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.5.23, at includes/class-metasync-debug-manager.php

975 lines 49.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 /**
7 * Debug / Error-Logging manager.
8 *
9 * Extracted from Metasync_Admin to keep the admin class focused on UI concerns.
10 * Handles debug-mode rendering, error-log display, wp-config updates, and
11 * related form-post handlers.
12 *
13 * @package Metasync
14 * @subpackage Metasync/includes
15 */
16 class Metasync_Debug_Manager
17 {
18 /** @var self|null */
19 private static $instance = null;
20
21 /**
22 * Get singleton instance.
23 *
24 * @return self
25 */
26 public static function instance()
27 {
28 if (self::$instance === null) {
29 self::$instance = new self();
30 }
31 return self::$instance;
32 }
33
34 private function __construct() {}
35
36 /**
37 * Get the admin page slug.
38 */
39 private function get_page_slug()
40 {
41 return Metasync_Admin::$page_slug;
42 }
43
44 /**
45 * Read an execution setting (mirrors Metasync_Admin::get_execution_setting).
46 */
47 private function get_execution_setting($key, $default = null)
48 {
49 $settings = get_option('metasync_execution_settings', array());
50 $defaults = array(
51 'max_execution_time' => 30,
52 'max_memory_limit' => 256,
53 'log_batch_size' => 1000,
54 'action_scheduler_batches' => 1,
55 'otto_rate_limit' => 10,
56 'queue_cleanup_days' => 31
57 );
58
59 if (empty($settings)) {
60 return isset($defaults[$key]) ? $defaults[$key] : $default;
61 }
62
63 return isset($settings[$key]) ? $settings[$key] : (isset($defaults[$key]) ? $defaults[$key] : $default);
64 }
65
66 // ------------------------------------------------------------------
67 // Error-log page (standalone page)
68 // ------------------------------------------------------------------
69
70 /**
71 * Display the standalone Error Log admin page.
72 *
73 * @param Metasync_Admin $admin The admin instance (needed for header/nav rendering).
74 */
75 public function metasync_display_error_log($admin) {
76 $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
77 if (!Metasync::current_user_has_plugin_access()) {
78 return;
79 }
80
81 if (isset($_POST['wp_debug_log_enabled']) &&
82 isset($_POST['wp_debug_enabled']) &&
83 isset($_POST['wp_debug_display_enabled']) &&
84 isset($_POST['wp_debug_nonce']) &&
85 wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
86
87 $wp_debug = in_array($_POST['wp_debug_enabled'], ['true', 'false']) ? $_POST['wp_debug_enabled'] : 'false';
88 $wp_debug_log = in_array($_POST['wp_debug_log_enabled'], ['true', 'false']) ? $_POST['wp_debug_log_enabled'] : 'false';
89 $wp_debug_display = in_array($_POST['wp_debug_display_enabled'], ['true', 'false']) ? $_POST['wp_debug_display_enabled'] : 'false';
90
91 update_option('wp_debug_enabled', $wp_debug);
92 update_option('wp_debug_log_enabled', $wp_debug_log);
93 update_option('wp_debug_display_enabled', $wp_debug_display);
94
95 $data = new ConfigControllerMetaSync();
96 $data->store();
97
98 add_settings_error(
99 'metasync_messages',
100 'metasync_message',
101 'WordPress debug settings updated successfully.',
102 'updated'
103 );
104 } elseif (isset($_POST['wp_debug_nonce']) && !wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
105 add_settings_error(
106 'metasync_messages',
107 'metasync_message',
108 'Security verification failed. Please try again.',
109 'error'
110 );
111 }
112
113
114 $log_enabled = get_option('metasync_log_enabled', 'yes');
115 $wp_debug_enabled = get_option('wp_debug_enabled', 'false');
116 $wp_debug_log_enabled = get_option('wp_debug_log_enabled', 'false');
117 $wp_debug_display_enabled = get_option('wp_debug_display_enabled', 'false');
118 ?>
119
120 <div class="wrap metasync-dashboard-wrap" data-theme="<?php echo esc_attr(get_option('metasync_theme', 'dark')); ?>">
121
122
123 <?php
124 $admin->render_plugin_header('Error Logs');
125 $admin->render_navigation_menu('error-log');
126 ?>
127
128 <!-- Log File Management -->
129 <div class="dashboard-card">
130 <h2>🗑 Error Log Management</h2>
131 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Clear WordPress error logs to free up space and remove old entries.</p>
132
133 <form method="post" style="margin-top: 15px;">
134 <input type="hidden" name="clear_log" value="yes" />
135 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
136 <?php submit_button('🧹 Clear Error Logs', 'secondary', 'clear-log', false, array('class' => 'button button-secondary')); ?>
137 </form>
138 </div>
139
140 <!-- WordPress Debug Settings -->
141 <form method="post">
142 <?php wp_nonce_field('metasync_wp_debug_settings', 'wp_debug_nonce'); ?>
143 <div class="dashboard-card">
144 <h2>🔧 WordPress Debug Configuration</h2>
145 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Configure WordPress debug settings to control error logging and display.</p>
146 <?php settings_errors('metasync_messages'); ?>
147
148 <table class="form-table">
149 <tr valign="top">
150 <th scope="row">WP_DEBUG</th>
151 <td>
152 <select name="wp_debug_enabled">
153 <option value="false" <?php selected('false', $wp_debug_enabled); ?>>Disabled</option>
154 <option value="true" <?php selected('true', $wp_debug_enabled); ?>>Enabled</option>
155 </select>
156 <p class="description">Enable or disable WordPress debugging mode.</p>
157 </td>
158 </tr>
159 <tr valign="top">
160 <th scope="row">WP_DEBUG_LOG</th>
161 <td>
162 <select name="wp_debug_log_enabled">
163 <option value="false" <?php selected('false', $wp_debug_log_enabled); ?>>Disabled</option>
164 <option value="true" <?php selected('true', $wp_debug_log_enabled); ?>>Enabled</option>
165 </select>
166 <p class="description">Save debug messages to a log file.</p>
167 </td>
168 </tr>
169 <tr valign="top">
170 <th scope="row">WP_DEBUG_DISPLAY</th>
171 <td>
172 <select name="wp_debug_display_enabled">
173 <option value="false" <?php selected('false', $wp_debug_display_enabled); ?>>Disabled</option>
174 <option value="true" <?php selected('true', $wp_debug_display_enabled); ?>>Enabled</option>
175 </select>
176 <p class="description">Display debug messages on the website (not recommended for production).</p>
177 </td>
178 </tr>
179 </table>
180 </div>
181
182 <div class="dashboard-card">
183 <h2>💾 Save Changes</h2>
184 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Apply your WordPress logging configuration changes.</p>
185 <?php submit_button('Save WordPress Logging Settings', 'primary', 'submit', false, array('class' => 'button button-primary')); ?>
186 </div>
187 </form>
188
189 <!-- Error Log Display -->
190 <div class="dashboard-card">
191 <h2>📄 Error Log Contents</h2>
192 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">View the current error log entries for troubleshooting and monitoring.</p>
193
194 <?php
195 if (file_exists($log_file)) {
196 $log_content = file_get_contents($log_file);
197 if (!empty($log_content)) {
198 echo '<div class="dashboard-code-block" style="width: 100%; box-sizing: border-box;">';
199 echo '<pre style="background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; overflow: auto; max-height: 400px; font-family: \'SF Mono\', Monaco, \'Cascadia Code\', \'Roboto Mono\', Consolas, monospace; font-size: 13px; line-height: 1.6; color: var(--dashboard-text-primary); margin: 0; box-shadow: var(--dashboard-shadow-sm); width: 100%; box-sizing: border-box; white-space: pre-wrap; word-wrap: break-word;">';
200 echo esc_html($log_content);
201 echo '</pre>';
202 echo '</div>';
203 } else {
204 echo '<div class="dashboard-empty-state">';
205 echo '<p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">�
206 Log file is empty - no errors recorded.</p>';
207 echo '</div>';
208 }
209 } else {
210 echo '<div class="dashboard-empty-state">';
211 echo '<p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">📝 No log file found. Error logging may not be enabled.</p>';
212 echo '</div>';
213 }
214 ?>
215 </div>
216 </div>
217 <?php
218 }
219
220 // ------------------------------------------------------------------
221 // wp-config.php manipulation
222 // ------------------------------------------------------------------
223
224 /**
225 * Update wp-config.php debug constants.
226 */
227 public function metasync_update_wp_config() {
228 $wp_config_path = ABSPATH . 'wp-config.php';
229 if (file_exists($wp_config_path) && is_writable($wp_config_path)) {
230 $config_file = file_get_contents($wp_config_path);
231
232 $wp_debug_enabled = get_option('wp_debug_enabled', 'false') === 'true' ? 'true' : 'false';
233 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
234 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", "define('WP_DEBUG', $wp_debug_enabled);", $config_file);
235 } else {
236 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG', $wp_debug_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
237 }
238
239 $wp_debug_log_enabled = get_option('wp_debug_log_enabled', 'false') === 'true' ? 'true' : 'false';
240 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG_LOG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
241 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG_LOG['\"]\s*,\s*.*?\s*\)\s*;/", "define('WP_DEBUG_LOG', $wp_debug_log_enabled);", $config_file);
242 } else {
243 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG_LOG', $wp_debug_log_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
244 }
245
246 $wp_debug_display_enabled = get_option('wp_debug_display_enabled', 'false') === 'true' ? 'true' : 'false';
247 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG_DISPLAY['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
248 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG_DISPLAY['\"]\s*,\s*.*?\s*\)\s*;/","define('WP_DEBUG_DISPLAY', $wp_debug_display_enabled);", $config_file);
249 } else {
250 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG_DISPLAY', $wp_debug_display_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
251 }
252
253 file_put_contents($wp_config_path, $config_file);
254 } else {
255 wp_die('The wp-config.php file is not writable. Please check the file permissions.');
256 }
257 }
258
259 // ------------------------------------------------------------------
260 // Options hook
261 // ------------------------------------------------------------------
262
263 /**
264 * Sync plugin file headers when metasync_options is updated.
265 *
266 * @param mixed $old_value The old option value.
267 * @param mixed $new_value The new option value.
268 */
269 public function on_options_updated_sync_file_headers($old_value, $new_value)
270 {
271 $old_general = is_array($old_value) ? ($old_value['general'] ?? []) : [];
272 $new_general = is_array($new_value) ? ($new_value['general'] ?? []) : [];
273
274 $whitelabel_keys = [
275 'white_label_plugin_name',
276 'white_label_plugin_description',
277 'white_label_plugin_author',
278 'white_label_plugin_author_uri',
279 'white_label_plugin_uri',
280 ];
281
282 $changed = false;
283 foreach ($whitelabel_keys as $key) {
284 if (($old_general[$key] ?? '') !== ($new_general[$key] ?? '')) {
285 $changed = true;
286 break;
287 }
288 }
289
290 if ($changed) {
291 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
292 Metasync_Activator::sync_plugin_file_headers();
293 }
294 }
295
296 // ------------------------------------------------------------------
297 // Rendering (Advanced-tab sections)
298 // ------------------------------------------------------------------
299
300 /**
301 * Render debug mode section for inclusion in Advanced settings.
302 */
303 public function render_debug_mode_section()
304 {
305 if (!class_exists('Metasync_Debug_Mode_Manager')) {
306 ?>
307 <div style="background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
308 <p style="color: var(--dashboard-text-primary); margin: 0;">
309 ⚠️ Debug Mode Manager is not available. Please ensure the plugin is properly installed.
310 </p>
311 </div>
312 <?php
313 return;
314 }
315
316 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
317 $status = $debug_manager->get_status();
318 ?>
319
320 <!-- Debug Mode Status Overview -->
321 <div style="margin-bottom: 30px;">
322 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Current Status</h4>
323 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 20px; border-radius: 8px;">
324 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
325 <!-- Status Badge -->
326 <div style="padding: 10px; border-left: 4px solid <?php echo $status['enabled'] ? '#ffc107' : '#4caf50'; ?>; background: rgba(<?php echo $status['enabled'] ? '255, 193, 7' : '76, 175, 80'; ?>, 0.1); border-radius: 4px;">
327 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Status</div>
328 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 16px;">
329 <?php echo $status['enabled'] ? '⚠️ Active' : '✓ Inactive'; ?>
330 </div>
331 </div>
332
333 <?php if ($status['enabled']): ?>
334 <!-- Mode Type -->
335 <div style="padding: 10px; border-left: 4px solid #2196f3; background: rgba(33, 150, 243, 0.1); border-radius: 4px;">
336 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Mode</div>
337 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;">
338 <?php echo $status['indefinite'] ? 'Indefinite' : '24-Hour Auto-Disable'; ?>
339 </div>
340 </div>
341
342 <?php if (!$status['indefinite']): ?>
343 <!-- Time Remaining -->
344 <div style="padding: 10px; border-left: 4px solid #9c27b0; background: rgba(156, 39, 176, 0.1); border-radius: 4px;">
345 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Time Remaining</div>
346 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;" class="debug-time-remaining">
347 <?php echo esc_html($status['time_remaining_formatted']); ?>
348 </div>
349 </div>
350 <?php endif; ?>
351
352 <!-- Log File Size -->
353 <div style="padding: 10px; border-left: 4px solid #ff5722; background: rgba(255, 87, 34, 0.1); border-radius: 4px;">
354 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Log File Size</div>
355 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;">
356 <?php echo esc_html($status['log_file_size_formatted']); ?>
357 </div>
358 <div style="font-size: 11px; color: var(--dashboard-text-secondary); margin-top: 2px;">
359 <?php echo number_format($status['percentage_used'], 1); ?>% of <?php echo esc_html($status['max_log_size_formatted']); ?>
360 </div>
361 </div>
362 <?php endif; ?>
363 </div>
364
365 <?php if ($status['enabled']): ?>
366 <!-- Progress Bar -->
367 <div style="margin-top: 15px;">
368 <div style="background: rgba(255, 255, 255, 0.1); height: 8px; border-radius: 4px; overflow: hidden;">
369 <div style="height: 100%; width: <?php echo esc_attr($status['percentage_used']); ?>%; background: linear-gradient(90deg, #4caf50 0%, #ffc107 70%, #f44336 100%); transition: width 0.3s ease;"></div>
370 </div>
371 </div>
372 <?php endif; ?>
373 </div>
374 </div>
375
376 <!-- Debug Mode Controls -->
377 <div style="margin-bottom: 30px;">
378 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Controls</h4>
379
380 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>">
381 <input type="hidden" name="metasync_debug_mode_action_advanced" value="1" />
382 <?php wp_nonce_field('metasync_debug_mode_action_advanced', 'metasync_debug_mode_nonce_advanced'); ?>
383
384 <?php if (!$status['enabled']): ?>
385 <!-- Enable Debug Mode -->
386 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 20px; border-radius: 8px; margin-bottom: 15px;">
387 <p style="color: var(--dashboard-text-secondary); margin-top: 0; margin-bottom: 15px;">
388 Activate debug mode to troubleshoot issues. Debug mode will automatically disable after 24 hours unless you enable indefinite mode.
389 </p>
390
391 <label style="display: flex; align-items: center; margin: 15px 0; cursor: pointer;">
392 <input type="checkbox" name="indefinite" value="1" id="indefinite-mode-advanced" style="margin-right: 8px;" />
393 <span style="font-weight: 500; color: var(--dashboard-text-primary);">Keep debug mode enabled indefinitely</span>
394 </label>
395
396 <div id="indefinite-warning-advanced" style="display: none; background: rgba(255, 193, 7, 0.1); border-left: 4px solid #ffc107; padding: 12px; border-radius: 4px; margin: 15px 0;">
397 <strong style="color: var(--dashboard-text-primary);">⚠️ Warning:</strong>
398 <span style="color: var(--dashboard-text-secondary);"> Indefinite debug mode may cause log files to grow without limits. This should only be used for extended troubleshooting sessions.</span>
399 </div>
400
401 <input type="hidden" name="action_type" value="enable" />
402 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto; min-width: 200px;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
403 🐛 Enable Debug Mode
404 </button>
405 </div>
406
407 <?php else: ?>
408 <!-- Manage Active Debug Mode -->
409 <div style="display: flex; gap: 10px; flex-wrap: wrap;">
410 <?php if (!$status['indefinite']): ?>
411 <div>
412 <input type="hidden" name="action_type" value="extend" />
413 <button type="submit" class="metasync-btn-secondary" style="background: rgba(255, 255, 255, 0.1); color: var(--dashboard-text-primary); border: 1px solid var(--dashboard-border); padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; display: inline-block; width: auto;" onmouseover="this.style.background='rgba(255, 255, 255, 0.15)';" onmouseout="this.style.background='rgba(255, 255, 255, 0.1)';">
414 ⏱️ Extend for 24 Hours
415 </button>
416 </div>
417 <?php endif; ?>
418
419 <div>
420 <input type="hidden" name="action_type" value="disable" />
421 <button type="submit" class="metasync-btn-danger" onclick="return confirm('Are you sure you want to disable debug mode?');" style="background: linear-gradient(135deg, #f44336, #d32f2f); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
422 ⏹️ Disable Debug Mode Now
423 </button>
424 </div>
425 </div>
426 <?php endif; ?>
427 </form>
428 </div>
429
430 <!-- Configuration Details -->
431 <div style="margin-bottom: 30px;">
432 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Configuration Details</h4>
433 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px;">
434 <table style="width: 100%; border-collapse: collapse;">
435 <tbody>
436 <tr style="border-bottom: 1px solid var(--dashboard-border);">
437 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Maximum Log Size</td>
438 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);"><?php echo esc_html($status['max_log_size_formatted']); ?></td>
439 </tr>
440 <tr style="border-bottom: 1px solid var(--dashboard-border);">
441 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Auto-Disable Duration</td>
442 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">24 hours</td>
443 </tr>
444 <tr style="border-bottom: 1px solid var(--dashboard-border);">
445 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Log Rotation</td>
446 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">Automatic when size limit reached</td>
447 </tr>
448 <tr style="border-bottom: 1px solid var(--dashboard-border);">
449 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Rotated Files Kept</td>
450 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">1 (current + 1 old)</td>
451 </tr>
452 <tr style="border-bottom: 1px solid var(--dashboard-border);">
453 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Check Frequency</td>
454 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">Hourly (via WP Cron)</td>
455 </tr>
456 <tr>
457 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Log File Path</td>
458 <td style="padding: 10px 0; color: var(--dashboard-text-secondary); font-family: monospace; font-size: 12px; word-break: break-all;">
459 <?php echo esc_html($status['log_file_path']); ?>
460 </td>
461 </tr>
462 </tbody>
463 </table>
464 </div>
465 </div>
466
467 <script>
468 jQuery(document).ready(function($) {
469 $('#indefinite-mode-advanced').on('change', function() {
470 if ($(this).is(':checked')) {
471 $('#indefinite-warning-advanced').slideDown();
472 } else {
473 $('#indefinite-warning-advanced').slideUp();
474 }
475 });
476
477 <?php if ($status['enabled'] && !$status['indefinite']): ?>
478 var initialTimeRemaining = <?php echo $status['time_remaining']; ?>;
479 var hasReloaded = false;
480
481 function updateDebugTimeRemaining() {
482 if (initialTimeRemaining <= 0 || hasReloaded) {
483 return;
484 }
485
486 $.ajax({
487 url: '<?php echo rest_url('metasync/v1/debug-mode/status'); ?>',
488 method: 'GET',
489 beforeSend: function(xhr) {
490 xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
491 },
492 success: function(response) {
493 console.log('MetaSync Debug Mode Status:', response);
494
495 if (response && typeof response.time_remaining !== 'undefined') {
496 if (response.time_remaining_formatted) {
497 $('.debug-time-remaining').text(response.time_remaining_formatted);
498 }
499
500 if (response.time_remaining <= 0 && initialTimeRemaining > 0 && !hasReloaded) {
501 hasReloaded = true;
502 console.log('Debug mode expired, reloading page...');
503 setTimeout(function() {
504 window.location.reload();
505 }, 2000);
506 }
507 }
508 },
509 error: function(xhr, status, error) {
510 console.error('MetaSync Debug Mode: Failed to update time remaining', error);
511 }
512 });
513 }
514
515 if (initialTimeRemaining > 0) {
516 setTimeout(updateDebugTimeRemaining, 2000);
517 setInterval(updateDebugTimeRemaining, 60000);
518 } else {
519 console.log('Debug mode already expired, skipping AJAX updates');
520 }
521 <?php endif; ?>
522 });
523 </script>
524 <?php
525 }
526
527 /**
528 * Render error log content for inclusion in Advanced settings.
529 */
530 public function render_error_log_content()
531 {
532 ?>
533 <!-- Error Summary Section -->
534 <div style="margin-bottom: 30px;">
535 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">📊 Error Summary</h4>
536 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View categorized error statistics with counts and last occurrence times.</p>
537
538 <?php
539 if (class_exists('Metasync_Error_Logger')) {
540 $error_summary = Metasync_Error_Logger::get_error_summary();
541
542 if (!empty($error_summary) && is_array($error_summary)) {
543 uasort($error_summary, function($a, $b) {
544 return strtotime($b['last_seen']) - strtotime($a['last_seen']);
545 });
546 ?>
547 <div style="overflow-x: auto; margin-bottom: 20px;">
548 <table class="wp-list-table widefat fixed striped" style="background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border);">
549 <thead>
550 <tr style="background: var(--dashboard-card-bg);">
551 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Error Category</th>
552 <th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Error Code</th>
553 <th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Count</th>
554 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Last Occurred</th>
555 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Message</th>
556 </tr>
557 </thead>
558 <tbody>
559 <?php foreach ($error_summary as $key => $error): ?>
560 <tr>
561 <td style="padding: 10px 12px; color: var(--dashboard-text-primary);">
562 <strong><?php echo esc_html($error['category']); ?></strong>
563 </td>
564 <td style="padding: 10px 12px; text-align: center; color: var(--dashboard-text-secondary); font-family: monospace;">
565 <code style="background: rgba(255, 255, 255, 0.1); padding: 2px 6px; border-radius: 3px;"><?php echo esc_html($error['code']); ?></code>
566 </td>
567 <td style="padding: 10px 12px; text-align: center;">
568 <span style="display: inline-block; background: var(--dashboard-accent); color: #ffffff; padding: 4px 10px; border-radius: 12px; font-weight: 600; font-size: 13px;">
569 <?php echo esc_html(number_format($error['count'])); ?>
570 </span>
571 </td>
572 <td style="padding: 10px 12px; color: var(--dashboard-text-secondary); font-size: 13px;">
573 <?php
574 $last_seen = strtotime($error['last_seen']);
575 $time_diff = human_time_diff($last_seen, current_time('timestamp'));
576 echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . $time_diff . ' ago)</span>';
577 ?>
578 </td>
579 <td style="padding: 10px 12px; color: var(--dashboard-text-primary); max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<?php echo esc_attr($error['message']); ?>">
580 <?php echo esc_html($error['message']); ?>
581 </td>
582 </tr>
583 <?php endforeach; ?>
584 </tbody>
585 </table>
586 </div>
587
588 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
589 <input type="hidden" name="clear_error_summary" value="yes" />
590 <?php wp_nonce_field('metasync_clear_error_summary_nonce', 'clear_error_summary_nonce'); ?>
591 <button type="submit" class="button button-secondary" style="background: #dc3232; color: #ffffff; border: none; padding: 8px 16px; border-radius: 4px; font-weight: 500; cursor: pointer;">
592 🗑 Clear Error Summary
593 </button>
594 </form>
595 <?php
596 } else {
597 ?>
598 <div class="dashboard-empty-state" style="padding: 30px; text-align: center; background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border); border-radius: 8px;">
599 <p style="color: var(--dashboard-text-secondary); font-style: italic; margin: 0;">
600
601 No errors recorded yet. Error summary will appear here once errors are logged.
602 </p>
603 </div>
604 <?php
605 }
606 } else {
607 ?>
608 <div class="dashboard-empty-state" style="padding: 30px; text-align: center; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px;">
609 <p style="color: var(--dashboard-text-primary); margin: 0;">
610 ⚠️ Error Logger class not available. Please ensure the plugin is properly loaded.
611 </p>
612 </div>
613 <?php
614 }
615 ?>
616 </div>
617
618 <!-- Error Log Management -->
619 <div style="margin-bottom: 30px;">
620 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Clear Error Logs</h4>
621 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Clear WordPress error logs to free up space and remove old entries.</p>
622
623 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
624 <input type="hidden" name="clear_log" value="yes" />
625 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
626 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto; min-width: 240px; max-width: fit-content;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
627 🧹 Clear Error Logs
628 </button>
629 </form>
630 </div>
631
632 <!-- WordPress Debug Settings -->
633 <div style="margin-bottom: 30px;">
634 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">WordPress Debug Configuration</h4>
635 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Configure WordPress debug settings to control error logging and display.</p>
636
637 <form method="post">
638
639 <?php
640 $wp_debug = defined('WP_DEBUG') && WP_DEBUG;
641 $debug_log = defined('WP_DEBUG_LOG') && WP_DEBUG_LOG;
642 $debug_display = defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY;
643 ?>
644
645 <p><strong>Current WordPress Debug Status:</strong></p>
646 <ul>
647 <li>WP_DEBUG: <?php echo $wp_debug ? '�
648 Enabled' : ' Disabled'; ?></li>
649 <li>WP_DEBUG_LOG: <?php echo $debug_log ? '�
650 Enabled' : '❌ Disabled'; ?></li>
651 <li>WP_DEBUG_DISPLAY: <?php echo $debug_display ? '�
652 Enabled' : ' Disabled'; ?></li>
653 </ul>
654
655 <?php if (!$wp_debug): ?>
656 <p style="color: var(--dashboard-accent);">💡 To enable error logging, add these lines to your wp-config.php file:</p>
657 <pre style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 10px; border-radius: 4px; color: var(--dashboard-text-primary);">define('WP_DEBUG', true);
658 define('WP_DEBUG_LOG', true);
659 define('WP_DEBUG_DISPLAY', false);</pre>
660 <?php endif; ?>
661 </form>
662 </div>
663
664 <!-- Error Log Display -->
665 <div style="margin-bottom: 30px;">
666 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Error Log Contents</h4>
667 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View the current error log entries for troubleshooting and monitoring.</p>
668
669 <?php
670 $error_logs = new Metasync_Error_Logs();
671
672 if ($error_logs->can_show_error_logs()):
673 $log_content = $error_logs->get_error_logs(50);
674
675 if (!empty(trim($log_content))):
676 $error_logs->show_copy_button();
677 $error_logs->show_logs();
678 $error_logs->show_info();
679 else: ?>
680 <div class="dashboard-empty-state">
681 <p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">�
682 Log file is empty - no errors recorded.</p>
683 </div>
684 <?php endif;
685 else:
686 $error_message = $error_logs->get_error_message();
687 if (!empty($error_message)): ?>
688 <div class="dashboard-empty-state">
689 <p style="color: var(--dashboard-text-primary); font-weight: bold; text-align: center; padding: 20px; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 4px; margin: 20px 0;">
690 ⚠️ <?php echo esc_html($error_message); ?>
691 </p>
692 </div>
693 <?php else: ?>
694 <div class="dashboard-empty-state">
695 <p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">⚠️ Unable to access error log file. Please check permissions.</p>
696 </div>
697 <?php endif;
698 endif; ?>
699 </div>
700 <?php
701 }
702
703 // ------------------------------------------------------------------
704 // Form-post handlers
705 // ------------------------------------------------------------------
706
707 /**
708 * Handle debug mode operations (enable/disable/extend).
709 */
710 public function handle_debug_mode_operations()
711 {
712 if (isset($_POST['metasync_debug_mode_action_advanced'])) {
713 if (!wp_verify_nonce($_POST['metasync_debug_mode_nonce_advanced'], 'metasync_debug_mode_action_advanced')) {
714 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1');
715 wp_redirect($redirect_url);
716 exit;
717 }
718
719 if (!current_user_can('manage_options')) {
720 wp_die('Unauthorized');
721 }
722
723 if (!class_exists('Metasync_Debug_Mode_Manager')) {
724 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1&msg=manager_not_available');
725 wp_redirect($redirect_url);
726 exit;
727 }
728
729 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
730 $action = sanitize_text_field($_POST['action_type'] ?? '');
731 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced');
732
733 switch ($action) {
734 case 'enable':
735 $indefinite = isset($_POST['indefinite']) && $_POST['indefinite'] === '1';
736 $result = $debug_manager->enable_debug_mode($indefinite);
737
738 $redirect_url = add_query_arg('debug_mode_enabled', '1', $redirect_url);
739 if ($indefinite) {
740 $redirect_url = add_query_arg('indefinite', '1', $redirect_url);
741 }
742 break;
743
744 case 'disable':
745 $result = $debug_manager->disable_debug_mode('manual');
746 if ($result) {
747 $redirect_url = add_query_arg('debug_mode_disabled', '1', $redirect_url);
748 } else {
749 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
750 }
751 break;
752
753 case 'extend':
754 $result = $debug_manager->extend_debug_mode();
755
756 $redirect_url = add_query_arg('debug_mode_extended', '1', $redirect_url);
757 break;
758
759 default:
760 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
761 break;
762 }
763
764 wp_redirect($redirect_url);
765 exit;
766 }
767 }
768
769 /**
770 * Handle error log operations (clear).
771 */
772 public function handle_error_log_operations()
773 {
774 if (isset($_POST['clear_log'])) {
775 if (wp_verify_nonce($_POST['clear_log_nonce'], 'metasync_clear_log_nonce')) {
776 $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
777
778 if (file_exists($log_file)) {
779 file_put_contents($log_file, '');
780
781 $backup_files = glob(WP_CONTENT_DIR . '/metasync_data/plugin_errors.log.old.*');
782 if ($backup_files) {
783 foreach ($backup_files as $backup_file) {
784 @unlink($backup_file);
785 }
786 }
787 }
788
789 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&log_cleared=1');
790 wp_redirect($redirect_url);
791 exit;
792 } else {
793 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_error=1');
794 wp_redirect($redirect_url);
795 exit;
796 }
797 }
798
799 if (isset($_POST['clear_error_summary']) && isset($_POST['clear_error_summary_nonce'])) {
800 if (wp_verify_nonce($_POST['clear_error_summary_nonce'], 'metasync_clear_error_summary_nonce')) {
801 if (class_exists('Metasync_Error_Logger')) {
802 Metasync_Error_Logger::clear_error_summary();
803 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_cleared=1');
804 wp_redirect($redirect_url);
805 exit;
806 } else {
807 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
808 wp_redirect($redirect_url);
809 exit;
810 }
811 } else {
812 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
813 wp_redirect($redirect_url);
814 exit;
815 }
816 }
817 }
818
819 /**
820 * Handle clear all settings operations.
821 */
822 public function handle_clear_all_settings()
823 {
824 if (isset($_POST['clear_all_settings'])) {
825 if (wp_verify_nonce($_POST['clear_all_settings_nonce'], 'metasync_clear_all_settings_nonce')) {
826
827 $metasync_options_to_clear = [
828 'metasync_options',
829 'metasync_options_instant_indexing',
830 'metasync_options_bing_instant_indexing',
831 'metasync_otto_crawldata',
832 'metasync_logging_data',
833 'metasync_wp_sa_connect_token',
834 'wp_debug_enabled',
835 'wp_debug_log_enabled',
836 'wp_debug_display_enabled',
837 ];
838
839 $cleared_count = 0;
840 foreach ($metasync_options_to_clear as $option_name) {
841 if (get_option($option_name) !== false) {
842 delete_option($option_name);
843 $cleared_count++;
844 }
845 }
846
847 $transients_to_clear = [
848 'metasync_heartbeat_status_cache',
849 ];
850
851 foreach ($transients_to_clear as $transient_name) {
852 delete_transient($transient_name);
853 }
854
855 $timestamp = wp_next_scheduled('metasync_heartbeat_cron_check');
856 if ($timestamp) {
857 wp_unschedule_event($timestamp, 'metasync_heartbeat_cron_check');
858 }
859
860 $new_plugin_auth_token = wp_generate_password(32, false, false);
861
862 $fresh_options = [
863 'general' => [
864 'apikey' => $new_plugin_auth_token
865 ]
866 ];
867
868 update_option('metasync_options', $fresh_options);
869
870 Metasync::log_api_key_event('settings_reset', 'plugin_auth_token', array(
871 'options_cleared_count' => $cleared_count,
872 'new_token_prefix' => substr($new_plugin_auth_token, 0, 8) . '...',
873 'triggered_by' => 'settings_reset_action'
874 ), 'info');
875
876 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&settings_cleared=1');
877 wp_redirect($redirect_url);
878 exit;
879 } else {
880 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
881 wp_redirect($redirect_url);
882 exit;
883 }
884 }
885 }
886
887 // ------------------------------------------------------------------
888 // Log-file helpers
889 // ------------------------------------------------------------------
890
891 /**
892 * Get error log content for display.
893 */
894 public function get_error_log_content()
895 {
896 $execution_time = $this->get_execution_setting('max_execution_time');
897 if (function_exists('set_time_limit')) {
898 @set_time_limit($execution_time);
899 }
900
901 $log_file = WP_CONTENT_DIR . '/debug.log';
902
903 if (!file_exists($log_file) || !is_readable($log_file)) {
904 return false;
905 }
906
907 $batch_size = $this->get_execution_setting('log_batch_size');
908
909 $file_size = filesize($log_file);
910 if ($file_size > 10 * 1024 * 1024) {
911 return $this->get_log_tail($log_file, $batch_size);
912 }
913
914 $content = file_get_contents($log_file);
915 if ($content === false) {
916 return false;
917 }
918
919 $lines = explode("\n", $content);
920 $recent_lines = array_slice($lines, -$batch_size);
921
922 return implode("\n", $recent_lines);
923 }
924
925 /**
926 * Memory-efficient function to get last N lines from a large file.
927 */
928 public function get_log_tail($file_path, $lines = null)
929 {
930 $execution_time = $this->get_execution_setting('max_execution_time');
931 if (function_exists('set_time_limit')) {
932 @set_time_limit($execution_time);
933 }
934
935 if ($lines === null) {
936 $lines = $this->get_execution_setting('log_batch_size');
937 }
938
939 $handle = fopen($file_path, 'r');
940 if (!$handle) {
941 return false;
942 }
943
944 fseek($handle, -1, SEEK_END);
945
946 $result_lines = array();
947 $line = '';
948 $line_count = 0;
949
950 while (ftell($handle) > 0 && $line_count < $lines) {
951 $char = fgetc($handle);
952
953 if ($char === "\n") {
954 if (!empty($line)) {
955 array_unshift($result_lines, strrev($line));
956 $line = '';
957 $line_count++;
958 }
959 } else {
960 $line .= $char;
961 }
962
963 fseek($handle, -2, SEEK_CUR);
964 }
965
966 if (!empty($line) && $line_count < $lines) {
967 array_unshift($result_lines, strrev($line));
968 }
969
970 fclose($handle);
971
972 return implode("\n", $result_lines);
973 }
974 }
975