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-first-time-wizard-steps-new.php
wp-2fa / includes / classes / Admin / Views Last commit date
class-first-time-wizard-steps-new.php 1 week ago class-first-time-wizard-steps.php 1 week ago class-grace-period-notifications.php 1 week ago class-passord-reset-2fa.php 1 week ago class-re-login-2fa.php 1 week ago class-wizard-steps.php 1 week ago index.php 1 week ago
class-first-time-wizard-steps-new.php
786 lines
1 <?php
2 /**
3 * First Time Wizard Steps (New Design) – renders each wizard step
4 * using the Settings_Builder component system.
5 *
6 * @package wp2fa
7 * @subpackage views
8 * @since 4.0.0
9 */
10
11 namespace WP2FA\Admin\Views;
12
13 use WP2FA\WP2FA;
14 use WP2FA\Methods\TOTP;
15 use WP2FA\Methods\Email;
16 use WP2FA\Methods\Backup_Codes;
17 use WP2FA\Utils\Settings_Utils;
18 use WP2FA\Admin\Settings_Builder;
19 use WP2FA\Admin\Helpers\WP_Helper;
20 use WP2FA\Admin\Helpers\Methods_Helper;
21 use WP2FA\Admin\Views\Grace_Period_Notifications;
22
23 defined( 'ABSPATH' ) || exit;
24
25 if ( ! class_exists( '\WP2FA\Admin\Views\First_Time_Wizard_Steps_New' ) ) {
26 /**
27 * Static helper that outputs each wizard step's form fields
28 * using the new Settings_Builder components.
29 *
30 * @since 4.0.0
31 */
32 class First_Time_Wizard_Steps_New {
33
34 /**
35 * Name prefix used in all form fields so they are collected
36 * under a single $_POST key.
37 */
38 private const NAME_PREFIX = WP_2FA_POLICY_SETTINGS_NAME;
39
40 /*
41 ==============================================================
42 * STEP 1 – 2FA Methods
43 * ==============================================================
44 */
45
46 /**
47 * Render the primary 2FA methods step.
48 *
49 * @return void
50 */
51 public static function step_methods() {
52 $totp_enabled = ! empty( Settings_Utils::get_setting_role( null, TOTP::POLICY_SETTINGS_NAME, true ) );
53 $email_enabled = ! empty( Settings_Utils::get_setting_role( null, Email::POLICY_SETTINGS_NAME, true ) );
54
55 // Build primary methods sortable list via the same filter used in policies.
56 $all_methods = array();
57 $all_methods = \apply_filters( WP_2FA_PREFIX . 'methods_policy_settings_new', $all_methods, null );
58
59 $methods_order = Settings_Utils::get_setting_role( null, Methods_Helper::POLICY_SETTINGS_NAME, true );
60 if ( \is_array( $methods_order ) && ! empty( $methods_order ) ) {
61 $ordered = array();
62 foreach ( $methods_order as $slug ) {
63 if ( isset( $all_methods[ $slug ] ) ) {
64 $ordered[ $slug ] = $all_methods[ $slug ];
65 }
66 }
67 foreach ( $all_methods as $slug => $m ) {
68 if ( ! isset( $ordered[ $slug ] ) ) {
69 $ordered[ $slug ] = $m;
70 }
71 }
72 $all_methods = $ordered;
73 }
74 ?>
75 <h3><?php \esc_html_e( 'Which 2FA methods can your users use?', 'wp-2fa' ); ?></h3>
76 <!-- <p class="description">
77 <?php // \esc_html_e( 'Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone.', 'wp-2fa' ); ?>
78 </p> -->
79
80 <?php
81 $all_methods = null;
82 if ( ! empty( $all_methods ) ) {
83 $sortable_items = array();
84 foreach ( $all_methods as $method ) {
85 $item = array(
86 'id' => $method['setting'],
87 'title' => $method['title'],
88 'order_id' => $method['id'],
89 'checkbox_name' => self::NAME_PREFIX . '[' . $method['setting'] . ']',
90 'checked' => ! empty( $method['enabled'] ),
91 );
92 if ( ! empty( $method['extra_settings'] ) ) {
93 $item['extra_settings'] = $method['extra_settings'];
94 }
95 $sortable_items[] = $item;
96 }
97
98 Settings_Builder::build_option(
99 array(
100 'id' => 'wizard-methods-order',
101 'type' => 'sortable-list',
102 'items' => $sortable_items,
103 'name_prefix' => self::NAME_PREFIX . '[methods_order]',
104 )
105 );
106 } else {
107 // Fallback: render simple checkboxes for TOTP and Email.
108 ?>
109 <div class="wizard-methods-list">
110 <div class="wizard-method-card">
111 <!-- Hidden input ensures unchecked box submits empty value -->
112 <input type="hidden" name="<?php echo esc_attr( self::NAME_PREFIX . '[' . TOTP::POLICY_SETTINGS_NAME . ']' ); ?>" value="">
113 <?php
114 Settings_Builder::build_option(
115 array(
116 'id' => 'wizard-totp',
117 'type' => 'checkbox',
118 'option_name' => self::NAME_PREFIX . '[' . TOTP::POLICY_SETTINGS_NAME . ']',
119 'default' => $totp_enabled ? TOTP::POLICY_SETTINGS_NAME : '',
120 'text' => '<strong>' . \esc_html__( 'One-time code via 2FA app', 'wp-2fa' ). '</strong>'/*. ' - <a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=totp_aplications_help" target="_blank" rel="noopener">' . \esc_html__( 'complete list of supported 2FA apps.', 'wp-2fa' ) . '</a>'*/,
121 'not_bool' => true,
122 'not_id' => true,
123 'value' => TOTP::POLICY_SETTINGS_NAME,
124 )
125 );
126 ?>
127 <p class="wizard-method-desc">
128 <?php
129 echo \wp_sprintf(
130 /* translators: link to the knowledge base website */
131 \esc_html__( 'When using this method, users will need to configure a 2FA app to get the one-time login code. The plugin supports all standard 2FA apps. Refer to the %s for more information. Allowing users to configure a secondary 2FA method is highly recommended. You can configure this in the next step of the wizard. This allows users to log in using an alternative method if they lose access to their primary 2FA device, such as their phone.', 'wp-2fa' ),
132 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=guide_how_to_setup_2fa_apps" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
133 );
134 ?>
135 </p>
136 </div>
137
138 <div class="wizard-method-card">
139 <!-- Hidden input ensures unchecked box submits empty value -->
140 <input type="hidden" name="<?php echo \esc_attr( self::NAME_PREFIX . '[' . Email::POLICY_SETTINGS_NAME . ']' ); ?>" value="">
141 <?php
142 Settings_Builder::build_option(
143 array(
144 'id' => 'wizard-email',
145 'type' => 'checkbox',
146 'option_name' => self::NAME_PREFIX . '[' . Email::POLICY_SETTINGS_NAME . ']',
147 'default' => $email_enabled ? Email::POLICY_SETTINGS_NAME : '',
148 'text' => '<strong>' . \esc_html__( 'One-time code via email', 'wp-2fa' ) . '</strong>- ' . \esc_html__( 'ensure email deliverability with the free plugin ', 'wp-2fa' ) . '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a>',
149 'not_bool' => true,
150 'not_id' => true,
151 'value' => Email::POLICY_SETTINGS_NAME,
152 )
153 );
154 ?>
155 <p class="wizard-method-desc">
156 <?php
157 echo \wp_sprintf(
158 /* translators: 1. Link to WP Mail SMTP plugin, 2. Recommendation to allow users to set up a secondary method. */
159 \esc_html__( 'When using this method, users will receive the one-time login code over email. Therefore, email deliverability is very important. Users using this method should whitelist the address from which the codes are sent. By default, this is the email address configured in your WordPress. You can run an email test from the plugin\'s settings to confirm email deliverability. If you have had email deliverability / reliability issues, we highly recommend you to install the free plugin %1$s', 'wp-2fa' ),
160 '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a>'
161 );
162 ?>
163 </p>
164 </div>
165 </div>
166 <?php
167 }
168
169 /**
170 * Allow extensions to add additional methods below the main list.
171 *
172 * @param bool $wizard True – this is the wizard context.
173 * @param string $data_role Data-role attribute (empty in new wizard).
174 * @param string|null $name Role name (null for global).
175 *
176 * @since 2.0.0
177 */
178 \do_action( WP_2FA_PREFIX . 'after_primary_methods_toggles', null );
179
180 ?>
181
182 <div class="wizard-info-blue-notice">
183 <p><?php \esc_html_e( 'Allowing users to configure a secondary 2FA method is highly recommended. You can configure this in the next step of the wizard. This allows users to log in using an alternative method if they lose access to their primary 2FA device, such as their phone.', 'wp-2fa' ); ?></p>
184 </div>
185 <?php
186 ?>
187 <?php
188 }
189
190 /*
191 ==============================================================
192 * STEP 2 – Alternative Methods
193 * ==============================================================
194 */
195
196 /**
197 * Render the alternative / backup methods step.
198 *
199 * @return void
200 */
201 public static function step_alternative_methods() {
202 $backup_enabled = Settings_Utils::string_to_bool( Settings_Utils::get_setting_role( null, Backup_Codes::get_settings_name(), true ) );
203 $email_backup_enabled = Settings_Utils::get_setting_role( null, 'enable-email-backup', true );
204
205 ?>
206 <h3><?php \esc_html_e( 'Which secondary 2FA methods can users use?', 'wp-2fa' ); ?></h3>
207 <p class="description">
208 <?php \esc_html_e( 'A secondary 2FA method acts as a backup when the primary method cannot be used. For example, if a user loses access to their phone, the device runs out of battery, or email delivery fails. We strongly recommend allowing users to configure at least one backup method at all times.', 'wp-2fa' ); ?>
209 </p>
210 <!-- <p class="description">
211 <?php // \esc_html_e( 'Available secondary 2FA methods:', 'wp-2fa' ); ?>
212 </p> -->
213
214 <div class="wizard-method-card">
215 <?php
216 Settings_Builder::build_option(
217 array(
218 'id' => 'wizard-backup-codes',
219 'type' => 'checkbox',
220 'option_name' => self::NAME_PREFIX . '[' . Backup_Codes::get_settings_name() . ']',
221 'default' => $backup_enabled ? 'yes' : '',
222 'text' => '<strong>' . \esc_html__( 'Backup codes', 'wp-2fa' ) . '</strong>',
223 'not_bool' => true,
224 'not_id' => true,
225 'value' => 'yes',
226 )
227 );
228 ?>
229 <p class="wizard-method-desc">
230 <?php
231 printf(
232 '%1$s <a href="%2$s" target="_blank" rel="noopener noreferrer">%3$s</a>',
233 \esc_html__( 'Backup codes allow users to log in to WordPress should they find themselves unable to log in via the primary 2FA method. Backup codes are enabled by default and are generated during the 2FA configuration process. Each backup code can be used only once. Once the initial list is exhausted, more backup codes can be generated through the user\'s WordPress profile page -', 'wp-2fa' ),
234 \esc_url( 'https://melapress.com/support/kb/wp-2fa-what-are-2fa-backup-codes/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=backup_codes_info' ),
235 \esc_html__( 'More information', 'wp-2fa' )
236 );
237 ?>
238 </p>
239 </div>
240
241
242 <?php
243 /**
244 * Allow extensions to register additional backup methods.
245 *
246 * @param bool $wizard True - wizard context.
247 * @param string $data_role Data-role attribute.
248 * @param string|null $role Role name.
249 *
250 * @since 2.0.0
251 */
252 \do_action( WP_2FA_PREFIX . 'after_secondary_methods_toggles', null );
253
254 // @free:start
255 ?>
256 <div class="wizard-upgrade-notice">
257 <span>
258 <strong><?php \esc_html_e( 'Send backup codes instantly via email', 'wp-2fa' ); ?></strong><br>
259 <?php \esc_html_e( 'Let users receive a one-time backup code by email in seconds — plus more premium 2FA methods with WP 2FA Premium.', 'wp-2fa' ); ?>
260 </span>
261 <a href="https://melapress.com/wordpress-2fa/pricing/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=upgrade_now" target="_blank" class="button button-secondary"><?php \esc_html_e( 'Upgrade Now', 'wp-2fa' ); ?></a>
262 </div>
263 <?php
264 // @free:end
265 }
266
267 /*
268 ==============================================================
269 * STEP 3 – 2FA Policy (Enforcement)
270 * ==============================================================
271 */
272
273 /**
274 * Render the enforcement policy step.
275 *
276 * @return void
277 */
278 public static function step_enforcement_policy() {
279 $enforcement_policy = WP2FA::get_wp2fa_setting( 'enforcement-policy' );
280 $enforced_users = array_filter( (array) WP2FA::get_wp2fa_setting( 'enforced_users' ) );
281 $enforced_roles = array_filter( (array) WP2FA::get_wp2fa_setting( 'enforced_roles' ) );
282 $all_roles = WP_Helper::get_roles_wp();
283
284 $enforced_users_items = array_map(
285 function ( $user ) {
286 return array(
287 'id' => $user,
288 'text' => $user,
289 );
290 },
291 $enforced_users
292 );
293
294 $enforced_roles_items = array_map(
295 function ( $role ) use ( $all_roles ) {
296 return array(
297 'id' => $role,
298 'text' => isset( $all_roles[ $role ] ) ? $all_roles[ $role ] : ucfirst( $role ),
299 );
300 },
301 $enforced_roles
302 );
303
304 // Build radio options – add multisite-specific choices when applicable.
305 $radio_options = array(
306 'all-users' => \esc_html__( 'All users', 'wp-2fa' ),
307 );
308
309 $toggle_map = array(
310 'all-users' => '',
311 );
312
313 if ( WP_Helper::is_multisite() ) {
314 $radio_options['superadmins-only'] = \esc_html__( 'Only super admins', 'wp-2fa' );
315 $radio_options['superadmins-siteadmins-only'] = \esc_html__( 'Only super admins and site admins', 'wp-2fa' );
316 $toggle_map['superadmins-only'] = '';
317 $toggle_map['superadmins-siteadmins-only'] = '';
318 }
319
320 $radio_options['certain-roles-only'] = \esc_html__( 'Only for specific users and roles', 'wp-2fa' );
321 $toggle_map['certain-roles-only'] = '#wizard-enforcement-specific-inputs';
322
323 if ( WP_Helper::is_multisite() ) {
324 $radio_options['enforce-on-multisite'] = \esc_html__( 'These sub-sites', 'wp-2fa' );
325 $toggle_map['enforce-on-multisite'] = '#wizard-enforcement-sites-inputs';
326 }
327
328 $radio_options['do-not-enforce'] = \esc_html__( 'Do not enforce on any users', 'wp-2fa' );
329 $toggle_map['do-not-enforce'] = '';
330
331 ?>
332 <h3><?php \esc_html_e( 'Do you want to enforce 2FA for some, or all users?', 'wp-2fa' ); ?></h3>
333 <p class="description">
334 <?php \esc_html_e( 'When 2FA is enforced, users will be prompted to configure it the next time they log in. Users are given a grace period to complete the setup before 2FA becomes mandatory. You can configure the grace period and exclude specific users or roles later from the plugin\'s settings.', 'wp-2fa' ); ?>
335 <a href="https://melapress.com/support/kb/wp-2fa-configure-2fa-policies-enforce/?&utm_source=plugin&utm_medium=wp2fa&utm_campaign=enforcement_policy_help" target="_blank" rel="noopener"><?php \esc_html_e( 'Learn more.', 'wp-2fa' ); ?></a>
336 </p>
337
338 <div class="form-group settings-row">
339 <?php
340 Settings_Builder::build_option(
341 array(
342 'id' => 'wizard-enforcement-policy',
343 'type' => 'radio',
344 'option_name' => self::NAME_PREFIX . '[enforcement-policy]',
345 'default' => $enforcement_policy,
346 'options' => $radio_options,
347 'toggle' => $toggle_map,
348 )
349 );
350 ?>
351 </div>
352
353 <!-- Specific users / roles selection -->
354 <div id="wizard-enforcement-specific-inputs" style="display:none; padding-bottom: 20px;">
355 <div class="form-group settings-row" style="margin-top:12px;">
356 <?php
357 Settings_Builder::build_option(
358 array(
359 'text' => \esc_html__( 'Users', 'wp-2fa' ),
360 'id' => 'wizard-enforced-users-label',
361 'type' => 'settings-label',
362 )
363 );
364
365 Settings_Builder::build_option(
366 array(
367 'id' => 'wizard-enforced-users',
368 'type' => 'multi-select-ajax',
369 'option_name' => self::NAME_PREFIX . '[enforced_users]',
370 'default' => implode( ',', $enforced_users ),
371 'placeholder' => \esc_attr__( 'Type to search users…', 'wp-2fa' ),
372 'items' => $enforced_users_items,
373 'data_attrs' => array(
374 'search-type' => 'users',
375 'min-chars' => '2',
376 ),
377 )
378 );
379 ?>
380 </div>
381
382 <div class="form-group settings-row">
383 <?php
384 Settings_Builder::build_option(
385 array(
386 'text' => \esc_html__( 'Roles', 'wp-2fa' ),
387 'id' => 'wizard-enforced-roles-label',
388 'type' => 'settings-label',
389 )
390 );
391
392 Settings_Builder::build_option(
393 array(
394 'id' => 'wizard-enforced-roles',
395 'type' => 'multi-select-ajax',
396 'option_name' => self::NAME_PREFIX . '[enforced_roles]',
397 'default' => implode( ',', $enforced_roles ),
398 'placeholder' => \esc_attr__( 'Type to search roles…', 'wp-2fa' ),
399 'items' => $enforced_roles_items,
400 'data_attrs' => array(
401 'search-type' => 'roles',
402 'min-chars' => '2',
403 ),
404 )
405 );
406 ?>
407 </div>
408 </div>
409
410 <?php if ( WP_Helper::is_multisite() ) : ?>
411 <!-- Sub-sites selection -->
412 <div id="wizard-enforcement-sites-inputs" style="display:none; padding-bottom: 20px;">
413 <div class="form-group settings-row" style="margin-top:12px;">
414 <?php
415 $included_sites = array_filter( (array) WP2FA::get_wp2fa_setting( 'included_sites' ) );
416 $included_sites_items = array();
417 foreach ( WP_Helper::get_multi_sites() as $site ) {
418 if ( in_array( $site->blog_id, $included_sites, true ) || in_array( (string) $site->blog_id, $included_sites, true ) ) {
419 $included_sites_items[] = array(
420 'id' => $site->blog_id,
421 'text' => $site->blogname,
422 );
423 }
424 }
425
426 Settings_Builder::build_option(
427 array(
428 'text' => \esc_html__( 'Sites', 'wp-2fa' ),
429 'id' => 'wizard-enforced-sites-label',
430 'type' => 'settings-label',
431 )
432 );
433
434 Settings_Builder::build_option(
435 array(
436 'id' => 'wizard-enforced-sites',
437 'type' => 'multi-select-ajax',
438 'option_name' => self::NAME_PREFIX . '[included_sites]',
439 'default' => implode( ',', $included_sites ),
440 'placeholder' => \esc_attr__( 'Type to search sites…', 'wp-2fa' ),
441 'items' => $included_sites_items,
442 'data_attrs' => array(
443 'search-type' => 'sites',
444 'min-chars' => '2',
445 ),
446 )
447 );
448 ?>
449 </div>
450 </div>
451 <?php endif; ?>
452 <?php
453 }
454
455 /*
456 ==============================================================
457 * STEP 4 – Exclude Users
458 * ==============================================================
459 */
460
461 /**
462 * Render the exclusions step.
463 *
464 * @return void
465 */
466 public static function step_exclude_users() {
467 $excluded_users = array_filter( (array) WP2FA::get_wp2fa_setting( 'excluded_users' ) );
468 $excluded_roles = array_filter( (array) WP2FA::get_wp2fa_setting( 'excluded_roles' ) );
469 $all_roles = WP_Helper::get_roles_wp();
470
471 $excluded_users_items = array_map(
472 function ( $user ) {
473 return array(
474 'id' => $user,
475 'text' => $user,
476 );
477 },
478 $excluded_users
479 );
480
481 $excluded_roles_items = array_map(
482 function ( $role ) use ( $all_roles ) {
483 return array(
484 'id' => $role,
485 'text' => isset( $all_roles[ $role ] ) ? $all_roles[ $role ] : ucfirst( $role ),
486 );
487 },
488 $excluded_roles
489 );
490 ?>
491 <h3><?php \esc_html_e( 'Do you want to exclude any users or roles from 2FA?', 'wp-2fa' ); ?></h3>
492 <p class="description">
493 <?php \esc_html_e( 'If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below:', 'wp-2fa' ); ?>
494 </p>
495
496 <div class="form-group settings-row">
497 <?php
498 Settings_Builder::build_option(
499 array(
500 'text' => \esc_html__( 'Exclude the following users', 'wp-2fa' ),
501 'id' => 'wizard-excluded-users-label',
502 'type' => 'settings-label',
503 )
504 );
505
506 Settings_Builder::build_option(
507 array(
508 'id' => 'wizard-excluded-users',
509 'type' => 'multi-select-ajax',
510 'option_name' => self::NAME_PREFIX . '[excluded_users]',
511 'default' => implode( ',', $excluded_users ),
512 'placeholder' => \esc_attr__( 'Add users', 'wp-2fa' ),
513 'items' => $excluded_users_items,
514 'data_attrs' => array(
515 'search-type' => 'users',
516 'min-chars' => '2',
517 ),
518 )
519 );
520 ?>
521 </div>
522
523 <div class="form-group settings-row">
524 <?php
525 Settings_Builder::build_option(
526 array(
527 'text' => \esc_html__( 'Exclude the following roles', 'wp-2fa' ),
528 'id' => 'wizard-excluded-roles-label',
529 'type' => 'settings-label',
530 )
531 );
532
533 Settings_Builder::build_option(
534 array(
535 'id' => 'wizard-excluded-roles',
536 'type' => 'multi-select-ajax',
537 'option_name' => self::NAME_PREFIX . '[excluded_roles]',
538 'default' => implode( ',', $excluded_roles ),
539 'placeholder' => \esc_attr__( 'Add roles', 'wp-2fa' ),
540 'items' => $excluded_roles_items,
541 'data_attrs' => array(
542 'search-type' => 'roles',
543 'min-chars' => '2',
544 ),
545 )
546 );
547 ?>
548 </div>
549 <?php
550 }
551
552 /*
553 ==============================================================
554 * STEP 5 – Exclude Sites (Multisite only)
555 * ==============================================================
556 */
557
558 /**
559 * Render the exclude sites step (multisite only).
560 *
561 * @return void
562 */
563 public static function step_exclude_sites() {
564 if ( ! WP_Helper::is_multisite() ) {
565 return;
566 }
567
568 $excluded_sites = array_filter( (array) WP2FA::get_wp2fa_setting( 'excluded_sites' ) );
569 $excluded_sites_items = array();
570 foreach ( WP_Helper::get_multi_sites() as $site ) {
571 if ( in_array( $site->blog_id, $excluded_sites, true ) || in_array( (string) $site->blog_id, $excluded_sites, true ) ) {
572 $excluded_sites_items[] = array(
573 'id' => $site->blog_id,
574 'text' => $site->blogname,
575 );
576 }
577 }
578 ?>
579 <h3><?php \esc_html_e( 'Do you want to exclude all the users of a site from 2FA?', 'wp-2fa' ); ?></h3>
580 <p class="description">
581 <?php \esc_html_e( 'If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:', 'wp-2fa' ); ?>
582 </p>
583
584 <div class="form-group settings-row" style="margin-top:12px;">
585 <?php
586 Settings_Builder::build_option(
587 array(
588 'text' => \esc_html__( 'Exclude the following sites', 'wp-2fa' ),
589 'id' => 'wizard-excluded-sites-label',
590 'type' => 'settings-label',
591 )
592 );
593
594 Settings_Builder::build_option(
595 array(
596 'id' => 'wizard-excluded-sites',
597 'type' => 'multi-select-ajax',
598 'option_name' => self::NAME_PREFIX . '[excluded_sites]',
599 'default' => implode( ',', $excluded_sites ),
600 'placeholder' => \esc_attr__( 'Type to search sites…', 'wp-2fa' ),
601 'items' => $excluded_sites_items,
602 'data_attrs' => array(
603 'search-type' => 'sites',
604 'min-chars' => '2',
605 ),
606 )
607 );
608 ?>
609 </div>
610 <?php
611 }
612
613 /*
614 ==============================================================
615 * STEP 6 – Grace Period
616 * ==============================================================
617 */
618
619 /**
620 * Render the grace period step.
621 *
622 * @return void
623 */
624 public static function step_grace_period() {
625 $grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy', true );
626 $grace_period = (int) WP2FA::get_wp2fa_setting( 'grace-period', true );
627 $grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator', true );
628 $grace_after_expire_action = WP2FA::get_wp2fa_setting( 'grace-policy-after-expire-action', true );
629
630 if ( ! $grace_policy ) {
631 $grace_policy = 'use-grace-period';
632 }
633 if ( ! $grace_period ) {
634 $grace_period = 3;
635 }
636 if ( ! $grace_period_denominator ) {
637 $grace_period_denominator = 'days';
638 }
639 if ( ! $grace_after_expire_action ) {
640 $grace_after_expire_action = 'configure-right-away';
641 }
642
643 $user = \wp_get_current_user();
644 $last_user_to_update_settings = $user->ID;
645
646 $grace_notification = Settings_Utils::get_setting_role( null, Grace_Period_Notifications::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME, true );
647 if ( ! $grace_notification ) {
648 $grace_notification = 'after-login-notification';
649 }
650 ?>
651 <h3><?php \esc_html_e( 'How long should the grace period for your users be?', 'wp-2fa' ); ?></h3>
652 <p class="description">
653 <?php \esc_html_e( 'When you configure the 2FA policies and require users to configure 2FA, they can either have a grace period to configure 2FA, or can be required to configure 2FA before the next time they login.', 'wp-2fa' ); ?>
654 </p>
655
656 <div class="settings-card">
657 <?php
658 Settings_Builder::build_option(
659 array(
660 'title' => \esc_html__( 'Choose which method you\'d like to use:', 'wp-2fa' ),
661 'id' => 'wizard-grace-method-title',
662 'type' => 'section-sub-title',
663 )
664 );
665 ?>
666
667 <div class="form-group settings-row">
668 <div class="settings-control">
669 <label class="radio-option">
670 <input type="radio"
671 name="<?php echo \esc_attr( self::NAME_PREFIX ); ?>[grace-policy]"
672 id="wizard-no-grace-period"
673 value="no-grace-period"
674 <?php \checked( $grace_policy, 'no-grace-period' ); ?>>
675 <span><?php \esc_html_e( 'Users have to configure 2FA straight away.', 'wp-2fa' ); ?></span>
676 </label>
677 <label class="radio-option">
678 <input type="radio"
679 name="<?php echo \esc_attr( self::NAME_PREFIX ); ?>[grace-policy]"
680 id="wizard-use-grace-period"
681 value="use-grace-period"
682 <?php \checked( $grace_policy, 'use-grace-period' ); ?>
683 data-toggle-target="#wizard-grace-period-sub-settings">
684 <span><?php \esc_html_e( 'Give users a grace period to configure 2FA', 'wp-2fa' ); ?></span>
685 </label>
686 </div>
687 </div>
688
689 <!-- Grace period sub-settings -->
690 <div id="wizard-grace-period-sub-settings" class="grace-period-sub-settings"<?php echo ( 'use-grace-period' !== $grace_policy ) ? ' style="display:none;"' : ''; ?>>
691 <div class="form-group settings-row" style="margin-left: 35px;">
692 <div class="settings-control inline-controls">
693 <?php
694 Settings_Builder::build_option(
695 array(
696 'id' => 'wizard-grace-period',
697 'type' => 'number',
698 'min' => 1,
699 'max' => 90,
700 'class' => 'small-text',
701 'option_name' => self::NAME_PREFIX . '[grace-period]',
702 'default' => $grace_period,
703 )
704 );
705 Settings_Builder::build_option(
706 array(
707 'id' => 'wizard-grace-period-denominator',
708 'type' => 'select',
709 'option_name' => self::NAME_PREFIX . '[grace-period-denominator]',
710 'default' => $grace_period_denominator,
711 'options' => array(
712 'hours' => \esc_html__( 'Hours', 'wp-2fa' ),
713 'days' => \esc_html__( 'Days', 'wp-2fa' ),
714 ),
715 )
716 );
717 ?>
718 </div>
719 </div>
720
721 <!-- What happens after grace period expires -->
722 <div style="margin-left: 35px;">
723 <?php
724 Settings_Builder::build_option(
725 array(
726 'title' => \esc_html__( 'Users who missed the grace period', 'wp-2fa' ),
727 'id' => 'wizard-grace-expire-label',
728 'type' => 'section-sub-title',
729 )
730 );
731 ?>
732 <div class="form-group settings-row">
733 <?php
734 Settings_Builder::build_option(
735 array(
736 'id' => 'wizard-grace-policy-after-expire-action',
737 'type' => 'radio',
738 'option_name' => self::NAME_PREFIX . '[grace-policy-after-expire-action]',
739 'default' => $grace_after_expire_action,
740 'options' => array(
741 'configure-right-away' => \esc_html__( 'Disable user access to the dashboard / user page once they log in, until they configure 2FA', 'wp-2fa' ),
742 'manual-block' => \esc_html__( 'Block the user (administrators have to manually unblock them)', 'wp-2fa' ),
743 ),
744 )
745 );
746 ?>
747 </div>
748 </div>
749
750 <!-- Notification settings -->
751 <div style="margin-left: 35px;">
752 <?php
753 Settings_Builder::build_option(
754 array(
755 'title' => \esc_html__( 'Notification settings', 'wp-2fa' ),
756 'id' => 'wizard-grace-notification-label',
757 'type' => 'section-sub-title',
758 )
759 );
760 ?>
761 <div class="form-group settings-row">
762 <?php
763 Settings_Builder::build_option(
764 array(
765 'id' => 'wizard-grace-notification',
766 'type' => 'radio',
767 'option_name' => self::NAME_PREFIX . '[' . Grace_Period_Notifications::GRACE_PERIOD_NOTIFICATION_SETTINGS_NAME . ']',
768 'default' => $grace_notification,
769 'options' => array(
770 'dashboard-notification' => \esc_html__( 'Show an admin notice in the dashboard', 'wp-2fa' ),
771 'after-login-notification' => \esc_html__( 'Show a notification on a page on its own after the user authenticates and before accessing the dashboard', 'wp-2fa' ),
772 ),
773 )
774 );
775 ?>
776 </div>
777 </div>
778 </div>
779 </div>
780
781 <input type="hidden" name="<?php echo \esc_attr( self::NAME_PREFIX ); ?>[2fa_settings_last_updated_by]" value="<?php echo \esc_attr( $last_user_to_update_settings ); ?>">
782 <?php
783 }
784 }
785 }
786