PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
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 +128 -42 2.6.6 → 2.7.1 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 -->
@@ -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>
@@ -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,8 +893,15 @@
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
827 906 $cleared_count = 0;
828 907 foreach ($metasync_options_to_clear as $option_name) {
@@ -839,11 +918,18 @@
839 918 foreach ($transients_to_clear as $transient_name) {
840 919 delete_transient($transient_name);
841 920 }
842 921
843 - $timestamp = wp_next_scheduled('metasync_heartbeat_cron_check');
844 - if ($timestamp) {
845 - wp_unschedule_event($timestamp, 'metasync_heartbeat_cron_check');
922 + $cron_hooks_to_clear = [
923 + 'metasync_heartbeat_cron_check',
924 + 'metasync_media_batch_optimize_cron',
925 + ];
926 +
927 + foreach ($cron_hooks_to_clear as $cron_hook) {
928 + $timestamp = wp_next_scheduled($cron_hook);
929 + if ($timestamp) {
930 + wp_unschedule_event($timestamp, $cron_hook);
931 + }
846 932 }
847 933
848 934 $new_plugin_auth_token = wp_generate_password(32, false, false);
849 935
@@ -861,13 +947,13 @@
861 947 'triggered_by' => 'settings_reset_action'
862 948 ), 'info');
863 949
864 950 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&settings_cleared=1');
865 - wp_redirect($redirect_url);
951 + wp_safe_redirect($redirect_url);
866 952 exit;
867 953 } else {
868 954 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
869 - wp_redirect($redirect_url);
955 + wp_safe_redirect($redirect_url);
870 956 exit;
871 957 }
872 958 }
873 959 }
@@ -882,9 +968,9 @@
882 968 public function get_error_log_content()
883 969 {
884 970 $execution_time = $this->get_execution_setting('max_execution_time');
885 971 if (function_exists('set_time_limit')) {
886 - @set_time_limit($execution_time);
972 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
887 973 }
888 974
889 975 $log_file = WP_CONTENT_DIR . '/debug.log';
890 976
@@ -916,9 +1002,9 @@
916 1002 public function get_log_tail($file_path, $lines = null)
917 1003 {
918 1004 $execution_time = $this->get_execution_setting('max_execution_time');
919 1005 if (function_exists('set_time_limit')) {
920 - @set_time_limit($execution_time);
1006 + @set_time_limit($execution_time); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
921 1007 }
922 1008
923 1009 if ($lines === null) {
924 1010 $lines = $this->get_execution_setting('log_batch_size');
@@ -923,9 +1009,9 @@
923 1009 if ($lines === null) {
924 1010 $lines = $this->get_execution_setting('log_batch_size');
925 1011 }
926 1012
927 - $handle = fopen($file_path, 'r');
1013 + $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 1014 if (!$handle) {
929 1015 return false;
930 1016 }
931 1017
@@ -954,9 +1040,9 @@
954 1040 if (!empty($line) && $line_count < $lines) {
955 1041 array_unshift($result_lines, strrev($line));
956 1042 }
957 1043
958 - fclose($handle);
1044 + fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- operates on a native stream handle
959 1045
960 1046 return implode("\n", $result_lines);
961 1047 }
962 1048 }