PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / trunk
Search Atlas SEO – OTTO AI SEO Automation for WordPress vtrunk
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / admin / class-metasync-dev-panel.php

class-metasync-dev-panel.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress trunk, at admin/class-metasync-dev-panel.php

653 lines 21.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // If this file is called directly, abort.
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Developer Panel for MetaSync Plugin
9 *
10 * Provides a password-protected panel for switching between production and staging endpoints.
11 *
12 * @package Metasync
13 * @subpackage Metasync/admin
14 */
15
16 /**
17 * The developer panel class.
18 *
19 * Manages the developer tools panel with endpoint switching capabilities.
20 *
21 * @package Metasync
22 * @subpackage Metasync/admin
23 */
24 class Metasync_Dev_Panel {
25
26 /**
27 * The ID of this plugin.
28 *
29 * @var string
30 */
31 private $plugin_name;
32
33 /**
34 * The version of this plugin.
35 *
36 * @var string
37 */
38 private $version;
39
40 /**
41 * Option name for storing the developer panel password.
42 */
43 const PASSWORD_OPTION = 'metasync_dev_panel_password';
44 const PASSWORD_HASH_PREFIX = 'metasync-hash:';
45
46 /**
47 * Authentication manager instance.
48 *
49 * @var Metasync_Auth_Manager
50 */
51 private $auth;
52
53 /**
54 * Initialize the class and set its properties.
55 *
56 * @param string $plugin_name The name of this plugin.
57 * @param string $version The version of this plugin.
58 */
59 public function __construct( $plugin_name, $version ) {
60 $this->plugin_name = $plugin_name;
61 $this->version = $version;
62
63 // Initialize auth manager with 30-minute timeout
64 if ( class_exists( 'Metasync_Auth_Manager' ) ) {
65 $this->auth = new Metasync_Auth_Manager( 'dev_panel', 1800 );
66 }
67
68 // Register hooks
69 add_action( 'admin_menu', array( $this, 'add_dev_panel_menu' ) );
70 add_action( 'admin_notices', array( $this, 'display_staging_mode_banner' ) );
71 add_action( 'wp_ajax_metasync_switch_endpoints', array( $this, 'ajax_switch_endpoints' ) );
72 add_action( 'wp_ajax_metasync_update_dev_password', array( $this, 'ajax_update_dev_password' ) );
73 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
74 }
75
76 /**
77 * Hash and store the developer tools password.
78 *
79 * @param string $password Plaintext password.
80 * @return bool Whether the option was updated.
81 */
82 private function store_hashed_password( $password ) {
83 $stored = self::PASSWORD_HASH_PREFIX . wp_hash_password( $password );
84
85 return update_option( self::PASSWORD_OPTION, $stored );
86 }
87
88 /**
89 * Verify the stored password and migrate legacy plaintext values on success.
90 *
91 * @param string $submitted Submitted plaintext password.
92 * @return bool Whether the password is valid.
93 */
94 private function verify_dev_password( $submitted ) {
95 $stored = get_option( self::PASSWORD_OPTION, '' );
96
97 if ( ! is_string( $stored ) || $stored === '' ) {
98 return false;
99 }
100
101 if ( strpos( $stored, self::PASSWORD_HASH_PREFIX ) === 0 ) {
102 $hash = substr( $stored, strlen( self::PASSWORD_HASH_PREFIX ) );
103
104 return wp_check_password( $submitted, $hash );
105 }
106
107 if ( hash_equals( $stored, $submitted ) ) {
108 $this->store_hashed_password( $submitted );
109 return true;
110 }
111
112 return false;
113 }
114
115 /**
116 * Register the developer panel menu (hidden from menu, accessible via URL).
117 */
118 public function add_dev_panel_menu() {
119 // Add hidden submenu page (no parent = hidden from menu)
120 // Accessible to admins only (developer tools)
121 add_submenu_page(
122 null,
123 'Developer Tools',
124 'Developer Tools',
125 'manage_options',
126 Metasync_Admin::$page_slug . '-dev-tools',
127 array( $this, 'render_dev_panel_page' )
128 );
129 }
130
131 /**
132 * Display staging mode banner on all admin pages.
133 */
134 public function display_staging_mode_banner() {
135 // Only show if staging mode is active
136 if ( ! Metasync_Endpoint_Manager::is_staging_mode() ) {
137 return;
138 }
139
140 // Only show in admin area
141 if ( ! is_admin() ) {
142 return;
143 }
144
145 // Don't show on dev panel page itself
146 $current_page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
147 if ( $current_page === Metasync_Admin::$page_slug . '-dev-tools' ) {
148 return;
149 }
150
151 $dev_panel_url = admin_url( 'admin.php?page=' . Metasync_Admin::$page_slug . '-dev-tools' );
152 ?>
153 <div class="notice notice-warning" style="border-left: 4px solid #ff9800; background: #fff3cd; padding: 12px 15px;">
154 <p style="font-size: 14px; margin: 0;">
155 <strong>STAGING MODE ACTIVE</strong><br>
156 All <?php echo esc_html( Metasync::get_effective_plugin_name() ); ?> API endpoints are currently pointing to STAGING servers. This is intended for development and testing only.
157 <br>
158 <a href="<?php echo esc_url( $dev_panel_url ); ?>" class="button button-secondary" style="margin-top: 8px;">
159 <span class="dashicons dashicons-admin-tools" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Open Developer Tools
160 </a>
161 </p>
162 </div>
163 <?php
164 }
165
166 /**
167 * Enqueue scripts and styles for dev panel.
168 *
169 * @param string $hook The current admin page.
170 */
171 public function enqueue_scripts( $hook ) {
172 // Only load on dev panel page
173 if ( isset( $_GET['page'] ) && $_GET['page'] === Metasync_Admin::$page_slug . '-dev-tools' ) {
174 wp_enqueue_style( 'metasync-admin' );
175 }
176 }
177
178 /**
179 * Render the developer panel page.
180 */
181 public function render_dev_panel_page() {
182 // Check permissions (admin only for developer tools)
183 if ( ! current_user_can( 'manage_options' ) ) {
184 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'metasync' ) );
185 }
186
187 // Check if password is set
188 $saved_password = get_option( self::PASSWORD_OPTION, '' );
189
190 // If no password is set, show password setup form
191 if ( empty( $saved_password ) ) {
192 $this->render_password_setup_form();
193 return;
194 }
195
196 // Check authentication
197 if ( ! $this->auth || ! $this->auth->has_access() ) {
198 $this->render_password_form();
199 return;
200 }
201
202 // User is authenticated - show endpoint switcher
203 $this->render_endpoint_switcher();
204 }
205
206 /**
207 * Render the password setup form (first-time setup).
208 */
209 private function render_password_setup_form() {
210 $setup_error = '';
211
212 // Handle password setup submission
213 if ( isset( $_POST['dev_panel_setup_submit'] ) ) {
214 if ( wp_verify_nonce( $_POST['dev_panel_setup_nonce'], 'metasync_dev_panel_setup' ) ) {
215 $new_password = isset( $_POST['dev_panel_new_password'] ) ? (string) wp_unslash( $_POST['dev_panel_new_password'] ) : '';
216 $confirm_password = isset( $_POST['dev_panel_confirm_password'] ) ? (string) wp_unslash( $_POST['dev_panel_confirm_password'] ) : '';
217
218 if ( empty( $new_password ) ) {
219 $setup_error = 'Password cannot be empty.';
220 } elseif ( $new_password !== $confirm_password ) {
221 $setup_error = 'Passwords do not match.';
222 } elseif ( strlen( $new_password ) < 6 ) {
223 $setup_error = 'Password must be at least 6 characters long.';
224 } else {
225 $result = $this->store_hashed_password( $new_password );
226 if ( $result ) {
227 // Grant access immediately
228 if ( $this->auth ) {
229 $this->auth->grant_transient_access();
230 }
231 // Refresh to show authenticated state - add timestamp to prevent caching
232 wp_safe_redirect( add_query_arg( 'setup', 'complete', $_SERVER['REQUEST_URI'] ) );
233 exit;
234 } else {
235 $setup_error = 'Failed to save password. Please try again.';
236 }
237 }
238 }
239 }
240 ?>
241 <div class="wrap metasync-dashboard-wrap">
242 <?php Metasync_Admin::render_static_header( 'Developer Tools - Setup' ); ?>
243
244 <div class="dashboard-card" style="max-width: 600px; margin: 0 auto;">
245 <h2 style="text-align: center; color: #fff;">Developer Tools - Initial Setup</h2>
246 <p style="color: #646970; margin-bottom: 30px; text-align: center;">
247 Welcome to the Developer Tools panel. Please create a password to secure access to endpoint switching features.
248 </p>
249
250 <?php if ( ! empty( $setup_error ) ) : ?>
251 <div style="background: #f8d7da; color: #721c24; padding: 12px; border-radius: 6px; margin-bottom: 20px; border: 1px solid #f5c6cb;">
252 <strong>Error:</strong> <?php echo esc_html( $setup_error ); ?>
253 </div>
254 <?php endif; ?>
255
256 <form method="post" action="" style="max-width: 400px; margin: 0 auto;">
257 <?php wp_nonce_field( 'metasync_dev_panel_setup', 'dev_panel_setup_nonce' ); ?>
258
259 <div style="margin-bottom: 20px;">
260 <label for="dev_panel_new_password" style="display: block; font-weight: 600; margin-bottom: 8px; color: #fff;">
261 Create Password
262 </label>
263 <input
264 type="password"
265 id="dev_panel_new_password"
266 name="dev_panel_new_password"
267 placeholder="Enter a strong password (min 6 characters)"
268 style="width: 100%; padding: 12px; border: 2px solid #ddd; border-radius: 6px; font-size: 14px; box-sizing: border-box;"
269 required
270 autocomplete="off"
271 />
272 </div>
273
274 <div style="margin-bottom: 20px;">
275 <label for="dev_panel_confirm_password" style="display: block; font-weight: 600; margin-bottom: 8px; color: #fff;">
276 Confirm Password
277 </label>
278 <input
279 type="password"
280 id="dev_panel_confirm_password"
281 name="dev_panel_confirm_password"
282 placeholder="Re-enter your password"
283 style="width: 100%; padding: 12px; border: 2px solid #ddd; border-radius: 6px; font-size: 14px; box-sizing: border-box;"
284 required
285 autocomplete="off"
286 />
287 </div>
288
289 <div style="text-align: center;">
290 <button
291 type="submit"
292 name="dev_panel_setup_submit"
293 value="1"
294 class="button button-primary"
295 style="padding: 12px 24px; font-size: 14px; font-weight: 600;"
296 >
297 Create Password & Continue
298 </button>
299 </div>
300 </form>
301 </div>
302 </div>
303
304 <script>
305 jQuery(document).ready(function($) {
306 $('#dev_panel_new_password').focus();
307 });
308 </script>
309 <?php
310 }
311
312 /**
313 * Render the password authentication form.
314 */
315 private function render_password_form() {
316 $password_error = '';
317
318 // Handle password submission
319 if ( isset( $_POST['dev_panel_password_submit'] ) ) {
320 if ( wp_verify_nonce( $_POST['dev_panel_nonce'], 'metasync_dev_panel_nonce' ) ) {
321 $submitted_password = isset( $_POST['dev_panel_password'] ) ? (string) wp_unslash( $_POST['dev_panel_password'] ) : '';
322
323 if ( $this->auth && $this->verify_dev_password( $submitted_password ) ) {
324 $this->auth->grant_transient_access();
325 // Refresh page to show authenticated state - add timestamp to prevent caching
326 wp_safe_redirect( add_query_arg( 'login', 'success', $_SERVER['REQUEST_URI'] ) );
327 exit;
328 } else {
329 $password_error = 'Incorrect password. Please try again.';
330 }
331 }
332 }
333 ?>
334 <div class="wrap metasync-dashboard-wrap">
335 <?php Metasync_Admin::render_static_header( 'Developer Tools' ); ?>
336
337 <div class="dashboard-card" style="max-width: 500px; margin: 0 auto;">
338 <h2 style="text-align: center; color: #fff;">Protected Area</h2>
339 <p style="color: #646970; margin-bottom: 30px; text-align: center;">
340 Please enter the password to access the Developer Tools panel.
341 </p>
342
343 <?php if ( ! empty( $password_error ) ) : ?>
344 <div style="background: #f8d7da; color: #721c24; padding: 12px; border-radius: 6px; margin-bottom: 20px; border: 1px solid #f5c6cb;">
345 <strong>Access Denied:</strong> <?php echo esc_html( $password_error ); ?>
346 </div>
347 <?php endif; ?>
348
349 <form method="post" action="" style="max-width: 400px; margin: 0 auto;">
350 <?php wp_nonce_field( 'metasync_dev_panel_nonce', 'dev_panel_nonce' ); ?>
351
352 <div style="margin-bottom: 20px;">
353 <label for="dev_panel_password" style="display: block; font-weight: 600; margin-bottom: 8px; color: #fff;">
354 Enter Password
355 </label>
356 <input
357 type="password"
358 id="dev_panel_password"
359 name="dev_panel_password"
360 placeholder="Enter developer panel password"
361 style="width: 100%; padding: 12px; border: 2px solid #ddd; border-radius: 6px; font-size: 14px; box-sizing: border-box;"
362 required
363 autocomplete="off"
364 />
365 </div>
366
367 <div style="text-align: center;">
368 <button
369 type="submit"
370 name="dev_panel_password_submit"
371 value="1"
372 class="button button-primary"
373 style="padding: 12px 24px; font-size: 14px; font-weight: 600;"
374 >
375 Submit Password
376 </button>
377 </div>
378 </form>
379 </div>
380 </div>
381
382 <script>
383 jQuery(document).ready(function($) {
384 $('#dev_panel_password').focus();
385 });
386 </script>
387 <?php
388 }
389
390 /**
391 * Render the endpoint switcher interface.
392 */
393 private function render_endpoint_switcher() {
394 $current_mode = Metasync_Endpoint_Manager::get_mode();
395 $all_endpoints = Metasync_Endpoint_Manager::get_all_endpoints();
396 $message = '';
397 $message_type = '';
398
399 // Handle mode switch
400 if ( isset( $_POST['switch_endpoints_submit'] ) ) {
401 if ( wp_verify_nonce( $_POST['switch_endpoints_nonce'], 'metasync_switch_endpoints' ) ) {
402 $new_mode = sanitize_text_field( $_POST['endpoint_mode'] );
403 $result = Metasync_Endpoint_Manager::set_mode( $new_mode );
404
405 if ( $result ) {
406 $message = 'Successfully switched to ' . esc_html( $new_mode ) . ' mode!';
407 $message_type = 'success';
408 $current_mode = $new_mode;
409 $all_endpoints = Metasync_Endpoint_Manager::get_all_endpoints();
410 } else {
411 $message = 'Failed to switch endpoints.';
412 $message_type = 'error';
413 }
414 }
415 }
416
417 // Handle password update
418 if ( isset( $_POST['update_password_submit'] ) ) {
419 if ( wp_verify_nonce( $_POST['update_password_nonce'], 'metasync_update_dev_password' ) ) {
420 $new_password = isset( $_POST['dev_panel_new_password'] ) ? (string) wp_unslash( $_POST['dev_panel_new_password'] ) : '';
421
422 if ( empty( $new_password ) ) {
423 $message = 'Password cannot be empty.';
424 $message_type = 'error';
425 } elseif ( strlen( $new_password ) < 6 ) {
426 $message = 'Password must be at least 6 characters long.';
427 $message_type = 'error';
428 } else {
429 $result = $this->store_hashed_password( $new_password );
430 if ( $result ) {
431 $message = 'Password updated successfully!';
432 $message_type = 'success';
433 } else {
434 $message = 'Failed to update password.';
435 $message_type = 'error';
436 }
437 }
438 }
439 }
440
441 // Handle logout
442 if ( isset( $_POST['logout_submit'] ) ) {
443 if ( wp_verify_nonce( $_POST['logout_nonce'], 'metasync_dev_panel_logout' ) ) {
444 if ( $this->auth ) {
445 $this->auth->revoke_access();
446 }
447 wp_safe_redirect( remove_query_arg( array( 'setup', 'login' ), $_SERVER['REQUEST_URI'] ) );
448 exit;
449 }
450 }
451 ?>
452 <div class="wrap metasync-dashboard-wrap">
453 <?php Metasync_Admin::render_static_header( 'Developer Tools' ); ?>
454
455 <?php if ( ! empty( $message ) ) : ?>
456 <div class="notice notice-<?php echo esc_attr( $message_type ); ?>" style="margin: 15px 0; padding: 12px;">
457 <p><?php echo esc_html( $message ); ?></p>
458 </div>
459 <?php endif; ?>
460
461 <div class="dashboard-card">
462 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
463 <div>
464 <h2 style="margin: 0; color: #fff;">Developer Tools - Endpoint Switching</h2>
465 <p style="color: #646970; margin: 5px 0 0 0;">
466 Switch between production and staging API endpoints for testing.
467 </p>
468 </div>
469 <form method="post" style="margin: 0;">
470 <?php wp_nonce_field( 'metasync_dev_panel_logout', 'logout_nonce' ); ?>
471 <button type="submit" name="logout_submit" value="1" class="button" style="margin: 0;">
472 <span class="dashicons dashicons-exit" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Logout
473 </button>
474 </form>
475 </div>
476
477 <div style="background: <?php echo $current_mode === 'staging' ? '#fff3cd' : '#d1ecf1'; ?>; border-left: 4px solid <?php echo $current_mode === 'staging' ? '#ff9800' : '#0c5460'; ?>; padding: 15px; border-radius: 6px; margin-bottom: 30px;">
478 <h3 style="margin: 0 0 5px 0; font-size: 16px; color: #000;">
479 Current Mode: <span style="color: <?php echo $current_mode === 'staging' ? '#ff9800' : '#0c5460'; ?>; font-weight: 700;">
480 <?php echo esc_html( strtoupper( $current_mode ) ); ?>
481 </span>
482 </h3>
483 <p style="margin: 0; font-size: 13px; color: #3c434a;">
484 <?php
485 if ( $current_mode === 'staging' ) {
486 echo 'All API calls are pointing to staging servers. Remember to switch back to production when done testing!';
487 } else {
488 echo 'All API calls are pointing to production servers (live data).';
489 }
490 ?>
491 </p>
492 </div>
493
494 <h3 style="margin-top: 30px; margin-bottom: 15px; color: #fff;">Switch Endpoint Mode</h3>
495 <form method="post">
496 <?php wp_nonce_field( 'metasync_switch_endpoints', 'switch_endpoints_nonce' ); ?>
497
498 <div style="margin-bottom: 20px;">
499 <label style="display: block; margin-bottom: 10px; padding: 15px; border: 2px solid <?php echo $current_mode === 'production' ? '#2271b1' : '#ddd'; ?>; border-radius: 6px; cursor: pointer; background: <?php echo $current_mode === 'production' ? '#f0f6fc' : '#fff'; ?>; color: #1d2327;">
500 <input type="radio" name="endpoint_mode" value="production" <?php checked( $current_mode, 'production' ); ?> style="margin-right: 10px;">
501 <strong style="color: #1d2327;">Production</strong> - Live <?php echo esc_html( Metasync::get_effective_plugin_name() ); ?> servers (api.searchatlas.com)
502 </label>
503
504 <label style="display: block; margin-bottom: 10px; padding: 15px; border: 2px solid <?php echo $current_mode === 'staging' ? '#d63638' : '#ddd'; ?>; border-radius: 6px; cursor: pointer; background: <?php echo $current_mode === 'staging' ? '#fcf0f1' : '#fff'; ?>; color: #1d2327;">
505 <input type="radio" name="endpoint_mode" value="staging" <?php checked( $current_mode, 'staging' ); ?> style="margin-right: 10px;">
506 <strong style="color: #1d2327;">Staging</strong> - Testing servers (api.staging.searchatlas.com) - For development only
507 </label>
508 </div>
509
510 <button type="submit" name="switch_endpoints_submit" value="1" class="button button-primary">
511 <span class="dashicons dashicons-controls-repeat"></span> Switch Endpoints
512 </button>
513 </form>
514 </div>
515
516 <div class="dashboard-card">
517 <h3 style="color: #fff;">Current Endpoint Configuration</h3>
518 <table class="widefat" style="border: 1px solid #ddd;">
519 <thead>
520 <tr>
521 <th style="padding: 12px; background: #f6f7f7; color: #1d2327;">Endpoint Type</th>
522 <th style="padding: 12px; background: #f6f7f7; color: #1d2327;">Current URL</th>
523 </tr>
524 </thead>
525 <tbody>
526 <?php foreach ( $all_endpoints as $key => $url ) : ?>
527 <tr>
528 <td style="padding: 12px; border-top: 1px solid #ddd; color: #1d2327;"><code><?php echo esc_html( $key ); ?></code></td>
529 <td style="padding: 12px; border-top: 1px solid #ddd; font-family: monospace; font-size: 13px; color: #1d2327;">
530 <?php echo esc_html( $url ); ?>
531 </td>
532 </tr>
533 <?php endforeach; ?>
534 </tbody>
535 </table>
536 </div>
537
538 <div class="dashboard-card">
539 <h3 style="color: #fff;">Password Management</h3>
540 <p style="color: #646970; margin-bottom: 20px;">
541 Update the password required to access this developer panel.
542 </p>
543
544 <form method="post" style="max-width: 500px;">
545 <?php wp_nonce_field( 'metasync_update_dev_password', 'update_password_nonce' ); ?>
546
547 <div style="margin-bottom: 15px;">
548 <label for="dev_panel_new_password" style="display: block; font-weight: 600; margin-bottom: 8px; color: #fff;">
549 New Password (min 6 characters)
550 </label>
551 <input
552 type="password"
553 id="dev_panel_new_password"
554 name="dev_panel_new_password"
555 placeholder="Enter new password"
556 class="regular-text"
557 style="padding: 8px;"
558 />
559 </div>
560
561 <button type="submit" name="update_password_submit" value="1" class="button">
562 Update Password
563 </button>
564 </form>
565 </div>
566 </div>
567 <?php
568 }
569
570 /**
571 * AJAX handler for switching endpoints.
572 */
573 public function ajax_switch_endpoints() {
574 // Verify nonce
575 check_ajax_referer( 'metasync_switch_endpoints', 'nonce' );
576
577 // Developer tools are administrator-only.
578 if ( ! current_user_can( 'manage_options' ) ) {
579 wp_send_json_error( array( 'message' => 'Insufficient permissions' ), 403 );
580 return;
581 }
582
583 // Verify authentication
584 if ( ! $this->auth || ! $this->auth->has_access() ) {
585 wp_send_json_error( array( 'message' => 'Authentication required' ) );
586 return;
587 }
588
589 // Get and validate mode
590 $mode = isset( $_POST['endpoint_mode'] ) ? sanitize_text_field( $_POST['endpoint_mode'] ) : '';
591 if ( ! in_array( $mode, array( 'production', 'staging' ), true ) ) {
592 wp_send_json_error( array( 'message' => 'Invalid mode' ) );
593 return;
594 }
595
596 // Switch mode
597 $result = Metasync_Endpoint_Manager::set_mode( $mode );
598
599 if ( $result ) {
600 wp_send_json_success(
601 array(
602 'message' => "Successfully switched to {$mode} mode",
603 'mode' => $mode,
604 'endpoints' => Metasync_Endpoint_Manager::get_all_endpoints(),
605 )
606 );
607 } else {
608 wp_send_json_error( array( 'message' => 'Failed to switch endpoints' ) );
609 }
610 }
611
612 /**
613 * AJAX handler for updating dev panel password.
614 */
615 public function ajax_update_dev_password() {
616 // Verify nonce
617 check_ajax_referer( 'metasync_update_dev_password', 'nonce' );
618
619 // Developer tools are administrator-only.
620 if ( ! current_user_can( 'manage_options' ) ) {
621 wp_send_json_error( array( 'message' => 'Insufficient permissions' ), 403 );
622 return;
623 }
624
625 // Verify authentication
626 if ( ! $this->auth || ! $this->auth->has_access() ) {
627 wp_send_json_error( array( 'message' => 'Authentication required' ) );
628 return;
629 }
630
631 // Get new password
632 $new_password = isset( $_POST['dev_panel_password'] ) ? (string) wp_unslash( $_POST['dev_panel_password'] ) : '';
633
634 if ( empty( $new_password ) ) {
635 wp_send_json_error( array( 'message' => 'Password cannot be empty' ) );
636 return;
637 }
638
639 if ( strlen( $new_password ) < 6 ) {
640 wp_send_json_error( array( 'message' => 'Password must be at least 6 characters long' ) );
641 return;
642 }
643
644 $result = $this->store_hashed_password( $new_password );
645
646 if ( $result ) {
647 wp_send_json_success( array( 'message' => 'Password updated successfully' ) );
648 } else {
649 wp_send_json_error( array( 'message' => 'Failed to update password' ) );
650 }
651 }
652 }
653