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 / Helpers / class-email-templates.php
wp-2fa / includes / classes / Admin / Helpers Last commit date
class-ajax-helper.php 5 days ago class-classes-helper.php 5 days ago class-email-templates.php 5 days ago class-file-writer.php 5 days ago class-methods-helper.php 5 days ago class-php-helper.php 5 days ago class-sms-templates.php 5 days ago class-user-helper.php 5 days ago class-wp-helper.php 5 days ago index.php 5 days ago
class-email-templates.php
478 lines
1 <?php
2 /**
3 * Responsible for the User's operations.
4 *
5 * @package wp2fa
6 * @subpackage helpers
7 *
8 * @since 3.1.1.2
9 *
10 * @copyright 2026 Melapress
11 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
12 *
13 * @see https://wordpress.org/plugins/wp-2fa/
14 */
15
16 declare(strict_types=1);
17
18 namespace WP2FA\Admin\Helpers;
19
20 use WP2FA\WP2FA;
21 use WP2FA\Admin\Settings_Page;
22 use WP2FA\Utils\Request_Utils;
23 use WP2FA\Utils\Settings_Utils;
24 use WP2FA\Utils\Date_Time_Utils;
25 use WP2FA\Admin\Controllers\Settings;
26
27 // Exit if accessed directly.
28 if ( ! defined( 'ABSPATH' ) ) {
29 exit;
30 }
31
32 if ( ! class_exists( '\WP2FA\Admin\Helpers\Email_Templates' ) ) {
33 /**
34 * Responsible for the email templates.
35 */
36 class Email_Templates {
37
38 /**
39 * Local static cache for email template settings.
40 *
41 * @var array
42 */
43 protected static $wp_2fa_email_templates = null;
44
45 /**
46 * Function to return the email templates.
47 *
48 * @param string $email_template Optional parameter to return only a specific email template.
49 *
50 * @return array Array with the email templates.
51 *
52 * @since 3.1.1.2
53 */
54 public static function get_email_templates( $email_template = '' ): array {
55 // Mapping of the email names to the templates. We must unify that.
56 $internal_mapping = array(
57 'user_backup_codes' => array(
58 'subject' => 'user_backup_codes_email_subject',
59 'body' => 'user_backup_codes_email_body',
60 'title' => __( 'User backup codes email', 'wp-2fa' ),
61 'description' => __( 'This email can be sent to a user once backup codes are generated.', 'wp-2fa' ),
62 ),
63 'login_code_setup' => array(
64 'subject' => 'login_code_setup_email_subject',
65 'body' => 'login_code_setup_email_body',
66 'title' => __( '2FA setup code email', 'wp-2fa' ),
67 'description' => __( 'This is the email sent to a user when setting up 2FA via email.', 'wp-2fa' ),
68 ),
69 'login_code' => array(
70 'subject' => 'login_code_email_subject',
71 'body' => 'login_code_email_body',
72 'title' => __( 'Login code email', 'wp-2fa' ),
73 'description' => __( 'This is the email sent to a user when a login code is required.', 'wp-2fa' ),
74 ),
75 'user_account_locked' => array(
76 'subject' => 'user_account_locked_email_subject',
77 'body' => 'user_account_locked_email_body',
78 'title' => __( 'User account locked email', 'wp-2fa' ),
79 'description' => __( 'This is the email sent to a user when their account has been locked.', 'wp-2fa' ),
80 ),
81 'user_account_unlocked' => array(
82 'subject' => 'user_account_unlocked_email_subject',
83 'body' => 'user_account_unlocked_email_body',
84 'title' => __( 'User account unlocked email', 'wp-2fa' ),
85 'description' => __( 'This is the email sent to a user when the user\'s account has been unlocked.', 'wp-2fa' ),
86 ),
87 );
88
89 if ( isset( $email_template ) && ! empty( $email_template ) && array_key_exists( $email_template, $internal_mapping ) ) {
90 return array(
91 'subject' => self::get_wp2fa_email_templates( $internal_mapping[ $email_template ]['subject'] ),
92 'body' => self::get_wp2fa_email_templates( $internal_mapping[ $email_template ]['body'] ),
93 'title' => $internal_mapping[ $email_template ]['title'],
94 'description' => $internal_mapping[ $email_template ]['description'],
95 );
96 }
97
98 foreach ( $internal_mapping as $template_key => $template_data ) {
99 $internal_mapping[ $template_key ]['subject'] = self::get_wp2fa_email_templates( $template_data['subject'] );
100 $internal_mapping[ $template_key ]['body'] = self::get_wp2fa_email_templates( $template_data['body'] );
101 }
102
103 /**
104 * Add an option for external providers to implement their own email template settings for the settings tab.
105 *
106 * The structure of the array should be the same as the internal mapping, with the addition of the 'title' and 'description' keys for each template, which will be used in the settings page to display the template name and description.
107 * - key - unique identifier for the template, used for filtering and retrieving the template.
108 * - subject - the setting name for the email subject in the database.
109 * - body - the setting name for the email body in the database.
110 * - title - the title of the email template, used for display purposes.
111 * - description - the description of the email template, used for display purposes.
112 *
113 * @param array $result - The array with all the email templates.
114 *
115 * @since 3.1.1.2
116 */
117 $result = \apply_filters( WP_2FA_PREFIX . 'email_templates', $internal_mapping );
118
119 return $result;
120 }
121
122 /**
123 * Util which we use to replace our {strings} with actual, useful stuff.
124 *
125 * @param string $input Text we are working on.
126 * @param int|string $user_id User id, if its needed.
127 * @param string $token Login code, if its needed..
128 * @param string $override_grace_period - Value to override grace period with.
129 *
130 * @return string The output, with all the {strings} swapped out.
131 *
132 * @since 3.1.1.2
133 */
134 public static function replace_email_strings( $input = '', $user_id = '', $token = '', $override_grace_period = '' ): string {
135
136 /**
137 * 3rd party plugins could change the mail strings, or provide their own.
138 *
139 * @param array $replacements - The array with all the currently supported strings.
140 */
141 $replacements = \apply_filters(
142 WP_2FA_PREFIX . 'replacement_email_strings',
143 self::get_mail_template_tags( false, $user_id, $token, $override_grace_period )
144 );
145
146 $final_output = str_replace( array_keys( $replacements ), array_values( $replacements ), $input );
147
148 return $final_output;
149 }
150
151 /**
152 * Get the mail template tags with their replacements.
153 *
154 * @param bool $only_tags - Whether to return only the tags or the tags with their replacements.
155 * @param int|string $user_id User id, if its needed.
156 * @param string $token Login code, if its needed..
157 * @param string $override_grace_period - Value to override grace period with.
158 *
159 * @return array The mail template tags with their replacements or only the tags.
160 *
161 * @since 3.1.1.2
162 */
163 public static function get_mail_template_tags( bool $only_tags = false, $user_id = '', $token = '', $override_grace_period = '' ) {
164
165 if ( $only_tags ) {
166 return array(
167 '{site_url}',
168 '{site_name}',
169 '{grace_period}',
170 '{user_login_name}',
171 '{user_first_name}',
172 '{user_last_name}',
173 '{user_display_name}',
174 '{login_code}',
175 '{2fa_settings_page_url}',
176 '{user_ip_address}',
177 '{admin_email}',
178 '{wp_admin_email}',
179 );
180 }
181
182 if ( empty( $GLOBALS['wp_rewrite'] ) ) {
183 $GLOBALS['wp_rewrite'] = new \WP_Rewrite(); // phpcs:ignore -- WordPress.WP.GlobalVariablesOverride.Prohibited
184 }
185
186 // Setup user data.
187 if ( isset( $user_id ) && ! empty( $user_id ) ) {
188 $user = \get_userdata( intval( $user_id ) );
189 } else {
190 $user = \wp_get_current_user();
191 }
192
193 // Setup token.
194 $token = trim( (string) $token );
195 if ( isset( $token ) && ! empty( $token ) ) {
196 $login_code = $token;
197 } else {
198 $login_code = '';
199 }
200
201 // Gather grace period.
202 $grace_period_string = '';
203 if ( isset( $override_grace_period ) && ! empty( $override_grace_period ) ) {
204 $grace_period_string = \sanitize_text_field( $override_grace_period );
205 } else {
206 $grace_policy = WP2FA::get_wp2fa_setting( 'grace-policy' );
207 $grace_period_string = Date_Time_Utils::format_grace_period_expiration_string( $grace_policy );
208 }
209
210 $new_page_id = Settings_Utils::get_setting_role( User_Helper::get_user_role( $user ), 'custom-user-page-id' );
211 if ( ! empty( $new_page_id ) ) {
212 $new_page_permalink = \esc_url( \get_permalink( intval( $new_page_id ) ) );
213 } else {
214 $new_page_id = Settings::get_custom_settings_page_id( '', $user );
215 if ( ! empty( $new_page_id ) ) {
216 $new_page_permalink = \esc_url( \get_permalink( intval( $new_page_id ) ) );
217 } else {
218 $new_page_permalink = '';
219 }
220 }
221
222 $admin_email = null;
223 if ( 'use-custom-email' === self::get_wp2fa_email_templates( 'email_from_setting' ) ) {
224 $admin_email = \sanitize_email( self::get_wp2fa_email_templates( 'custom_from_email_address' ) );
225 } else {
226 $admin_email = Settings_Page::get_default_email_address();
227 }
228
229 return array(
230 '{site_url}' => \esc_url( \get_bloginfo( 'url' ) ),
231 '{site_name}' => \sanitize_text_field( \get_bloginfo( 'name' ) ),
232 '{grace_period}' => \sanitize_text_field( $grace_period_string ),
233 '{user_login_name}' => \sanitize_text_field( $user->user_login ),
234 '{user_first_name}' => \sanitize_text_field( $user->user_firstname ),
235 '{user_last_name}' => \sanitize_text_field( $user->user_lastname ),
236 '{user_display_name}' => \sanitize_text_field( $user->display_name ),
237 '{login_code}' => $login_code,
238 '{2fa_settings_page_url}' => $new_page_permalink,
239 '{user_ip_address}' => \esc_attr( Request_Utils::get_ip() ),
240 '{admin_email}' => $admin_email,
241 '{wp_admin_email}' => \sanitize_email( \is_multisite() ? \get_blog_option( \get_current_blog_id(), 'admin_email' ) : \get_option( 'admin_email' ) ) ?: $admin_email,
242 );
243 }
244
245 /**
246 * Util function to grab EMAIL settings or apply defaults if no settings are saved into the db.
247 *
248 * @param string $setting_name Settings to grab value of.
249 *
250 * @since 2.0.0
251 */
252 public static function get_wp2fa_email_templates( $setting_name = '' ) {
253
254 if ( is_null( self::$wp_2fa_email_templates ) ) {
255 self::$wp_2fa_email_templates = Settings_Utils::get_option( WP_2FA_EMAIL_SETTINGS_NAME );
256 }
257
258 // If we have no setting name, return whatever is saved.
259 if ( empty( $setting_name ) ) {
260 return self::$wp_2fa_email_templates;
261 }
262
263 // If we have a saved setting, return it.
264 if ( $setting_name && isset( self::$wp_2fa_email_templates[ $setting_name ] ) ) {
265 return self::$wp_2fa_email_templates[ $setting_name ];
266 }
267
268 // Array of defaults, now we have things setup above.
269 $default_settings = array(
270 'email_from_setting' => 'use-defaults',
271 'custom_from_email_address' => '',
272 'custom_from_display_name' => '',
273 'send_account_locked_email' => 'enable_account_locked_email',
274 'send_account_unlocked_email' => 'enable_account_unlocked_email',
275 'send_login_code_email' => 'enable_send_login_code_email',
276 'send_reset_password_code_email' => 'enable_send_reset_password_code_email',
277 );
278
279 $default_settings = array_merge( $default_settings, self::login_code_email_template() );
280 $default_settings = array_merge( $default_settings, self::login_code_setup_email_template() );
281 $default_settings = array_merge( $default_settings, self::user_locked_email_template() );
282 $default_settings = array_merge( $default_settings, self::user_unlocked_email_template() );
283 $default_settings = array_merge( $default_settings, self::user_backup_codes_email_template() );
284
285 /**
286 * Allows 3rd party providers to their own settings for the mail templates.
287 *
288 * @param array $default_settings - Array with the default settings.
289 *
290 * @since 2.0.0
291 */
292 $default_settings = \apply_filters( WP_2FA_PREFIX . 'mail_default_settings', $default_settings );
293
294 return $default_settings[ $setting_name ];
295 }
296
297 /**
298 * Function to return the default user backup codes email subject and body.
299 *
300 * @return array Array with the default user backup codes email subject and body.
301 *
302 * @since 3.1.1.2
303 */
304 private static function user_backup_codes_email_template(): array {
305 // Create User backup codes Message.
306 $user_backup_codes_subject = __( '2FA backup codes for user {user_login_name} on {site_name}', 'wp-2fa' );
307
308 $user_backup_codes_body = \wp_sprintf(
309 '<p>%s</p><p>%s <strong>%s</strong> %s <strong>%s</strong>. %s %s </p>%s<p>%s</p>',
310 __( 'Hello,', 'wp-2fa' ),
311 \esc_html__( 'Below please find the 2FA backup codes for your user', 'wp-2fa' ),
312 '{user_login_name}',
313 \esc_html__( 'on the website', 'wp-2fa' ),
314 '{site_name}',
315 __( 'The website\'s URL is', 'wp-2fa' ),
316 '{site_url}',
317 '{backup_codes}',
318 __( 'Thank you for enabling 2FA on your account and helping us keeping the website secure.', 'wp-2fa' )
319 );
320
321 return array(
322 'user_backup_codes_email_subject' => $user_backup_codes_subject,
323 'user_backup_codes_email_body' => $user_backup_codes_body,
324 );
325 }
326
327 /**
328 * Function to return the default user unlocked email subject and body.
329 *
330 * @return array Array with the default user unlocked email subject and body.
331 *
332 * @since 3.1.1.2
333 */
334 private static function user_unlocked_email_template(): array {
335 // Create User unlocked Message.
336 $user_unlocked_subject = __( 'Your user on {site_name} has been unlocked', 'wp-2fa' );
337
338 $user_unlocked_body = \wp_sprintf(
339 '<p>%s</p><p>%s <strong>%s</strong> %s %s</p>' . ( ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ? '<p>%s <a href="%s" target="_blank">%s</a></p>' : '' ) . '<p>%s</p>',
340 __( 'Hello,', 'wp-2fa' ),
341 \esc_html__( 'Your user', 'wp-2fa' ),
342 '{user_login_name}',
343 \esc_html__( 'on the website', 'wp-2fa' ),
344 __( 'has been unlocked. Please configure two-factor authentication within the grace period, otherwise your account will be locked again.', 'wp-2fa' ),
345 ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ? __( 'You can configure 2FA from this page:', 'wp-2fa' ) : '',
346 ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ? '{2fa_settings_page_url}' : '',
347 ! empty( WP2FA::get_wp2fa_setting( 'custom-user-page-id' ) ) ? '{2fa_settings_page_url}.' : '',
348 __( 'Thank you.', 'wp-2fa' )
349 );
350
351 return array(
352 'user_account_unlocked_email_subject' => $user_unlocked_subject,
353 'user_account_unlocked_email_body' => $user_unlocked_body,
354 );
355 }
356
357 /**
358 * Function to return the default user locked email subject and body.
359 *
360 * @return array Array with the default user locked email subject and body.
361 *
362 * @since 3.1.1.2
363 */
364 private static function user_locked_email_template(): array {
365 // Create User Locked Message.
366 $user_locked_subject = __( 'Your user on {site_name} has been locked', 'wp-2fa' );
367
368 $user_locked_body = \wp_sprintf(
369 '<p>%s</p><p>%s</p><p>%s</p><p>%s</p>',
370 \esc_html__( 'Hello.', 'wp-2fa' ),
371 sprintf(
372 // translators: %1s - the name of the user
373 // translators: %2s - the name of the site.
374 \esc_html__( 'Since you have not enabled two-factor authentication for the user %1$1s on the website %2$2s within the grace period, your account has been locked.', 'wp-2fa' ),
375 '{user_login_name}',
376 '{site_name}'
377 ),
378 \esc_html__( 'Contact your website administrator to unlock your account.', 'wp-2fa' ),
379 \esc_html__( 'Thank you.', 'wp-2fa' )
380 );
381
382 return array(
383 'user_account_locked_email_subject' => $user_locked_subject,
384 'user_account_locked_email_body' => $user_locked_body,
385 );
386 }
387
388 /**
389 * Function to return the default login code setup email subject and body.
390 *
391 * @return array Array with the default login code setup email subject and body.
392 *
393 * @since 3.1.1.2
394 */
395 private static function login_code_setup_email_template(): array {
396 $login_code_setup_subject = __( 'Your 2FA Setup Verification Code for {site_name}', 'wp-2fa' );
397
398 $login_code_setup_body = \wp_sprintf(
399 '<p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p>',
400 \esc_html__( 'Hello {user_display_name},', 'wp-2fa' ),
401 \esc_html__( 'You have requested to set up two-factor authentication for your user {user_login_name} on the website {site_name} ({site_url}).', 'wp-2fa' ),
402 sprintf(
403 // translators: The login code provided from the plugin.
404 \esc_html__( 'Please enter the following code to complete your setup: %1$1s', 'wp-2fa' ),
405 '<strong>{login_code}</strong>'
406 ),
407 \esc_html__( 'This request was made from IP address {user_ip_address}. If you did not request this, please contact the site administrator at {admin_email}.', 'wp-2fa' ),
408 \esc_html__( 'Thank you.', 'wp-2fa' ),
409 \esc_html__( 'The {site_name} Team', 'wp-2fa' )
410 );
411
412 return array(
413 'login_code_setup_email_subject' => $login_code_setup_subject,
414 'login_code_setup_email_body' => $login_code_setup_body,
415 );
416 }
417
418 /**
419 * Function to return the default login code email subject and body.
420 *
421 * @return array Array with the default login code email subject and body.
422 *
423 * @since 3.1.1.2
424 */
425 private static function login_code_email_template(): array {
426 // Create Login Code Message.
427 $login_code_subject = __( 'Your login confirmation code for {site_name}', 'wp-2fa' );
428
429 $login_code_body = \wp_sprintf(
430 '<p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p>',
431 \esc_html__( 'Hello {user_display_name},', 'wp-2fa' ),
432 \esc_html__( 'You are trying to log in to {site_name} using the username {user_login_name}. To complete your login, please enter the following one-time 2FA code:', 'wp-2fa' ),
433 \esc_html__( '{login_code}', 'wp-2fa' ),
434 \esc_html__( 'Enter this code on the login page to finish the authentication process and access your account.', 'wp-2fa' ),
435 \esc_html__( 'This request was made from IP address {user_ip_address}. If you did not request this, please contact the site administrator at {admin_email}.', 'wp-2fa' ),
436 \esc_html__( 'If you encounter any other issues logging in, feel free to contact us at {admin_email}.', 'wp-2fa' ),
437 \esc_html__( 'Kind regards, The {site_name} Team', 'wp-2fa' )
438 );
439
440 return array(
441 'login_code_email_subject' => $login_code_subject,
442 'login_code_email_body' => $login_code_body,
443 );
444 }
445
446 /**
447 * Function to return the default reset password code email subject and body.
448 *
449 * @return array Array with the default reset password code email subject and body.
450 *
451 * @since 3.1.1.2
452 */
453 private static function reset_password_code_email_template(): array {
454 // Create Reset PW Code Message.
455 $reset_password_code_subject = __( '2FA code for password reset', 'wp-2fa' );
456
457 $reset_password_code_body = \wp_sprintf(
458 '<p>%s</p><p>%s</p><p><strong>%s</strong></p><p>%s</p>',
459 \esc_html__( 'Hello,', 'wp-2fa' ),
460 sprintf(
461 // translators: The login code provided from the plugin.
462 \esc_html__( 'Someone from the IP address %1$1s has requested a password reset for the user %2$2s on the website %3$3s. If this was you please use the below code to proceed with the password reset:', 'wp-2fa' ),
463 '{user_ip_address}',
464 '{user_login_name}',
465 '{site_url}'
466 ),
467 '{login_code}',
468 \esc_html__( 'If this was not you, ignore this email and contact your website administrator.', 'wp-2fa' )
469 );
470
471 return array(
472 'reset_password_code_email_subject' => $reset_password_code_subject,
473 'reset_password_code_email_body' => $reset_password_code_body,
474 );
475 }
476 }
477 }
478