Changeset 3459095
- Timestamp:
- 02/11/2026 03:42:10 PM (6 weeks ago)
- Location:
- debugger-troubleshooter/trunk
- Files:
-
- 1 added
- 4 edited
-
README.md (added)
-
assets/css/admin.css (modified) (9 diffs)
-
assets/js/admin.js (modified) (24 diffs)
-
debug-troubleshooter.php (modified) (45 diffs)
-
readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
debugger-troubleshooter/trunk/assets/css/admin.css
r3325892 r3459095 1 1 /* This is a simplified set for basic layout and styling */ 2 .flex { display: flex; } 3 .justify-between { justify-content: space-between; } 4 .items-center { align-items: center; } 5 .p-2 { padding: 0.5rem; } 6 .ml-2 { margin-left: 0.5rem; } 7 .rounded-md { border-radius: 0.375rem; } 8 .transition-colors { transition-property: background-color, border-color, color, fill, stroke; } 9 .duration-200 { transition-duration: 200ms; } 10 .bg-gray-100 { background-color: #f3f4f6; } 11 .hover\:bg-gray-200:hover { background-color: #e5e7eb; } 12 .text-gray-700 { color: #374151; } 13 .text-xl { font-size: 1.25rem; } 14 .font-bold { font-weight: 700; } 15 .mb-4 { margin-bottom: 1rem; } 16 .mb-6 { margin-bottom: 1.5rem; } 17 .fixed { position: fixed; } 18 .inset-0 { top: 0; right: 0; bottom: 0; left: 0; } 19 .bg-gray-600 { background-color: #4b5563; } 20 .bg-opacity-50 { background-color: rgba(75, 85, 99, 0.5); } 21 .z-50 { z-index: 50; } 22 .max-w-sm { max-width: 24rem; } 23 .w-full { width: 100%; } 24 .text-center { text-align: center; } 25 .bg-white { background-color: #fff; margin-left: auto; margin-right: auto; } 26 .p-6 { padding: 1.5rem; } 27 .shadow-xl { box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); } 28 .hidden { display: none; } 2 .flex { 3 display: flex; 4 } 5 6 .justify-between { 7 justify-content: space-between; 8 } 9 10 .items-center { 11 align-items: center; 12 } 13 14 .p-2 { 15 padding: 0.5rem; 16 } 17 18 .ml-2 { 19 margin-left: 0.5rem; 20 } 21 22 .rounded-md { 23 border-radius: 0.375rem; 24 } 25 26 .transition-colors { 27 transition-property: background-color, border-color, color, fill, stroke; 28 } 29 30 .duration-200 { 31 transition-duration: 200ms; 32 } 33 34 .bg-gray-100 { 35 background-color: #f3f4f6; 36 } 37 38 .hover\:bg-gray-200:hover { 39 background-color: #e5e7eb; 40 } 41 42 .text-gray-700 { 43 color: #374151; 44 } 45 46 .text-xl { 47 font-size: 1.25rem; 48 } 49 50 .font-bold { 51 font-weight: 700; 52 } 53 54 .mb-4 { 55 margin-bottom: 1rem; 56 } 57 58 .mb-6 { 59 margin-bottom: 1.5rem; 60 } 61 62 .fixed { 63 position: fixed; 64 } 65 66 .inset-0 { 67 top: 0; 68 right: 0; 69 bottom: 0; 70 left: 0; 71 } 72 73 .bg-gray-600 { 74 background-color: #4b5563; 75 } 76 77 .bg-opacity-50 { 78 background-color: rgba(75, 85, 99, 0.5); 79 } 80 81 .z-50 { 82 z-index: 50; 83 } 84 85 .max-w-sm { 86 max-width: 24rem; 87 } 88 89 .w-full { 90 width: 100%; 91 } 92 93 .text-center { 94 text-align: center; 95 } 96 97 .bg-white { 98 background-color: #fff; 99 margin-left: auto; 100 margin-right: auto; 101 } 102 103 .p-6 { 104 padding: 1.5rem; 105 } 106 107 .shadow-xl { 108 box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); 109 } 110 111 .hidden { 112 display: none; 113 !important 114 } 29 115 30 116 /* Custom styles for the plugin UI */ 117 #debug-troubleshoot-alert-modal { 118 z-index: 60 !important; 119 } 120 121 #debug-troubleshoot-confirm-modal { 122 z-index: 50 !important; 123 } 124 31 125 .debug-troubleshooter-wrap { 32 margin-top: 20px; 33 } 126 margin-top: 20px; 127 } 128 34 129 .debug-troubleshooter-content { 35 display: grid; 36 grid-template-columns: 1fr; 37 gap: 20px; 38 margin-top: 20px; 39 } 40 @media (min-width: 782px) { /* WordPress admin breakpoint */ 41 .debug-troubleshooter-content { 42 grid-template-columns: 1fr 1fr; 43 } 44 .full-width-section { 45 grid-column: 1 / -1; 46 } 47 } 130 display: grid; 131 grid-template-columns: 1fr; 132 gap: 20px; 133 margin-top: 20px; 134 } 135 136 @media (min-width: 782px) { 137 138 /* WordPress admin breakpoint */ 139 .debug-troubleshooter-content { 140 grid-template-columns: 1fr 1fr; 141 } 142 143 .full-width-section { 144 grid-column: 1 / -1; 145 } 146 } 147 48 148 .debug-troubleshooter-section { 49 background: #fff; 50 border: 1px solid #c3c4c7; 51 box-shadow: 0 1px 1px rgba(0,0,0,.04); 52 padding: 0; 53 border-radius: 5px; 54 } 149 background: #fff; 150 border: 1px solid #c3c4c7; 151 box-shadow: 0 1px 1px rgba(0, 0, 0, .04); 152 padding: 0; 153 border-radius: 5px; 154 } 155 55 156 .debug-troubleshooter-section.standalone-section { 56 157 padding: 20px; 57 158 } 159 58 160 .section-header { 59 161 display: flex; … … 63 165 border-bottom: 1px solid #c3c4c7; 64 166 } 167 65 168 .section-header h2 { 66 169 margin: 0 !important; … … 69 172 font-size: 1.3em; 70 173 } 174 71 175 .section-content { 72 176 padding: 20px; 73 177 } 178 74 179 .standalone-section .section-header { 75 180 padding: 0 0 15px 0; … … 86 191 87 192 .debug-troubleshooter-card { 88 background: #fff; 89 border: 1px solid #e0e0e0; 90 padding: 0; 91 border-radius: 4px; 92 margin-bottom: 15px; 93 } 193 background: #fff; 194 border: 1px solid #e0e0e0; 195 padding: 0; 196 border-radius: 4px; 197 margin-bottom: 15px; 198 } 199 94 200 .debug-troubleshooter-card:last-child { 95 margin-bottom: 0; 96 } 201 margin-bottom: 0; 202 } 203 97 204 .debug-troubleshooter-card h3 { 98 margin: 0; 99 font-size: 1.1em; 100 color: #2c3338; 101 } 205 margin: 0; 206 font-size: 1.1em; 207 color: #2c3338; 208 } 209 102 210 .debug-troubleshooter-card h4 { 103 211 font-size: 1em; … … 108 216 border-bottom: 1px solid #eee; 109 217 } 218 110 219 .debug-troubleshooter-card p, 111 220 .debug-troubleshooter-card li { 112 margin-bottom: 8px; 113 padding: 5px 0; 114 border-bottom: 1px dashed #f0f0f0; 115 font-size: 13px; 116 } 221 margin-bottom: 8px; 222 padding: 5px 0; 223 border-bottom: 1px dashed #f0f0f0; 224 font-size: 13px; 225 } 226 117 227 .debug-troubleshooter-card ul { 118 margin: 0; 119 padding: 0; 120 list-style: none; 121 } 228 margin: 0; 229 padding: 0; 230 list-style: none; 231 } 232 122 233 .debug-troubleshooter-card li:last-child { 123 border-bottom: none;234 border-bottom: none; 124 235 } 125 236 … … 153 264 text-decoration: none; 154 265 } 266 155 267 .info-sub-list-toggle:hover { 156 268 text-decoration: underline; 157 269 } 158 270 159 .status-active, .status-network-active, .status-inactive { 271 .status-active, 272 .status-network-active, 273 .status-inactive { 160 274 padding: 2px 6px; 161 275 border-radius: 3px; … … 190 304 background: #f9f9f9; 191 305 } 306 192 307 .debug-troubleshooter-card.collapsible .card-collapsible-header { 193 border-bottom: 1px solid #e0e0e0; 194 } 308 border-bottom: 1px solid #e0e0e0; 309 } 310 195 311 .card-collapsible-header:hover { 196 312 background: #f1f1f1; 197 313 } 314 198 315 .card-collapsible-header .dashicons { 199 316 transition: transform 0.2s ease-in-out; 200 317 } 318 201 319 .card-collapsible-header.collapsed .dashicons { 202 320 transform: rotate(-90deg); 203 321 } 322 204 323 .card-collapsible-content { 205 324 padding: 15px; 206 325 } 326 207 327 .card-collapsible-content.hidden { 208 328 display: none; 209 329 } 330 210 331 .debug-troubleshooter-card:not(.collapsible) { 211 332 padding: 15px; 212 333 } 334 213 335 .debug-troubleshooter-card:not(.collapsible) h3 { 214 336 padding-bottom: 5px; … … 218 340 219 341 #troubleshoot-mode-controls { 220 margin-top: 20px; 221 padding-top: 20px; 222 border-top: 1px solid #eee; 223 } 342 margin-top: 20px; 343 padding-top: 20px; 344 border-top: 1px solid #eee; 345 } 346 224 347 #troubleshoot-theme-select { 225 width: 100%; 226 max-width: 300px; 227 } 348 width: 100%; 349 max-width: 300px; 350 } 351 228 352 .plugin-list { 229 max-height: 400px; /* Scrollable area for plugins */ 230 overflow-y: auto; 231 border: 1px solid #e0e0e0; 232 border-radius: 4px; 233 padding: 10px; 234 background: #fefefe; 235 } 353 max-height: 400px; 354 /* Scrollable area for plugins */ 355 overflow-y: auto; 356 border: 1px solid #e0e0e0; 357 border-radius: 4px; 358 padding: 10px; 359 background: #fefefe; 360 } 361 236 362 .plugin-list .plugin-item { 237 display: flex; 238 align-items: center; 239 padding: 8px 10px; 240 border-bottom: 1px solid #eee; 241 } 363 display: flex; 364 align-items: center; 365 padding: 8px 10px; 366 border-bottom: 1px solid #eee; 367 } 368 242 369 .plugin-list .plugin-item:last-child { 243 border-bottom: none; 244 } 370 border-bottom: none; 371 } 372 245 373 .plugin-list .plugin-item:hover { 246 background-color: #f8f8f8; 247 } 374 background-color: #f8f8f8; 375 } 376 248 377 .plugin-list .plugin-item input[type="checkbox"] { 249 margin-right: 10px; 250 } 378 margin-right: 10px; 379 } 380 251 381 .button-danger { 252 382 background: #dc3232; 253 383 border-color: #dc3232; 254 384 color: #fff; 255 box-shadow: 0 1px 0 rgba(0,0,0,.08); 256 } 385 box-shadow: 0 1px 0 rgba(0, 0, 0, .08); 386 } 387 257 388 .button-danger:hover { 258 389 background: #e25b5b; … … 260 391 color: #fff; 261 392 } 393 262 394 .debug-troubleshoot-notice { 263 border-left-color: #fbc02d !important; /* Yellow for warning */ 395 border-left-color: #fbc02d !important; 396 /* Yellow for warning */ 264 397 } 265 398 266 399 /* Debug Log Viewer */ 267 400 .debug-log-viewer-wrapper { 268 margin-top: 20px; 269 } 401 margin-top: 20px; 402 } 403 270 404 .debug-log-header { 271 display: flex; 272 justify-content: space-between; 273 align-items: center; 274 margin-bottom: 10px; 275 } 405 display: flex; 406 justify-content: space-between; 407 align-items: center; 408 margin-bottom: 10px; 409 } 410 276 411 .debug-log-header h3 { 277 margin: 0; 278 font-size: 1.2em; 279 } 412 margin: 0; 413 font-size: 1.2em; 414 } 415 280 416 #debug-log-viewer { 281 width: 100%;282 background-color: #282c34;283 color: #abb2bf;284 font-family: monospace;285 font-size: 13px;286 line-height: 1.5;287 border: 1px solid #ccc;288 border-radius: 4px;289 padding: 10px;290 white-space: pre;291 overflow-wrap: normal;292 overflow-x: auto;417 width: 100%; 418 background-color: #282c34; 419 color: #abb2bf; 420 font-family: monospace; 421 font-size: 13px; 422 line-height: 1.5; 423 border: 1px solid #ccc; 424 border-radius: 4px; 425 padding: 10px; 426 white-space: pre; 427 overflow-wrap: normal; 428 overflow-x: auto; 293 429 } 294 430 -
debugger-troubleshooter/trunk/assets/js/admin.js
r3400206 r3459095 1 jQuery(document).ready(function ($) {1 jQuery(document).ready(function ($) { 2 2 var isTroubleshooting = debugTroubleshoot.is_troubleshooting; 3 3 var troubleshootState = debugTroubleshoot.current_state; … … 22 22 23 23 // Close alert modal 24 $('#debug-troubleshoot-alert-close').on('click', function () {24 $('#debug-troubleshoot-alert-close').on('click', function () { 25 25 $('#debug-troubleshoot-alert-modal').addClass('hidden'); 26 26 }); 27 27 28 28 // Close confirmation modal 29 $('#debug-troubleshoot-confirm-cancel').on('click', function () {29 $('#debug-troubleshoot-confirm-cancel').on('click', function () { 30 30 $('#debug-troubleshoot-confirm-modal').addClass('hidden'); 31 31 }); … … 35 35 36 36 // Handle toggle button for troubleshooting mode 37 $('#troubleshoot-mode-toggle').on('click', function () {37 $('#troubleshoot-mode-toggle').on('click', function () { 38 38 var $button = $(this); 39 39 var enableMode = !isTroubleshooting; // Determine if we are enabling or disabling … … 49 49 enable: enableMode ? 1 : 0 50 50 }, 51 success: function (response) {51 success: function (response) { 52 52 if (response.success) { 53 53 showAlert(debugTroubleshoot.alert_title_success, response.data.message); 54 54 isTroubleshooting = enableMode; // Update state 55 55 // Refresh the page to apply cookie changes immediately 56 setTimeout(function () { location.reload(); }, 500);56 setTimeout(function () { location.reload(); }, 500); 57 57 } else { 58 58 showAlert(debugTroubleshoot.alert_title_error, response.data.message, 'error'); … … 60 60 } 61 61 }, 62 error: function () {62 error: function () { 63 63 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 64 64 $button.prop('disabled', false); … … 68 68 69 69 // Handle toggle button for Live Debug mode 70 $('#debug-mode-toggle').on('click', function () {70 $('#debug-mode-toggle').on('click', function () { 71 71 var $button = $(this); 72 72 var enableMode = !isDebugMode; … … 81 81 nonce: debugTroubleshoot.nonce, 82 82 }, 83 success: function (response) {83 success: function (response) { 84 84 if (response.success) { 85 85 showAlert(debugTroubleshoot.alert_title_success, response.data.message); … … 95 95 } 96 96 }, 97 error: function () {98 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 99 }, 100 complete: function () {97 error: function () { 98 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 99 }, 100 complete: function () { 101 101 $button.prop('disabled', false); 102 102 } … … 105 105 106 106 // Handle Clear Log button - Show confirmation modal 107 $('#clear-debug-log').on('click', function () {107 $('#clear-debug-log').on('click', function () { 108 108 var modal = $('#debug-troubleshoot-confirm-modal'); 109 109 $('#debug-troubleshoot-confirm-title').text('Confirm Action'); … … 111 111 modal.removeClass('hidden'); 112 112 }); 113 113 114 114 // Handle the actual log clearing after confirmation 115 $('#debug-troubleshoot-confirm-ok').on('click', function () {115 $('#debug-troubleshoot-confirm-ok').on('click', function () { 116 116 var $button = $('#clear-debug-log'); 117 117 $button.prop('disabled', true); 118 119 // IMMEDIATELY hide the confirm modal before showing the alert 118 120 $('#debug-troubleshoot-confirm-modal').addClass('hidden'); 119 121 120 122 $.ajax({ 121 url: debugTroubleshoot.ajax_url, 122 type: 'POST', 123 data: { 124 action: 'debug_troubleshoot_clear_debug_log', 125 nonce: debugTroubleshoot.nonce 126 }, 127 success: function(response) { 123 // ... existing ajax code ... 124 success: function (response) { 128 125 if (response.success) { 129 126 $('#debug-log-viewer').val('Debug log cleared successfully.'); … … 133 130 } 134 131 }, 135 error: function () {136 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 137 }, 138 complete: function () {132 error: function () { 133 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 134 }, 135 complete: function () { 139 136 $button.prop('disabled', false); 140 137 } … … 153 150 154 151 // Check plugins based on troubleshooting state 155 $('.plugin-list input[type="checkbox"]').each(function () {152 $('.plugin-list input[type="checkbox"]').each(function () { 156 153 var $checkbox = $(this); 157 154 var pluginFile = $checkbox.val(); … … 171 168 172 169 // Handle applying troubleshooting changes 173 $('#apply-troubleshoot-changes').on('click', function () {170 $('#apply-troubleshoot-changes').on('click', function () { 174 171 if (!isTroubleshooting) { 175 172 showAlert(debugTroubleshoot.alert_title_error, 'Please enter troubleshooting mode first.', 'error'); … … 182 179 var selectedTheme = $('#troubleshoot-theme-select').val(); 183 180 var selectedPlugins = []; 184 $('.plugin-list input[type="checkbox"]:checked').each(function () {181 $('.plugin-list input[type="checkbox"]:checked').each(function () { 185 182 selectedPlugins.push($(this).val()); 186 183 }); … … 195 192 plugins: selectedPlugins 196 193 }, 197 success: function (response) {194 success: function (response) { 198 195 if (response.success) { 199 196 showAlert(debugTroubleshoot.alert_title_success, response.data.message); 200 197 // Refresh the page to apply cookie changes immediately 201 setTimeout(function () { location.reload(); }, 500);202 } else { 203 showAlert(debugTroubleshoot.alert_title_error, response.data.message, 'error'); 204 } 205 }, 206 error: function () {207 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 208 }, 209 complete: function () {198 setTimeout(function () { location.reload(); }, 500); 199 } else { 200 showAlert(debugTroubleshoot.alert_title_error, response.data.message, 'error'); 201 } 202 }, 203 error: function () { 204 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 205 }, 206 complete: function () { 210 207 $button.prop('disabled', false).text('Apply Troubleshooting Changes'); 211 208 } … … 216 213 217 214 // Collapsible Site Info Cards 218 $('.card-collapsible-header').on('click', function () {215 $('.card-collapsible-header').on('click', function () { 219 216 var $header = $(this); 220 217 var $content = $header.siblings('.card-collapsible-content'); 221 218 222 219 $content.slideToggle(200); 223 220 $header.toggleClass('collapsed'); … … 225 222 226 223 // Toggle for theme/plugin sub-lists 227 $('.info-sub-list-toggle').on('click', function (e) {224 $('.info-sub-list-toggle').on('click', function (e) { 228 225 e.preventDefault(); 229 226 var $link = $(this); … … 241 238 242 239 // Copy Site Info to Clipboard 243 $('#copy-site-info').on('click', function (e) {240 $('#copy-site-info').on('click', function (e) { 244 241 e.stopPropagation(); // Prevent any other click events 245 242 var $button = $(this); … … 252 249 var infoList = card.querySelectorAll('p, li, h4'); 253 250 siteInfoText += '### ' + title + ' ###\n'; 254 infoList.forEach(function (item) {255 if (item.tagName.toLowerCase() === 'h4') {256 siteInfoText += '\n--- ' + item.textContent.trim() + ' ---\n';257 } else {258 var key = item.querySelector('strong') ? item.querySelector('strong').textContent.trim() : '';259 var itemClone = item.cloneNode(true);260 if (itemClone.querySelector('strong')) {261 itemClone.querySelector('strong').remove();262 }263 var value = itemClone.textContent.trim().replace(/\s+/g, ' ');264 if (key) {265 siteInfoText += key + ' ' + value + '\n';266 } else {267 siteInfoText += value + '\n';268 }269 }251 infoList.forEach(function (item) { 252 if (item.tagName.toLowerCase() === 'h4') { 253 siteInfoText += '\n--- ' + item.textContent.trim() + ' ---\n'; 254 } else { 255 var key = item.querySelector('strong') ? item.querySelector('strong').textContent.trim() : ''; 256 var itemClone = item.cloneNode(true); 257 if (itemClone.querySelector('strong')) { 258 itemClone.querySelector('strong').remove(); 259 } 260 var value = itemClone.textContent.trim().replace(/\s+/g, ' '); 261 if (key) { 262 siteInfoText += key + ' ' + value + '\n'; 263 } else { 264 siteInfoText += value + '\n'; 265 } 266 } 270 267 }); 271 268 siteInfoText += '\n'; … … 276 273 277 274 // Use modern Clipboard API 278 navigator.clipboard.writeText(siteInfoText.trim()).then(function () {275 navigator.clipboard.writeText(siteInfoText.trim()).then(function () { 279 276 var originalText = debugTroubleshoot.copy_button_text; 280 277 $button.text(debugTroubleshoot.copied_button_text); 281 setTimeout(function () {278 setTimeout(function () { 282 279 $button.text(originalText); 283 280 }, 2000); 284 }).catch(function (err) {281 }).catch(function (err) { 285 282 // Fallback for older browsers 286 283 var textArea = document.createElement("textarea"); … … 304 301 var originalText = debugTroubleshoot.copy_button_text; 305 302 $button.text(debugTroubleshoot.copied_button_text); 306 setTimeout(function () {303 setTimeout(function () { 307 304 $button.text(originalText); 308 305 }, 2000); … … 317 314 }); 318 315 // Handle User Simulation 319 $('#simulate-user-btn').on('click', function () {316 $('#simulate-user-btn').on('click', function () { 320 317 var $button = $(this); 321 318 var userId = $('#simulate-user-select').val(); … … 337 334 user_id: userId 338 335 }, 339 success: function (response) {340 if (response.success) { 341 showAlert(debugTroubleshoot.alert_title_success, response.data.message); 342 setTimeout(function () { location.reload(); }, 500);336 success: function (response) { 337 if (response.success) { 338 showAlert(debugTroubleshoot.alert_title_success, response.data.message); 339 setTimeout(function () { location.reload(); }, 500); 343 340 } else { 344 341 showAlert(debugTroubleshoot.alert_title_error, response.data.message, 'error'); … … 346 343 } 347 344 }, 348 error: function () {345 error: function () { 349 346 showAlert(debugTroubleshoot.alert_title_error, 'An AJAX error occurred.', 'error'); 350 347 $button.prop('disabled', false).text('Simulate User'); -
debugger-troubleshooter/trunk/debug-troubleshooter.php
r3400548 r3459095 4 4 * Plugin URI: https://wordpress.org/plugins/debugger-troubleshooter 5 5 * Description: A WordPress plugin for debugging and troubleshooting, allowing simulated plugin deactivation and theme switching without affecting the live site. 6 * Version: 1.3. 16 * Version: 1.3.2 7 7 * Author: Jhimross 8 8 * Author URI: https://profiles.wordpress.org/jhimross … … 15 15 16 16 // Exit if accessed directly. 17 if ( ! defined( 'ABSPATH' )) {17 if (!defined('ABSPATH')) { 18 18 exit; 19 19 } … … 22 22 * Define plugin constants. 23 23 */ 24 define( 'DBGTBL_VERSION', '1.3.1');25 define( 'DBGTBL_DIR', plugin_dir_path( __FILE__ ));26 define( 'DBGTBL_URL', plugin_dir_url( __FILE__ ));27 define( 'DBGTBL_BASENAME', plugin_basename( __FILE__ ));24 define('DBGTBL_VERSION', '1.3.2'); 25 define('DBGTBL_DIR', plugin_dir_path(__FILE__)); 26 define('DBGTBL_URL', plugin_dir_url(__FILE__)); 27 define('DBGTBL_BASENAME', plugin_basename(__FILE__)); 28 28 29 29 /** 30 30 * The main plugin class. 31 31 */ 32 class Debug_Troubleshooter { 32 class Debug_Troubleshooter 33 { 33 34 34 35 /** … … 36 37 */ 37 38 const TROUBLESHOOT_COOKIE = 'wp_debug_troubleshoot_mode'; 38 const DEBUG_MODE_OPTION = 'wp_debug_troubleshoot_debug_mode';39 const DEBUG_MODE_OPTION = 'wp_debug_troubleshoot_debug_mode'; 39 40 const SIMULATE_USER_COOKIE = 'wp_debug_troubleshoot_simulate_user'; 40 41 … … 56 57 * Constructor. 57 58 */ 58 public function __construct() { 59 public function __construct() 60 { 59 61 // Load text domain for internationalization. 60 62 // Load text domain for internationalization. … … 62 64 63 65 // Initialize admin hooks. 64 add_action( 'admin_menu', array( $this, 'add_admin_menu' ));65 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ));66 add_action( 'wp_ajax_debug_troubleshoot_toggle_mode', array( $this, 'ajax_toggle_troubleshoot_mode' ));67 add_action( 'wp_ajax_debug_troubleshoot_update_state', array( $this, 'ajax_update_troubleshoot_state' ));68 add_action( 'wp_ajax_debug_troubleshoot_toggle_debug_mode', array( $this, 'ajax_toggle_debug_mode' ));69 add_action( 'wp_ajax_debug_troubleshoot_clear_debug_log', array( $this, 'ajax_clear_debug_log' ));70 add_action( 'wp_ajax_debug_troubleshoot_toggle_simulate_user', array( $this, 'ajax_toggle_simulate_user' ));66 add_action('admin_menu', array($this, 'add_admin_menu')); 67 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); 68 add_action('wp_ajax_debug_troubleshoot_toggle_mode', array($this, 'ajax_toggle_troubleshoot_mode')); 69 add_action('wp_ajax_debug_troubleshoot_update_state', array($this, 'ajax_update_troubleshoot_state')); 70 add_action('wp_ajax_debug_troubleshoot_toggle_debug_mode', array($this, 'ajax_toggle_debug_mode')); 71 add_action('wp_ajax_debug_troubleshoot_clear_debug_log', array($this, 'ajax_clear_debug_log')); 72 add_action('wp_ajax_debug_troubleshoot_toggle_simulate_user', array($this, 'ajax_toggle_simulate_user')); 71 73 72 74 // Core troubleshooting logic (very early hook). 73 add_action( 'plugins_loaded', array( $this, 'init_troubleshooting_mode' ), 0);74 add_action( 'plugins_loaded', array( $this, 'init_live_debug_mode' ), 0);75 add_action( 'plugins_loaded', array( $this, 'init_user_simulation' ), 0);75 add_action('plugins_loaded', array($this, 'init_troubleshooting_mode'), 0); 76 add_action('plugins_loaded', array($this, 'init_live_debug_mode'), 0); 77 add_action('plugins_loaded', array($this, 'init_user_simulation'), 0); 76 78 77 79 // Admin notice for troubleshooting mode. 78 add_action( 'admin_notices', array( $this, 'troubleshooting_mode_notice' ));79 add_action( 'admin_bar_menu', array( $this, 'admin_bar_exit_simulation' ), 999);80 add_action('admin_notices', array($this, 'troubleshooting_mode_notice')); 81 add_action('admin_bar_menu', array($this, 'admin_bar_exit_simulation'), 999); 80 82 } 81 83 … … 85 87 * Add admin menu page. 86 88 */ 87 public function add_admin_menu() { 89 public function add_admin_menu() 90 { 88 91 add_management_page( 89 __( 'Debugger & Troubleshooter', 'debugger-troubleshooter'),90 __( 'Debugger & Troubleshooter', 'debugger-troubleshooter'),92 __('Debugger & Troubleshooter', 'debugger-troubleshooter'), 93 __('Debugger & Troubleshooter', 'debugger-troubleshooter'), 91 94 'manage_options', 92 95 'debugger-troubleshooter', 93 array( $this, 'render_admin_page')96 array($this, 'render_admin_page') 94 97 ); 95 98 } … … 100 103 * @param string $hook The current admin page hook. 101 104 */ 102 public function enqueue_admin_scripts( $hook ) { 103 if ( 'tools_page_debugger-troubleshooter' !== $hook ) { 105 public function enqueue_admin_scripts($hook) 106 { 107 if ('tools_page_debugger-troubleshooter' !== $hook) { 104 108 return; 105 109 } 106 110 107 111 // Enqueue the main admin stylesheet. 108 wp_enqueue_style( 'debug-troubleshooter-admin', DBGTBL_URL . 'assets/css/admin.css', array(), DBGTBL_VERSION);112 wp_enqueue_style('debug-troubleshooter-admin', DBGTBL_URL . 'assets/css/admin.css', array(), DBGTBL_VERSION); 109 113 // Enqueue the main admin JavaScript. 110 wp_enqueue_script( 'debug-troubleshooter-admin', DBGTBL_URL . 'assets/js/admin.js', array( 'jquery' ), DBGTBL_VERSION, true);114 wp_enqueue_script('debug-troubleshooter-admin', DBGTBL_URL . 'assets/js/admin.js', array('jquery'), DBGTBL_VERSION, true); 111 115 112 116 // Localize script with necessary data. … … 115 119 'debugTroubleshoot', 116 120 array( 117 'ajax_url' => admin_url( 'admin-ajax.php'),118 'nonce' => wp_create_nonce( 'debug_troubleshoot_nonce'),119 'is_troubleshooting' => $this->is_troubleshooting_active(),120 'current_state' => $this->get_troubleshoot_state(),121 'is_debug_mode' => get_option( self::DEBUG_MODE_OPTION, 'disabled') === 'enabled',122 'active_plugins' => get_option( 'active_plugins', array()),123 'active_sitewide_plugins' => is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() )) : array(),124 'current_theme' => get_stylesheet(),125 'alert_title_success' => __( 'Success', 'debugger-troubleshooter'),126 'alert_title_error' => __( 'Error', 'debugger-troubleshooter'),127 'copy_button_text' => __( 'Copy to Clipboard', 'debugger-troubleshooter'),128 'copied_button_text' => __( 'Copied!', 'debugger-troubleshooter'),129 'show_all_text' => __( 'Show All', 'debugger-troubleshooter'),130 'hide_text' => __( 'Hide', 'debugger-troubleshooter'),131 'is_simulating_user' => $this->is_simulating_user(),121 'ajax_url' => admin_url('admin-ajax.php'), 122 'nonce' => wp_create_nonce('debug_troubleshoot_nonce'), 123 'is_troubleshooting' => $this->is_troubleshooting_active(), 124 'current_state' => $this->get_troubleshoot_state(), 125 'is_debug_mode' => get_option(self::DEBUG_MODE_OPTION, 'disabled') === 'enabled', 126 'active_plugins' => get_option('active_plugins', array()), 127 'active_sitewide_plugins' => is_multisite() ? array_keys(get_site_option('active_sitewide_plugins', array())) : array(), 128 'current_theme' => get_stylesheet(), 129 'alert_title_success' => __('Success', 'debugger-troubleshooter'), 130 'alert_title_error' => __('Error', 'debugger-troubleshooter'), 131 'copy_button_text' => __('Copy to Clipboard', 'debugger-troubleshooter'), 132 'copied_button_text' => __('Copied!', 'debugger-troubleshooter'), 133 'show_all_text' => __('Show All', 'debugger-troubleshooter'), 134 'hide_text' => __('Hide', 'debugger-troubleshooter'), 135 'is_simulating_user' => $this->is_simulating_user(), 132 136 ) 133 137 ); … … 137 141 * Renders the admin page content. 138 142 */ 139 public function render_admin_page() { 140 $is_debug_mode_enabled = get_option( self::DEBUG_MODE_OPTION, 'disabled' ) === 'enabled'; 143 public function render_admin_page() 144 { 145 $is_debug_mode_enabled = get_option(self::DEBUG_MODE_OPTION, 'disabled') === 'enabled'; 141 146 ?> 142 147 <div class="wrap debug-troubleshooter-wrap"> 143 <h1 class="wp-heading-inline"><?php esc_html_e( 'Debugger & Troubleshooter', 'debugger-troubleshooter'); ?></h1>148 <h1 class="wp-heading-inline"><?php esc_html_e('Debugger & Troubleshooter', 'debugger-troubleshooter'); ?></h1> 144 149 <hr class="wp-header-end"> 145 150 … … 147 152 <div class="debug-troubleshooter-section"> 148 153 <div class="section-header"> 149 <h2><?php esc_html_e( 'Site Information', 'debugger-troubleshooter' ); ?></h2> 150 <button id="copy-site-info" class="button button-secondary"><?php esc_html_e( 'Copy to Clipboard', 'debugger-troubleshooter' ); ?></button> 154 <h2><?php esc_html_e('Site Information', 'debugger-troubleshooter'); ?></h2> 155 <button id="copy-site-info" 156 class="button button-secondary"><?php esc_html_e('Copy to Clipboard', 'debugger-troubleshooter'); ?></button> 151 157 </div> 152 158 <div id="site-info-content" class="section-content"> … … 157 163 <div class="debug-troubleshooter-section standalone-section"> 158 164 <div class="section-header"> 159 <h2><?php esc_html_e( 'Troubleshooting Mode', 'debugger-troubleshooter' ); ?></h2> 160 <button id="troubleshoot-mode-toggle" class="button button-large <?php echo $this->is_troubleshooting_active() ? 'button-danger' : 'button-primary'; ?>"> 161 <?php echo $this->is_troubleshooting_active() ? esc_html__( 'Exit Troubleshooting Mode', 'debugger-troubleshooter' ) : esc_html__( 'Enter Troubleshooting Mode', 'debugger-troubleshooter' ); ?> 165 <h2><?php esc_html_e('Troubleshooting Mode', 'debugger-troubleshooter'); ?></h2> 166 <button id="troubleshoot-mode-toggle" 167 class="button button-large <?php echo $this->is_troubleshooting_active() ? 'button-danger' : 'button-primary'; ?>"> 168 <?php echo $this->is_troubleshooting_active() ? esc_html__('Exit Troubleshooting Mode', 'debugger-troubleshooter') : esc_html__('Enter Troubleshooting Mode', 'debugger-troubleshooter'); ?> 162 169 </button> 163 170 </div> 164 171 <div class="section-content"> 165 172 <p class="description"> 166 <?php esc_html_e( 'Enter Troubleshooting Mode to simulate deactivating plugins and switching themes without affecting your live website for other visitors. This mode uses browser cookies and only applies to your session.', 'debugger-troubleshooter'); ?>173 <?php esc_html_e('Enter Troubleshooting Mode to simulate deactivating plugins and switching themes without affecting your live website for other visitors. This mode uses browser cookies and only applies to your session.', 'debugger-troubleshooter'); ?> 167 174 </p> 168 175 169 <div id="troubleshoot-mode-controls" class="troubleshoot-mode-controls <?php echo $this->is_troubleshooting_active() ? '' : 'hidden'; ?>"> 176 <div id="troubleshoot-mode-controls" 177 class="troubleshoot-mode-controls <?php echo $this->is_troubleshooting_active() ? '' : 'hidden'; ?>"> 170 178 <div class="debug-troubleshooter-card"> 171 <h3><?php esc_html_e( 'Simulate Theme Switch', 'debugger-troubleshooter' ); ?></h3> 172 <p class="description"><?php esc_html_e( 'Select a theme to preview. This will change the theme for your session only.', 'debugger-troubleshooter' ); ?></p> 179 <h3><?php esc_html_e('Simulate Theme Switch', 'debugger-troubleshooter'); ?></h3> 180 <p class="description"> 181 <?php esc_html_e('Select a theme to preview. This will change the theme for your session only.', 'debugger-troubleshooter'); ?> 182 </p> 173 183 <select id="troubleshoot-theme-select" class="regular-text"> 174 184 <?php 175 $themes = wp_get_themes();176 $current_active = get_stylesheet();177 $troubleshoot_theme = $this->troubleshoot_state && ! empty( $this->troubleshoot_state['theme']) ? $this->troubleshoot_state['theme'] : $current_active;178 179 foreach ( $themes as $slug => $theme) {180 echo '<option value="' . esc_attr( $slug ) . '"' . selected( $slug, $troubleshoot_theme, false ) . '>' . esc_html( $theme->get( 'Name' )) . '</option>';185 $themes = wp_get_themes(); 186 $current_active = get_stylesheet(); 187 $troubleshoot_theme = $this->troubleshoot_state && !empty($this->troubleshoot_state['theme']) ? $this->troubleshoot_state['theme'] : $current_active; 188 189 foreach ($themes as $slug => $theme) { 190 echo '<option value="' . esc_attr($slug) . '"' . selected($slug, $troubleshoot_theme, false) . '>' . esc_html($theme->get('Name')) . '</option>'; 181 191 } 182 192 ?> … … 185 195 186 196 <div class="debug-troubleshooter-card"> 187 <h3><?php esc_html_e( 'Simulate Plugin Deactivation', 'debugger-troubleshooter' ); ?></h3> 188 <p class="description"><?php esc_html_e( 'Check plugins to simulate deactivating them for your session. Unchecked plugins will remain active.', 'debugger-troubleshooter' ); ?></p> 197 <h3><?php esc_html_e('Simulate Plugin Deactivation', 'debugger-troubleshooter'); ?></h3> 198 <p class="description"> 199 <?php esc_html_e('Check plugins to simulate deactivating them for your session. Unchecked plugins will remain active.', 'debugger-troubleshooter'); ?> 200 </p> 189 201 <?php 190 $plugins = get_plugins();191 $troubleshoot_active_plugins = $this->troubleshoot_state && ! empty( $this->troubleshoot_state['plugins'] ) ? $this->troubleshoot_state['plugins'] : get_option( 'active_plugins', array());192 $troubleshoot_active_sitewide_plugins = $this->troubleshoot_state && ! empty( $this->troubleshoot_state['sitewide_plugins'] ) ? $this->troubleshoot_state['sitewide_plugins'] : ( is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) : array());193 194 if ( ! empty( $plugins )) {202 $plugins = get_plugins(); 203 $troubleshoot_active_plugins = $this->troubleshoot_state && !empty($this->troubleshoot_state['plugins']) ? $this->troubleshoot_state['plugins'] : get_option('active_plugins', array()); 204 $troubleshoot_active_sitewide_plugins = $this->troubleshoot_state && !empty($this->troubleshoot_state['sitewide_plugins']) ? $this->troubleshoot_state['sitewide_plugins'] : (is_multisite() ? array_keys(get_site_option('active_sitewide_plugins', array())) : array()); 205 206 if (!empty($plugins)) { 195 207 echo '<div class="plugin-list">'; 196 foreach ( $plugins as $plugin_file => $plugin_data) {197 $is_active_for_site = in_array( $plugin_file, get_option( 'active_plugins', array() ) ) || ( is_multisite() && array_key_exists( $plugin_file, get_site_option( 'active_sitewide_plugins', array() ) ));208 foreach ($plugins as $plugin_file => $plugin_data) { 209 $is_active_for_site = in_array($plugin_file, get_option('active_plugins', array())) || (is_multisite() && array_key_exists($plugin_file, get_site_option('active_sitewide_plugins', array()))); 198 210 $is_checked_in_troubleshoot_mode = ( 199 in_array( $plugin_file, $troubleshoot_active_plugins) ||200 ( is_multisite() && in_array( $plugin_file, $troubleshoot_active_sitewide_plugins ))211 in_array($plugin_file, $troubleshoot_active_plugins) || 212 (is_multisite() && in_array($plugin_file, $troubleshoot_active_sitewide_plugins)) 201 213 ); 202 214 ?> 203 215 <label class="plugin-item flex items-center p-2 rounded-md transition-colors duration-200"> 204 <input type="checkbox" name="troubleshoot_plugins[]" value="<?php echo esc_attr( $plugin_file ); ?>" <?php checked( $is_checked_in_troubleshoot_mode ); ?> data-original-state="<?php echo $is_active_for_site ? 'active' : 'inactive'; ?>"> 216 <input type="checkbox" name="troubleshoot_plugins[]" 217 value="<?php echo esc_attr($plugin_file); ?>" <?php checked($is_checked_in_troubleshoot_mode); ?> 218 data-original-state="<?php echo $is_active_for_site ? 'active' : 'inactive'; ?>"> 205 219 <span class="ml-2"> 206 <strong><?php echo esc_html( $plugin_data['Name'] ); ?></strong> 207 <br><small><?php echo esc_html( $plugin_data['Version'] ); ?> | <?php echo esc_html( $plugin_data['AuthorName'] ); ?></small> 220 <strong><?php echo esc_html($plugin_data['Name']); ?></strong> 221 <br><small><?php echo esc_html($plugin_data['Version']); ?> | 222 <?php echo esc_html($plugin_data['AuthorName']); ?></small> 208 223 </span> 209 224 </label> … … 212 227 echo '</div>'; 213 228 } else { 214 echo '<p>' . esc_html__( 'No plugins found.', 'debugger-troubleshooter') . '</p>';229 echo '<p>' . esc_html__('No plugins found.', 'debugger-troubleshooter') . '</p>'; 215 230 } 216 231 ?> 217 232 </div> 218 233 219 <button id="apply-troubleshoot-changes" class="button button-primary button-large"><?php esc_html_e( 'Apply Troubleshooting Changes', 'debugger-troubleshooter' ); ?></button> 220 <p class="description"><?php esc_html_e( 'Applying changes will refresh the page to reflect your simulated theme and plugin states.', 'debugger-troubleshooter' ); ?></p> 234 <button id="apply-troubleshoot-changes" 235 class="button button-primary button-large"><?php esc_html_e('Apply Troubleshooting Changes', 'debugger-troubleshooter'); ?></button> 236 <p class="description"> 237 <?php esc_html_e('Applying changes will refresh the page to reflect your simulated theme and plugin states.', 'debugger-troubleshooter'); ?> 238 </p> 221 239 </div><!-- #troubleshoot-mode-controls --> 222 240 </div> … … 227 245 <div class="debug-troubleshooter-section standalone-section full-width-section"> 228 246 <div class="section-header"> 229 <h2><?php esc_html_e( 'User Role Simulator', 'debugger-troubleshooter'); ?></h2>247 <h2><?php esc_html_e('User Role Simulator', 'debugger-troubleshooter'); ?></h2> 230 248 </div> 231 249 <div class="section-content"> 232 250 <p class="description"> 233 <?php esc_html_e( 'View the site as a specific user or role. This allows you to test permissions and user-specific content without logging out. This only affects your session.', 'debugger-troubleshooter'); ?>251 <?php esc_html_e('View the site as a specific user or role. This allows you to test permissions and user-specific content without logging out. This only affects your session.', 'debugger-troubleshooter'); ?> 234 252 </p> 235 253 <?php $this->render_user_simulation_section(); ?> … … 239 257 <div class="debug-troubleshooter-section standalone-section full-width-section"> 240 258 <div class="section-header"> 241 <h2><?php esc_html_e( 'Live Debugging', 'debugger-troubleshooter' ); ?></h2> 242 <button id="debug-mode-toggle" class="button button-large <?php echo $is_debug_mode_enabled ? 'button-danger' : 'button-primary'; ?>"> 243 <?php echo $is_debug_mode_enabled ? esc_html__( 'Disable Live Debug', 'debugger-troubleshooter' ) : esc_html__( 'Enable Live Debug', 'debugger-troubleshooter' ); ?> 259 <h2><?php esc_html_e('Live Debugging', 'debugger-troubleshooter'); ?></h2> 260 <button id="debug-mode-toggle" 261 class="button button-large <?php echo $is_debug_mode_enabled ? 'button-danger' : 'button-primary'; ?>"> 262 <?php echo $is_debug_mode_enabled ? esc_html__('Disable Live Debug', 'debugger-troubleshooter') : esc_html__('Enable Live Debug', 'debugger-troubleshooter'); ?> 244 263 </button> 245 264 </div> 246 265 <div class="section-content"> 247 266 <p class="description"> 248 <?php esc_html_e( 'Enable this to turn on WP_DEBUG without editing your wp-config.php file. Errors will be logged to the debug.log file below, not displayed on the site.', 'debugger-troubleshooter'); ?>267 <?php esc_html_e('Enable this to turn on WP_DEBUG without editing your wp-config.php file. Errors will be logged to the debug.log file below, not displayed on the site.', 'debugger-troubleshooter'); ?> 249 268 </p> 250 269 251 270 <div class="debug-log-viewer-wrapper"> 252 271 <div class="debug-log-header"> 253 <h3><?php esc_html_e( 'Debug Log Viewer', 'debugger-troubleshooter' ); ?></h3> 254 <button id="clear-debug-log" class="button button-secondary"><?php esc_html_e( 'Clear Log', 'debugger-troubleshooter' ); ?></button> 272 <h3><?php esc_html_e('Debug Log Viewer', 'debugger-troubleshooter'); ?></h3> 273 <button id="clear-debug-log" 274 class="button button-secondary"><?php esc_html_e('Clear Log', 'debugger-troubleshooter'); ?></button> 255 275 </div> 256 <textarea id="debug-log-viewer" readonly class="large-text" rows="15"><?php echo esc_textarea( $this->get_debug_log_content() ); ?></textarea> 276 <textarea id="debug-log-viewer" readonly class="large-text" 277 rows="15"><?php echo esc_textarea($this->get_debug_log_content()); ?></textarea> 257 278 </div> 258 279 </div> … … 262 283 </div> 263 284 264 <div id="debug-troubleshoot-alert-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"> 285 <div id="debug-troubleshoot-alert-modal" 286 class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"> 265 287 <div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full text-center"> 266 288 <h3 id="debug-troubleshoot-alert-title" class="text-xl font-bold mb-4"></h3> 267 289 <p id="debug-troubleshoot-alert-message" class="text-gray-700 mb-6"></p> 268 <button id="debug-troubleshoot-alert-close" class="button button-primary"><?php esc_html_e( 'OK', 'debugger-troubleshooter' ); ?></button> 290 <button id="debug-troubleshoot-alert-close" 291 class="button button-primary"><?php esc_html_e('OK', 'debugger-troubleshooter'); ?></button> 269 292 </div> 270 293 </div> 271 294 272 <div id="debug-troubleshoot-confirm-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"> 295 <div id="debug-troubleshoot-confirm-modal" 296 class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50"> 273 297 <div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full text-center"> 274 298 <h3 id="debug-troubleshoot-confirm-title" class="text-xl font-bold mb-4"></h3> 275 299 <p id="debug-troubleshoot-confirm-message" class="text-gray-700 mb-6"></p> 276 300 <div class="confirm-buttons"> 277 <button id="debug-troubleshoot-confirm-cancel" class="button button-secondary"><?php esc_html_e( 'Cancel', 'debugger-troubleshooter' ); ?></button> 278 <button id="debug-troubleshoot-confirm-ok" class="button button-danger"><?php esc_html_e( 'Confirm', 'debugger-troubleshooter' ); ?></button> 301 <button id="debug-troubleshoot-confirm-cancel" 302 class="button button-secondary"><?php esc_html_e('Cancel', 'debugger-troubleshooter'); ?></button> 303 <button id="debug-troubleshoot-confirm-ok" 304 class="button button-danger"><?php esc_html_e('Confirm', 'debugger-troubleshooter'); ?></button> 279 305 </div> 280 306 </div> … … 287 313 * Displays useful site information. 288 314 */ 289 private function display_site_info() { 315 private function display_site_info() 316 { 290 317 global $wpdb; 291 318 echo '<div class="site-info-grid">'; … … 293 320 // WordPress Information Card 294 321 echo '<div class="debug-troubleshooter-card collapsible">'; 295 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__( 'WordPress Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>';322 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__('WordPress Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>'; 296 323 echo '<div class="card-collapsible-content hidden">'; 297 echo '<p><strong>' . esc_html__( 'WordPress Version:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( get_bloginfo( 'version' )) . '</p>';298 echo '<p><strong>' . esc_html__( 'Site Language:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( get_locale()) . '</p>';299 echo '<p><strong>' . esc_html__( 'Permalink Structure:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( get_option( 'permalink_structure' ) ?: 'Plain') . '</p>';300 echo '<p><strong>' . esc_html__( 'Multisite:', 'debugger-troubleshooter' ) . '</strong> ' . ( is_multisite() ? 'Yes' : 'No') . '</p>';324 echo '<p><strong>' . esc_html__('WordPress Version:', 'debugger-troubleshooter') . '</strong> ' . esc_html(get_bloginfo('version')) . '</p>'; 325 echo '<p><strong>' . esc_html__('Site Language:', 'debugger-troubleshooter') . '</strong> ' . esc_html(get_locale()) . '</p>'; 326 echo '<p><strong>' . esc_html__('Permalink Structure:', 'debugger-troubleshooter') . '</strong> ' . esc_html(get_option('permalink_structure') ?: 'Plain') . '</p>'; 327 echo '<p><strong>' . esc_html__('Multisite:', 'debugger-troubleshooter') . '</strong> ' . (is_multisite() ? 'Yes' : 'No') . '</p>'; 301 328 302 329 // Themes List 303 $all_themes = wp_get_themes();304 $active_theme_obj = wp_get_theme();305 $inactive_themes_count = count( $all_themes) - 1;306 307 echo '<h4>' . esc_html__( 'Themes', 'debugger-troubleshooter') . '</h4>';308 echo '<p><strong>' . esc_html__( 'Active Theme:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( $active_theme_obj->get( 'Name' ) ) . ' (' . esc_html( $active_theme_obj->get( 'Version' )) . ')</p>';309 if ( $inactive_themes_count > 0) {310 echo '<p><strong>' . esc_html__( 'Inactive Themes:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( $inactive_themes_count ) . ' <a href="#" class="info-sub-list-toggle" data-target="themes-list">' . esc_html__( 'Show All', 'debugger-troubleshooter') . '</a></p>';311 } 312 313 if ( ! empty( $all_themes )) {330 $all_themes = wp_get_themes(); 331 $active_theme_obj = wp_get_theme(); 332 $inactive_themes_count = count($all_themes) - 1; 333 334 echo '<h4>' . esc_html__('Themes', 'debugger-troubleshooter') . '</h4>'; 335 echo '<p><strong>' . esc_html__('Active Theme:', 'debugger-troubleshooter') . '</strong> ' . esc_html($active_theme_obj->get('Name')) . ' (' . esc_html($active_theme_obj->get('Version')) . ')</p>'; 336 if ($inactive_themes_count > 0) { 337 echo '<p><strong>' . esc_html__('Inactive Themes:', 'debugger-troubleshooter') . '</strong> ' . esc_html($inactive_themes_count) . ' <a href="#" class="info-sub-list-toggle" data-target="themes-list">' . esc_html__('Show All', 'debugger-troubleshooter') . '</a></p>'; 338 } 339 340 if (!empty($all_themes)) { 314 341 echo '<ul id="themes-list" class="info-sub-list hidden">'; 315 foreach ( $all_themes as $stylesheet => $theme) {316 $status = ( $stylesheet === $active_theme_obj->get_stylesheet()) ? '<span class="status-active">Active</span>' : '<span class="status-inactive">Inactive</span>';317 echo '<li><div>' . esc_html( $theme->get( 'Name' ) ) . ' (' . esc_html( $theme->get( 'Version' ) ) . ')</div>' . wp_kses_post( $status) . '</li>';342 foreach ($all_themes as $stylesheet => $theme) { 343 $status = ($stylesheet === $active_theme_obj->get_stylesheet()) ? '<span class="status-active">Active</span>' : '<span class="status-inactive">Inactive</span>'; 344 echo '<li><div>' . esc_html($theme->get('Name')) . ' (' . esc_html($theme->get('Version')) . ')</div>' . wp_kses_post($status) . '</li>'; 318 345 } 319 346 echo '</ul>'; … … 321 348 322 349 // Plugins List 323 $all_plugins = get_plugins();324 $active_plugins = (array) get_option( 'active_plugins', array());325 $network_active_plugins = is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() )) : array();326 $inactive_plugins_count = count( $all_plugins ) - count( $active_plugins ) - count( $network_active_plugins);327 328 echo '<h4>' . esc_html__( 'Plugins', 'debugger-troubleshooter') . '</h4>';329 echo '<p><strong>' . esc_html__( 'Active Plugins:', 'debugger-troubleshooter' ) . '</strong> ' . count( $active_plugins) . '</p>';330 if ( is_multisite()) {331 echo '<p><strong>' . esc_html__( 'Network Active Plugins:', 'debugger-troubleshooter' ) . '</strong> ' . count( $network_active_plugins) . '</p>';332 } 333 echo '<p><strong>' . esc_html__( 'Inactive Plugins:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( $inactive_plugins_count ) . ' <a href="#" class="info-sub-list-toggle" data-target="plugins-list">' . esc_html__( 'Show All', 'debugger-troubleshooter') . '</a></p>';334 335 if ( ! empty( $all_plugins )) {350 $all_plugins = get_plugins(); 351 $active_plugins = (array) get_option('active_plugins', array()); 352 $network_active_plugins = is_multisite() ? array_keys(get_site_option('active_sitewide_plugins', array())) : array(); 353 $inactive_plugins_count = count($all_plugins) - count($active_plugins) - count($network_active_plugins); 354 355 echo '<h4>' . esc_html__('Plugins', 'debugger-troubleshooter') . '</h4>'; 356 echo '<p><strong>' . esc_html__('Active Plugins:', 'debugger-troubleshooter') . '</strong> ' . count($active_plugins) . '</p>'; 357 if (is_multisite()) { 358 echo '<p><strong>' . esc_html__('Network Active Plugins:', 'debugger-troubleshooter') . '</strong> ' . count($network_active_plugins) . '</p>'; 359 } 360 echo '<p><strong>' . esc_html__('Inactive Plugins:', 'debugger-troubleshooter') . '</strong> ' . esc_html($inactive_plugins_count) . ' <a href="#" class="info-sub-list-toggle" data-target="plugins-list">' . esc_html__('Show All', 'debugger-troubleshooter') . '</a></p>'; 361 362 if (!empty($all_plugins)) { 336 363 echo '<ul id="plugins-list" class="info-sub-list hidden">'; 337 foreach ( $all_plugins as $plugin_file => $plugin_data) {364 foreach ($all_plugins as $plugin_file => $plugin_data) { 338 365 $status = '<span class="status-inactive">Inactive</span>'; 339 if ( in_array( $plugin_file, $active_plugins, true )) {366 if (in_array($plugin_file, $active_plugins, true)) { 340 367 $status = '<span class="status-active">Active</span>'; 341 } elseif ( in_array( $plugin_file, $network_active_plugins, true )) {368 } elseif (in_array($plugin_file, $network_active_plugins, true)) { 342 369 $status = '<span class="status-network-active">Network Active</span>'; 343 370 } 344 echo '<li><div>' . esc_html( $plugin_data['Name'] ) . ' (' . esc_html( $plugin_data['Version'] ) . ')</div>' . wp_kses_post( $status) . '</li>';371 echo '<li><div>' . esc_html($plugin_data['Name']) . ' (' . esc_html($plugin_data['Version']) . ')</div>' . wp_kses_post($status) . '</li>'; 345 372 } 346 373 echo '</ul>'; … … 351 378 // PHP Information Card 352 379 echo '<div class="debug-troubleshooter-card collapsible">'; 353 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__( 'PHP Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>';380 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__('PHP Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>'; 354 381 echo '<div class="card-collapsible-content hidden">'; 355 echo '<p><strong>' . esc_html__( 'PHP Version:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( phpversion()) . '</p>';356 echo '<p><strong>' . esc_html__( 'Memory Limit:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( ini_get( 'memory_limit' )) . '</p>';357 echo '<p><strong>' . esc_html__( 'Peak Memory Usage:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( size_format( memory_get_peak_usage( true ) )) . '</p>';358 echo '<p><strong>' . esc_html__( 'Post Max Size:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( ini_get( 'post_max_size' )) . '</p>';359 echo '<p><strong>' . esc_html__( 'Upload Max Filesize:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( ini_get( 'upload_max_filesize' )) . '</p>';360 echo '<p><strong>' . esc_html__( 'Max Execution Time:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( ini_get( 'max_execution_time' )) . 's</p>';361 echo '<p><strong>' . esc_html__( 'Max Input Vars:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( ini_get( 'max_input_vars' )) . '</p>';362 echo '<p><strong>' . esc_html__( 'cURL Extension:', 'debugger-troubleshooter' ) . '</strong> ' . ( extension_loaded( 'curl' ) ? 'Enabled' : 'Disabled') . '</p>';363 echo '<p><strong>' . esc_html__( 'GD Library:', 'debugger-troubleshooter' ) . '</strong> ' . ( extension_loaded( 'gd' ) ? 'Enabled' : 'Disabled') . '</p>';364 echo '<p><strong>' . esc_html__( 'Imagick Library:', 'debugger-troubleshooter' ) . '</strong> ' . ( extension_loaded( 'imagick' ) ? 'Enabled' : 'Disabled') . '</p>';382 echo '<p><strong>' . esc_html__('PHP Version:', 'debugger-troubleshooter') . '</strong> ' . esc_html(phpversion()) . '</p>'; 383 echo '<p><strong>' . esc_html__('Memory Limit:', 'debugger-troubleshooter') . '</strong> ' . esc_html(ini_get('memory_limit')) . '</p>'; 384 echo '<p><strong>' . esc_html__('Peak Memory Usage:', 'debugger-troubleshooter') . '</strong> ' . esc_html(size_format(memory_get_peak_usage(true))) . '</p>'; 385 echo '<p><strong>' . esc_html__('Post Max Size:', 'debugger-troubleshooter') . '</strong> ' . esc_html(ini_get('post_max_size')) . '</p>'; 386 echo '<p><strong>' . esc_html__('Upload Max Filesize:', 'debugger-troubleshooter') . '</strong> ' . esc_html(ini_get('upload_max_filesize')) . '</p>'; 387 echo '<p><strong>' . esc_html__('Max Execution Time:', 'debugger-troubleshooter') . '</strong> ' . esc_html(ini_get('max_execution_time')) . 's</p>'; 388 echo '<p><strong>' . esc_html__('Max Input Vars:', 'debugger-troubleshooter') . '</strong> ' . esc_html(ini_get('max_input_vars')) . '</p>'; 389 echo '<p><strong>' . esc_html__('cURL Extension:', 'debugger-troubleshooter') . '</strong> ' . (extension_loaded('curl') ? 'Enabled' : 'Disabled') . '</p>'; 390 echo '<p><strong>' . esc_html__('GD Library:', 'debugger-troubleshooter') . '</strong> ' . (extension_loaded('gd') ? 'Enabled' : 'Disabled') . '</p>'; 391 echo '<p><strong>' . esc_html__('Imagick Library:', 'debugger-troubleshooter') . '</strong> ' . (extension_loaded('imagick') ? 'Enabled' : 'Disabled') . '</p>'; 365 392 echo '</div></div>'; 366 393 367 394 // Database Information Card 368 395 echo '<div class="debug-troubleshooter-card collapsible">'; 369 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__( 'Database Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>';396 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__('Database Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>'; 370 397 echo '<div class="card-collapsible-content hidden">'; 371 echo '<p><strong>' . esc_html__( 'Database Engine:', 'debugger-troubleshooter') . '</strong> MySQL</p>';398 echo '<p><strong>' . esc_html__('Database Engine:', 'debugger-troubleshooter') . '</strong> MySQL</p>'; 372 399 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 373 400 // Direct query is necessary to get the MySQL server version. Caching is not beneficial for this one-off diagnostic read. 374 echo '<p><strong>' . esc_html__( 'MySQL Version:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( $wpdb->get_var( 'SELECT VERSION()' )) . '</p>';401 echo '<p><strong>' . esc_html__('MySQL Version:', 'debugger-troubleshooter') . '</strong> ' . esc_html($wpdb->get_var('SELECT VERSION()')) . '</p>'; 375 402 // phpcs:enable 376 echo '<p><strong>' . esc_html__( 'DB Name:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( DB_NAME) . '</p>';377 echo '<p><strong>' . esc_html__( 'DB Host:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( DB_HOST) . '</p>';378 echo '<p><strong>' . esc_html__( 'DB Charset:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( DB_CHARSET) . '</p>';379 echo '<p><strong>' . esc_html__( 'DB Collate:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( DB_COLLATE) . '</p>';403 echo '<p><strong>' . esc_html__('DB Name:', 'debugger-troubleshooter') . '</strong> ' . esc_html(DB_NAME) . '</p>'; 404 echo '<p><strong>' . esc_html__('DB Host:', 'debugger-troubleshooter') . '</strong> ' . esc_html(DB_HOST) . '</p>'; 405 echo '<p><strong>' . esc_html__('DB Charset:', 'debugger-troubleshooter') . '</strong> ' . esc_html(DB_CHARSET) . '</p>'; 406 echo '<p><strong>' . esc_html__('DB Collate:', 'debugger-troubleshooter') . '</strong> ' . esc_html(DB_COLLATE) . '</p>'; 380 407 echo '</div></div>'; 381 408 382 409 // Server Information Card 383 410 echo '<div class="debug-troubleshooter-card collapsible">'; 384 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__( 'Server Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>';411 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__('Server Information', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>'; 385 412 echo '<div class="card-collapsible-content hidden">'; 386 echo '<p><strong>' . esc_html__( 'Web Server:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : 'N/A') . '</p>';387 echo '<p><strong>' . esc_html__( 'Server Protocol:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( isset( $_SERVER['SERVER_PROTOCOL'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_PROTOCOL'] ) ) : 'N/A') . '</p>';388 echo '<p><strong>' . esc_html__( 'Server Address:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : 'N/A') . '</p>';389 echo '<p><strong>' . esc_html__( 'Document Root:', 'debugger-troubleshooter' ) . '</strong> ' . esc_html( isset( $_SERVER['DOCUMENT_ROOT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) : 'N/A') . '</p>';390 echo '<p><strong>' . esc_html__( 'HTTPS:', 'debugger-troubleshooter' ) . '</strong> ' . ( is_ssl() ? 'On' : 'Off') . '</p>';413 echo '<p><strong>' . esc_html__('Web Server:', 'debugger-troubleshooter') . '</strong> ' . esc_html(isset($_SERVER['SERVER_SOFTWARE']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_SOFTWARE'])) : 'N/A') . '</p>'; 414 echo '<p><strong>' . esc_html__('Server Protocol:', 'debugger-troubleshooter') . '</strong> ' . esc_html(isset($_SERVER['SERVER_PROTOCOL']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_PROTOCOL'])) : 'N/A') . '</p>'; 415 echo '<p><strong>' . esc_html__('Server Address:', 'debugger-troubleshooter') . '</strong> ' . esc_html(isset($_SERVER['SERVER_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : 'N/A') . '</p>'; 416 echo '<p><strong>' . esc_html__('Document Root:', 'debugger-troubleshooter') . '</strong> ' . esc_html(isset($_SERVER['DOCUMENT_ROOT']) ? sanitize_text_field(wp_unslash($_SERVER['DOCUMENT_ROOT'])) : 'N/A') . '</p>'; 417 echo '<p><strong>' . esc_html__('HTTPS:', 'debugger-troubleshooter') . '</strong> ' . (is_ssl() ? 'On' : 'Off') . '</p>'; 391 418 echo '</div></div>'; 392 419 393 420 // WordPress Constants Card 394 421 echo '<div class="debug-troubleshooter-card collapsible">'; 395 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__( 'WordPress Constants', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>';422 echo '<div class="card-collapsible-header collapsed"><h3>' . esc_html__('WordPress Constants', 'debugger-troubleshooter') . '</h3><span class="dashicons dashicons-arrow-down-alt2"></span></div>'; 396 423 echo '<div class="card-collapsible-content hidden">'; 397 424 echo '<ul>'; … … 416 443 'FS_CHMOD_FILE', 417 444 ); 418 foreach ( $wp_constants as $constant) {419 echo '<li><strong>' . esc_html( $constant) . ':</strong> ';420 if ( defined( $constant )) {421 $value = constant( $constant);422 if ( is_bool( $value )) {423 echo esc_html( $value ? 'true' : 'false');424 } elseif ( is_numeric( $value )) {425 echo esc_html( $value);426 } elseif ( is_string( $value ) && ! empty( $value )) {427 echo '"' . esc_html( $value) . '"';445 foreach ($wp_constants as $constant) { 446 echo '<li><strong>' . esc_html($constant) . ':</strong> '; 447 if (defined($constant)) { 448 $value = constant($constant); 449 if (is_bool($value)) { 450 echo esc_html($value ? 'true' : 'false'); 451 } elseif (is_numeric($value)) { 452 echo esc_html($value); 453 } elseif (is_string($value) && !empty($value)) { 454 echo '"' . esc_html($value) . '"'; 428 455 } else { 429 echo esc_html__( 'Defined but empty/non-scalar', 'debugger-troubleshooter');456 echo esc_html__('Defined but empty/non-scalar', 'debugger-troubleshooter'); 430 457 } 431 458 } else { 432 echo esc_html__( 'Undefined', 'debugger-troubleshooter');459 echo esc_html__('Undefined', 'debugger-troubleshooter'); 433 460 } 434 461 echo '</li>'; … … 444 471 * This hook runs very early to ensure filters are applied before most of WP loads. 445 472 */ 446 public function init_troubleshooting_mode() { 447 if ( isset( $_COOKIE[ self::TROUBLESHOOT_COOKIE ] ) ) { 473 public function init_troubleshooting_mode() 474 { 475 if (isset($_COOKIE[self::TROUBLESHOOT_COOKIE])) { 448 476 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 449 $this->troubleshoot_state = json_decode( wp_unslash( $_COOKIE[ self::TROUBLESHOOT_COOKIE ] ), true);450 451 if ( ! empty( $this->troubleshoot_state )) {477 $this->troubleshoot_state = json_decode(wp_unslash($_COOKIE[self::TROUBLESHOOT_COOKIE]), true); 478 479 if (!empty($this->troubleshoot_state)) { 452 480 // Define DONOTCACHEPAGE to prevent caching plugins from interfering. 453 if ( ! defined( 'DONOTCACHEPAGE' )) {481 if (!defined('DONOTCACHEPAGE')) { 454 482 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound 455 define( 'DONOTCACHEPAGE', true);483 define('DONOTCACHEPAGE', true); 456 484 } 457 485 // Send no-cache headers as a secondary measure. … … 459 487 460 488 // Filter active plugins. 461 add_filter( 'option_active_plugins', array( $this, 'filter_active_plugins' ));462 if ( is_multisite()) {463 add_filter( 'site_option_active_sitewide_plugins', array( $this, 'filter_active_sitewide_plugins' ));489 add_filter('option_active_plugins', array($this, 'filter_active_plugins')); 490 if (is_multisite()) { 491 add_filter('site_option_active_sitewide_plugins', array($this, 'filter_active_sitewide_plugins')); 464 492 } 465 493 466 494 // Filter theme. 467 add_filter( 'pre_option_template', array( $this, 'filter_theme' ));468 add_filter( 'pre_option_stylesheet', array( $this, 'filter_theme' ));495 add_filter('pre_option_template', array($this, 'filter_theme')); 496 add_filter('pre_option_stylesheet', array($this, 'filter_theme')); 469 497 } 470 498 } … … 474 502 * Initializes the live debug mode. 475 503 */ 476 public function init_live_debug_mode() { 477 if ( get_option( self::DEBUG_MODE_OPTION, 'disabled' ) === 'enabled' ) { 478 if ( ! defined( 'WP_DEBUG' ) ) { 479 define( 'WP_DEBUG', true ); 480 } 481 if ( ! defined( 'WP_DEBUG_LOG' ) ) { 482 define( 'WP_DEBUG_LOG', true ); 483 } 484 if ( ! defined( 'WP_DEBUG_DISPLAY' ) ) { 485 define( 'WP_DEBUG_DISPLAY', false ); 504 public function init_live_debug_mode() 505 { 506 if (get_option(self::DEBUG_MODE_OPTION, 'disabled') === 'enabled') { 507 if (!defined('WP_DEBUG')) { 508 define('WP_DEBUG', true); 509 } 510 if (!defined('WP_DEBUG_LOG')) { 511 define('WP_DEBUG_LOG', true); 512 } 513 if (!defined('WP_DEBUG_DISPLAY')) { 514 define('WP_DEBUG_DISPLAY', false); 486 515 } 487 516 // This is necessary for the feature to function as intended. 488 517 // phpcs:ignore WordPress.PHP.IniSet.display_errors_Disallowed, Squiz.PHP.DiscouragedFunctions.Discouraged 489 @ini_set( 'display_errors', 0);518 @ini_set('display_errors', 0); 490 519 } 491 520 } … … 496 525 * @return bool 497 526 */ 498 public function is_troubleshooting_active() { 499 return ! empty( $this->troubleshoot_state ); 527 public function is_troubleshooting_active() 528 { 529 return !empty($this->troubleshoot_state); 500 530 } 501 531 … … 505 535 * @return array|false 506 536 */ 507 public function get_troubleshoot_state() { 537 public function get_troubleshoot_state() 538 { 508 539 return $this->troubleshoot_state; 509 540 } … … 515 546 * @return string 516 547 */ 517 private function get_debug_log_content( $lines_count = 200 ) { 548 private function get_debug_log_content($lines_count = 200) 549 { 518 550 $log_file = WP_CONTENT_DIR . '/debug.log'; 519 551 520 if ( ! file_exists( $log_file ) || ! is_readable( $log_file )) {521 return __( 'debug.log file does not exist or is not readable.', 'debugger-troubleshooter');522 } 523 524 if ( 0 === filesize( $log_file )) {525 return __( 'debug.log is empty.', 'debugger-troubleshooter');552 if (!file_exists($log_file) || !is_readable($log_file)) { 553 return __('debug.log file does not exist or is not readable.', 'debugger-troubleshooter'); 554 } 555 556 if (0 === filesize($log_file)) { 557 return __('debug.log is empty.', 'debugger-troubleshooter'); 526 558 } 527 559 528 560 // More efficient way to read last N lines of a large file. 529 $file = new SplFileObject( $log_file, 'r');530 $file->seek( PHP_INT_MAX);561 $file = new SplFileObject($log_file, 'r'); 562 $file->seek(PHP_INT_MAX); 531 563 $last_line = $file->key(); 532 $lines = new LimitIterator( $file, max( 0, $last_line - $lines_count ), $last_line);533 534 return implode( '', iterator_to_array( $lines ));564 $lines = new LimitIterator($file, max(0, $last_line - $lines_count), $last_line); 565 566 return implode('', iterator_to_array($lines)); 535 567 } 536 568 … … 538 570 * AJAX handler to toggle Live Debug mode. 539 571 */ 540 public function ajax_toggle_debug_mode() { 541 check_ajax_referer( 'debug_troubleshoot_nonce', 'nonce' ); 542 543 if ( ! current_user_can( 'manage_options' ) ) { 544 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'debugger-troubleshooter' ) ) ); 545 } 546 547 $current_status = get_option( self::DEBUG_MODE_OPTION, 'disabled' ); 548 $new_status = ( 'enabled' === $current_status ) ? 'disabled' : 'enabled'; 549 update_option( self::DEBUG_MODE_OPTION, $new_status ); 550 551 if ( 'enabled' === $new_status ) { 552 wp_send_json_success( array( 'message' => __( 'Live Debug mode enabled.', 'debugger-troubleshooter' ) ) ); 572 public function ajax_toggle_debug_mode() 573 { 574 check_ajax_referer('debug_troubleshoot_nonce', 'nonce'); 575 576 if (!current_user_can('manage_options')) { 577 wp_send_json_error(array('message' => __('Permission denied.', 'debugger-troubleshooter'))); 578 } 579 580 $current_status = get_option(self::DEBUG_MODE_OPTION, 'disabled'); 581 $new_status = ('enabled' === $current_status) ? 'disabled' : 'enabled'; 582 update_option(self::DEBUG_MODE_OPTION, $new_status); 583 584 if ('enabled' === $new_status) { 585 wp_send_json_success(array('message' => __('Live Debug mode enabled.', 'debugger-troubleshooter'))); 553 586 } else { 554 wp_send_json_success( array( 'message' => __( 'Live Debug mode disabled.', 'debugger-troubleshooter' ) ));587 wp_send_json_success(array('message' => __('Live Debug mode disabled.', 'debugger-troubleshooter'))); 555 588 } 556 589 } … … 559 592 * AJAX handler to clear the debug log. 560 593 */ 561 public function ajax_clear_debug_log() { 562 check_ajax_referer( 'debug_troubleshoot_nonce', 'nonce' ); 563 564 if ( ! current_user_can( 'manage_options' ) ) { 565 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'debugger-troubleshooter' ) ) ); 594 public function ajax_clear_debug_log() 595 { 596 check_ajax_referer('debug_troubleshoot_nonce', 'nonce'); 597 598 if (!current_user_can('manage_options')) { 599 wp_send_json_error(array('message' => __('Permission denied.', 'debugger-troubleshooter'))); 566 600 } 567 601 568 602 global $wp_filesystem; 569 if ( ! $wp_filesystem) {603 if (!$wp_filesystem) { 570 604 require_once ABSPATH . 'wp-admin/includes/file.php'; 571 605 WP_Filesystem(); … … 574 608 $log_file = WP_CONTENT_DIR . '/debug.log'; 575 609 576 if ( $wp_filesystem->exists( $log_file )) {577 if ( ! $wp_filesystem->is_writable( $log_file )) {578 wp_send_json_error( array( 'message' => __( 'Debug log is not writable.', 'debugger-troubleshooter' ) ));579 } 580 if ( $wp_filesystem->put_contents( $log_file, '' )) {581 wp_send_json_success( array( 'message' => __( 'Debug log cleared successfully.', 'debugger-troubleshooter' ) ));610 if ($wp_filesystem->exists($log_file)) { 611 if (!$wp_filesystem->is_writable($log_file)) { 612 wp_send_json_error(array('message' => __('Debug log is not writable.', 'debugger-troubleshooter'))); 613 } 614 if ($wp_filesystem->put_contents($log_file, '')) { 615 wp_send_json_success(array('message' => __('Debug log cleared successfully.', 'debugger-troubleshooter'))); 582 616 } else { 583 wp_send_json_error( array( 'message' => __( 'Could not clear the debug log.', 'debugger-troubleshooter' ) ));617 wp_send_json_error(array('message' => __('Could not clear the debug log.', 'debugger-troubleshooter'))); 584 618 } 585 619 } else { 586 wp_send_json_success( array( 'message' => __( 'Debug log does not exist.', 'debugger-troubleshooter' ) ));620 wp_send_json_success(array('message' => __('Debug log does not exist.', 'debugger-troubleshooter'))); 587 621 } 588 622 } … … 595 629 * @return array Filtered array of active plugins. 596 630 */ 597 public function filter_active_plugins( $plugins ) { 598 if ( $this->is_troubleshooting_active() && isset( $this->troubleshoot_state['plugins'] ) ) { 631 public function filter_active_plugins($plugins) 632 { 633 if ($this->is_troubleshooting_active() && isset($this->troubleshoot_state['plugins'])) { 599 634 return $this->troubleshoot_state['plugins']; 600 635 } … … 608 643 * @return array Filtered array of active sitewide plugins. 609 644 */ 610 public function filter_active_sitewide_plugins( $plugins ) { 611 if ( $this->is_troubleshooting_active() && isset( $this->troubleshoot_state['sitewide_plugins'] ) ) { 645 public function filter_active_sitewide_plugins($plugins) 646 { 647 if ($this->is_troubleshooting_active() && isset($this->troubleshoot_state['sitewide_plugins'])) { 612 648 // Convert indexed array from cookie back to associative array expected by 'active_sitewide_plugins'. 613 649 $new_plugins = array(); 614 foreach ( $this->troubleshoot_state['sitewide_plugins'] as $plugin_file) {615 $new_plugins[ $plugin_file] = time(); // Value doesn't matter much for activation state.650 foreach ($this->troubleshoot_state['sitewide_plugins'] as $plugin_file) { 651 $new_plugins[$plugin_file] = time(); // Value doesn't matter much for activation state. 616 652 } 617 653 return $new_plugins; … … 626 662 * @return string|false Filtered theme stylesheet or template. 627 663 */ 628 public function filter_theme( $theme ) { 629 if ( $this->is_troubleshooting_active() && isset( $this->troubleshoot_state['theme'] ) ) { 664 public function filter_theme($theme) 665 { 666 if ($this->is_troubleshooting_active() && isset($this->troubleshoot_state['theme'])) { 630 667 return $this->troubleshoot_state['theme']; 631 668 } … … 636 673 * AJAX handler to toggle troubleshooting mode on/off. 637 674 */ 638 public function ajax_toggle_troubleshoot_mode() { 639 check_ajax_referer( 'debug_troubleshoot_nonce', 'nonce' ); 640 641 if ( ! current_user_can( 'manage_options' ) ) { 642 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'debugger-troubleshooter' ) ) ); 643 } 644 645 $enable_mode = isset( $_POST['enable'] ) ? (bool) $_POST['enable'] : false; 646 647 if ( $enable_mode ) { 675 public function ajax_toggle_troubleshoot_mode() 676 { 677 check_ajax_referer('debug_troubleshoot_nonce', 'nonce'); 678 679 if (!current_user_can('manage_options')) { 680 wp_send_json_error(array('message' => __('Permission denied.', 'debugger-troubleshooter'))); 681 } 682 683 $enable_mode = isset($_POST['enable']) ? (bool) $_POST['enable'] : false; 684 685 if ($enable_mode) { 648 686 // Get current active plugins and theme to initialize the troubleshooting state. 649 $current_active_plugins = get_option( 'active_plugins', array());650 $current_theme = get_stylesheet();651 $current_sitewide_plugins = is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() )) : array();687 $current_active_plugins = get_option('active_plugins', array()); 688 $current_theme = get_stylesheet(); 689 $current_sitewide_plugins = is_multisite() ? array_keys(get_site_option('active_sitewide_plugins', array())) : array(); 652 690 653 691 $state = array( 654 'theme' => $current_theme,655 'plugins' => $current_active_plugins,692 'theme' => $current_theme, 693 'plugins' => $current_active_plugins, 656 694 'sitewide_plugins' => $current_sitewide_plugins, 657 'timestamp' => time(),695 'timestamp' => time(), 658 696 ); 659 697 // Set cookie with HttpOnly flag for security, and secure flag if site is HTTPS. 660 setcookie( self::TROUBLESHOOT_COOKIE, wp_json_encode( $state), array(661 'expires' => time() + DAY_IN_SECONDS,662 'path' => COOKIEPATH,663 'domain' => COOKIE_DOMAIN,698 setcookie(self::TROUBLESHOOT_COOKIE, wp_json_encode($state), array( 699 'expires' => time() + DAY_IN_SECONDS, 700 'path' => COOKIEPATH, 701 'domain' => COOKIE_DOMAIN, 664 702 'samesite' => 'Lax', // or 'Strict' if preferred, 'Lax' is a good balance. 665 703 'httponly' => true, 666 'secure' => is_ssl(),667 ) );668 wp_send_json_success( array( 'message' => __( 'Troubleshooting mode activated.', 'debugger-troubleshooter' ) ));704 'secure' => is_ssl(), 705 )); 706 wp_send_json_success(array('message' => __('Troubleshooting mode activated.', 'debugger-troubleshooter'))); 669 707 } else { 670 708 // Unset the cookie to exit troubleshooting mode. 671 setcookie( self::TROUBLESHOOT_COOKIE, '', array(672 'expires' => time() - 3600, // Expire the cookie.673 'path' => COOKIEPATH,674 'domain' => COOKIE_DOMAIN,709 setcookie(self::TROUBLESHOOT_COOKIE, '', array( 710 'expires' => time() - 3600, // Expire the cookie. 711 'path' => COOKIEPATH, 712 'domain' => COOKIE_DOMAIN, 675 713 'samesite' => 'Lax', 676 714 'httponly' => true, 677 'secure' => is_ssl(),678 ) );679 wp_send_json_success( array( 'message' => __( 'Troubleshooting mode deactivated.', 'debugger-troubleshooter' ) ));715 'secure' => is_ssl(), 716 )); 717 wp_send_json_success(array('message' => __('Troubleshooting mode deactivated.', 'debugger-troubleshooter'))); 680 718 } 681 719 } … … 684 722 * AJAX handler to update troubleshooting state (theme/plugins). 685 723 */ 686 public function ajax_update_troubleshoot_state() { 687 check_ajax_referer( 'debug_troubleshoot_nonce', 'nonce' ); 688 689 if ( ! current_user_can( 'manage_options' ) ) { 690 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'debugger-troubleshooter' ) ) ); 724 public function ajax_update_troubleshoot_state() 725 { 726 check_ajax_referer('debug_troubleshoot_nonce', 'nonce'); 727 728 if (!current_user_can('manage_options')) { 729 wp_send_json_error(array('message' => __('Permission denied.', 'debugger-troubleshooter'))); 691 730 } 692 731 693 732 // Sanitize inputs. 694 $selected_theme = isset( $_POST['theme'] ) ? sanitize_text_field( wp_unslash( $_POST['theme'] )) : get_stylesheet();695 $selected_plugins = isset( $_POST['plugins'] ) && is_array( $_POST['plugins'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['plugins'] )) : array();733 $selected_theme = isset($_POST['theme']) ? sanitize_text_field(wp_unslash($_POST['theme'])) : get_stylesheet(); 734 $selected_plugins = isset($_POST['plugins']) && is_array($_POST['plugins']) ? array_map('sanitize_text_field', wp_unslash($_POST['plugins'])) : array(); 696 735 697 736 // For multisite, we need to distinguish regular active plugins from network active ones. 698 737 $all_plugins = get_plugins(); // Get all installed plugins to validate existence. 699 $current_sitewide_plugins = is_multisite() ? array_keys( get_site_option( 'active_sitewide_plugins', array() )) : array();738 $current_sitewide_plugins = is_multisite() ? array_keys(get_site_option('active_sitewide_plugins', array())) : array(); 700 739 701 740 $new_active_plugins = array(); 702 741 $new_active_sitewide_plugins = array(); 703 742 704 foreach ( $selected_plugins as $plugin_file) {743 foreach ($selected_plugins as $plugin_file) { 705 744 // Check if the plugin file actually exists in the plugin directory. 706 if ( isset( $all_plugins[ $plugin_file ] )) {745 if (isset($all_plugins[$plugin_file])) { 707 746 // If it's a network active plugin, add it to the sitewide array. 708 if ( is_multisite() && in_array( $plugin_file, $current_sitewide_plugins, true )) {747 if (is_multisite() && in_array($plugin_file, $current_sitewide_plugins, true)) { 709 748 $new_active_sitewide_plugins[] = $plugin_file; 710 749 } else { … … 716 755 717 756 $state = array( 718 'theme' => $selected_theme,719 'plugins' => $new_active_plugins,757 'theme' => $selected_theme, 758 'plugins' => $new_active_plugins, 720 759 'sitewide_plugins' => $new_active_sitewide_plugins, 721 'timestamp' => time(),760 'timestamp' => time(), 722 761 ); 723 762 724 763 // Set cookie with HttpOnly flag for security, and secure flag if site is HTTPS. 725 setcookie( self::TROUBLESHOOT_COOKIE, wp_json_encode( $state), array(726 'expires' => time() + DAY_IN_SECONDS,727 'path' => COOKIEPATH,728 'domain' => COOKIE_DOMAIN,764 setcookie(self::TROUBLESHOOT_COOKIE, wp_json_encode($state), array( 765 'expires' => time() + DAY_IN_SECONDS, 766 'path' => COOKIEPATH, 767 'domain' => COOKIE_DOMAIN, 729 768 'samesite' => 'Lax', 730 769 'httponly' => true, 731 'secure' => is_ssl(),732 ) );733 wp_send_json_success( array( 'message' => __( 'Troubleshooting state updated successfully. Refreshing page...', 'debugger-troubleshooter' ) ));770 'secure' => is_ssl(), 771 )); 772 wp_send_json_success(array('message' => __('Troubleshooting state updated successfully. Refreshing page...', 'debugger-troubleshooter'))); 734 773 } 735 774 … … 737 776 * Display an admin notice if troubleshooting mode is active. 738 777 */ 739 public function troubleshooting_mode_notice() { 740 if ( $this->is_troubleshooting_active() ) { 741 $troubleshoot_url = admin_url( 'tools.php?page=debug-troubleshooter' ); 778 public function troubleshooting_mode_notice() 779 { 780 if ($this->is_troubleshooting_active()) { 781 $troubleshoot_url = admin_url('tools.php?page=debug-troubleshooter'); 742 782 ?> 743 783 <div class="notice notice-warning is-dismissible debug-troubleshoot-notice"> 744 784 <p> 745 <strong><?php esc_html_e( 'Troubleshooting Mode is Active!', 'debugger-troubleshooter' ); ?></strong> 746 <?php esc_html_e( 'You are currently in a special troubleshooting session. Your simulated theme and plugin states are not affecting the live site for other visitors.', 'debugger-troubleshooter' ); ?> 747 <a href="<?php echo esc_url( $troubleshoot_url ); ?>"><?php esc_html_e( 'Go to Debugger & Troubleshooter page to manage.', 'debugger-troubleshooter' ); ?></a> 785 <strong><?php esc_html_e('Troubleshooting Mode is Active!', 'debugger-troubleshooter'); ?></strong> 786 <?php esc_html_e('You are currently in a special troubleshooting session. Your simulated theme and plugin states are not affecting the live site for other visitors.', 'debugger-troubleshooter'); ?> 787 <a 788 href="<?php echo esc_url($troubleshoot_url); ?>"><?php esc_html_e('Go to Debugger & Troubleshooter page to manage.', 'debugger-troubleshooter'); ?></a> 748 789 </p> 749 790 </div> … … 754 795 * Initializes the user simulation mode. 755 796 */ 756 public function init_user_simulation() { 757 if ( isset( $_COOKIE[ self::SIMULATE_USER_COOKIE ] ) ) { 758 $this->simulated_user_id = (int) $_COOKIE[ self::SIMULATE_USER_COOKIE ]; 759 797 public function init_user_simulation() 798 { 799 if (isset($_COOKIE[self::SIMULATE_USER_COOKIE])) { 800 $this->simulated_user_id = (int) $_COOKIE[self::SIMULATE_USER_COOKIE]; 801 760 802 // Hook into determine_current_user to override the user ID. 761 803 // Priority 20 ensures we run after most standard authentication checks. 762 add_filter( 'determine_current_user', array( $this, 'simulate_user_filter' ), 20);804 add_filter('determine_current_user', array($this, 'simulate_user_filter'), 20); 763 805 } 764 806 } … … 770 812 * @return int|false The simulated user ID or the original ID. 771 813 */ 772 public function simulate_user_filter( $user_id ) { 773 if ( $this->simulated_user_id ) { 814 public function simulate_user_filter($user_id) 815 { 816 if ($this->simulated_user_id) { 774 817 return $this->simulated_user_id; 775 818 } … … 782 825 * @return bool 783 826 */ 784 public function is_simulating_user() { 785 return ! empty( $this->simulated_user_id ); 827 public function is_simulating_user() 828 { 829 return !empty($this->simulated_user_id); 786 830 } 787 831 … … 789 833 * Renders the User Role Simulator section content. 790 834 */ 791 public function render_user_simulation_section() { 792 $users = get_users( array( 'fields' => array( 'ID', 'display_name', 'user_login' ), 'number' => 50 ) ); // Limit to 50 for performance in dropdown 835 public function render_user_simulation_section() 836 { 837 $users = get_users(array('fields' => array('ID', 'display_name', 'user_login'), 'number' => 50)); // Limit to 50 for performance in dropdown 793 838 $roles = wp_roles()->get_names(); 794 839 ?> 795 840 <div class="user-simulation-controls"> 796 841 <div class="debug-troubleshooter-card"> 797 <h3><?php esc_html_e( 'Select User to Simulate', 'debugger-troubleshooter'); ?></h3>842 <h3><?php esc_html_e('Select User to Simulate', 'debugger-troubleshooter'); ?></h3> 798 843 <div class="flex items-center gap-4"> 799 844 <select id="simulate-user-select" class="regular-text"> 800 <option value=""><?php esc_html_e( '-- Select a User --', 'debugger-troubleshooter'); ?></option>801 <?php foreach ( $users as $user ): ?>802 <option value="<?php echo esc_attr( $user->ID); ?>">803 <?php echo esc_html( $user->display_name . ' (' . $user->user_login . ')'); ?>845 <option value=""><?php esc_html_e('-- Select a User --', 'debugger-troubleshooter'); ?></option> 846 <?php foreach ($users as $user): ?> 847 <option value="<?php echo esc_attr($user->ID); ?>"> 848 <?php echo esc_html($user->display_name . ' (' . $user->user_login . ')'); ?> 804 849 </option> 805 850 <?php endforeach; ?> 806 851 </select> 807 <button id="simulate-user-btn" class="button button-primary"><?php esc_html_e( 'Simulate User', 'debugger-troubleshooter' ); ?></button> 852 <button id="simulate-user-btn" 853 class="button button-primary"><?php esc_html_e('Simulate User', 'debugger-troubleshooter'); ?></button> 808 854 </div> 809 855 <p class="description mt-2"> 810 <?php esc_html_e( 'Note: You can exit the simulation at any time using the "Exit Simulation" button in the Admin Bar.', 'debugger-troubleshooter'); ?>856 <?php esc_html_e('Note: You can exit the simulation at any time using the "Exit Simulation" button in the Admin Bar.', 'debugger-troubleshooter'); ?> 811 857 </p> 812 858 </div> … … 820 866 * @param WP_Admin_Bar $wp_admin_bar The admin bar object. 821 867 */ 822 public function admin_bar_exit_simulation( $wp_admin_bar ) { 823 if ( $this->is_simulating_user() ) { 824 $wp_admin_bar->add_node( array( 825 'id' => 'debug-troubleshooter-exit-sim', 826 'title' => '<span style="color: #ff4444; font-weight: bold;">' . __( 'Exit User Simulation', 'debugger-troubleshooter' ) . '</span>', 827 'href' => '#', 828 'meta' => array( 868 public function admin_bar_exit_simulation($wp_admin_bar) 869 { 870 if ($this->is_simulating_user()) { 871 $wp_admin_bar->add_node(array( 872 'id' => 'debug-troubleshooter-exit-sim', 873 'title' => '<span style="color: #ff4444; font-weight: bold;">' . __('Exit User Simulation', 'debugger-troubleshooter') . '</span>', 874 'href' => '#', 875 'meta' => array( 829 876 'onclick' => 'debugTroubleshootExitSimulation(); return false;', 830 'title' => __( 'Click to return to your original user account', 'debugger-troubleshooter'),877 'title' => __('Click to return to your original user account', 'debugger-troubleshooter'), 831 878 ), 832 ) );879 )); 833 880 834 881 // Add inline script for the exit action since we might be on the frontend 835 882 // where our admin.js isn't enqueued, or we need a global handler. 836 add_action( 'wp_footer', array( $this, 'print_exit_simulation_script' ));837 add_action( 'admin_footer', array( $this, 'print_exit_simulation_script' ));883 add_action('wp_footer', array($this, 'print_exit_simulation_script')); 884 add_action('admin_footer', array($this, 'print_exit_simulation_script')); 838 885 } 839 886 } … … 842 889 * Prints the inline script for exiting simulation from the admin bar. 843 890 */ 844 public function print_exit_simulation_script() { 891 public function print_exit_simulation_script() 892 { 845 893 ?> 846 894 <script type="text/javascript"> 847 function debugTroubleshootExitSimulation() {848 if (confirm('<?php echo esc_js( __( 'Are you sure you want to exit User Simulation?', 'debugger-troubleshooter' )); ?>')) {849 var data = new FormData();850 data.append('action', 'debug_troubleshoot_toggle_simulate_user');851 data.append('enable', '0');852 // We might not have the nonce available globally on frontend, so we rely on cookie check in backend mostly,853 // but for AJAX we need it. If we are on frontend, we might need to expose it.854 // For simplicity in this MVP, we'll assume admin-ajax is accessible.855 // SECURITY NOTE: In a real scenario, we should localize the nonce on wp_enqueue_scripts as well if we want frontend support.856 // For now, let's try to fetch it from a global if available, or just rely on the cookie clearing which is less secure but functional for a dev tool.857 // BETTER APPROACH: Use a dedicated endpoint or just a simple GET parameter that we intercept on init to clear the cookie.858 859 // Let's use a simple redirect to a URL that handles the exit.860 window.location.href = '<?php echo esc_url( admin_url( 'admin-ajax.php?action=debug_troubleshoot_toggle_simulate_user&enable=0' )); ?>';861 }862 }895 function debugTroubleshootExitSimulation() { 896 if (confirm('<?php echo esc_js(__('Are you sure you want to exit User Simulation?', 'debugger-troubleshooter')); ?>')) { 897 var data = new FormData(); 898 data.append('action', 'debug_troubleshoot_toggle_simulate_user'); 899 data.append('enable', '0'); 900 // We might not have the nonce available globally on frontend, so we rely on cookie check in backend mostly, 901 // but for AJAX we need it. If we are on frontend, we might need to expose it. 902 // For simplicity in this MVP, we'll assume admin-ajax is accessible. 903 // SECURITY NOTE: In a real scenario, we should localize the nonce on wp_enqueue_scripts as well if we want frontend support. 904 // For now, let's try to fetch it from a global if available, or just rely on the cookie clearing which is less secure but functional for a dev tool. 905 // BETTER APPROACH: Use a dedicated endpoint or just a simple GET parameter that we intercept on init to clear the cookie. 906 907 // Let's use a simple redirect to a URL that handles the exit. 908 window.location.href = '<?php echo esc_url(admin_url('admin-ajax.php?action=debug_troubleshoot_toggle_simulate_user&enable=0')); ?>'; 909 } 910 } 863 911 </script> 864 912 <?php … … 868 916 * AJAX handler to toggle User Simulation. 869 917 */ 870 public function ajax_toggle_simulate_user() { 918 public function ajax_toggle_simulate_user() 919 { 871 920 // Note: For the "Exit" action via GET request (from Admin Bar), we might not have a nonce. 872 921 // Since this is a dev tool and we are just clearing a cookie, the risk is low, but ideally we'd check a nonce. 873 922 // For the "Enter" action (POST), we definitely check the nonce. 874 875 $is_post = isset( $_SERVER['REQUEST_METHOD']) && 'POST' === $_SERVER['REQUEST_METHOD'];876 if ( $is_post) {877 check_ajax_referer( 'debug_troubleshoot_nonce', 'nonce');878 } 879 880 if ( ! current_user_can( 'manage_options' ) && ! $this->is_simulating_user()) {923 924 $is_post = isset($_SERVER['REQUEST_METHOD']) && 'POST' === $_SERVER['REQUEST_METHOD']; 925 if ($is_post) { 926 check_ajax_referer('debug_troubleshoot_nonce', 'nonce'); 927 } 928 929 if (!current_user_can('manage_options') && !$this->is_simulating_user()) { 881 930 // Only allow admins to START simulation. 882 931 // Anyone (simulated user) can STOP simulation. 883 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'debugger-troubleshooter' ) ));884 } 885 886 $enable = isset( $_REQUEST['enable']) ? (bool) $_REQUEST['enable'] : false;887 $user_id = isset( $_REQUEST['user_id']) ? (int) $_REQUEST['user_id'] : 0;888 889 if ( $enable && $user_id) {932 wp_send_json_error(array('message' => __('Permission denied.', 'debugger-troubleshooter'))); 933 } 934 935 $enable = isset($_REQUEST['enable']) ? (bool) $_REQUEST['enable'] : false; 936 $user_id = isset($_REQUEST['user_id']) ? (int) $_REQUEST['user_id'] : 0; 937 938 if ($enable && $user_id) { 890 939 // Set cookie 891 setcookie( self::SIMULATE_USER_COOKIE, $user_id, array(892 'expires' => time() + DAY_IN_SECONDS,893 'path' => COOKIEPATH,894 'domain' => COOKIE_DOMAIN,940 setcookie(self::SIMULATE_USER_COOKIE, $user_id, array( 941 'expires' => time() + DAY_IN_SECONDS, 942 'path' => COOKIEPATH, 943 'domain' => COOKIE_DOMAIN, 895 944 'samesite' => 'Lax', 896 945 'httponly' => true, 897 'secure' => is_ssl(),898 ) );899 wp_send_json_success( array( 'message' => __( 'User simulation activated. Reloading...', 'debugger-troubleshooter' ) ));946 'secure' => is_ssl(), 947 )); 948 wp_send_json_success(array('message' => __('User simulation activated. Reloading...', 'debugger-troubleshooter'))); 900 949 } else { 901 950 // Clear cookie 902 setcookie( self::SIMULATE_USER_COOKIE, '', array(903 'expires' => time() - 3600,904 'path' => COOKIEPATH,905 'domain' => COOKIE_DOMAIN,951 setcookie(self::SIMULATE_USER_COOKIE, '', array( 952 'expires' => time() - 3600, 953 'path' => COOKIEPATH, 954 'domain' => COOKIE_DOMAIN, 906 955 'samesite' => 'Lax', 907 956 'httponly' => true, 908 'secure' => is_ssl(),909 ) );910 911 if ( ! $is_post) {957 'secure' => is_ssl(), 958 )); 959 960 if (!$is_post) { 912 961 // If it was a GET request (from Admin Bar), redirect back to home or dashboard. 913 wp_safe_redirect( admin_url());962 wp_safe_redirect(admin_url()); 914 963 exit; 915 964 } 916 917 wp_send_json_success( array( 'message' => __( 'User simulation deactivated.', 'debugger-troubleshooter' ) ));965 966 wp_send_json_success(array('message' => __('User simulation deactivated.', 'debugger-troubleshooter'))); 918 967 } 919 968 } -
debugger-troubleshooter/trunk/readme.txt
r3400548 r3459095 5 5 Requires PHP: 7.4 6 6 Tested up to: 6.8 7 Stable tag: 1.3. 17 Stable tag: 1.3.2 8 8 License: GPL-2.0+ 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.txt … … 92 92 == Changelog == 93 93 94 =1.3.2 - 2026-02-11 = 95 * **Fix:** Resolved a UI conflict where the confirmation modal appeared automatically upon page load due to CSS class interference from other plugins. 96 * **Fix:** Improved modal layering (z-index) to ensure the Success/Error alert modal correctly appears above the confirmation modal. 97 * **Fix:** Updated the "Confirm" action in the Debug Log viewer to automatically close the confirmation dialog before showing the result, preventing the UI from becoming unclickable. 98 * **Enhancement:** Hardened the .hidden CSS utility with !important to prevent external themes or plugins from forcing hidden elements to display. 99 94 100 = 1.3.1 - 2025-11-21 = 95 101 * **Fix:** Resolved a critical issue where admin scripts were not loading due to a hook name mismatch. … … 125 131 == Upgrade Notice == 126 132 133 = 1.3.2 = 134 This release fixes a UI conflict where the confirmation modal appeared automatically upon page load due to CSS class interference from other plugins. 135 127 136 = 1.3.1 = 128 137 This release fixes a critical bug affecting the plugin's layout and functionality, and improves code standards. … … 136 145 = 1.2.0 = 137 146 This version adds a major new feature: "Live Debugging." You can now enable WP_DEBUG and view the debug.log file directly from the plugin's admin page without editing any files. 147
Note: See TracChangeset
for help on using the changeset viewer.