PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.1
WP 2FA – Two-factor authentication for WordPress v2.9.1
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 / Methods / class-email-wizard-steps.php
wp-2fa / includes / classes / Admin / Methods Last commit date
Traits 11 months ago class-backup-codes.php 11 months ago class-email-wizard-steps.php 11 months ago class-email.php 11 months ago class-totp-wizard-steps.php 11 months ago class-totp.php 11 months ago index.php 11 months ago
class-email-wizard-steps.php
349 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's Email manipulation.
4 *
5 * @package wp2fa
6 * @subpackage methods-wizard
7 * @since 2.6.0
8 * @copyright 2025 Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 *
11 * @see https://wordpress.org/plugins/wp-2fa/
12 */
13
14 declare(strict_types=1);
15
16 namespace WP2FA\Methods\Wizards;
17
18 use WP2FA\WP2FA;
19 use WP2FA\Methods\Email;
20 use WP2FA\Admin\Settings_Page;
21 use WP2FA\Utils\Settings_Utils;
22 use WP2FA\Admin\Helpers\WP_Helper;
23 use WP2FA\Admin\Views\Wizard_Steps;
24 use WP2FA\Admin\Controllers\Methods;
25 use WP2FA\Admin\Helpers\User_Helper;
26 use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
27
28 /**
29 * Class for handling email codes.
30 *
31 * @since 2.6.0
32 *
33 * @package WP2FA
34 */
35 if ( ! class_exists( '\WP2FA\Methods\Wizards\Email_Wizard_Steps' ) ) {
36 /**
37 * Email code class, for handling email code generation and such.
38 *
39 * @since 2.6.0
40 */
41 class Email_Wizard_Steps extends Wizard_Steps {
42
43 use Methods_Wizards_Trait;
44
45 /**
46 * Keeps the main class method name, so we can call it when needed.
47 *
48 * @var string
49 *
50 * @since 2.6.0
51 */
52 private static $main_class = Email::class;
53
54 /**
55 * The default value of the method order in the wizards.
56 *
57 * @var integer
58 *
59 * @since 2.6.0
60 */
61 private static $order = 2;
62
63 /**
64 * Inits the class hooks
65 *
66 * @return void
67 *
68 * @since 2.4.0
69 */
70 public static function init() {
71 \add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'email_option' ), 10, 2 );
72 \add_action( WP_2FA_PREFIX . 'modal_methods', array( __CLASS__, 'email_modal_configure' ) );
73 \add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'email_re_configure' ), 10, 2 );
74 \add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'email_wizard_settings' ), 10, 4 );
75 }
76
77 /**
78 * Shows the option for email method reconfiguring (if applicable)
79 *
80 * @param array $methods - Array of methods collected.
81 * @param string $role - The name of the role to show option to.
82 *
83 * @since 2.6.0 - Parameter $methods is added, parameter $role (name) is added and array is now returned
84 *
85 * @return array
86 */
87 public static function email_re_configure( array $methods, string $role ): array {
88
89 if ( ! Email::is_enabled( $role ) ) {
90 return $methods;
91 }
92 \ob_start();
93 ?>
94 <div class="option-pill">
95 <?php echo \wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'hotp_reconfigure_intro', true ), User_Helper::get_user_object()->ID, 'hotp' ) ); ?>
96 <div class="wp2fa-setup-actions">
97 <a class="button button-primary wp-2fa-button-primary" data-name="next_step_setting_modal_wizard" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-next-step="2fa-wizard-email"><?php \esc_html_e( 'Change email address', 'wp-2fa' ); ?></a>
98 </div>
99 </div>
100 <?php
101 $output = ob_get_clean();
102
103 $methods[ self::get_order( $role, $methods ) ] = array(
104 'name' => self::$main_class::METHOD_NAME,
105 'output' => $output,
106 );
107
108 return $methods;
109 }
110
111 /**
112 * Shows the initial email setup options based on enabled methods
113 *
114 * @param array $methods - Array of methods collected.
115 * @param string $role - The name of the role to show option to.
116 *
117 * @since 2.6.0 - Parameter $methods is added, parameter $role (name) is added and array is now returned
118 *
119 * @return array
120 */
121 public static function email_option( array $methods, string $role ): array {
122 if ( Email::is_enabled( $role ) && Methods::is_method_enabled_for_role( Email::METHOD_NAME, $role ) ) {
123 \ob_start();
124 ?>
125 <div class="option-pill">
126 <label for="geek">
127 <input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email">
128 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'email-option-label', true ) ); ?>
129 <?php
130 $show = ( 'show_help_text' === WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) && WP2FA::get_wp2fa_white_label_setting( \esc_attr( Email::METHOD_NAME ) . '-option-label-hint', true ) ) ?? false;
131 if ( $show ) {
132 echo '<br><span class="wizard-tooltip" data-tooltip-content="data-' . \esc_attr( Email::METHOD_NAME ) . '-tooltip-content-wrapper">i</span>';
133 }
134 ?>
135 </label>
136 <?php
137 if ( $show ) {
138 echo '<p class="description tooltip-content-wrapper" data-' . \esc_attr( Email::METHOD_NAME ) . '-tooltip-content-wrapper>' . \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( \esc_attr( Email::METHOD_NAME ) . '-option-label-hint', true ) ) . '</p>';
139 }
140 ?>
141 </div>
142 <?php
143 $output = ob_get_clean();
144
145 $methods[ self::get_order( $role, $methods ) ] = $output;
146 }
147
148 return $methods;
149 }
150
151 /**
152 * Settings page and first time wizard settings render
153 *
154 * @param array $methods - Array with all the methods in which we have to add this one.
155 * @param boolean $setup_wizard - Is that the first time setup wizard.
156 * @param string $data_role - Additional HTML data attribute.
157 * @param mixed $role - Name of the role.
158 *
159 * @return array - The array with the methods with all the methods wizard steps.
160 *
161 * @since 2.6.0
162 */
163 public static function email_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
164 $name_prefix = \WP_2FA_POLICY_SETTINGS_NAME;
165 $role_id = '';
166 if ( null !== $role && '' !== trim( (string) $role ) ) {
167 $name_prefix .= "[{$role}]";
168 $data_role = 'data-role="' . $role . '"';
169 $role_id = '-' . $role;
170 }
171 \ob_start();
172 ?>
173 <div id="<?php echo \esc_attr( Email::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
174 <?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
175 <label for="hotp<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
176 <input type="checkbox" id="hotp<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_email]" value="enable_email"
177 <?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
178
179 <?php \checked( Email::POLICY_SETTINGS_NAME, Settings_Utils::get_setting_role( $role, Email::POLICY_SETTINGS_NAME ), true ); ?>
180
181 >
182 <?php
183 \esc_html_e( 'One-time code via email (HOTP)', 'wp-2fa' );
184 \esc_html_e( ' - ensure email deliverability with the free plugin ', 'wp-2fa' );
185 echo '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a>.';
186 ?>
187 </label>
188 <?php
189 if ( $setup_wizard ) {
190 echo '<p class="description">' . \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 ', 'wp-2fa' ) . '<a href="https://wordpress.org/plugins/wp-mail-smtp/" target="_blank" rel="nofollow">WP Mail SMTP</a><br><br>' . \esc_html__( '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' ) . '</p>';
191 }
192 ?>
193 <?php
194
195 $enabled_settings = Settings_Utils::get_setting_role( $role, 'enable_email', true );
196
197 ?>
198 <?php
199 ?>
200 <?php if ( ! $setup_wizard ) { ?>
201 <div class="use-different-hotp-mail<?php echo \esc_attr( ( false === $enabled_settings ? ' disabled' : '' ) ); ?>">
202 <p class="description">
203 <?php \esc_html_e( 'Allow user to specify the email address of choice', 'wp-2fa' ); ?>
204 </p>
205
206 <fieldset class="email-hotp-options">
207 <?php
208 $options = array(
209 'yes' => array(
210 'label' => \esc_html__( 'Yes', 'wp-2fa' ),
211 'value' => 'specify-email_hotp',
212 ),
213 'no' => array(
214 'label' => \esc_html__( 'No', 'wp-2fa' ),
215 'value' => '',
216 ),
217 );
218
219 foreach ( $options as $option_key => $option_settings ) {
220 ?>
221 <label for="specify-email_hotp-<?php echo \esc_attr( $option_key ); ?><?php echo \esc_attr( $role_id ); ?>">
222 <input type="radio"
223 name="<?php echo \esc_attr( $name_prefix ); ?>[specify-email_hotp]"
224 <?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
225 id="specify-email_hotp-<?php echo \esc_attr( $option_key ); ?><?php echo \esc_attr( $role_id ); ?>"
226 value="<?php echo \esc_attr( $option_settings['value'] ); ?>" class="js-nested"
227 <?php echo \checked( Settings_Utils::get_setting_role( $role, 'specify-email_hotp', \true ), $option_settings['value'] ); ?>
228 >
229 <span><?php echo $option_settings['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
230 </label>
231 <?php
232 }
233 ?>
234 </fieldset>
235 </div>
236 <script>
237 document.querySelector('.enforce-email-hotp #enforce-email<?php echo \esc_attr( $role_id ); ?>').onclick = function() {
238 if (this.checked == true){
239 this.closest('.use-different-hotp-mail').querySelector('.email-hotp-options').classList.add('disabled');
240 document.querySelector('#specify-email_hotp-<?php echo \esc_attr( $option_key ); ?><?php echo \esc_attr( $role_id ); ?>').checked = true;
241 } else {
242 this.closest('.use-different-hotp-mail').querySelector('.email-hotp-options').classList.remove('disabled');
243 }
244 };
245 </script>
246 <?php } ?>
247 </div>
248 <?php
249 $output = ob_get_clean();
250
251 $methods[ self::get_order( $role, $methods ) ] = $output;
252
253 return $methods;
254 }
255
256 /**
257 * Reconfigures email form
258 *
259 * @since 2.6.0
260 *
261 * @return void
262 */
263 public static function email_modal_configure() {
264
265 if ( ! Email::is_enabled() ) {
266 return;
267 }
268 ?>
269 <div class="wizard-step" id="2fa-wizard-email">
270 <fieldset>
271 <div class="step-setting-wrapper active">
272 <div class="mb-20">
273 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_intro', true ) ); ?>
274 </div>
275 <fieldset class="radio-cells">
276 <div class="option-pill">
277 <label for="use_wp_email">
278 <input type="radio" name="wp_2fa_email_address" id="use_wp_email" value="<?php echo \esc_attr( User_Helper::get_user_object()->user_email ); ?>" checked>
279 <span><?php \esc_html_e( 'Use my user email (', 'wp-2fa' ); ?><small><?php echo \esc_attr( User_Helper::get_user_object()->user_email ); ?></small><?php \esc_html_e( ')', 'wp-2fa' ); ?></span>
280 </label>
281 </div>
282 <?php
283 if ( Settings_Utils::get_setting_role( User_Helper::get_user_role(), 'specify-email_hotp' ) ) {
284 ?>
285 <div class="option-pill">
286 <label for="use_custom_email">
287 <input type="radio" name="wp_2fa_email_address" id="use_custom_email" value="use_custom_email">
288 <span><?php \esc_html_e( 'Use a different email address:', 'wp-2fa' ); ?></span>
289 <?php \esc_html_e( 'Email address', 'wp-2fa' ); ?>
290 <input type="email" name="custom-email-address" id="custom-email-address" class="input" value=""/>
291 </label>
292 </div>
293 <?php
294 }
295 ?>
296 </fieldset>
297 <p class="description"><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help', true ) ); ?></p><br>
298
299 <?php
300 $from_email = \get_option( 'admin_email' );
301
302 $custom_mail = WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' );
303
304 if ( isset( $custom_mail ) && ! empty( (string) $custom_mail ) ) {
305 $from_email = $custom_mail;
306 } else {
307 $from_email = Settings_Page::get_default_email_address();
308 }
309
310 echo \wp_kses_post( str_replace( '{from_email}', \esc_html( $from_email ), WP2FA::get_wp2fa_white_label_setting( 'method_help_hotp_help_email', true ) ) );
311 ?>
312
313 <div class="wp2fa-setup-actions">
314 <button class="button button-primary wp-2fa-button-primary" name="next_step_setting_email_verify" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> type="button"><?php \esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
315 <a class="button button-primary wp-2fa-button-primary modal_cancel"><?php \esc_attr_e( 'Cancel', 'wp-2fa' ); ?></a>
316 </div>
317 </div>
318
319 <div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>" id="2fa-wizard-email">
320 <div class="mb-20">
321 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_verification_hotp_pre', true ) ); ?>
322 </div>
323 <fieldset>
324 <label for="2fa-email-authcode">
325 <?php \esc_html_e( 'Verification Code:', 'wp-2fa' ); ?>
326 <input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off"/>
327 <script>
328 const email_authcode = document.getElementById('wp-2fa-email-authcode');
329 email_authcode.addEventListener('input', function() {
330 this.value = this.value.trim();
331 });
332 </script>
333 </label>
334 <div class="verification-response"></div>
335 </fieldset>
336 <br />
337 <a href="#" class="button wp-2fa-button-primary" data-validate-authcode-ajax <?php echo WP_Helper::create_data_nonce( 'wp-2fa-validate-authcode' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php \esc_html_e( 'Validate & Save', 'wp-2fa' ); ?></a>
338 <a href="#" class="button wp-2fa-button-primary resend-email-code" data-trigger-setup-email data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" <?php echo WP_Helper::create_data_nonce( 'wp-2fa-send-setup-email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
339 <span class="resend-inner"><?php \esc_html_e( 'Send me another code', 'wp-2fa' ); ?></span>
340 </a>
341 <button class="wp-2fa-button-secondary button" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
342 </div>
343 </fieldset>
344 </div>
345 <?php
346 }
347 }
348 }
349