PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.4.2
WP 2FA – Two-factor authentication for WordPress v2.4.2
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-settings-page.php
wp-2fa / includes / classes / Admin Last commit date
Controllers 3 years ago Helpers 3 years ago SettingsPages 3 years ago Views 3 years ago class-help-contact-us.php 3 years ago class-premium-features.php 3 years ago class-settings-page.php 3 years ago class-settingspage.php 3 years ago class-setup-wizard.php 3 years ago class-user-listing.php 3 years ago class-user-notices.php 3 years ago class-user-profile.php 3 years ago class-user-registered.php 3 years ago class-user.php 3 years ago index.php 5 years ago
class-settings-page.php
693 lines
1 <?php
2 /**
3 * Settings rendering class.
4 *
5 * @package wp2fa
6 * @subpackage settings
7 * @copyright 2023 WP White Security
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;
13
14 use \WP2FA\WP2FA as WP2FA;
15 use WP2FA\Utils\User_Utils;
16 use WP2FA\Utils\Settings_Utils;
17 use WP2FA\Admin\SettingsPages\{
18 Settings_Page_Policies,
19 Settings_Page_General,
20 Settings_Page_Email
21 };
22 use WP2FA\Admin\Helpers\WP_Helper;
23 use WP2FA\Admin\Helpers\User_Helper;
24 use WP2FA\Admin\Controllers\Settings;
25
26 /**
27 * Class for handling settings
28 */
29 if ( ! class_exists( '\WP2FA\Admin\Settings_Page' ) ) {
30 /**
31 * Class for handling settings
32 */
33 class Settings_Page {
34
35 const TOP_MENU_SLUG = 'wp-2fa-policies';
36
37 /**
38 * Holds the status of the backup codes functionality
39 *
40 * @var bool[]
41 */
42 private static $backup_codes_enabled = array();
43
44 /**
45 * Create admin menu entry and settings page
46 */
47 public static function create_settings_admin_menu() {
48 // Create admin menu item.
49 add_menu_page(
50 esc_html__( 'WP 2FA', 'wp-2fa' ),
51 esc_html__( 'WP 2FA', 'wp-2fa' ),
52 'manage_options',
53 self::TOP_MENU_SLUG,
54 null,
55 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
56 81
57 );
58
59 $settings_policies = new Settings_Page_Policies();
60
61 add_submenu_page(
62 self::TOP_MENU_SLUG,
63 esc_html__( '2FA Policies', 'wp-2fa' ),
64 esc_html__( '2FA Policies', 'wp-2fa' ),
65 'manage_options',
66 self::TOP_MENU_SLUG,
67 array( $settings_policies, 'render' ),
68 1
69 );
70
71 add_submenu_page(
72 self::TOP_MENU_SLUG,
73 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
74 esc_html__( 'Settings', 'wp-2fa' ),
75 'manage_options',
76 'wp-2fa-settings',
77 array( \WP2FA\Admin\Views\Settings_Page_Render::class, 'render' ),
78 2
79 );
80
81 // Register our policy settings.
82 register_setting(
83 WP_2FA_POLICY_SETTINGS_NAME,
84 WP_2FA_POLICY_SETTINGS_NAME,
85 array( $settings_policies, 'validate_and_sanitize' )
86 );
87
88 // Register our white label settings.
89 register_setting(
90 WP_2FA_WHITE_LABEL_SETTINGS_NAME,
91 WP_2FA_WHITE_LABEL_SETTINGS_NAME,
92 array( \WP2FA\Admin\SettingsPages\Settings_Page_White_Label::class, 'validate_and_sanitize' )
93 );
94
95 // Register our settings page.
96 register_setting(
97 WP_2FA_SETTINGS_NAME,
98 WP_2FA_SETTINGS_NAME,
99 array( \WP2FA\Admin\SettingsPages\Settings_Page_General::class, 'validate_and_sanitize' )
100 );
101
102 register_setting(
103 WP_2FA_EMAIL_SETTINGS_NAME,
104 WP_2FA_EMAIL_SETTINGS_NAME,
105 array( \WP2FA\Admin\SettingsPages\Settings_Page_Email::class, 'validate_and_sanitize' )
106 );
107
108 /**
109 * Fires after the main menu settings are registered.
110 *
111 * @param string - The menu slug.
112 * @param bool - Is that multisite install or not.
113 *
114 * @since 2.0.0
115 */
116 do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, false );
117 }
118
119 /**
120 * Create admin menu entry and settings page
121 */
122 public static function create_settings_admin_menu_multisite() {
123 // Create admin menu item.
124 add_menu_page(
125 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
126 esc_html__( 'WP 2FA', 'wp-2fa' ),
127 'manage_options',
128 self::TOP_MENU_SLUG,
129 null,
130 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( WP_2FA_PATH . 'dist/images/wp-2fa-white-icon20x28.svg' ) ), // phpcs:ignore
131 81
132 );
133
134 $settings_policies = new Settings_Page_Policies();
135
136 add_submenu_page(
137 self::TOP_MENU_SLUG,
138 esc_html__( '2FA Policies', 'wp-2fa' ),
139 esc_html__( '2FA Policies', 'wp-2fa' ),
140 'manage_options',
141 self::TOP_MENU_SLUG,
142 array( $settings_policies, 'render' ),
143 1
144 );
145
146 add_submenu_page(
147 self::TOP_MENU_SLUG,
148 esc_html__( 'WP 2FA Settings', 'wp-2fa' ),
149 esc_html__( 'Settings', 'wp-2fa' ),
150 'manage_options',
151 'wp-2fa-settings',
152 array( \WP2FA\Admin\Views\Settings_Page_Render::class, 'render' ),
153 2
154 );
155
156 /**
157 * Fires after the main menu settings are registered.
158 *
159 * @param string - The menu slug.
160 * @param bool - Is that multisite install or not.
161 *
162 * @since 2.0.0
163 */
164 do_action( WP_2FA_PREFIX . 'after_admin_menu_created', self::TOP_MENU_SLUG, true );
165 }
166
167 /**
168 * Get all users
169 *
170 * @SuppressWarnings(PHPMD.ExitExpression)
171 */
172 public static function get_all_users() {
173 // Die if user does not have permission to view.
174 if ( ! current_user_can( 'manage_options' ) ) {
175 die( 'Access Denied.' );
176 }
177 // Filter $_GET array for security.
178 $get_array = filter_input_array( INPUT_GET );
179
180 // Die if nonce verification failed.
181 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
182 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
183 }
184
185 $users_args = array(
186 'fields' => array( 'ID', 'user_login' ),
187 );
188 if ( WP_Helper::is_multisite() ) {
189 $users_args['blog_id'] = 0;
190 }
191 $users_data = User_Utils::get_all_user_ids_and_login_names( 'query', $users_args );
192
193 // Create final array which we will fill in below.
194 $users = array();
195
196 foreach ( $users_data as $user ) {
197 if ( strpos( $user['user_login'], $get_array['term'] ) !== false ) {
198 array_push(
199 $users,
200 array(
201 'value' => $user['user_login'],
202 'label' => $user['user_login'],
203 )
204 );
205 }
206 }
207
208 echo wp_json_encode( $users );
209 exit;
210 }
211
212 /**
213 * Get all network sites
214 *
215 * @SuppressWarnings(PHPMD.ExitExpression)
216 */
217 public static function get_all_network_sites() {
218 // Die if user does not have permission to view.
219 if ( ! current_user_can( 'manage_options' ) ) {
220 die( 'Access Denied.' );
221 }
222 // Filter $_GET array for security.
223 $get_array = filter_input_array( INPUT_GET );
224 // Die if nonce verification failed.
225 if ( ! wp_verify_nonce( sanitize_text_field( $get_array['wp_2fa_nonce'] ), 'wp-2fa-settings-nonce' ) ) {
226 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
227 }
228 // Fetch sites.
229 $sites_found = array();
230
231 foreach ( get_sites() as $site ) {
232 $subsite_id = get_object_vars( $site )['blog_id'];
233 $subsite_name = get_blog_details( $subsite_id )->blogname;
234 $site_details = '';
235 $site_details[ $subsite_id ] = $subsite_name;
236 if ( false !== stripos( $subsite_name, $get_array['term'] ) ) {
237 array_push(
238 $sites_found,
239 array(
240 'label' => $subsite_id,
241 'value' => $subsite_name,
242 )
243 );
244 }
245 }
246 echo wp_json_encode( $sites_found );
247 exit;
248 }
249
250 /**
251 * Unlock users accounts if they have overrun grace period
252 *
253 * @param int $user_id User ID.
254 *
255 * @SuppressWarnings(PHPMD.ExitExpression)
256 */
257 public static function unlock_account( $user_id ) {
258 // Die if user does not have permission to view.
259 if ( ! current_user_can( 'manage_options' ) ) {
260 die( 'Access Denied.' );
261 }
262
263 $grace_period = WP2FA::get_wp2fa_setting( 'grace-period' );
264 $grace_period_denominator = WP2FA::get_wp2fa_setting( 'grace-period-denominator' );
265 $create_a_string = $grace_period . ' ' . $grace_period_denominator;
266 // Turn that string into a time.
267 $grace_expiry = strtotime( $create_a_string );
268
269 // Filter $_GET array for security.
270 $get_array = filter_input_array( INPUT_GET );
271 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
272
273 // Die if nonce verification failed.
274 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-unlock-account-nonce' ) ) {
275 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
276 }
277
278 if ( isset( $get_array['user_id'] ) ) {
279 global $wpdb;
280 $wpdb->query( // phpcs:ignore
281 $wpdb->prepare(
282 "
283 DELETE FROM $wpdb->usermeta
284 WHERE user_id = %d
285 AND meta_key IN ( %s, %s )
286 ",
287 array(
288 intval( $get_array['user_id'] ),
289 User_Helper::USER_GRACE_KEY,
290 WP_2FA_PREFIX . 'locked_account_notification',
291 )
292 )
293 );
294 User_Helper::set_user_expiry_date( $grace_expiry, intval( $get_array['user_id'] ) );
295 self::send_account_unlocked_email( intval( $get_array['user_id'] ) );
296 add_action( 'admin_notices', array( __CLASS__, 'user_unlocked_notice' ) );
297 }
298 }
299
300 /**
301 * Remove user 2fa config
302 *
303 * @param int $user_id User ID.
304 *
305 * @SuppressWarnings(PHPMD.ExitExpression)
306 */
307 public static function remove_user_2fa( $user_id ) {
308 // Filter $_GET array for security.
309 $get_array = filter_input_array( INPUT_GET );
310 $nonce = sanitize_text_field( $get_array['wp_2fa_nonce'] );
311
312 if ( ! wp_verify_nonce( $nonce, 'wp-2fa-remove-user-2fa-nonce' ) ) {
313 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
314 }
315
316 if ( isset( $get_array['user_id'] ) ) {
317 $user_id = intval( $get_array['user_id'] );
318
319 if ( ! current_user_can( 'manage_options' ) && get_current_user_id() !== $user_id ) {
320 return;
321 }
322
323 User_Helper::remove_2fa_for_user( $user_id );
324
325 if ( isset( $get_array['admin_reset'] ) ) {
326 add_action( 'admin_notices', array( __CLASS__, 'admin_deleted_2fa_notice' ) );
327 } else {
328 add_action( 'admin_notices', array( __CLASS__, 'user_deleted_2fa_notice' ) );
329 }
330 }
331 }
332
333 /**
334 * Send account unlocked notification via email.
335 *
336 * @param int $user_id user ID.
337 *
338 * @return boolean
339 */
340 public static function send_account_unlocked_email( $user_id ) {
341 // Bail if the user has not enabled this email.
342 if ( 'enable_account_unlocked_email' !== WP2FA::get_wp2fa_email_templates( 'send_account_unlocked_email' ) ) {
343 return false;
344 }
345
346 // Grab user data.
347 $user = get_userdata( $user_id );
348 // Grab user email.
349 $email = $user->user_email;
350 // Setup the email contents.
351 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_subject' ) ) );
352 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'user_account_unlocked_email_body' ), $user_id ) );
353
354 return self::send_email( $email, $subject, $message );
355 }
356
357 /**
358 * Hide settings menu item
359 */
360 public static function hide_settings() {
361 $user = wp_get_current_user();
362
363 // Check we have a user before doing anything else.
364 if ( is_a( $user, '\WP_User' ) ) {
365 if ( ! empty( WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' ) ) ) {
366 $main_user = (int) WP2FA::get_wp2fa_setting( '2fa_settings_last_updated_by' );
367 } else {
368 $main_user = get_current_user_id();
369 }
370 if ( ! empty( WP2FA::get_wp2fa_general_setting( 'limit_access' ) ) && $user->ID !== $main_user ) {
371 // Remove admin menu item.
372 remove_submenu_page( 'options-general.php', self::TOP_MENU_SLUG );
373 }
374 }
375 }
376
377 /**
378 * Add unlock user link to user actions.
379 *
380 * @param array $links Default row content.
381 *
382 * @return array
383 * @throws \Freemius_Exception - freemius exception.
384 */
385 public static function add_plugin_action_links( $links ) {
386 // add link to the external free trial page in free version and also in premium version if license is not active.
387 if ( ! function_exists( 'wp2fa_freemius' ) || ! wp2fa_freemius()->has_active_valid_license() ) {
388 $trial_link = 'https://wp2fa.io/get-wp-2fa-premium-trial/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA';
389 $links = array_merge(
390 array(
391 '<a style="font-weight:bold" href="' . $trial_link . '" target="_blank">' . __( 'Free 14-day Premium Trial', 'wp-2fa' ) . '</a>',
392 ),
393 $links
394 );
395 }
396
397 // add link to the plugin settings page.
398 $url = Settings::get_settings_page_link();
399 $links = array_merge(
400 array(
401 '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Configure 2FA Settings', 'wp-2fa' ) . '</a>',
402 ),
403 $links
404 );
405
406 return $links;
407 }
408
409 /**
410 * User unlocked notice.
411 */
412 public static function user_unlocked_notice() {
413 ?>
414 <div class="notice notice-success is-dismissible">
415 <p><?php esc_html_e( 'User account successfully unlocked. User can login again.', 'wp-2fa' ); ?></p>
416 <button type="button" class="notice-dismiss">
417 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
418 </button>
419 </div>
420 <?php
421 }
422
423 /**
424 * User deleted 2FA settings notification
425 */
426 public static function user_deleted_2fa_notice() {
427 ?>
428 <div class="notice notice-success is-dismissible">
429 <p><?php esc_html_e( 'Your 2FA settings have been removed.', 'wp-2fa' ); ?></p>
430 <button type="button" class="notice-dismiss">
431 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
432 </button>
433 </div>
434 <?php
435 }
436
437 /**
438 * Admin deleted user 2FA settings notification
439 */
440 public static function admin_deleted_2fa_notice() {
441 ?>
442 <div class="notice notice-success is-dismissible">
443 <p><?php esc_html_e( 'User 2FA settings have been removed.', 'wp-2fa' ); ?></p>
444 <button type="button" class="notice-dismiss">
445 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
446 </button>
447 </div>
448 <?php
449 }
450
451 /**
452 * Updates options for multisite
453 *
454 * @return void
455 *
456 * @since 2.0.0
457 */
458 public static function update_wp2fa_network_options() {
459
460 $settings_policies = new Settings_Page_Policies();
461
462 $settings_policies->update_wp2fa_network_options();
463
464 Settings_Page_General::update_wp2fa_network_options();
465
466 \WP2FA\Admin\SettingsPages\Settings_Page_White_Label::update_wp2fa_network_options();
467
468 /**
469 * Gives the ability for extensions to set their settings in the plugin.
470 *
471 * @since 2.2.0
472 */
473 do_action( WP_2FA_PREFIX . 'update_network_settings' );
474 }
475
476 /**
477 * Handle saving email options to the network main site options.
478 */
479 public static function update_wp2fa_network_email_options() {
480 Settings_Page_Email::update_wp2fa_network_options();
481 }
482
483 /**
484 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
485 */
486 public static function settings_saved_network_admin_notice() {
487 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'true' === $_GET['wp_2fa_network_settings_updated'] ) { // phpcs:ignore
488 ?>
489 <div class="notice notice-success is-dismissible">
490 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
491 <button type="button" class="notice-dismiss">
492 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
493 </button>
494 </div>
495 <?php
496 }
497 if ( isset( $_GET['wp_2fa_network_settings_updated'] ) && 'false' === $_GET['wp_2fa_network_settings_updated'] ) { // phpcs:ignore
498 ?>
499 <div class="notice notice-error is-dismissible">
500 <?php
501 if ( isset( $_GET['wp_2fa_network_settings_custom_error_message'] ) ) { // phpcs:ignore
502 ?>
503 <p><?php echo \wp_unslash( $_GET['wp_2fa_network_settings_custom_error_message'] ); // phpcs:ignore ?></p>
504 <button type="button" class="notice-dismiss">
505 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
506 </button>
507 <?php
508 } else {
509 ?>
510 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
511 <button type="button" class="notice-dismiss">
512 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
513 </button>
514 <?php
515 }
516 ?>
517 </div>
518 <?php
519 }
520 if ( isset( $_GET['wp_2fa_network_settings_error'] ) ) { // phpcs:ignore
521 ?>
522 <div class="notice notice-error is-dismissible">
523 <p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( \wp_unslash( $_GET['wp_2fa_network_settings_error'] ) ) ) ); // phpcs:ignore ?></p>
524 <button type="button" class="notice-dismiss">
525 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
526 </button>
527 </div>
528 <?php
529 }
530 }
531
532 /**
533 * These are used instead of add_settings_error which in a network site. Used to show if settings have been updated or failed.
534 *
535 * @return void
536 *
537 * @since 2.0.0
538 */
539 public static function settings_saved_admin_notice() {
540 if ( isset( $_GET['page'] ) && 0 === strpos( $_GET['page'], 'wp-2fa-' ) ) {
541 if ( isset( $_GET['settings-updated'] ) && 'true' === $_GET['settings-updated'] ) {
542 $wp_settings_errors = get_settings_errors();
543
544 if ( count( $wp_settings_errors ) ) {
545 foreach ( $wp_settings_errors as $error ) {
546 ?>
547 <div class="notice notice-<?php echo \esc_attr( $error['type'] ); ?> is-dismissible">
548 <p><?php echo \esc_html( $error['message'] ); ?></p>
549 <button type="button" class="notice-dismiss">
550 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
551 </button>
552 </div>
553 <?php
554 }
555 } else {
556 ?>
557 <div class="notice notice-success is-dismissible">
558 <p><?php esc_html_e( '2FA Settings Updated', 'wp-2fa' ); ?></p>
559 <button type="button" class="notice-dismiss">
560 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
561 </button>
562 </div>
563 <?php
564 }
565 }
566 if ( isset( $_GET['settings-updated'] ) && 'false' === $_GET['settings-updated'] ) {
567 ?>
568 <div class="notice notice-error is-dismissible">
569 <p><?php esc_html_e( 'Please ensure both custom email address and display name are provided.', 'wp-2fa' ); ?></p>
570 <button type="button" class="notice-dismiss">
571 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
572 </button>
573 </div>
574 <?php
575 }
576 if ( isset( $_GET['settings_error'] ) ) {
577 ?>
578 <div class="notice notice-error is-dismissible">
579 <p><?php echo \esc_attr( \esc_url_raw( \urldecode_deep( \wp_unslash( $_GET['settings_error'] ) ) ) ); ?></p>
580 <button type="button" class="notice-dismiss">
581 <span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wp-2fa' ); ?></span>
582 </button>
583 </div>
584 <?php
585 }
586 }
587 }
588
589 /**
590 * Add our custom state to our created page.
591 *
592 * @param array $post_states - array with the post states.
593 * @param WP_Post $post - the WP post.
594 *
595 * @return array
596 */
597 public static function add_display_post_states( $post_states, $post ) {
598 if ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ) {
599 if ( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) === $post->ID ) {
600 $post_states['wp_2fa_page_for_user'] = __( 'WP 2FA User Page', 'wp-2fa' );
601 }
602 }
603
604 return $post_states;
605 }
606
607 /**
608 * Handles sending of an email. It sets necessary header such as content type and custom from email address and name.
609 *
610 * @param string $recipient_email Email address to send message to.
611 * @param string $subject Email subject.
612 * @param string $message Message contents.
613 *
614 * @return bool Whether the email contents were sent successfully.
615 */
616 public static function send_email( $recipient_email, $subject, $message ) {
617
618 // Specify our desired headers.
619 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
620
621 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
622 $headers .= 'From: ' . WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) . ' <' . WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) . '>' . "\r\n";
623 } else {
624 $headers .= 'From: ' . get_bloginfo( 'name' ) . ' <' . get_bloginfo( 'admin_email' ) . '>' . "\r\n";
625 }
626
627 // Fire our email.
628 return wp_mail( $recipient_email, $subject, $message, $headers );
629
630 }
631
632 /**
633 * Turns user roles data in any form and shape to an array of strings.
634 *
635 * @param mixed $value User role names (slugs) as raw value.
636 *
637 * @return string[] List of user role names (slugs).
638 */
639 public static function extract_roles_from_input( $value ) {
640 if ( is_array( $value ) ) {
641 return $value;
642 }
643
644 if ( is_string( $value ) && ! empty( $value ) ) {
645 return explode( ',', $value );
646 }
647
648 return array();
649 }
650
651 /**
652 * Determine if any BG processes are currently running.
653 *
654 * @return int|false Number of jobs.
655 */
656 public static function get_current_number_of_active_bg_processes() {
657 global $wpdb;
658
659 $bg_jobs = $wpdb->get_results( // phpcs:ignore
660 "SELECT option_value FROM $wpdb->options
661 WHERE option_name LIKE '%_2fa_bg_%'"
662 );
663
664 return count( $bg_jobs );
665 }
666
667 /**
668 * Checks if the backup codes option is enabled for the role
669 *
670 * @param string $role - The role name.
671 *
672 * @return bool
673 */
674 public static function are_backup_codes_enabled( $role = 'global' ) {
675
676 $role = ( is_null( $role ) || empty( $role ) ) ? 'global' : $role;
677
678 if ( ! isset( self::$backup_codes_enabled[ $role ] ) ) {
679 self::$backup_codes_enabled[ $role ] = false;
680
681 if ( 'global' === $role ) {
682 $setting_value = Settings::get_role_or_default_setting( 'backup_codes_enabled' );
683 } else {
684 $setting_value = Settings::get_role_or_default_setting( 'backup_codes_enabled', 'current', $role );
685 }
686 self::$backup_codes_enabled[ $role ] = Settings_Utils::string_to_bool( $setting_value );
687 }
688
689 return self::$backup_codes_enabled[ $role ];
690 }
691 }
692 }
693