PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.7
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.7
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.6.7, at includes/class-metasync-debug-manager.php

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