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
← All changes | includes/class-metasync-debug-manager.php +172 -50 2.6.8 → 2.7.2 View file →
@@ -72,14 +72,23 @@
72 72 *
73 73 * @param Metasync_Admin $admin The admin instance (needed for header/nav rendering).
74 74 */
75 75 public function metasync_display_error_log($admin) {
76 - $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
76 + $log_file = Metasync_Data_Store::file_path('plugin_errors.log');
77 + if (false === $log_file) {
78 + $log_file = '';
79 + }
77 80 if (!Metasync::current_user_has_plugin_access()) {
78 81 return;
79 82 }
83 +
84 + $can_manage_debug_settings = current_user_can('manage_options');
85 + if (isset($_POST['wp_debug_nonce']) && !$can_manage_debug_settings) {
86 + return;
87 + }
80 88
81 - if (isset($_POST['wp_debug_log_enabled']) &&
89 + if ($can_manage_debug_settings &&
90 + isset($_POST['wp_debug_log_enabled']) &&
82 91 isset($_POST['wp_debug_enabled']) &&
83 92 isset($_POST['wp_debug_display_enabled']) &&
84 93 isset($_POST['wp_debug_nonce']) &&
85 94 wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
@@ -92,20 +101,30 @@
92 101 update_option('wp_debug_log_enabled', $wp_debug_log);
93 102 update_option('wp_debug_display_enabled', $wp_debug_display);
94 103
95 104 $data = new ConfigControllerMetaSync();
96 - $data->store();
97 -
105 + $saved = $data->store();
106 +
107 + if ($saved) {
108 + add_settings_error(
109 + 'metasync_messages',
110 + 'metasync_message',
111 + 'WordPress debug settings updated successfully.',
112 + 'updated'
113 + );
114 + } else {
115 + add_settings_error(
116 + 'metasync_messages',
117 + 'metasync_message',
118 + 'Debug settings were saved, but wp-config.php could not be updated: '
119 + . ($data->getConfigError() !== '' ? $data->getConfigError() : 'the file is not writable.'),
120 + 'error'
121 + );
122 + }
123 + } elseif ($can_manage_debug_settings && isset($_POST['wp_debug_nonce']) && !wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
98 124 add_settings_error(
99 125 'metasync_messages',
100 126 '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 127 'Security verification failed. Please try again.',
109 128 'error'
110 129 );
111 130 }
@@ -218,9 +237,9 @@
218 237 * Update wp-config.php debug constants.
219 238 */
220 239 public function metasync_update_wp_config() {
221 240 $wp_config_path = ABSPATH . 'wp-config.php';
222 - if (file_exists($wp_config_path) && is_writable($wp_config_path)) {
241 + if (file_exists($wp_config_path) && wp_is_writable($wp_config_path)) {
223 242 $config_file = file_get_contents($wp_config_path);
224 243
225 244 $wp_debug_enabled = get_option('wp_debug_enabled', 'false') === 'true' ? 'true' : 'false';
226 245 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
@@ -242,8 +261,14 @@
242 261 } else {
243 262 $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);
244 263 }
245 264
265 + // ABSPATH is intentional and verified: the Debug Mode feature
266 + // toggles WP_DEBUG constants in wp-config.php itself, the one
267 + // file whose location ABSPATH exists to describe. It is not a
268 + // plugin-directory or generated-content write, so it stays here
269 + // (Plugin Check's ABSPATHDetected warning is a false positive —
270 + // wp-config.php cannot live in the uploads directory).
246 271 file_put_contents($wp_config_path, $config_file);
247 272 } else {
248 273 wp_die('The wp-config.php file is not writable. Please check the file permissions.');
249 274 }
@@ -280,9 +305,15 @@
280 305 }
281 306 }
282 307
283 308 if ($changed) {
284 - require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
309 + if (!current_user_can('manage_options')) {
310 + return;
311 + }
312 +
313 + if (!class_exists('Metasync_Activator')) {
314 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
315 + }
285 316 Metasync_Activator::sync_plugin_file_headers();
286 317 }
287 318 }
288 319
@@ -310,9 +341,9 @@
310 341 $status = $debug_manager->get_status();
311 342 ?>
312 343
313 344 <!-- Debug Mode Status Overview -->
314 - <div style="margin-bottom: 30px;">
345 + <div style="margin-bottom: 30px; padding-top: 20px;">
315 346 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Current Status</h4>
316 347 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 20px; border-radius: 8px;">
317 348 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
318 349 <!-- Status Badge -->
@@ -369,9 +400,9 @@
369 400 <!-- Debug Mode Controls -->
370 401 <div style="margin-bottom: 30px;">
371 402 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Controls</h4>
372 403
373 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>">
404 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>">
374 405 <input type="hidden" name="metasync_debug_mode_action_advanced" value="1" />
375 406 <?php wp_nonce_field('metasync_debug_mode_action_advanced', 'metasync_debug_mode_nonce_advanced'); ?>
376 407
377 408 <?php if (!$status['enabled']): ?>
@@ -467,9 +498,9 @@
467 498 }
468 499 });
469 500
470 501 <?php if ($status['enabled'] && !$status['indefinite']): ?>
471 - var initialTimeRemaining = <?php echo $status['time_remaining']; ?>;
502 + var initialTimeRemaining = <?php echo (int) $status['time_remaining']; ?>;
472 503 var hasReloaded = false;
473 504
474 505 function updateDebugTimeRemaining() {
475 506 if (initialTimeRemaining <= 0 || hasReloaded) {
@@ -476,12 +507,12 @@
476 507 return;
477 508 }
478 509
479 510 $.ajax({
480 - url: '<?php echo rest_url('metasync/v1/debug-mode/status'); ?>',
511 + url: '<?php echo esc_url(rest_url('metasync/v1/debug-mode/status')); ?>',
481 512 method: 'GET',
482 513 beforeSend: function(xhr) {
483 - xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
514 + xhr.setRequestHeader('X-WP-Nonce', '<?php echo esc_attr(wp_create_nonce('wp_rest')); ?>');
484 515 },
485 516 success: function(response) {
486 517 console.log('MetaSync Debug Mode Status:', response);
487 518
@@ -523,15 +554,15 @@
523 554 public function render_error_log_content()
524 555 {
525 556 ?>
526 557 <!-- Error Summary Section -->
527 - <div style="margin-bottom: 30px;">
558 + <div style="margin-bottom: 30px; padding-top: 20px;">
528 559 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Error Summary</h4>
529 560 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View categorized error statistics with counts and last occurrence times.</p>
530 561
531 562 <?php
532 563 if (class_exists('Metasync_Error_Logger')) {
533 - $error_summary = Metasync_Error_Logger::get_error_summary();
564 + $error_summary = Metasync_Error_Logger::get_visible_error_summary();
534 565
535 566 if (!empty($error_summary) && is_array($error_summary)) {
536 567 uasort($error_summary, function($a, $b) {
537 568 return strtotime($b['last_seen']) - strtotime($a['last_seen']);
@@ -551,9 +582,9 @@
551 582 <tbody>
552 583 <?php foreach ($error_summary as $key => $error): ?>
553 584 <tr>
554 585 <td style="padding: 10px 12px; color: var(--dashboard-text-primary);">
555 - <strong><?php echo esc_html($error['category']); ?></strong>
586 + <strong><?php echo esc_html(Metasync_Error_Logger::get_display_label($error['category'])); ?></strong>
556 587 </td>
557 588 <td style="padding: 10px 12px; text-align: center; color: var(--dashboard-text-secondary); font-family: monospace;">
558 589 <code style="background: rgba(255, 255, 255, 0.1); padding: 2px 6px; border-radius: 3px;"><?php echo esc_html($error['code']); ?></code>
559 590 </td>
@@ -565,9 +596,9 @@
565 596 <td style="padding: 10px 12px; color: var(--dashboard-text-secondary); font-size: 13px;">
566 597 <?php
567 598 $last_seen = strtotime($error['last_seen']);
568 599 $time_diff = human_time_diff($last_seen, current_time('timestamp'));
569 - echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . $time_diff . ' ago)</span>';
600 + echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . esc_html($time_diff) . ' ago)</span>';
570 601 ?>
571 602 </td>
572 603 <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']); ?>">
573 604 <?php echo esc_html($error['message']); ?>
@@ -577,9 +608,9 @@
577 608 </tbody>
578 609 </table>
579 610 </div>
580 611
581 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
612 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>" style="margin-bottom: 20px;">
582 613 <input type="hidden" name="clear_error_summary" value="yes" />
583 614 <?php wp_nonce_field('metasync_clear_error_summary_nonce', 'clear_error_summary_nonce'); ?>
584 615 <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;">
585 616 <span class="dashicons dashicons-trash" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Clear Error Summary
@@ -611,9 +642,9 @@
611 642 <div style="margin-bottom: 30px;">
612 643 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Clear Error Logs</h4>
613 644 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Clear WordPress error logs to free up space and remove old entries.</p>
614 645
615 - <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
646 + <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced')); ?>" style="margin-bottom: 20px;">
616 647 <input type="hidden" name="clear_log" value="yes" />
617 648 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
618 649 <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)';">
619 650 🧹 Clear Error Logs
@@ -642,11 +673,41 @@
642 673 </ul>
643 674
644 675 <?php if (!$wp_debug): ?>
645 676 <p style="color: var(--dashboard-accent);">💡 To enable error logging, add these lines to your wp-config.php file:</p>
646 - <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);
677 + <div class="metasync-copy-wrap"><pre class="metasync-copy-snippet" 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);
647 678 define('WP_DEBUG_LOG', true);
648 -define('WP_DEBUG_DISPLAY', false);</pre>
679 +define('WP_DEBUG_DISPLAY', false);</pre><span class="metasync-copy-badge">Copy</span></div>
680 + <?php /* click-to-copy for the wp-config snippet above */ ?>
681 + <style>
682 + .metasync-copy-wrap { position: relative; }
683 + .metasync-copy-snippet { cursor: pointer; outline: 1px solid transparent; transition: outline-color .15s ease; }
684 + .metasync-copy-wrap:hover .metasync-copy-snippet { outline-color: var(--dashboard-border, #374151); }
685 + .metasync-copy-badge { position: absolute; top: 8px; right: 10px; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 4px; pointer-events: none; opacity: 0; transition: opacity .15s ease; background: var(--dashboard-border, #374151); color: var(--dashboard-text-primary, #fff); }
686 + .metasync-copy-wrap:hover .metasync-copy-badge { opacity: .85; }
687 + .metasync-copy-badge.metasync-copy-done { opacity: 1; background: var(--dashboard-gradient-primary, linear-gradient(135deg, #667eea 0%, #764ba2 100%)); color: #fff; }
688 + </style>
689 + <script>
690 + (function(){
691 + if (window.__metasyncCopyInit) return; window.__metasyncCopyInit = true;
692 + function fallback(text){ var ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.opacity='0'; document.body.appendChild(ta); ta.focus(); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); }
693 + function attach(){
694 + document.querySelectorAll('.metasync-copy-snippet').forEach(function(pre){
695 + if (pre.dataset.copyBound) return; pre.dataset.copyBound='1';
696 + pre.setAttribute('title','Click to copy');
697 + pre.addEventListener('click', function(){
698 + var text = pre.textContent.trim();
699 + var wrap = pre.closest('.metasync-copy-wrap') || pre.parentNode;
700 + var badge = wrap.querySelector('.metasync-copy-badge');
701 + var done = function(){ if(!badge) return; badge.textContent='Copied!'; badge.classList.add('metasync-copy-done'); clearTimeout(badge._t); badge._t=setTimeout(function(){ badge.textContent='Copy'; badge.classList.remove('metasync-copy-done'); },1500); };
702 + if (navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).then(done).catch(function(){ fallback(text); done(); }); }
703 + else { fallback(text); done(); }
704 + });
705 + });
706 + }
707 + if (document.readyState==='loading'){ document.addEventListener('DOMContentLoaded', attach); } else { attach(); }
708 + })();
709 + </script>
649 710 <?php endif; ?>
650 711 </form>
651 712 </div>
652 713
@@ -699,9 +760,9 @@
699 760 {
700 761 if (isset($_POST['metasync_debug_mode_action_advanced'])) {
701 762 if (!wp_verify_nonce($_POST['metasync_debug_mode_nonce_advanced'], 'metasync_debug_mode_action_advanced')) {
702 763 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1');
703 - wp_redirect($redirect_url);
764 + wp_safe_redirect($redirect_url);
704 765 exit;
705 766 }
706 767
707 768 if (!current_user_can('manage_options')) {
@@ -709,9 +770,9 @@
709 770 }
710 771
711 772 if (!class_exists('Metasync_Debug_Mode_Manager')) {
712 773 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1&msg=manager_not_available');
713 - wp_redirect($redirect_url);
774 + wp_safe_redirect($redirect_url);
714 775 exit;
715 776 }
716 777
717 778 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
@@ -748,9 +809,9 @@
748 809 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
749 810 break;
750 811 }
751 812
752 - wp_redirect($redirect_url);
813 + wp_safe_redirect($redirect_url);
753 814 exit;
754 815 }
755 816 }
756 817
@@ -758,29 +819,36 @@
758 819 * Handle error log operations (clear).
759 820 */
760 821 public function handle_error_log_operations()
761 822 {
823 + if ((isset($_POST['clear_log']) || isset($_POST['clear_error_summary'])) && !current_user_can('manage_options')) {
824 + return;
825 + }
826 +
762 827 if (isset($_POST['clear_log'])) {
763 828 if (wp_verify_nonce($_POST['clear_log_nonce'], 'metasync_clear_log_nonce')) {
764 - $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
829 + $data_dir = Metasync_Data_Store::base_dir();
830 + $log_file = Metasync_Data_Store::file_path('plugin_errors.log');
765 831
766 - if (file_exists($log_file)) {
832 + if (false !== $log_file && file_exists($log_file)) {
767 833 file_put_contents($log_file, '');
768 834
769 - $backup_files = glob(WP_CONTENT_DIR . '/metasync_data/plugin_errors.log.old.*');
770 - if ($backup_files) {
771 - foreach ($backup_files as $backup_file) {
772 - @unlink($backup_file);
835 + if (false !== $data_dir) {
836 + $backup_files = glob($data_dir . '/plugin_errors.log.old.*');
837 + if ($backup_files) {
838 + foreach ($backup_files as $backup_file) {
839 + @wp_delete_file($backup_file);
840 + }
773 841 }
774 842 }
775 843 }
776 844
777 845 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&log_cleared=1');
778 - wp_redirect($redirect_url);
846 + wp_safe_redirect($redirect_url);
779 847 exit;
780 848 } else {
781 849 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_error=1');
782 - wp_redirect($redirect_url);
850 + wp_safe_redirect($redirect_url);
783 851 exit;
784 852 }
785 853 }
786 854
@@ -788,18 +856,18 @@
788 856 if (wp_verify_nonce($_POST['clear_error_summary_nonce'], 'metasync_clear_error_summary_nonce')) {
789 857 if (class_exists('Metasync_Error_Logger')) {
790 858 Metasync_Error_Logger::clear_error_summary();
791 859 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_cleared=1');
792 - wp_redirect($redirect_url);
860 + wp_safe_redirect($redirect_url);
793 861 exit;
794 862 } else {
795 863 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
796 - wp_redirect($redirect_url);
864 + wp_safe_redirect($redirect_url);
797 865 exit;
798 866 }
799 867 } else {
800 868 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
801 - wp_redirect($redirect_url);
869 + wp_safe_redirect($redirect_url);
802 870 exit;
803 871 }
804 872 }
805 873 }
@@ -808,8 +876,12 @@
808 876 * Handle clear all settings operations.
809 877 */
810 878 public function handle_clear_all_settings()
811 879 {
880 + if (isset($_POST['clear_all_settings']) && !current_user_can('manage_options')) {
881 + return;
882 + }
883 +
812 884 if (isset($_POST['clear_all_settings'])) {
813 885 if (wp_verify_nonce($_POST['clear_all_settings_nonce'], 'metasync_clear_all_settings_nonce')) {
814 886
815 887 $metasync_options_to_clear = [
@@ -821,10 +893,27 @@
821 893 'metasync_wp_sa_connect_token',
822 894 'wp_debug_enabled',
823 895 'wp_debug_log_enabled',
824 896 'wp_debug_display_enabled',
897 + // Media Optimization settings & cache
898 + 'metasync_media_optimization',
899 + 'metasync_batch_optimize_queue',
900 + 'metasync_batch_optimize_progress',
901 + 'metasync_batch_optimize_settings',
902 + // Code Minification settings
903 + 'metasync_code_minification',
825 904 ];
826 905
906 + // Capture the complete white-label identity BEFORE the
907 + // destructive clear so it can be restored afterwards. The
908 + // reset is meant to remove operational data and credentials,
909 + // not the reseller's public branding, logos, domains, custom
910 + // OTTO name, visibility/access controls, or recovery settings.
911 + if (!class_exists('Metasync_Whitelabel_Preservation')) {
912 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-whitelabel-preservation.php';
913 + }
914 + $whitelabel_state = Metasync_Whitelabel_Preservation::extract_whitelabel_state();
915 +
827 916 $cleared_count = 0;
828 917 foreach ($metasync_options_to_clear as $option_name) {
829 918 if (get_option($option_name) !== false) {
830 919 delete_option($option_name);
@@ -839,11 +928,18 @@
839 928 foreach ($transients_to_clear as $transient_name) {
840 929 delete_transient($transient_name);
841 930 }
842 931
843 - $timestamp = wp_next_scheduled('metasync_heartbeat_cron_check');
844 - if ($timestamp) {
845 - wp_unschedule_event($timestamp, 'metasync_heartbeat_cron_check');
932 + $cron_hooks_to_clear = [
933 + 'metasync_heartbeat_cron_check',
934 + 'metasync_media_batch_optimize_cron',
935 + ];
936 +
937 + foreach ($cron_hooks_to_clear as $cron_hook) {
938 + $timestamp = wp_next_scheduled($cron_hook);
939 + if ($timestamp) {
940 + wp_unschedule_event($timestamp, $cron_hook);
941 + }
846 942 }
847 943
848 944 $new_plugin_auth_token = wp_generate_password(32, false, false);
849 945
@@ -859,15 +955,41 @@
859 955 'options_cleared_count' => $cleared_count,
860 956 'new_token_prefix' => substr($new_plugin_auth_token, 0, 8) . '...',
861 957 'triggered_by' => 'settings_reset_action'
862 958 ), 'info');
863 -
959 +
960 + // Restore the preserved white-label identity over the freshly
961 + // cleared options. A failed restore must not report success.
962 + $restored = Metasync_Whitelabel_Preservation::restore_whitelabel_state($whitelabel_state);
963 + if (!$restored) {
964 + $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
965 + wp_redirect($redirect_url);
966 + exit;
967 + }
968 +
969 + // Re-synchronise the branded plugin file headers so the
970 + // WordPress plugin list and the plugin UI stay branded after
971 + // the reset. Only attempted when white-label branding exists,
972 + // and a failed sync is logged as a warning, not reported as a
973 + // reset failure: the reset itself (operational clear + fresh
974 + // token + branding restore) has already succeeded at this
975 + // point, and hosts with read-only plugin files would otherwise
976 + // never see a successful reset.
977 + if (!empty($whitelabel_state['whitelabel']) || !empty($whitelabel_state['general'])) {
978 + if (!class_exists('Metasync_Activator')) {
979 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
980 + }
981 + if (!Metasync_Activator::sync_plugin_file_headers()) {
982 + error_log('MetaSync: failed to re-synchronise branded plugin file headers after the settings reset.');
983 + }
984 + }
985 +
864 986 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&settings_cleared=1');
865 - wp_redirect($redirect_url);
987 + wp_safe_redirect($redirect_url);
866 988 exit;
867 989 } else {
868 990 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
869 - wp_redirect($redirect_url);
991 + wp_safe_redirect($redirect_url);
870 992 exit;
871 993 }
872 994 }
873 995 }
@@ -882,9 +1004,9 @@
882 1004 public function get_error_log_content()
883 1005 {
884 1006 $execution_time = $this->get_execution_setting('max_execution_time');
885 1007 if (function_exists('set_time_limit')) {
886 - @set_time_limit($execution_time);
1008 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
887 1009 }
888 1010
889 1011 $log_file = WP_CONTENT_DIR . '/debug.log';
890 1012
@@ -916,9 +1038,9 @@
916 1038 public function get_log_tail($file_path, $lines = null)
917 1039 {
918 1040 $execution_time = $this->get_execution_setting('max_execution_time');
919 1041 if (function_exists('set_time_limit')) {
920 - @set_time_limit($execution_time);
1042 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
921 1043 }
922 1044
923 1045 if ($lines === null) {
924 1046 $lines = $this->get_execution_setting('log_batch_size');
@@ -923,9 +1045,9 @@
923 1045 if ($lines === null) {
924 1046 $lines = $this->get_execution_setting('log_batch_size');
925 1047 }
926 1048
927 - $handle = fopen($file_path, 'r');
1049 + $handle = fopen($file_path, 'r'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming file I/O; WP_Filesystem cannot return a raw stream handle
928 1050 if (!$handle) {
929 1051 return false;
930 1052 }
931 1053
@@ -954,9 +1076,9 @@
954 1076 if (!empty($line) && $line_count < $lines) {
955 1077 array_unshift($result_lines, strrev($line));
956 1078 }
957 1079
958 - fclose($handle);
1080 + fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- operates on a native stream handle
959 1081
960 1082 return implode("\n", $result_lines);
961 1083 }
962 1084 }