PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / class-new-interface-notice.php
wp-2fa / includes / classes / Admin Last commit date
AdminSettings 1 day ago Controllers 1 day ago Fly-Out 1 day ago Helpers 1 day ago Methods 1 day ago Settings 1 day ago SettingsPages 1 day ago Views 1 day ago class-about-us.php 1 day ago class-docs-and-support.php 1 day ago class-free-support-notice.php 1 day ago class-help-contact-us.php 1 day ago class-license-page.php 1 day ago class-new-interface-notice.php 1 day ago class-plugin-updated-notice.php 1 day ago class-premium-features.php 1 day ago class-profile-section-renderer.php 1 day ago class-settings-page.php 1 day ago class-setup-wizard.php 1 day ago class-top-bar-banner.php 1 day ago class-user-listing.php 1 day ago class-user-notices.php 1 day ago class-user-profile.php 1 day ago class-user-registered.php 1 day ago class-wizard-integration.php 1 day ago index.php 1 day ago
class-new-interface-notice.php
556 lines
1 <?php
2 /**
3 * Responsible for showing the new interface announcement dialog after manual upgrades.
4 *
5 * @package wp2fa
6 * @subpackage admin
7 * @copyright 2026 Melapress
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 */
11
12 declare(strict_types=1);
13
14 namespace WP2FA\Admin;
15
16 use WP2FA\WP2FA;
17 use WP2FA\Utils\Settings_Utils;
18
19 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
20
21 if ( ! class_exists( '\WP2FA\Admin\New_Interface_Notice' ) ) {
22
23 /**
24 * Displays a one-time dialog prompting users to switch to the new interface after a manual plugin upgrade.
25 * Also shows a persistent banner on plugin pages if the user dismissed the dialog.
26 *
27 * @since 4.0.0
28 */
29 class New_Interface_Notice {
30
31 /**
32 * Option name used to flag that the dialog should be shown.
33 *
34 * @var string
35 */
36 public const SHOW_DIALOG_OPTION = 'show-new-interface-dialog';
37
38 /**
39 * Option name used to flag that the persistent banner should be shown.
40 *
41 * @var string
42 */
43 public const SHOW_BANNER_OPTION = 'show-new-interface-banner';
44
45 /**
46 * Initialize hooks.
47 *
48 * @return void
49 *
50 * @since 4.0.0
51 */
52 public static function init() {
53 // If the new interface is already in use, no need to advertise it.
54 if ( Settings_Utils::string_to_bool( WP2FA::get_wp2fa_general_setting( 'use_new_interface' ) ) ) {
55 return;
56 }
57
58 $show_dialog = Settings_Utils::get_option( self::SHOW_DIALOG_OPTION, false );
59 $show_banner = Settings_Utils::get_option( self::SHOW_BANNER_OPTION, false );
60
61 if ( ! $show_dialog && ! $show_banner ) {
62 return;
63 }
64
65 \add_action( 'wp_ajax_wp2fa_switch_new_interface', array( __CLASS__, 'ajax_switch_new_interface' ) );
66 \add_action( 'wp_ajax_wp2fa_dismiss_new_interface_dialog', array( __CLASS__, 'ajax_dismiss_dialog' ) );
67 \add_action( 'wp_ajax_wp2fa_dismiss_new_interface_banner', array( __CLASS__, 'ajax_dismiss_banner' ) );
68
69 if ( $show_dialog ) {
70 \add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_dialog_assets' ) );
71 \add_action( 'admin_footer', array( __CLASS__, 'render_dialog_script' ) );
72 } elseif ( $show_banner ) {
73 \add_action( 'admin_notices', array( __CLASS__, 'render_banner' ) );
74 \add_action( 'network_admin_notices', array( __CLASS__, 'render_banner' ) );
75 }
76 }
77
78 /**
79 * Enqueue the dialog JS and CSS assets.
80 *
81 * @return void
82 *
83 * @since 4.0.0
84 */
85 public static function enqueue_dialog_assets() {
86 if ( ! \current_user_can( 'manage_options' ) ) {
87 return;
88 }
89
90 \wp_enqueue_script( 'wp2fa-dialog' );
91 \wp_enqueue_style( 'wp2fa-dialog' );
92 }
93
94 /**
95 * Outputs inline JavaScript in the admin footer to show the new interface dialog.
96 *
97 * @return void
98 *
99 * @since 4.0.0
100 */
101 public static function render_dialog_script() {
102 if ( ! \current_user_can( 'manage_options' ) ) {
103 return;
104 }
105
106 $nonce = \wp_create_nonce( 'wp2fa_new_interface_dialog' );
107 $icon_url = \esc_url( WP_2FA_URL . 'dist/images/wp-2fa-square.png' );
108 $policies_url = \is_multisite() ? \network_admin_url( 'admin.php?page=wp-2fa-policies' ) : \admin_url( 'admin.php?page=wp-2fa-policies' );
109 ?>
110 <style type="text/css">
111 .wp2fa-new-ui-notice {
112 text-align: left;
113 }
114 .wp2fa-new-ui-notice__header {
115 display: flex;
116 align-items: center;
117 gap: 16px;
118 margin-bottom: 20px;
119 }
120 .wp2fa-new-ui-notice__header img {
121 width: 56px;
122 height: 56px;
123 flex-shrink: 0;
124 }
125 .wp2fa-new-ui-notice__header h2 {
126 margin: 0;
127 font-size: 22px;
128 font-weight: 700;
129 color: #1d2327;
130 line-height: 1.3;
131 max-width: 50%;
132 }
133 .wp2fa-new-ui-notice p {
134 font-size: 14px;
135 color: #3c434a;
136 line-height: 1.7;
137 margin: 0 0 16px;
138 }
139 .wp2fa-new-ui-notice__features {
140 list-style: none;
141 padding: 0;
142 margin: 20px 0 8px;
143 }
144 .wp2fa-new-ui-notice__features li {
145 display: flex;
146 align-items: center;
147 gap: 14px;
148 padding: 3px 0;
149 font-size: 15px;
150 font-weight: 600;
151 color: #1d2327;
152 }
153 .wp2fa-new-ui-notice__features li::before {
154 content: '';
155 flex-shrink: 0;
156 width: 28px;
157 height: 28px;
158 border-radius: 50%;
159 background: #2271b1;
160 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z'/%3E%3C/svg%3E");
161 background-repeat: no-repeat;
162 background-position: center;
163 background-size: 16px;
164 }
165 .wp2fa-dialog-buttons .wp2fa-btn-switch,
166 .wp2fa-dialog-buttons .wp2fa-btn-switch:focus,
167 .wp2fa-dialog-buttons .wp2fa-btn-switch:active {
168 background: #2271b1 !important;
169 border-color: #2271b1 !important;
170 color: #fff !important;
171 font-size: 14px;
172 font-weight: 600;
173 padding: 8px 20px;
174 border-radius: 4px;
175 cursor: pointer;
176 text-decoration: none;
177 display: inline-block;
178 line-height: 1.5;
179 box-shadow: none !important;
180 outline: none;
181 }
182 .wp2fa-dialog-buttons .wp2fa-btn-switch:hover {
183 background: #135e96 !important;
184 border-color: #135e96 !important;
185 color: #fff !important;
186 }
187 .wp2fa-dialog-buttons .wp2fa-btn-keep {
188 background: transparent;
189 border: 0;
190 color: #2271b1;
191 font-size: 14px;
192 font-weight: 500;
193 padding: 8px 0;
194 cursor: pointer;
195 text-decoration: underline;
196 display: inline-block;
197 line-height: 1.5;
198 box-shadow: none;
199 outline: none;
200 }
201 .wp2fa-dialog-buttons .wp2fa-btn-keep:hover {
202 color: #135e96;
203 }
204 .wp2fa-dialog-buttons .wp2fa-btn-keep:focus {
205 color: #135e96;
206 }
207 .wp2fa-new-ui-notice__title {
208 margin: 0 0 16px;
209 font-size: 22px;
210 font-weight: 700;
211 color: #1d2327;
212 line-height: 1.3;
213 }
214 </style>
215 <script type="text/javascript">
216 document.addEventListener( 'DOMContentLoaded', function() {
217 if ( typeof wp2faDialog === 'undefined' ) {
218 return;
219 }
220
221 var ajaxUrl = '<?php echo \esc_url( \admin_url( 'admin-ajax.php' ) ); ?>';
222 var nonce = '<?php echo \esc_attr( $nonce ); ?>';
223 var iconUrl = '<?php echo \esc_js( $icon_url ); ?>';
224
225 var messageHtml = '<div class="wp2fa-new-ui-notice">' +
226 '<div class="wp2fa-new-ui-notice__header">' +
227 '<img src="' + iconUrl + '" alt="WP 2FA" />' +
228 '<h2><?php echo \esc_js( \__( 'Announcing the New WP 2FA Interface Try it now!', 'wp-2fa' ) ); ?></h2>' +
229 '</div>' +
230 '<p><?php echo \esc_js( \__( 'WP 2FA 4.0 introduces a completely redesigned user interface, built to make configuration and day-to-day management easier, faster, and more intuitive.', 'wp-2fa' ) ); ?></p>' +
231 '<p><?php echo \esc_js( \__( 'To ensure a smooth upgrade experience, you are still using the previous interface. You can switch to the new interface by clicking the button "Switch to the new interface" below, or at any time with a single click from the plugin\'s General Settings.', 'wp-2fa' ) ); ?></p>' +
232 '<ul class="wp2fa-new-ui-notice__features">' +
233 '<li><?php echo \esc_js( \__( 'Improved navigation and usability', 'wp-2fa' ) ); ?></li>' +
234 '<li><?php echo \esc_js( \__( 'A more streamlined setup experience', 'wp-2fa' ) ); ?></li>' +
235 '<li><?php echo \esc_js( \__( 'Better organization of settings and options', 'wp-2fa' ) ); ?></li>' +
236 '<li><?php echo \esc_js( \__( 'A modern design consistent with the latest Melapress plugins', 'wp-2fa' ) ); ?></li>' +
237 '</ul>' +
238 '</div>';
239
240 var deferMessageHtml = '<div class="wp2fa-new-ui-notice">' +
241 '<h2 class="wp2fa-new-ui-notice__title"><?php echo \esc_js( \__( 'You can upgrade to the new UI at any time.', 'wp-2fa' ) ); ?></h2>' +
242 '<p><?php echo \esc_js( \__( 'You can switch to the new interface whenever you are ready by going to:', 'wp-2fa' ) ); ?> <strong><?php echo \esc_js( \__( 'Settings > General Settings', 'wp-2fa' ) ); ?></strong> <?php echo \esc_js( \__( 'and then clicking the Switch to the new interface button.', 'wp-2fa' ) ); ?></p>' +
243 '</div>';
244
245 wp2faDialog.open( {
246 content: messageHtml,
247 size: 'large',
248 onClose: function() {
249 dismissDialog();
250 },
251 buttons: [
252 {
253 text: '<?php echo \esc_js( \__( 'Switch to the new interface', 'wp-2fa' ) ); ?>',
254 className: 'wp2fa-btn-switch',
255 onClick: function() {
256 switchToNewInterface();
257 }
258 },
259 {
260 text: '<?php echo \esc_js( \__( 'Keep using the current interface for now', 'wp-2fa' ) ); ?>',
261 className: 'wp2fa-btn-keep',
262 element: 'link',
263 onClick: function() {
264 window.setTimeout( function() {
265 wp2faDialog.open( {
266 content: deferMessageHtml,
267 size: 'large',
268 onClose: function() {
269 dismissDialog();
270 },
271 buttons: [
272 {
273 text: '<?php echo \esc_js( \__( 'Understood', 'wp-2fa' ) ); ?>',
274 className: 'wp2fa-btn-switch',
275 onClick: function() {
276 dismissDialog();
277 }
278 }
279 ]
280 } );
281 }, 0 );
282 }
283 }
284 ]
285 } );
286
287 var policiesUrl = '<?php echo \esc_url( $policies_url ); ?>';
288
289 function switchToNewInterface() {
290 var xhr = new XMLHttpRequest();
291 xhr.open( 'POST', ajaxUrl, true );
292 xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
293 xhr.onload = function() {
294 window.location.href = policiesUrl;
295 };
296 xhr.send( 'action=wp2fa_switch_new_interface&nonce=' + encodeURIComponent( nonce ) );
297 }
298
299 function dismissDialog() {
300 var xhr = new XMLHttpRequest();
301 xhr.open( 'POST', ajaxUrl, true );
302 xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
303 xhr.onload = function() {
304 window.location.href = policiesUrl;
305 };
306 xhr.send( 'action=wp2fa_dismiss_new_interface_dialog&nonce=' + encodeURIComponent( nonce ) );
307 }
308 } );
309 </script>
310 <?php
311 }
312
313 /**
314 * AJAX handler: switch to the new interface.
315 *
316 * @return void
317 *
318 * @since 4.0.0
319 */
320 public static function ajax_switch_new_interface() {
321 $nonce = isset( $_POST['nonce'] ) ? \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ) : '';
322
323 if ( ! \current_user_can( 'manage_options' ) || ! \wp_verify_nonce( $nonce, 'wp2fa_new_interface_dialog' ) ) {
324 \wp_send_json_error( \esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
325 }
326
327 // Update the use_new_interface setting to true.
328 $settings = Settings_Utils::get_option( WP_2FA_SETTINGS_NAME, array() );
329
330 if ( ! \is_array( $settings ) ) {
331 $settings = array();
332 }
333
334 $settings['use_new_interface'] = true;
335
336 Settings_Utils::update_option( WP_2FA_SETTINGS_NAME, $settings );
337
338 // Clear the in-memory cached settings so the new value is picked up.
339 WP2FA::update_plugin_settings( $settings, true, WP_2FA_SETTINGS_NAME );
340
341 // Remove the dialog flag.
342 Settings_Utils::delete_option( self::SHOW_DIALOG_OPTION );
343
344 // Remove the banner flag too.
345 Settings_Utils::delete_option( self::SHOW_BANNER_OPTION );
346
347 \wp_send_json_success();
348 }
349
350 /**
351 * AJAX handler: dismiss the dialog without switching.
352 *
353 * @return void
354 *
355 * @since 4.0.0
356 */
357 public static function ajax_dismiss_dialog() {
358 $nonce = isset( $_POST['nonce'] ) ? \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ) : '';
359
360 if ( ! \current_user_can( 'manage_options' ) || ! \wp_verify_nonce( $nonce, 'wp2fa_new_interface_dialog' ) ) {
361 \wp_send_json_error( \esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
362 }
363
364 // Remove the dialog flag.
365 Settings_Utils::delete_option( self::SHOW_DIALOG_OPTION );
366
367 // Show the persistent banner on plugin pages instead.
368 Settings_Utils::update_option( self::SHOW_BANNER_OPTION, true );
369
370 \wp_send_json_success();
371 }
372
373 /**
374 * AJAX handler: dismiss the persistent banner.
375 *
376 * @return void
377 *
378 * @since 4.0.0
379 */
380 public static function ajax_dismiss_banner() {
381 $nonce = isset( $_POST['nonce'] ) ? \sanitize_text_field( \wp_unslash( $_POST['nonce'] ) ) : '';
382
383 if ( ! \current_user_can( 'manage_options' ) || ! \wp_verify_nonce( $nonce, 'wp2fa_new_interface_banner' ) ) {
384 \wp_send_json_error( \esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
385 }
386
387 Settings_Utils::delete_option( self::SHOW_BANNER_OPTION );
388
389 \wp_send_json_success();
390 }
391
392 /**
393 * Renders the persistent banner on plugin admin pages.
394 *
395 * @return void
396 *
397 * @since 4.0.0
398 */
399 public static function render_banner() {
400 if ( ! \current_user_can( 'manage_options' ) ) {
401 return;
402 }
403
404 // Only show on WP 2FA plugin pages.
405 $screen = \get_current_screen();
406 if ( ! $screen || ! \in_array( $screen->id, \WP2FA\Admin\Helpers\WP_Helper::PLUGIN_PAGES, true ) ) {
407 return;
408 }
409
410 $nonce = \wp_create_nonce( 'wp2fa_new_interface_banner' );
411 $switch_nonce = \wp_create_nonce( 'wp2fa_new_interface_dialog' );
412 $icon_url = \esc_url( WP_2FA_URL . 'dist/images/wp-2fa-square.png' );
413 ?>
414 <div class="wp2fa-new-interface-banner" id="wp2fa-new-interface-banner">
415 <div class="wp2fa-new-interface-banner__content">
416 <img src="<?php echo $icon_url; ?>" alt="WP 2FA" class="wp2fa-new-interface-banner__icon" />
417 <div class="wp2fa-new-interface-banner__text">
418 <span class="wp2fa-new-interface-banner__title">
419 <?php echo \esc_html__( 'New Interface Available', 'wp-2fa' ); ?>
420 &ndash;
421 <span class="wp2fa-new-interface-banner__highlight"><?php echo \esc_html__( 'Try WP 2FA 4.0 Now!', 'wp-2fa' ); ?></span>
422 </span>
423 <span class="wp2fa-new-interface-banner__desc"><?php echo \esc_html__( 'A completely redesigned UI, built to be easier and more intuitive. Switch in one click from General Settings.', 'wp-2fa' ); ?></span>
424 </div>
425 </div>
426 <div class="wp2fa-new-interface-banner__actions">
427 <button type="button" class="wp2fa-new-interface-banner__switch-btn" id="wp2fa-banner-switch-btn"><?php echo \esc_html__( 'Switch to new interface', 'wp-2fa' ); ?></button>
428 <button type="button" class="wp2fa-new-interface-banner__close" id="wp2fa-banner-close-btn" aria-label="<?php \esc_attr_e( 'Dismiss', 'wp-2fa' ); ?>">&times;</button>
429 </div>
430 </div>
431 <style>
432 .wp2fa-new-interface-banner {
433 display: flex;
434 align-items: center;
435 justify-content: space-between;
436 background: #fff;
437 border: 1px solid #dcdcde;
438 border-radius: 4px;
439 padding: 12px 16px;
440 margin: 10px 20px 16px 0;
441 gap: 16px;
442 }
443 .wp2fa-new-interface-banner__content {
444 display: flex;
445 align-items: center;
446 gap: 14px;
447 flex: 1;
448 min-width: 0;
449 }
450 .wp2fa-new-interface-banner__icon {
451 width: 36px;
452 height: 36px;
453 flex-shrink: 0;
454 }
455 .wp2fa-new-interface-banner__text {
456 display: flex;
457 flex-direction: column;
458 gap: 2px;
459 min-width: 0;
460 }
461 .wp2fa-new-interface-banner__title {
462 font-size: 14px;
463 font-weight: 600;
464 color: #1d2327;
465 white-space: nowrap;
466 }
467 .wp2fa-new-interface-banner__highlight {
468 color: #2271b1;
469 }
470 .wp2fa-new-interface-banner__desc {
471 font-size: 13px;
472 color: #50575e;
473 line-height: 1.4;
474 }
475 .wp2fa-new-interface-banner__actions {
476 display: flex;
477 align-items: center;
478 gap: 12px;
479 flex-shrink: 0;
480 }
481 .wp2fa-new-interface-banner__switch-btn {
482 background: #2271b1;
483 border: 1px solid #2271b1;
484 color: #fff;
485 font-size: 13px;
486 font-weight: 500;
487 padding: 7px 16px;
488 border-radius: 4px;
489 cursor: pointer;
490 white-space: nowrap;
491 line-height: 1.4;
492 }
493 .wp2fa-new-interface-banner__switch-btn:hover,
494 .wp2fa-new-interface-banner__switch-btn:focus {
495 background: #135e96;
496 border-color: #135e96;
497 color: #fff;
498 outline: none;
499 }
500 .wp2fa-new-interface-banner__close {
501 background: none;
502 border: none;
503 font-size: 20px;
504 line-height: 1;
505 color: #787c82;
506 cursor: pointer;
507 padding: 4px 8px;
508 border-radius: 3px;
509 }
510 /* .wp2fa-new-interface-banner__close:hover, */
511 .wp2fa-new-interface-banner__close:focus {
512 color: #1d2327;
513 background: #e8e8e8;
514 outline: none;
515 }
516 @media screen and (max-width: 600px) {
517 .wp2fa-new-interface-banner {
518 flex-direction: column;
519 align-items: flex-start;
520 }
521 .wp2fa-new-interface-banner__title {
522 white-space: normal;
523 }
524 }
525 </style>
526 <script>
527 (function() {
528 var ajaxUrl = '<?php echo \esc_url( \admin_url( 'admin-ajax.php' ) ); ?>';
529 var bannerNonce = '<?php echo \esc_attr( $nonce ); ?>';
530 var switchNonce = '<?php echo \esc_attr( $switch_nonce ); ?>';
531 var banner = document.getElementById('wp2fa-new-interface-banner');
532
533 document.getElementById('wp2fa-banner-close-btn').addEventListener('click', function() {
534 banner.style.display = 'none';
535 var xhr = new XMLHttpRequest();
536 xhr.open('POST', ajaxUrl, true);
537 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
538 xhr.send('action=wp2fa_dismiss_new_interface_banner&nonce=' + encodeURIComponent(bannerNonce));
539 });
540
541 document.getElementById('wp2fa-banner-switch-btn').addEventListener('click', function() {
542 var xhr = new XMLHttpRequest();
543 xhr.open('POST', ajaxUrl, true);
544 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
545 xhr.onload = function() {
546 window.location.reload();
547 };
548 xhr.send('action=wp2fa_switch_new_interface&nonce=' + encodeURIComponent(switchNonce));
549 });
550 })();
551 </script>
552 <?php
553 }
554 }
555 }
556