diff --git a/src/wp-admin/includes/privacy-tools.php b/src/wp-admin/includes/privacy-tools.php index 4f3379bff8909..e5f0e6eaecb38 100644 --- a/src/wp-admin/includes/privacy-tools.php +++ b/src/wp-admin/includes/privacy-tools.php @@ -623,121 +623,51 @@ function wp_privacy_send_personal_data_export_email( $request_id ) { $request_email = apply_filters( 'wp_privacy_personal_data_email_to', $request->email, $request ); $email_data = array( - 'request' => $request, - 'expiration' => $expiration, - 'expiration_date' => $expiration_date, - 'message_recipient' => $request_email, - 'export_file_url' => $export_file_url, - 'sitename' => $site_name, - 'siteurl' => $site_url, + 'request' => $request, + 'expiration' => $expiration, + 'expiration_date' => $expiration_date, + 'export_file_url' => $export_file_url, + 'sitename' => $site_name, + 'siteurl' => $site_url, ); - /* translators: Personal data export notification email subject. %s: Site title. */ - $subject = sprintf( __( '[%s] Personal Data Export' ), $site_name ); - - /** - * Filters the subject of the email sent when an export request is completed. - * - * @since 5.3.0 - * - * @param string $subject The email subject. - * @param string $sitename The name of the site. - * @param array $email_data { - * Data relating to the account action email. - * - * @type WP_User_Request $request User request object. - * @type int $expiration The time in seconds until the export file expires. - * @type string $expiration_date The localized date and time when the export file expires. - * @type string $message_recipient The address that the email will be sent to. Defaults - * to the value of `$request->email`, but can be changed - * by the `wp_privacy_personal_data_email_to` filter. - * @type string $export_file_url The export file URL. - * @type string $sitename The site name sending the mail. - * @type string $siteurl The site URL sending the mail. - * } - */ - $subject = apply_filters( 'wp_privacy_personal_data_email_subject', $subject, $site_name, $email_data ); - - /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ - $email_text = __( - 'Howdy, + WP_Mailer::register_email( + 'privacy_export', + 'privacy', + array( + /* translators: Personal data export notification email subject. %s: Site title. */ + 'subject' => __( '[{{sitename}}] Personal Data Export' ), + /* translators: Do not translate {{expiration_date}}, {{export_file_url}}, {{sitename}}, {{siteurl}}: those are placeholders. */ + 'body' => __( + 'Howdy, Your request for an export of personal data has been completed. You may download your personal data by clicking on the link below. For privacy -and security, we will automatically delete the file on ###EXPIRATION###, +and security, we will automatically delete the file on {{expiration_date}}, so please download it before then. -###LINK### +{{export_file_url}} Regards, -All at ###SITENAME### -###SITEURL###' +All at {{sitename}} +{{siteurl}}' + ), + ) ); - /** - * Filters the text of the email sent with a personal data export file. - * - * The following strings have a special meaning and will get replaced dynamically: - * - * - `###EXPIRATION###` The date when the URL will be automatically deleted. - * - `###LINK###` URL of the personal data export file for the user. - * - `###SITENAME###` The name of the site. - * - `###SITEURL###` The URL to the site. - * - * @since 4.9.6 - * @since 5.3.0 Introduced the `$email_data` array. - * - * @param string $email_text Text in the email. - * @param int $request_id The request ID for this personal data export. - * @param array $email_data { - * Data relating to the account action email. - * - * @type WP_User_Request $request User request object. - * @type int $expiration The time in seconds until the export file expires. - * @type string $expiration_date The localized date and time when the export file expires. - * @type string $message_recipient The address that the email will be sent to. Defaults - * to the value of `$request->email`, but can be changed - * by the `wp_privacy_personal_data_email_to` filter. - * @type string $export_file_url The export file URL. - * @type string $sitename The site name sending the mail. - * @type string $siteurl The site URL sending the mail. - */ - $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id, $email_data ); - - $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); - $content = str_replace( '###LINK###', sanitize_url( $export_file_url ), $content ); - $content = str_replace( '###EMAIL###', $request_email, $content ); - $content = str_replace( '###SITENAME###', $site_name, $content ); - $content = str_replace( '###SITEURL###', sanitize_url( $site_url ), $content ); - $headers = ''; - /** - * Filters the headers of the email sent with a personal data export file. - * - * @since 5.4.0 - * - * @param string|array $headers The email headers. - * @param string $subject The email subject. - * @param string $content The email content. - * @param int $request_id The request ID. - * @param array $email_data { - * Data relating to the account action email. - * - * @type WP_User_Request $request User request object. - * @type int $expiration The time in seconds until the export file expires. - * @type string $expiration_date The localized date and time when the export file expires. - * @type string $message_recipient The address that the email will be sent to. Defaults - * to the value of `$request->email`, but can be changed - * by the `wp_privacy_personal_data_email_to` filter. - * @type string $export_file_url The export file URL. - * @type string $sitename The site name sending the mail. - * @type string $siteurl The site URL sending the mail. - * } - */ - $headers = apply_filters( 'wp_privacy_personal_data_email_headers', $headers, $subject, $content, $request_id, $email_data ); + /** This filter is documented in wp-admin/includes/privacy-tools.php */ + $headers = apply_filters( 'wp_privacy_personal_data_email_headers', $headers, '', '', $request_id, $email_data ); - $mail_success = wp_mail( $request_email, $subject, $content, $headers ); + $mail_success = WP_Mailer::send( + 'privacy_export', + array( + 'to' => $request_email, + 'headers' => $headers, + ), + $email_data + ); if ( $switched_locale ) { restore_previous_locale(); diff --git a/src/wp-includes/class-wp-mailer.php b/src/wp-includes/class-wp-mailer.php new file mode 100644 index 0000000000000..15bfaa9f30009 --- /dev/null +++ b/src/wp-includes/class-wp-mailer.php @@ -0,0 +1,179 @@ + $group, + 'subject' => $args[ 'subject' ], + 'body' => $args[ 'body' ], + ); + } + + /** + * Retrieves an email template. + * + * @since 7.1.0 + * + * @param string $email_id Email identifier. + * @return array|false Email template data on success, false on failure. + */ + public static function get_email( $email_id ) { + if ( isset( self::$emails[ $email_id ] ) ) { + return self::$emails[ $email_id ]; + } + + /** + * Filters the email template before it is retrieved. + * + * This allows for lazy registration of emails. + * + * @since 7.1.0 + * + * @param array|null $email The email template data. + * @param string $email_id The email identifier. + */ + return apply_filters( "wp_mailer_get_email_{$email_id}", null, $email_id ); + } + + /** + * Renders a template with data. + * + * Supports {{mustache}} style placeholders. + * + * @since 7.1.0 + * + * @param string $template The template string. + * @param array $data The data for replacement. + * @return string The rendered string. + */ + public static function render( $template, $data ) { + return preg_replace_callback( + '/{{(.*?)}}/', + function( $matches ) use ( $data ) { + $key = trim( $matches[ 1 ] ); + return isset( $data[ $key ] ) ? $data[ $key ] : $matches[ 0 ]; + }, + $template + ); + } +} diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index fd659b600c379..17072fea81fb7 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -448,8 +448,8 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() try { $phpmailer->setFrom( $from_email, $from_name, false ); } catch ( PHPMailer\PHPMailer\Exception $e ) { - $mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' ); - $mail_error_data['phpmailer_exception_code'] = $e->getCode(); + $mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' ); + $mail_error_data[ 'phpmailer_exception_code' ] = $e->getCode(); /** This filter is documented in wp-includes/pluggable.php */ do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) ); @@ -476,11 +476,14 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) { if ( count( $matches ) === 3 ) { - $recipient_name = $matches[1]; - $address = $matches[2]; + $recipient_name = $matches[ 1 ]; + $address = $matches[ 2 ]; } } + $recipient_name = str_replace( '"', '', $recipient_name ); + $recipient_name = trim( $recipient_name ); + switch ( $address_header ) { case 'to': $phpmailer->addAddress( $address, $recipient_name ); @@ -2305,44 +2308,56 @@ function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) $switched_locale = switch_to_locale( get_locale() ); } - /* translators: %s: Site title. */ - $message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n"; - /* translators: %s: User login. */ - $message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; - /* translators: %s: User email address. */ - $message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n"; + $email_data = array( + 'sitename' => $blogname, + 'user_login' => $user->user_login, + 'user_email' => $user->user_email, + ); + + WP_Mailer::register_email( + 'new_user_admin', + 'admin', + array( + /* translators: New user registration notification email subject. %s: Site title. */ + 'subject' => __( '[{{sitename}}] New User Registration' ), + 'body' => __( + 'New user registration on your site {{sitename}}: + +Username: {{user_login}} + +Email: {{user_email}}' + ), + ) + ); + + /* translators: New user registration notification email subject. %s: Site title. */ + $subject = sprintf( __( '[%s] New User Registration' ), $blogname ); + + $message = WP_Mailer::render( WP_Mailer::get_email( 'new_user_admin' )[ 'body' ], $email_data ); $wp_new_user_notification_email_admin = array( 'to' => get_option( 'admin_email' ), - /* translators: New user registration notification email subject. %s: Site title. */ - 'subject' => __( '[%s] New User Registration' ), + 'subject' => $subject, 'message' => $message, 'headers' => '', ); - /** - * Filters the contents of the new user notification email sent to the site admin. - * - * @since 4.9.0 - * - * @param array $wp_new_user_notification_email_admin { - * Used to build wp_mail(). - * - * @type string $to The intended recipient - site admin email address. - * @type string $subject The subject of the email. - * @type string $message The body of the email. - * @type string $headers The headers of the email. - * } - * @param WP_User $user User object for new user. - * @param string $blogname The site title. - */ + /** This filter is documented in wp-includes/pluggable.php */ $wp_new_user_notification_email_admin = apply_filters( 'wp_new_user_notification_email_admin', $wp_new_user_notification_email_admin, $user, $blogname ); - wp_mail( - $wp_new_user_notification_email_admin['to'], - wp_specialchars_decode( sprintf( $wp_new_user_notification_email_admin['subject'], $blogname ) ), - $wp_new_user_notification_email_admin['message'], - $wp_new_user_notification_email_admin['headers'] + WP_Mailer::send( + 'new_user_admin', + array( + 'to' => $wp_new_user_notification_email_admin[ 'to' ], + 'headers' => $wp_new_user_notification_email_admin[ 'headers' ], + ), + array_merge( + $email_data, + array( + 'subject' => $wp_new_user_notification_email_admin[ 'subject' ], + 'body' => $wp_new_user_notification_email_admin[ 'message' ], + ) + ) ); if ( $switched_locale ) { @@ -2370,52 +2385,58 @@ function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) return; } - $switched_locale = switch_to_user_locale( $user_id ); + $set_password_url = network_site_url( 'wp-login.php?login=' . rawurlencode( $user->user_login ) . "&key=$key&action=rp", 'login' ); - /* translators: %s: User login. */ - $message = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; - $message .= __( 'To set your password, visit the following address:' ) . "\r\n\r\n"; + $email_data = array( + 'sitename' => $blogname, + 'user_login' => $user->user_login, + 'set_password_url' => $set_password_url, + ); - /* - * Since some user login names end in a period, this could produce ambiguous URLs that - * end in a period. To avoid the ambiguity, ensure that the login is not the last query - * arg in the URL. If moving it to the end, a trailing period will need to be escaped. - * - * @see https://core.trac.wordpress.org/tickets/42957 - */ - $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user->user_login ) . "&key=$key&action=rp", 'login' ) . "\r\n"; + WP_Mailer::register_email( + 'new_user', + 'user', + array( + /* translators: Login details notification email subject. %s: Site title. */ + 'subject' => __( '[{{sitename}}] Login Details' ), + 'body' => __( + 'Username: {{user_login}} + +To set your password, visit the following address: + +{{set_password_url}}' + ), + ) + ); + + /* translators: Login details notification email subject. %s: Site title. */ + $subject = sprintf( __( '[%s] Login Details' ), $blogname ); + + $message = WP_Mailer::render( WP_Mailer::get_email( 'new_user' )[ 'body' ], $email_data ); $wp_new_user_notification_email = array( 'to' => $user->user_email, - /* translators: Login details notification email subject. %s: Site title. */ - 'subject' => __( '[%s] Login Details' ), + 'subject' => $subject, 'message' => $message, 'headers' => '', ); - /** - * Filters the contents of the new user notification email sent to the new user. - * - * @since 4.9.0 - * - * @param array $wp_new_user_notification_email { - * Used to build wp_mail(). - * - * @type string $to The intended recipient - New user email address. - * @type string $subject The subject of the email. - * @type string $message The body of the email. - * @type string $headers The headers of the email. - * } - * @param WP_User $user User object for new user. - * @param string $blogname The site title. - */ + /** This filter is documented in wp-includes/pluggable.php */ $wp_new_user_notification_email = apply_filters( 'wp_new_user_notification_email', $wp_new_user_notification_email, $user, $blogname ); - wp_mail( - $wp_new_user_notification_email['to'], - wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ), - $wp_new_user_notification_email['message'], - $wp_new_user_notification_email['headers'] + WP_Mailer::send( + 'new_user', + array( + 'to' => $wp_new_user_notification_email[ 'to' ], + 'headers' => $wp_new_user_notification_email[ 'headers' ], + ), + array_merge( + $email_data, + array( + 'subject' => $wp_new_user_notification_email[ 'subject' ], + 'body' => $wp_new_user_notification_email[ 'message' ], + ) + ) ); if ( $switched_locale ) { diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 9c635f63d288a..7fd6182dc237a 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -3358,73 +3358,69 @@ function retrieve_password( $user_login = '' ) { $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); } - $message = __( 'Someone has requested a password reset for the following account:' ) . "\r\n\r\n"; - /* translators: %s: Site name. */ - $message .= sprintf( __( 'Site Name: %s' ), $site_name ) . "\r\n\r\n"; - /* translators: %s: User login. */ - $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n"; - $message .= __( 'If this was a mistake, ignore this email and nothing will happen.' ) . "\r\n\r\n"; - $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n"; - - /* - * Since some user login names end in a period, this could produce ambiguous URLs that - * end in a period. To avoid the ambiguity, ensure that the login is not the last query - * arg in the URL. If moving it to the end, a trailing period will need to be escaped. - * - * @see https://core.trac.wordpress.org/tickets/42957 - */ - $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user_login ) . "&key=$key&action=rp", 'login' ) . '&wp_lang=' . $locale . "\r\n\r\n"; + $reset_url = network_site_url( 'wp-login.php?login=' . rawurlencode( $user_login ) . "&key=$key&action=rp", 'login' ) . '&wp_lang=' . $locale; + $ip_notification = ''; if ( ! is_user_logged_in() ) { - $requester_ip = $_SERVER['REMOTE_ADDR']; + $requester_ip = $_SERVER[ 'REMOTE_ADDR' ]; if ( $requester_ip ) { - $message .= sprintf( - /* translators: %s: IP address of password reset requester. */ - __( 'This password reset request originated from the IP address %s.' ), - $requester_ip - ) . "\r\n"; + /* translators: %s: IP address of password reset requester. */ + $ip_notification = sprintf( __( 'This password reset request originated from the IP address %s.' ), $requester_ip ); } } + $email_data = array( + 'sitename' => $site_name, + 'user_login' => $user_login, + 'reset_url' => $reset_url, + 'ip_notification' => $ip_notification, + ); + + WP_Mailer::register_email( + 'retrieve_password', + 'user', + array( + /* translators: Password reset notification email subject. %s: Site title. */ + 'subject' => __( '[{{sitename}}] Password Reset' ), + 'body' => __( + 'Someone has requested a password reset for the following account: + +Site Name: {{sitename}} + +Username: {{user_login}} + +If this was a mistake, ignore this email and nothing will happen. + +To reset your password, visit the following address: + +{{reset_url}} + +{{ip_notification}}' + ), + ) + ); + /* translators: Password reset notification email subject. %s: Site title. */ $title = sprintf( __( '[%s] Password Reset' ), $site_name ); - /** - * Filters the subject of the password reset email. - * - * @since 2.8.0 - * @since 4.4.0 Added the `$user_login` and `$user_data` parameters. - * - * @param string $title Email subject. - * @param string $user_login The username for the user. - * @param WP_User $user_data WP_User object. - */ + /** This filter is documented in wp-includes/user.php */ $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data ); - /** - * Filters the message body of the password reset mail. - * - * If the filtered message is empty, the password reset email will not be sent. - * - * @since 2.8.0 - * @since 4.1.0 Added `$user_login` and `$user_data` parameters. - * - * @param string $message Email message. - * @param string $key The activation key. - * @param string $user_login The username for the user. - * @param WP_User $user_data WP_User object. - */ - $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data ); + // We pass the default message to the filter for backward compatibility, + // but we'll use WP_Mailer for the actual sending if the message isn't overridden. + $default_message = WP_Mailer::render( WP_Mailer::get_email( 'retrieve_password' )['body'], $email_data ); + + /** This filter is documented in wp-includes/user.php */ + $message = apply_filters( 'retrieve_password_message', $default_message, $key, $user_login, $user_data ); // Short-circuit on falsey $message value for backwards compatibility. if ( ! $message ) { + if ( $switched_locale ) { + restore_previous_locale(); + } return true; } - /* - * Wrap the single notification email arguments in an array - * to pass them to the retrieve_password_notification_email filter. - */ $defaults = array( 'to' => $user_email, 'subject' => $title, @@ -3432,41 +3428,32 @@ function retrieve_password( $user_login = '' ) { 'headers' => '', ); - /** - * Filters the contents of the reset password notification email sent to the user. - * - * @since 6.0.0 - * - * @param array $defaults { - * The default notification email arguments. Used to build wp_mail(). - * - * @type string $to The intended recipient - user email address. - * @type string $subject The subject of the email. - * @type string $message The body of the email. - * @type string $headers The headers of the email. - * } - * @param string $key The activation key. - * @param string $user_login The username for the user. - * @param WP_User $user_data WP_User object. - */ + /** This filter is documented in wp-includes/user.php */ $notification_email = apply_filters( 'retrieve_password_notification_email', $defaults, $key, $user_login, $user_data ); if ( $switched_locale ) { restore_previous_locale(); } - if ( is_array( $notification_email ) ) { - // Force key order and merge defaults in case any value is missing in the filtered array. - $notification_email = array_merge( $defaults, $notification_email ); - } else { - $notification_email = $defaults; - } - - list( $to, $subject, $message, $headers ) = array_values( $notification_email ); + $notification_email = array_merge( $defaults, (array) $notification_email ); - $subject = wp_specialchars_decode( $subject ); + $mail_success = WP_Mailer::send( + 'retrieve_password', + array( + 'to' => $notification_email[ 'to' ], + 'headers' => $notification_email[ 'headers' ], + ), + array_merge( + $email_data, + array( + // Overwrite subject and body if they were filtered. + 'subject' => $notification_email[ 'subject' ], + 'body' => $notification_email[ 'message' ], + ) + ) + ); - if ( ! wp_mail( $to, $subject, $message, $headers ) ) { + if ( ! $mail_success ) { $errors->add( 'retrieve_password_email_failure', sprintf( diff --git a/src/wp-settings.php b/src/wp-settings.php index dab1d8fd4c0de..9593ac0a7c695 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -47,6 +47,7 @@ require ABSPATH . WPINC . '/class-wp-recovery-mode-key-service.php'; require ABSPATH . WPINC . '/class-wp-recovery-mode-link-service.php'; require ABSPATH . WPINC . '/class-wp-recovery-mode-email-service.php'; +require ABSPATH . WPINC . '/class-wp-mailer.php'; require ABSPATH . WPINC . '/class-wp-recovery-mode.php'; require ABSPATH . WPINC . '/error-protection.php'; require ABSPATH . WPINC . '/default-constants.php'; diff --git a/tests/phpunit/tests/mail/wpMailer.php b/tests/phpunit/tests/mail/wpMailer.php new file mode 100644 index 0000000000000..6529290a8d108 --- /dev/null +++ b/tests/phpunit/tests/mail/wpMailer.php @@ -0,0 +1,183 @@ + 'Alice', + 'site' => 'WordPress', + ); + $expected = 'Hello Alice, welcome to WordPress!'; + $this->assertSame( $expected, WP_Mailer::render( $template, $data ) ); + } + + /** + * Tests the render method with missing data. + */ + public function test_render_missing_data() { + $template = 'Hello {{name}}, welcome to {{site}}!'; + $data = array( + 'name' => 'Alice', + ); + $expected = 'Hello Alice, welcome to {{site}}!'; + $this->assertSame( $expected, WP_Mailer::render( $template, $data ) ); + } + + /** + * Tests sending an email with registered template. + */ + public function test_send_registered_email() { + WP_Mailer::register_email( + 'test_email', + 'test_group', + array( + 'subject' => 'Subject for {{name}}', + 'body' => 'Body for {{name}} at {{site}}', + ) + ); + + WP_Mailer::send( + 'test_email', + array( 'to' => 'test@example.com' ), + array( + 'name' => 'Bob', + 'site' => 'Example Site', + ) + ); + + $mailer = tests_retrieve_phpmailer_instance(); + $this->assertSame( 'Subject for Bob', $mailer->Subject ); + $this->assertStringContainsString( 'Body for Bob at Example Site', $mailer->get_sent()->body ); + } + + /** + * Tests the Reply-To fix in wp_mail. + * + * @ticket 49661 + */ + public function test_wp_mail_reply_to_quoting_fix() { + $to = 'recipient@example.com'; + $subject = 'Testing Reply-To'; + $message = 'Message body'; + $headers = 'Reply-To: "John Doe" '; + + wp_mail( $to, $subject, $message, $headers ); + + $mailer = tests_retrieve_phpmailer_instance(); + $reply_tos = $mailer->getReplyToAddresses(); + + $this->assertCount( 1, $reply_tos ); + $reply_to = reset( $reply_tos ); + + // PHPMailer will add its own quotes if needed, but we should not have double quotes here. + $this->assertSame( 'john@example.com', $reply_to[ 0 ] ); + $this->assertSame( 'John Doe', $reply_to[ 1 ] ); + } + + /** + * Tests the retrieve_password template logic. + */ + public function test_retrieve_password_template() { + $site_name = 'My Awesome Site'; + $user_login = 'alice'; + $reset_url = 'https://example.com/reset'; + $ip = '127.0.0.1'; + + WP_Mailer::register_email( + 'retrieve_password', + 'user', + array( + 'subject' => '[{{sitename}}] Password Reset', + 'body' => 'Username: {{user_login}} Reset: {{reset_url}} IP: {{ip_notification}}', + ) + ); + + WP_Mailer::send( + 'retrieve_password', + array( 'to' => 'alice@example.com' ), + array( + 'sitename' => $site_name, + 'user_login' => $user_login, + 'reset_url' => $reset_url, + 'ip_notification' => $ip, + ) + ); + + $mailer = tests_retrieve_phpmailer_instance(); + $this->assertSame( '[' . $site_name . '] Password Reset', $mailer->Subject ); + $this->assertStringContainsString( 'Username: alice', $mailer->get_sent()->body ); + $this->assertStringContainsString( 'Reset: https://example.com/reset', $mailer->get_sent()->body ); + $this->assertStringContainsString( 'IP: 127.0.0.1', $mailer->get_sent()->body ); + } + + /** + * Tests the new_user templates. + */ + public function test_new_user_templates() { + // Admin notification. + WP_Mailer::register_email( + 'new_user_admin', + 'admin', + array( + 'subject' => '[{{sitename}}] New User', + 'body' => 'User: {{user_login}} Email: {{user_email}}', + ) + ); + + WP_Mailer::send( + 'new_user_admin', + array( 'to' => 'admin@example.com' ), + array( + 'sitename' => 'Site', + 'user_login' => 'bob', + 'user_email' => 'bob@example.com', + ) + ); + + $mailer = tests_retrieve_phpmailer_instance(); + $this->assertSame( '[Site] New User', $mailer->Subject ); + $this->assertStringContainsString( 'User: bob', $mailer->get_sent()->body ); + + // User notification. + reset_phpmailer_instance(); + WP_Mailer::register_email( + 'new_user', + 'user', + array( + 'subject' => '[{{sitename}}] Login Details', + 'body' => 'Login: {{user_login}} URL: {{set_password_url}}', + ) + ); + + WP_Mailer::send( + 'new_user', + array( 'to' => 'bob@example.com' ), + array( + 'sitename' => 'Site', + 'user_login' => 'bob', + 'set_password_url' => 'http://example.com/set-pw', + ) + ); + + $mailer = tests_retrieve_phpmailer_instance(); + $this->assertSame( '[Site] Login Details', $mailer->Subject ); + $this->assertStringContainsString( 'URL: http://example.com/set-pw', $mailer->get_sent()->body ); + } +}