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