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 / SettingsPages / class-settings-page-email.php
wp-2fa / includes / classes / Admin / SettingsPages Last commit date
class-settings-page-email.php 3 years ago class-settings-page-general.php 3 years ago class-settings-page-policies.php 3 years ago class-settings-page-white-label.php 3 years ago
class-settings-page-email.php
435 lines
1 <?php
2 /**
3 * Email settings class.
4 *
5 * @package wp2fa
6 * @subpackage settings-pages
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\SettingsPages;
13
14 use WP2FA\Email_Template;
15 use \WP2FA\WP2FA as WP2FA;
16 use WP2FA\Utils\Debugging;
17 use WP2FA\Admin\Controllers\Settings;
18 use WP2FA\Utils\Settings_Utils as Settings_Utils;
19
20 /**
21 * Email settings tab
22 */
23 if ( ! class_exists( '\WP2FA\Admin\SettingsPages\Settings_Page_Email' ) ) {
24 /**
25 * Settings_Page_Email - Class for handling email settings
26 *
27 * @since 2.0.0
28 */
29 class Settings_Page_Email {
30
31 /**
32 * Render the settings
33 *
34 * @return void
35 *
36 * @since 2.0.0
37 */
38 public static function render() {
39 settings_fields( WP_2FA_EMAIL_SETTINGS_NAME );
40 self::email_from_settings();
41 self::email_settings();
42 submit_button( esc_html__( 'Save email settings and templates', 'wp-2fa' ) );
43 }
44
45 /**
46 * Handle saving email options to the network main site options.
47 *
48 * @return void
49 *
50 * @since 2.0.0
51 *
52 * @SuppressWarnings(PHPMD.ExitExpressions)
53 */
54 public static function update_wp2fa_network_options() {
55 if ( isset( $_POST['email_from_setting'] ) ) { // phpcs:ignore
56 $options = self::validate_and_sanitize( wp_unslash( $_POST ) ); // phpcs:ignore
57
58 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) { // phpcs:ignore
59 // redirect back to our options page.
60 wp_safe_redirect(
61 add_query_arg(
62 array(
63 'page' => 'wp-2fa-settings',
64 'wp_2fa_network_settings_updated' => 'false',
65 'tab' => 'email-settings',
66 ),
67 network_admin_url( 'admin.php' )
68 )
69 );
70 exit;
71 }
72
73 Settings_Utils::update_option( WP_2FA_EMAIL_SETTINGS_NAME, $options );
74 }
75
76 // redirect back to our options page.
77 wp_safe_redirect(
78 add_query_arg(
79 array(
80 'page' => 'wp-2fa-settings',
81 'wp_2fa_network_settings_updated' => 'true',
82 'tab' => 'email-settings',
83 ),
84 network_admin_url( 'admin.php' )
85 )
86 );
87 exit;
88 }
89
90 /**
91 * Email settings
92 *
93 * @return void
94 *
95 * @since 2.0.0
96 */
97 private static function email_from_settings() {
98 ?>
99 <h3><?php esc_html_e( 'Which email address should the plugin use as a from address?', 'wp-2fa' ); ?></h3>
100 <p class="description">
101 <?php esc_html_e( 'Use these settings to customize the "from" name and email address for all correspondence sent from our plugin.', 'wp-2fa' ); ?>
102 </p>
103 <table class="form-table">
104 <tbody>
105 <tr>
106 <th><label for="2fa-method"><?php esc_html_e( 'From email & name', 'wp-2fa' ); ?></label>
107 </th>
108 <td>
109 <fieldset class="contains-hidden-inputs">
110 <label for="use-defaults">
111 <input type="radio" name="email_from_setting" id="use-defaults" value="use-defaults"
112 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-defaults' ); ?>
113 >
114 <span><?php esc_html_e( 'Use the email address from the WordPress general settings.', 'wp-2fa' ); ?></span>
115 </label>
116
117 <br/>
118 <label for="use-custom-email">
119 <input type="radio" name="email_from_setting" id="use-custom-email" value="use-custom-email"
120 <?php checked( WP2FA::get_wp2fa_email_templates( 'email_from_setting' ), 'use-custom-email' ); ?>
121 data-unhide-when-checked=".custom-from-inputs">
122 <span><?php esc_html_e( 'Use another email address', 'wp-2fa' ); ?></span>
123 </label>
124 <fieldset class="hidden custom-from-inputs">
125 <br/>
126 <span><?php esc_html_e( 'Email Address:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_email_address" name="custom_from_email_address" value="<?php echo esc_attr( WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) ); ?>"><br><br>
127 <span><?php esc_html_e( 'Display Name:', 'wp-2fa' ); ?></span> <input type="text" id="custom_from_display_name" name="custom_from_display_name" value="<?php echo esc_attr( WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) ); ?>">
128 </fieldset>
129
130 </fieldset>
131 </td>
132 </tr>
133 </tbody>
134 </table>
135
136 <br>
137 <hr>
138
139 <h3><?php esc_html_e( 'Email delivery test', 'wp-2fa' ); ?></h3>
140 <p class="description">
141 <?php esc_html_e( 'The plugin sends emails with one-time codes, blocked account notifications and more. Use the button below to confirm the plugin can successfully send emails.', 'wp-2fa' ); ?>
142 </p>
143 <p>
144 <button type="button" name="test_email_config_test"
145 class="button js-button-test-email-trigger"
146 data-email-id="config_test"
147 data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-config_test' ) ); ?>">
148 <?php esc_html_e( 'Test email delivery', 'wp-2fa' ); ?>
149 </button>
150 </p>
151
152 <br>
153 <hr>
154
155 <?php
156 }
157
158 /**
159 * Creates the email notification definitions.
160 *
161 * @return Email_Template[]
162 *
163 * @since 2.0.0
164 */
165 public static function get_email_notification_definitions() {
166 $result = array(
167 new Email_Template(
168 'login_code',
169 esc_html__( 'Login code email', 'wp-2fa' ),
170 esc_html__( 'This is the email sent to a user when a login code is required.', 'wp-2fa' )
171 ),
172 new Email_Template(
173 'account_locked',
174 esc_html__( 'User account locked email', 'wp-2fa' ),
175 esc_html__( 'This is the email sent to a user upon grace period expiry.', 'wp-2fa' )
176 ),
177 new Email_Template(
178 'account_unlocked',
179 esc_html__( 'User account unlocked email', 'wp-2fa' ),
180 esc_html__( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' )
181 ),
182 );
183
184 /**
185 * Add an option for external providers to implement their own email template settings for the settings tab.
186 *
187 * @param array $result - The array with all the email templates.
188 *
189 * @since 2.0.0
190 */
191 $result = apply_filters( WP_2FA_PREFIX . 'email_notification_definitions', $result );
192
193 if ( count( $result ) > 3 ) {
194 $result[0]->set_can_be_toggled( false );
195 $result[1]->set_can_be_toggled( false );
196 $result[2]->set_email_content_id( 'user_account_locked' );
197 $result[3]->set_email_content_id( 'user_account_unlocked' );
198 } else {
199 $result[0]->set_can_be_toggled( false );
200 $result[1]->set_email_content_id( 'user_account_locked' );
201 $result[2]->set_email_content_id( 'user_account_unlocked' );
202 }
203 return $result;
204 }
205
206 /**
207 * Validate email templates before saving
208 *
209 * @since 2.0.0
210 *
211 * @SuppressWarnings(PHPMD.ExitExpressions)
212 */
213 public static function validate_and_sanitize() {
214
215 // Bail if user doesn't have permissions to be here.
216 if ( ! current_user_can( 'manage_options' ) ) {
217 return;
218 }
219
220 Debugging::log( 'The following settings will be processed (E-mail): ' . "\n" . wp_json_encode( $_POST ) ); // phpcs:ignore
221
222 if ( empty( $_POST ) || ! isset( $_POST['_wpnonce'] ) || empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) || ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'email_settings-options' ) && ! wp_verify_nonce( $_POST['_wpnonce'], WP_2FA_PREFIX . 'settings-options' ) ) { // phpcs:ignore
223 die( esc_html__( 'Nonce verification failed.', 'wp-2fa' ) );
224 }
225
226 $output = array();
227
228 if ( isset( $_POST['email_from_setting'] ) && 'use-defaults' === $_POST['email_from_setting'] || isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] ) {
229 $output['email_from_setting'] = sanitize_text_field( wp_unslash( $_POST['email_from_setting'] ) );
230 }
231
232 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_email_address'] ) && empty( $_POST['custom_from_email_address'] ) ) {
233 add_settings_error(
234 WP_2FA_SETTINGS_NAME,
235 esc_attr( 'email_from_settings_error' ),
236 esc_html__( 'Please provide an email address', 'wp-2fa' ),
237 'error'
238 );
239 $output['custom_from_email_address'] = '';
240 }
241
242 if ( isset( $_POST['email_from_setting'] ) && 'use-custom-email' === $_POST['email_from_setting'] && isset( $_POST['custom_from_display_name'] ) && empty( $_POST['custom_from_display_name'] ) ) {
243 add_settings_error(
244 WP_2FA_SETTINGS_NAME,
245 esc_attr( 'display_name_settings_error' ),
246 esc_html__( 'Please provide a display name.', 'wp-2fa' ),
247 'error'
248 );
249 $output['custom_from_email_address'] = '';
250 }
251
252 if ( isset( $_POST['custom_from_email_address'] ) && ! empty( $_POST['custom_from_email_address'] ) ) {
253 if ( ! filter_var( wp_unslash( $_POST['custom_from_email_address'] ), FILTER_VALIDATE_EMAIL ) ) {
254 add_settings_error(
255 WP_2FA_SETTINGS_NAME,
256 esc_attr( 'email_invalid_settings_error' ),
257 esc_html__( 'Please provide a valid email address. Your email address has not been updated.', 'wp-2fa' ),
258 'error'
259 );
260 }
261 $output['custom_from_email_address'] = sanitize_email( wp_unslash( $_POST['custom_from_email_address'] ) );
262 }
263
264 if ( isset( $_POST['custom_from_display_name'] ) && ! empty( $_POST['custom_from_display_name'] ) ) {
265 // Check if the string contains HTML/tags.
266 preg_match( "/<\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/", sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) ), $matches );
267 if ( count( $matches ) > 0 ) {
268 add_settings_error(
269 WP_2FA_SETTINGS_NAME,
270 esc_attr( 'display_name_invalid_settings_error' ),
271 esc_html__( 'Please only use alphanumeric text. Your display name has not been updated.', 'wp-2fa' ),
272 'error'
273 );
274 } else {
275 $output['custom_from_display_name'] = sanitize_text_field( wp_unslash( $_POST['custom_from_display_name'] ) );
276 }
277 }
278
279 if ( isset( $_POST['login_code_email_subject'] ) ) {
280 $output['login_code_email_subject'] = wp_kses_post( wp_unslash( $_POST['login_code_email_subject'] ) );
281 }
282
283 if ( isset( $_POST['login_code_email_body'] ) ) {
284 $output['login_code_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['login_code_email_body'] ) ) );
285 }
286
287 if ( isset( $_POST['user_account_locked_email_subject'] ) ) {
288 $output['user_account_locked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_locked_email_subject'] ) );
289 }
290
291 if ( isset( $_POST['user_account_locked_email_body'] ) ) {
292 $output['user_account_locked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_locked_email_body'] ) ) );
293 }
294
295 if ( isset( $_POST['user_account_unlocked_email_subject'] ) ) {
296 $output['user_account_unlocked_email_subject'] = wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_subject'] ) );
297 }
298
299 if ( isset( $_POST['user_account_unlocked_email_body'] ) ) {
300 $output['user_account_unlocked_email_body'] = wpautop( wp_kses_post( wp_unslash( $_POST['user_account_unlocked_email_body'] ) ) );
301 }
302
303 $output['send_account_locked_email'] = '';
304 if ( isset( $_POST['send_account_locked_email'] ) && 'enable_account_locked_email' === $_POST['send_account_locked_email'] ) {
305 $output['send_account_locked_email'] = sanitize_text_field( wp_unslash( $_POST['send_account_locked_email'] ) );
306 }
307
308 $output['send_account_unlocked_email'] = '';
309 if ( isset( $_POST['send_account_unlocked_email'] ) && 'enable_account_unlocked_email' === $_POST['send_account_unlocked_email'] ) {
310 $output['send_account_unlocked_email'] = sanitize_text_field( wp_unslash( $_POST['send_account_unlocked_email'] ) );
311 }
312
313 /**
314 * Filter the values we are about to store in the plugin settings.
315 *
316 * @param array $output - The output array with all the data we will store in the settings.
317 *
318 * @since 2.0.0
319 */
320 $output = apply_filters( WP_2FA_PREFIX . 'filter_output_email_template_content', $output );
321
322 Debugging::log( 'The following settings are being saved (E-mail): ' . "\n" . wp_json_encode( $output ) );
323
324 // Remove duplicates from settings errors. We do this as this sanitization callback is actually fired twice, so we end up with duplicates when saving the settings for the FIRST TIME only. The issue is not present once the settings are in the DB as the sanitization wont fire again. For details on this core issue - https://core.trac.wordpress.org/ticket/21989.
325 global $wp_settings_errors;
326 if ( isset( $wp_settings_errors ) ) {
327 $errors = array_map( 'unserialize', array_unique( array_map( 'serialize', $wp_settings_errors ) ) );
328 $wp_settings_errors = $errors; // phpcs:ignore
329 }
330
331 if ( isset( $output ) ) {
332 return $output;
333 } else {
334 return;
335 }
336 }
337
338 /**
339 * Email settings
340 *
341 * @return void
342 *
343 * @since 2.0.0
344 */
345 private static function email_settings() {
346 $custom_user_page_id = Settings::check_setting_in_all_roles( 'custom-user-page-id' );
347 $email_template_definitions = self::get_email_notification_definitions();
348 ?>
349 <h1><?php esc_html_e( 'Email Templates', 'wp-2fa' ); ?></h1>
350 <?php foreach ( $email_template_definitions as $email_template ) : ?>
351 <?php $template_id = $email_template->get_id(); ?>
352 <h3><?php echo esc_html( $email_template->get_title() ); ?></h3>
353 <p class="description"><?php echo $email_template->get_description(); // phpcs:ignore ?></p>
354 <table class="form-table">
355 <tbody>
356 <?php if ( $email_template->can_be_toggled() ) : ?>
357 <tr>
358 <th><label for="send_<?php echo esc_attr( $template_id ); ?>_email"><?php esc_html_e( 'Send this email', 'wp-2fa' ); ?></label></th>
359 <td>
360 <fieldset>
361 <input type="checkbox" id="send_<?php echo esc_attr( $template_id ); ?>_email" name="send_<?php echo esc_attr( $template_id ); ?>_email" value="enable_<?php echo esc_attr( $template_id ); ?>_email"
362 <?php checked( 'enable_' . $template_id . '_email', WP2FA::get_wp2fa_email_templates( 'send_' . $template_id . '_email' ) ); ?>
363 >
364 <label for="send_<?php echo esc_attr( $template_id ); ?>_email"><?php esc_html_e( 'Uncheck to disable this message.', 'wp-2fa' ); ?></label>
365 </fieldset>
366 </td>
367 </tr>
368 <?php endif; ?>
369 <?php $template_id = $email_template->get_email_content_id(); ?>
370 <tr>
371 <th><label for="<?php echo esc_attr( $template_id ); ?>_email_subject"><?php esc_html_e( 'Email subject', 'wp-2fa' ); ?></label></th>
372 <td>
373 <fieldset>
374 <input type="text" id="<?php echo esc_attr( $template_id ); ?>_email_subject" name="<?php echo esc_attr( $template_id ); ?>_email_subject" class="large-text" value="<?php echo esc_attr( WP2FA::get_wp2fa_email_templates( $template_id . '_email_subject' ) ); ?>">
375 </fieldset>
376 </td>
377 </tr>
378 <tr>
379 <th>
380 <label for="<?php echo esc_attr( $template_id ); ?>_email_body"><?php esc_html_e( 'Email body', 'wp-2fa' ); ?></label>
381 </br>
382 <label for="<?php echo esc_attr( $template_id ); ?>_email_tags" style="font-weight: 400;"><?php esc_html_e( 'Available template tags:', 'wp-2fa' ); ?></label>
383 </br>
384 </br>
385 <span style="font-weight: 400;">
386 {site_url}</br>
387 {site_name}</br>
388 {grace_period}</br>
389 {user_login_name}</br>
390 {user_first_name}</br>
391 {user_last_name}</br>
392 {user_display_name}</br>
393 {login_code}</br>
394 {user_ip_address}
395 <?php
396 if ( ! empty( $custom_user_page_id ) ) {
397 echo '</br>{2fa_settings_page_url}';
398 }
399 ?>
400 </span>
401 </th>
402 <td>
403 <fieldset>
404 <?php
405 $message = WP2FA::get_wp2fa_email_templates( $template_id . '_email_body' );
406 $content = $message;
407 $editor_id = $template_id . '_email_body';
408 $settings = array(
409 'media_buttons' => false,
410 'editor_height' => 200,
411 );
412 wp_editor( $content, $editor_id, $settings );
413 ?>
414 </fieldset>
415 <p>
416 <button type="button" name="test_email_<?php echo esc_attr( $template_id ); ?>"
417 class="button js-button-test-email-trigger"
418 data-email-id="<?php echo esc_attr( $template_id ); ?>"
419 data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp-2fa-email-test-' . $template_id ) ); ?>">
420 <?php esc_html_e( 'Send test email', 'wp-2fa' ); ?>
421 </button>
422 </p>
423 </td>
424 </tr>
425 </tbody>
426 </table>
427
428 <br>
429 <hr>
430 <?php endforeach; ?>
431 <?php
432 }
433 }
434 }
435