diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index 9336e03c7e..09ec4503db 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -153,6 +153,11 @@ if ( ! function_exists( 'wp_mail' ) ) : * However, you can set the content type of the email by using the * {@see 'wp_mail_content_type'} filter. * + * If $message is an array, the key of each is used to add as an attachment + * with the value used as the body. The 'text/plain' element is used as the + * text version of the body, with the 'text/html' element used as the HTML + * version of the body. All other types are added as attachments. + * * The default charset is based on the charset used on the blog. The charset can * be set using the {@see 'wp_mail_charset'} filter. * @@ -162,7 +167,7 @@ if ( ! function_exists( 'wp_mail' ) ) : * * @param string|array $to Array or comma-separated list of email addresses to send message. * @param string $subject Email subject - * @param string $message Message contents + * @param string|array $message Message contents * @param string|array $headers Optional. Additional headers. * @param string|array $attachments Optional. Files to attach. * @return bool Whether the email contents were sent successfully. @@ -273,6 +278,10 @@ if ( ! function_exists( 'wp_mail' ) ) : } break; case 'content-type': + if ( is_array( $message ) ) { + // Multipart email, ignore the content-type header + break; + } if ( strpos( $content, ';' ) !== false ) { list( $type, $charset_content ) = explode( ';', $content ); $content_type = trim( $type ); @@ -312,6 +321,9 @@ if ( ! function_exists( 'wp_mail' ) ) : $phpmailer->clearCustomHeaders(); $phpmailer->clearReplyTos(); + $phpmailer->Body= ''; + $phpmailer->AltBody= ''; + // From email and name // If we don't have a name from the input headers if ( ! isset( $from_name ) ) { @@ -365,10 +377,6 @@ if ( ! function_exists( 'wp_mail' ) ) : return false; } - // Set mail's subject and body - $phpmailer->Subject = $subject; - $phpmailer->Body = $message; - // Set destination addresses, using appropriate methods for handling addresses $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' ); @@ -409,38 +417,12 @@ if ( ! function_exists( 'wp_mail' ) ) : } } - // Set to use PHP's mail() - $phpmailer->isMail(); - - // Set Content-Type and charset - // If we don't have a content-type from the input headers - if ( ! isset( $content_type ) ) { - $content_type = 'text/plain'; - } - - /** - * Filters the wp_mail() content type. - * - * @since 2.3.0 - * - * @param string $content_type Default wp_mail() content type. - */ - $content_type = apply_filters( 'wp_mail_content_type', $content_type ); - - $phpmailer->ContentType = $content_type; - - // Set whether it's plaintext, depending on $content_type - if ( 'text/html' == $content_type ) { - $phpmailer->isHTML( true ); - } - + // Set a charset // If we don't have a charset from the input headers if ( ! isset( $charset ) ) { $charset = get_bloginfo( 'charset' ); } - // Set the content-type and charset - /** * Filters the default wp_mail() charset. * @@ -450,15 +432,61 @@ if ( ! function_exists( 'wp_mail' ) ) : */ $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset ); - // Set custom headers - if ( ! empty( $headers ) ) { - foreach ( (array) $headers as $name => $content ) { - $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); + // Set mail's subject and body + $phpmailer->Subject = $subject; + + if ( is_string( $message ) ) { + $phpmailer->Body = $message; + + // Set Content-Type + // If we don't have a content-type from the input headers + if ( ! isset( $content_type ) ) { + $content_type = 'text/plain'; } + /** + * Filters the wp_mail() content type. + * + * @since 2.3.0 + * + * @param string $content_type Default wp_mail() content type. + */ + $content_type = apply_filters( 'wp_mail_content_type', $content_type ); + + $phpmailer->ContentType = $content_type; + + // Set whether it's plaintext, depending on $content_type + if ( 'text/html' === $content_type ) { + $phpmailer->isHTML( true ); + } + + // For backwards compatibility, new multipart emails should use + // the array style $message. This never really worked well anyway if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) { $phpmailer->addCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) ); } + } elseif ( is_array( $message ) ) { + foreach ( $message as $type => $bodies ) { + foreach ( (array) $bodies as $body ) { + if ( 'text/html' === $type ) { + $phpmailer->Body = $body; + } elseif ( 'text/plain' === $type ) { + $phpmailer->AltBody = $body; + } else { + $phpmailer->addAttachment( $body, '', 'base64', $type ); + } + } + } + } + + // Set to use PHP's mail() + $phpmailer->isMail(); + + // Set custom headers + if ( ! empty( $headers ) ) { + foreach ( (array) $headers as $name => $content ) { + $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); + } } if ( ! empty( $attachments ) ) { diff --git a/tests/phpunit/tests/mail.php b/tests/phpunit/tests/mail.php index 951615136b..ad0323e6ae 100644 --- a/tests/phpunit/tests/mail.php +++ b/tests/phpunit/tests/mail.php @@ -85,6 +85,71 @@ class Tests_Mail extends WP_UnitTestCase { $this->assertTrue( strpos( $mailer->get_sent()->header, 'charset=' ) > 0 ); } + /** + * @ticket 15448 + */ + function test_wp_mail_plain_and_html() { + $to = 'user@example.com'; + $subject = 'Test email with plain text and html versions'; + $messages = array( 'text/plain' => 'Here is some plain text.', + 'text/html' =>'Here is the HTML with UTF-8 γειά σου Κόσμε;-)' ); + + wp_mail( $to, $subject, $messages ); + + preg_match( '/boundary="(.*)"/', $GLOBALS['phpmailer']->mock_sent[0]['header'], $matches ); + $boundry = $matches[1]; + $body = 'This is a multi-part message in MIME format. + +--' . $boundry . ' +Content-Type: text/plain; charset=us-ascii + +Here is some plain text. + + +--' . $boundry . ' +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Here is the HTML with UTF-8 γειά σου Κόσμε;-) + + + +--' . $boundry . '-- +'; + // We need some better assertions here but these test the behaviour for now. + $this->assertEquals( $body, $GLOBALS['phpmailer']->mock_sent[0]['body'] ); + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type: multipart/alternative;' ) ); + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type:' ) ); + } + + /* + * 'phpmailer_init' action for test_wp_mail_plain_and_html_workaround(). + */ + function wp_mail_set_alt_body( $mailer ) { + $mailer->AltBody = strip_tags( $mailer->Body ); + } + + /** + * @ticket 15448 + * + * Check workarounds using phpmailer_init still work around. + */ + function test_wp_mail_plain_and_html_workaround() { + $to = 'user@example.com'; + $subject = 'Test email with plain text derived from html version'; + $message = '

Hello World! γειά σου Κόσμε

'; + + add_action( 'phpmailer_init', array( $this, 'wp_mail_set_alt_body' ) ); + wp_mail( $to, $subject, $message ); + remove_action( 'phpmailer_init', array( $this, 'wp_mail_set_alt_body' ) ); + + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type: multipart/alternative;' ) ); + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type:' ) ); + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type: text/plain; charset=UTF-8' ) ); + $this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type: text/html; charset=UTF-8' ) ); + $this->assertSame( 2, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type:' ) ); + } + /** * @ticket 17305 */