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 / Views / class-wizard-steps.php
wp-2fa / includes / classes / Admin / Views Last commit date
class-first-time-wizard-steps-new.php 1 day ago class-first-time-wizard-steps.php 1 day ago class-grace-period-notifications.php 1 day ago class-passord-reset-2fa.php 1 day ago class-re-login-2fa.php 1 day ago class-wizard-steps.php 1 day ago index.php 1 day ago
class-wizard-steps.php
496 lines
1 <?php
2 /**
3 * Settings page render class.
4 *
5 * @package wp2fa
6 * @subpackage views
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 namespace WP2FA\Admin\Views;
13
14 use WP2FA\WP2FA;
15 use WP2FA\Utils\User_Utils;
16 use WP2FA\Utils\Settings_Utils;
17 use WP2FA\Admin\Helpers\WP_Helper;
18 use WP2FA\Admin\Helpers\User_Helper;
19 use WP2FA\Admin\Controllers\Settings;
20
21 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
22
23 if ( ! class_exists( '\WP2FA\Admin\Views\Wizard_Steps' ) ) {
24 /**
25 * WP2FA Wizard Settings view controller
26 *
27 * @since 1.7
28 */
29 class Wizard_Steps {
30
31 /**
32 * Holds the nonce for json calls
33 *
34 * @since 1.7
35 *
36 * @var string
37 */
38 private static $json_nonce = null;
39
40 /**
41 * Holds the url to which to redirect the user after the setup is finished
42 *
43 * @var string
44 *
45 * @since 2.0.0
46 */
47 private static $redirect_url = null;
48
49 /**
50 * Introduction step form
51 *
52 * @since 1.7
53 *
54 * @return void
55 */
56 public static function optional_user_welcome_step() {
57 ?>
58 <div class="wizard-step active">
59 <div class="mb-20">
60 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'welcome', true ) ); ?>
61 </div>
62
63 <div class="wp2fa-setup-actions">
64 <button type="button" class="button wp-2fa-button-primary button-primary" data-name="next_step_setting_modal_wizard" data-next-step="choose-2fa-method"><?php \esc_html_e( 'Next Step', 'wp-2fa' ); ?></buttona>
65 <button class="wp-2fa-button-secondary button button-secondary wp-2fa-button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
66 <?php
67 /*
68 if ( User_Helper::is_enforced( User_Helper::get_user_object()->ID ) ) {
69 ?>
70 <a class="button button-primary wp-2fa-button-primary modal_logout" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-logout' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php \esc_attr_e( 'Log out', 'wp-2fa' ); ?></a>
71 <?php
72 }
73 */
74 ?>
75 </div>
76 </div>
77 <?php
78 }
79
80 /**
81 * Introduction step form
82 *
83 * @since 1.7
84 *
85 * @return void
86 */
87 public static function introduction_step() {
88 ?>
89 <form method="post" class="wp2fa-setup-form">
90 <?php wp_nonce_field( 'wp2fa-step-addon' ); ?>
91 <div class="mb-20">
92 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'wp-2fa_required_intro', true ) ); ?>
93 </div>
94
95 <div class="wp2fa-setup-actions">
96 <button class="button button-primary wp-2fa-button-primary"
97 type="submit"
98 name="save_step"
99 value="<?php \esc_attr_e( 'Next', 'wp-2fa' ); ?>">
100 <?php \esc_html_e( 'Next', 'wp-2fa' ); ?>
101 </button>
102 </div>
103 </form>
104 <?php
105 }
106
107 /**
108 * Welcome step of the wizard
109 *
110 * @since 1.7
111 *
112 * @param string $next_step - url of the next step.
113 *
114 * @return void
115 */
116 public static function welcome_step( $next_step ) {
117 $redirect = Settings::get_settings_page_link();
118
119 ?>
120 <h3><?php \esc_html_e( 'Let\'s get you started', 'wp-2fa' ); ?></h3>
121 <p><?php \esc_html_e( 'Thank you for installing WP 2FA. This quick setup wizard will guide you through configuring the plugin and setting up two-factor authentication (2FA) for your user account and the users on this website.', 'wp-2fa' ); ?></p>
122
123 <div class="wp2fa-setup-actions">
124 <a class="button button-primary"
125 href="<?php echo \esc_url( $next_step ); ?>">
126 <?php \esc_html_e( 'Let’s get started!', 'wp-2fa' ); ?>
127 </a>
128 <a class="button button-secondary wp-2fa-button-secondary first-time-wizard"
129 href="<?php echo \esc_url( $redirect ); ?>">
130 <?php \esc_html_e( 'Skip Wizard - I know how to do this', 'wp-2fa' ); ?>
131 </a>
132 </div>
133 <?php
134 }
135
136 /**
137 * Configure backup codes step
138 *
139 * @since 1.7
140 *
141 * @return void
142 */
143 public static function backup_codes_configure() {
144
145 $user_type = User_Utils::determine_user_2fa_status( User_Helper::get_user_object() );
146
147 $redirect = self::determine_redirect_url();
148 ?>
149 <div class="step-setting-wrapper active">
150 <?php
151 if ( in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) ) {
152 ?>
153 <div class="mb-20">
154 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro', true ) ); ?>
155 </div>
156 <?php } else { ?>
157 <div class="mb-20">
158 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro_continue', true ) ); ?>
159 </div>
160 <?php } ?>
161 <div class="wp2fa-setup-actions">
162 <?php if ( in_array( 'user_needs_to_setup_backup_codes', $user_type, true ) ) { ?>
163 <button class="button button-primary wp-2fa-button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
164 <?php \esc_html_e( 'Generate list of backup codes', 'wp-2fa' ); ?>
165 </button>
166 <?php
167 if ( ! empty( $redirect ) ) {
168 ?>
169 <a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary close-first-time-wizard">
170 <?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
171 </a>
172 <?php
173 } else {
174 ?>
175 <a href="#" class="button wp-2fa-button-secondary" data-close-2fa-modal value="<?php \esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>">
176 <?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
177 </a>
178 <?php } ?>
179 <?php } else { ?>
180 <?php
181 if ( ! empty( $redirect ) ) {
182 ?>
183 <a href="<?php echo \esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
184 <?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
185 </a>
186 <?php
187 } else {
188 ?>
189 <a href="#" class="button button-secondary wp-2fa-button-secondary" data-reload>
190 <?php \esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
191 </a>
192 <?php } ?>
193 <?php } ?>
194 </div>
195 </div>
196 <?php
197 }
198
199 /**
200 * Generate backup codes step
201 *
202 * @since 1.7
203 *
204 * @return void
205 */
206 public static function generate_backup_codes() {
207 ?>
208 <div class="step-setting-wrapper active" data-step-title="<?php \esc_html_e( 'Generate codes', 'wp-2fa' ); ?>">
209 <div class="mb-20">
210 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_generate_intro', true ) ); ?>
211 </div>
212 <div class="wp2fa-setup-actions">
213 <button class="button button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
214 <?php \esc_html_e( 'Generate list of backup codes', 'wp-2fa' ); ?>
215 </button>
216 <a href="#" class="button button-secondary wp-2fa-button-secondary" value="<?php \esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>" data-close-2fa-modal="">
217 <?php \esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
218 </a>
219 </div>
220 </div>
221
222 <?php
223 }
224
225 /**
226 * Creates link for generating the backup codes
227 *
228 * @since 1.7
229 *
230 * @return string
231 */
232 public static function get_generate_codes_label() {
233 $label = esc_html__( 'Backup 2FA methods:', 'wp-2fa' );
234
235 return $label . '</th><td>';
236 }
237
238 /**
239 * Creates backup codes URL link
240 *
241 * @return string
242 *
243 * @since 2.6.0
244 */
245 public static function get_backup_codes_link(): string {
246 return '<a href="#" class="button button-primary remove-2fa" data-trigger-generate-backup-codes ' . WP_Helper::create_data_nonce( self::json_nonce() ) . ' onclick="(jQuery(\'#wp-2fa-manual-backup-codes\').length ) ? jQuery(\'#wp-2fa-manual-backup-codes\').attr(\'id\', \'wp-2fa-manual-backup-codes-trigger\') : \'\';MicroModal.show( \'configure-2fa-backup-codes\' );">' . esc_html__( 'Generate list of backup codes', 'wp-2fa' ) . '</a>';
247 }
248
249 /**
250 * Shows the wrapper where backup code are generated and showed to the user
251 *
252 * @param boolean $backup_only - If we want to show backup window only - sets the class of the div to active.
253 *
254 * @since 1.7
255 *
256 * @return void
257 */
258 public static function generated_backup_codes( $backup_only = false ) {
259
260 $redirect = esc_url( self::determine_redirect_url() );
261
262 ?>
263 <div class="step-setting-wrapper align-center<?php echo ( $backup_only ) ? ' active' : ''; ?>" data-step-title="<?php esc_html_e( 'Your backup codes', 'wp-2fa' ); ?>">
264 <div class="mb-20">
265 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_generated', true ) ); ?>
266 </div>
267 <div class="backup-key-wrapper">
268 <textarea id="backup-codes-wrapper" readonly rows="4" cols="50" class="app-key"></textarea>
269 </div>
270 <div class="wp2fa-setup-actions">
271 <?php if ( is_ssl() ) { ?>
272 <button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-copy>
273 <?php esc_html_e( 'Copy', 'wp-2fa' ); ?>
274 </button>
275 <?php } else { ?>
276 <button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-download data-user="<?php echo esc_attr( User_Helper::get_user_object()->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>">
277 <?php esc_html_e( 'Download', 'wp-2fa' ); ?>
278 </button>
279 <?php } ?>
280 <button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php esc_attr_e( 'Print', 'wp-2fa' ); ?>" data-trigger-print <?php WP_Helper::create_data_nonce( self::json_nonce() ); ?> data-user-id="<?php echo esc_attr( User_Helper::get_user_object()->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>"><?php esc_html_e( 'Print', 'wp-2fa' ); ?></button>
281
282 <button class="button button-primary wp-2fa-button-primary" type="submit" value="<?php esc_attr_e( 'Send me the codes via email', 'wp-2fa' ); ?>" data-trigger-backup-code-email <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-backup-codes-email-nonce' ); ?> data-user-id="<?php echo esc_attr( User_Helper::get_user_object()->ID ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>"><?php esc_html_e( 'Send me the codes via email', 'wp-2fa' ); ?></button>
283 <?php
284 if ( ! empty( $redirect ) ) {
285 ?>
286 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary close-first-time-wizard"><?php esc_html_e( 'I\'m ready, close the wizard', 'wp-2fa' ); ?></a>
287 <?php
288 } else {
289 ?>
290 <button class="button button-secondary wp-2fa-button-secondary wp-2fa-button-secondary" type="submit" data-close-2fa-modal-and-refresh><?php esc_html_e( 'I\'m ready, close the wizard', 'wp-2fa' ); ?></button>
291 <?php } ?>
292 </div>
293 </div>
294 <?php
295 }
296
297 /**
298 * Final step for congratulating the user
299 *
300 * @since 1.7
301 *
302 * @param boolean $setup_wizard - Is that a call from setup wizard or not.
303 *
304 * @return void
305 */
306 public static function congratulations_step( $setup_wizard = false ) {
307
308 if ( $setup_wizard ) {
309 self::congratulations_step_plugin_wizard();
310 return;
311 }
312
313 $redirect = ( '' !== self::determine_redirect_url() ) ? esc_url( self::determine_redirect_url() ) : '';
314 ?>
315
316 <div class="step-setting-wrapper active">
317 <div class="mb-20">
318 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'no_further_action', true ) ); ?>
319 </div>
320 <div class="wp2fa-setup-actions">
321 <?php if ( '' !== trim( $redirect ) ) { ?>
322 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
323 <?php esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
324 </a>
325 <?php } else { ?>
326 <button class="modal__btn wp-2fa-button-secondary button" data-close-2fa-modal aria-label="Close this dialog window"><?php esc_html_e( 'Close wizard', 'wp-2fa' ); ?></button>
327 <?php } ?>
328 </div>
329 </div>
330 <?php
331 }
332
333 /**
334 * Final step for congratulating the user
335 *
336 * @since 1.7
337 *
338 * @return void
339 */
340 public static function congratulations_step_plugin_wizard() {
341 $redirect = ( '' !== self::determine_redirect_url() ) ? esc_url( self::determine_redirect_url() ) : esc_url( get_edit_profile_url( User_Helper::get_user_object()->ID ) );
342 $slide_title = ( User_Helper::is_excluded( User_Helper::get_user_object()->ID ) ) ? esc_html__( 'Congratulations.', 'wp-2fa' ) : esc_html__( 'Congratulations, you\'re almost there...', 'wp-2fa' );
343 ?>
344 <h3><?php echo esc_html( $slide_title ); ?></h3>
345 <p><?php esc_html_e( 'Great job, the plugin and 2FA policies are now configured. You can always change the plugin settings and 2FA policies at a later stage from the WP 2FA entry in the WordPress menu.', 'wp-2fa' ); ?></p>
346
347 <?php
348 if ( User_Helper::is_excluded( User_Helper::get_user_object()->ID ) ) {
349 ?>
350 <div class="wp2fa-setup-actions">
351 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
352 <?php esc_html_e( 'Close wizard', 'wp-2fa' ); ?>
353 </a>
354 </div>
355 <?php
356 } else {
357 ?>
358 <p><?php esc_html_e( 'Now you need to configure 2FA for your own user account. You can do this now (recommended) or later.', 'wp-2fa' ); ?></p>
359 <div class="wp2fa-setup-actions">
360 <a href="<?php echo \esc_url( Settings::get_setup_page_link() ); ?>" class="button button-primary wp-2fa-button-secondary">
361 <?php \esc_html_e( 'Configure 2FA now', 'wp-2fa' ); ?>
362 </a>
363 <a href="<?php echo \esc_url( Settings::get_settings_page_link() ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard">
364 <?php \esc_html_e( 'Close wizard & configure 2FA later', 'wp-2fa' ); ?>
365 </a>
366 </div>
367 <?php } ?>
368 <?php
369 }
370
371 /**
372 * Shows the methods in the modal wizard, so the user can choose from the available ones
373 *
374 * @param \WP_User $user - WP User object.
375 *
376 * @return void
377 *
378 * @since 3.0.0
379 */
380 public static function show_modal_methods( $user ) {
381 /**
382 * Add an option for external providers to add their own modal methods options.
383 *
384 * @param \WP_user $user - WP User object.
385 *
386 * @since 2.0.0
387 * @since 3.0.0 - Added $user parameter.
388 */
389 \do_action( WP_2FA_PREFIX . 'modal_methods', $user );
390 }
391
392 /**
393 * Choosing backup method step
394 * When there are more than one backup method - give the user ability to choose one
395 *
396 * @return void
397 *
398 * @since 2.0.0
399 */
400 public static function choose_backup_method() {
401 $redirect = esc_url( self::determine_redirect_url() );
402 ?>
403 <div class="wizard-step" id="wp-2fa-wizard-backup-methods">
404 <div class="option-pill mb-20">
405 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'backup_codes_intro_multi', true ) ); ?>
406 </div>
407 <div class="radio-cells">
408 <?php
409 $backup_methods = Settings::get_backup_methods();
410
411 $i = 0;
412 foreach ( $backup_methods as $method_name => $method ) {
413 $checked = '';
414 if ( ! $i ) {
415 $checked = ' checked="checked"';
416 }
417 $i = 1;
418 ?>
419 <div class="option-pill"><label for="<?php echo esc_attr( $method_name ); ?>"><input name="backup_method_select" data-step="<?php echo esc_attr( $method['wizard-step'] ); ?>" type="radio" id="<?php echo esc_attr( $method_name ); ?>" <?php echo $checked; ?>><?php echo $method['button_name']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label><br /></div>
420 <?php
421 }
422 ?>
423 </div>
424 <div class="wp2fa-setup-actions">
425 <a id="select-backup-method" href="<?php echo esc_url( Settings::get_setup_page_link() ); ?>" class="button button-primary wp-2fa-button-primary">
426 <?php esc_html_e( 'Configure backup 2FA method', 'wp-2fa' ); ?>
427 </a>
428 <a href="<?php echo esc_url( $redirect ); ?>" class="button button-secondary wp-2fa-button-secondary close-first-time-wizard" <?php echo ( ( '' === trim( (string) $redirect ) ) ? 'data-close-it=""' : '' ); ?> >
429 <?php esc_html_e( 'Close wizard & configure 2FA later', 'wp-2fa' ); ?>
430 </a>
431 <script>
432 const closeButton = document.querySelector('[data-close-it]');
433
434 if (closeButton) {
435 closeButton.addEventListener('click', (event) => {
436 event.preventDefault();
437 let url = new URL( location.href );
438 let params = new URLSearchParams( url.search );
439 params.delete('show');
440 location.replace( `${location.pathname}?${params}` );
441 });
442 }
443 </script>
444 </div>
445 </div>
446 <?php
447 }
448
449 /**
450 * Determines the redirect url for the user
451 *
452 * @return string
453 *
454 * @since 2.0.0
455 */
456 public static function determine_redirect_url(): string {
457 if ( null === self::$redirect_url ) {
458 $role = User_Helper::get_user_role();
459 $redirect_page = Settings_Utils::get_setting_role( $role, 'redirect-user-custom-page-global' );
460 self::$redirect_url = ( '' !== trim( (string) $redirect_page ) ) ? trailingslashit( get_site_url() ) . \sanitize_text_field( $redirect_page ) : '';
461
462 if (
463 'yes' === Settings_Utils::get_setting_role( $role, 'create-custom-user-page' ) ||
464 'yes' === Settings_Utils::get_setting_role( null, 'create-custom-user-page' ) ) {
465 if (
466 '' !== trim( (string) Settings_Utils::get_setting_role( $role, 'redirect-user-custom-page' ) ) ||
467 '' !== trim( (string) Settings_Utils::get_setting_role( null, 'redirect-user-custom-page' ) ) ) {
468 if ( 'yes' === Settings_Utils::get_setting_role( $role, 'create-custom-user-page' ) ) {
469 self::$redirect_url = trailingslashit( get_site_url() ) . \sanitize_text_field( Settings_Utils::get_setting_role( $role, 'redirect-user-custom-page' ) );
470 } else {
471 self::$redirect_url = trailingslashit( get_site_url() ) . \sanitize_text_field( Settings_Utils::get_setting_role( null, 'redirect-user-custom-page' ) );
472 }
473 }
474 }
475 }
476
477 return self::$redirect_url;
478 }
479
480 /**
481 * Generates nonce for JSON calls
482 *
483 * @since 1.7
484 *
485 * @return string
486 */
487 protected static function json_nonce() {
488 if ( null === self::$json_nonce ) {
489 self::$json_nonce = 'wp-2fa-backup-codes-generate-json-' . esc_attr( User_Helper::get_user_object()->ID );
490 }
491
492 return self::$json_nonce;
493 }
494 }
495 }
496