diff --git a/src/wp-admin/includes/ms.php b/src/wp-admin/includes/ms.php index 142aaee5f0..7dc6b055b8 100644 --- a/src/wp-admin/includes/ms.php +++ b/src/wp-admin/includes/ms.php @@ -325,10 +325,10 @@ All at ###SITENAME### $content = str_replace( '###USERNAME###', $current_user->user_login, $content ); $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash='.$hash ) ), $content ); $content = str_replace( '###EMAIL###', $value, $content ); - $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content ); + $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content ); $content = str_replace( '###SITEURL###', network_home_url(), $content ); - wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); + wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content ); if ( $switched_locale ) { restore_previous_locale(); @@ -410,10 +410,10 @@ All at ###SITENAME### $content = str_replace( '###USERNAME###', $current_user->user_login, $content ); $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'profile.php?newuseremail=' . $hash ) ), $content ); $content = str_replace( '###EMAIL###', $_POST['email'], $content); - $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content ); + $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content ); $content = str_replace( '###SITEURL###', network_home_url(), $content ); - wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); + wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content ); $_POST['email'] = $current_user->user_email; if ( $switched_locale ) { diff --git a/tests/phpunit/tests/user/multisite.php b/tests/phpunit/tests/user/multisite.php index f6856844df..663f08707f 100644 --- a/tests/phpunit/tests/user/multisite.php +++ b/tests/phpunit/tests/user/multisite.php @@ -424,6 +424,185 @@ class Tests_Multisite_User extends WP_UnitTestCase { $wp_roles->remove_role( $role ); } + + + /** + * Ensure blog's admin e-mail change notification emails do not contain encoded HTML entities + * @ticket 40015 + */ + function test_ms_new_admin_email_notification_html_entities_decoded() { + reset_phpmailer_instance(); + + $existing_email = get_option( 'admin_email' ); + $new_email = 'new-admin-email@test.dev'; + + // Give the site and blog a name containing HTML entities + update_site_option( 'site_name', ''Test' site's "name" has <html entities> &' ); + update_option( 'blogname', ''Test' blog's "name" has <html entities> &' ); + + update_option_new_admin_email( $existing_email, $new_email ); + + $mailer = tests_retrieve_phpmailer_instance(); + + $recipient = $mailer->get_recipient( 'to' ); + $email = $mailer->get_sent(); + + // Assert reciepient is correct + $this->assertSame( $new_email, $recipient->address, 'Admin email change notification recipient not as expected' ); + + // Assert that HTML entites have been decode in body and subject + $this->assertContains( '\'Test\' site\'s "name" has &', $email->body, 'Email body does not contain the decoded HTML entities' ); + $this->assertNotContains( ''Test' site's "name" has <html entities> &', 'Email body does contains HTML entities' ); + $this->assertContains( '\'Test\' blog\'s "name" has &', $email->subject, 'Email body does not contain the decoded HTML entities' ); + $this->assertNotContains( ''Test' blog's "name" has <html entities> &', $email->subject, $email->subject, 'Email subject does contains HTML entities' ); + } + + /** + * A notification e-mail should not be sent if the new admin e-mail: + * - Matches thee existing admin email, or + * - is not a valid e-mail, or + * + * @dataProvider data_user_admin_email_notification_emails + */ + function test_ms_new_admin_email_notification_not_sent_when_email_invalid( $email, $message ) { + reset_phpmailer_instance(); + + update_option( 'admin_email', 'existing-email@test.dev' ); + update_option_new_admin_email( 'existing-email@test.dev', $email ); + + $mailer = tests_retrieve_phpmailer_instance(); + + $this->assertFalse( $mailer->get_sent(), $message ); + } + + /** + * Data provider for test_ms_new_admin_email_notification_not_sent_when_email_invalid(). + * + * @return array { + * @type array { + * @type string $email The new e-mail for admin_email + * @type string $message An error message to display if the test fails + * } + * } + */ + function data_user_admin_email_notification_emails() { + return array( + array( + 'existing-email@test.dev', + 'A notification e-mail should not be sent if the current admin e-mail matches the new e-mail', + ), + array( + 'not an email', + 'A notification e-mail should not be sent if it is not a valid e-mail', + ) + ); + } + + /** + * Ensure email change confirmation emails do not contain encoded HTML entities + * @ticket 40015 + */ + function test_ms_send_confirmation_on_profile_email_html_entities_decoded() { + + $old_current = get_current_user_id(); + $user_id = self::factory()->user->create( array( + 'role' => 'subscriber', + 'user_email' => 'old-email@test.dev', + ) ); + wp_set_current_user( $user_id ); + + reset_phpmailer_instance(); + + // Give the site and blog a name containing HTML entities + update_site_option( 'site_name', ''Test' site's "name" has <html entities> &' ); + update_option( 'blogname', ''Test' blog's "name" has <html entities> &' ); + + // Set $_POST['email'] with new e-mail and $_POST['id'] with user's ID. + $_POST['user_id'] = $user_id; + $_POST['email'] = 'new-email@test.dev'; + send_confirmation_on_profile_email( ); + + $mailer = tests_retrieve_phpmailer_instance(); + + $recipient = $mailer->get_recipient( 'to' ); + $email = $mailer->get_sent(); + + // Assert reciepient is correct + $this->assertSame( 'new-email@test.dev', $recipient->address, 'Admin email change notification recipient not as expected' ); + + // Assert that HTML entites have been decode in body and subject + $this->assertContains( '\'Test\' site\'s "name" has &', $email->body, 'Email body does not contain the decoded HTML entities' ); + $this->assertNotContains( ''Test' site's "name" has <html entities> &', $email->body, 'Email body does contains HTML entities' ); + $this->assertContains( '\'Test\' blog\'s "name" has &', $email->subject, 'Email body does not contain the decoded HTML entities' ); + $this->assertNotContains( ''Test' blog's "name" has <html entities> &', $email->subject, 'Email subject does contains HTML entities' ); + + wp_set_current_user( $old_current ); + } + + /** + * A confirmation e-mail should not be sent if user's new e-mail: + * - Matches their existing email, or + * - is not a valid e-mail, or + * - Matches another user's email + * + * @dataProvider data_user_change_email_confirmation_emails + */ + function test_ms_profile_email_confirmation_not_sent_invalid_email( $email, $message ) { + + $old_current = get_current_user_id(); + + $user_id = self::factory()->user->create( array( + 'role' => 'subscriber', + 'user_email' => 'email@test.dev', + ) ); + wp_set_current_user( $user_id ); + + self::factory()->user->create( array( + 'role' => 'subscriber', + 'user_email' => 'another-user@test.dev', + ) ); + + reset_phpmailer_instance(); + + // Set $_POST['email'] with new e-mail and $_POST['id'] with user's ID. + $_POST['user_id'] = $user_id; + $_POST['email'] = $email; + send_confirmation_on_profile_email(); + + $mailer = tests_retrieve_phpmailer_instance(); + + $this->assertFalse( $mailer->get_sent(), $message ); + + wp_set_current_user( $old_current ); + } + + /** + * Data provider for test_ms_profile_email_confirmation_not_sent_invalid_email(). + * + * @return array { + * @type array { + * @type string $email The user's new e-amil. + * @type string $message An error message to display if the test fails + * } + * } + */ + function data_user_change_email_confirmation_emails() { + return array( + array( + 'email@test.dev', + 'Confirmation e-mail should not be sent if it matches the user\'s existing e-mail', + ), + array( + 'not an email', + 'Confirmation e-mail should not be sent if it is not a valid e-mail', + ), + array( + 'another-user@test.dev', + 'Confirmation e-mail should not be sent if it matches another user\'s e-mail', + ), + ); + } + } endif ;