Skip to content

Commit

Permalink
Add multipart email support
Browse files Browse the repository at this point in the history
  • Loading branch information
yscik committed Dec 14, 2023
1 parent 9a06455 commit b8388b7
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 25 deletions.
2 changes: 1 addition & 1 deletion includes/abstracts/abstract-wp-job-manager-email.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public function get_headers() {
* @return string
*/
public function get_plain_content() {
return wp_strip_all_tags( $this->get_rich_content() );
return normalize_whitespace( wp_strip_all_tags( $this->get_rich_content() ) );
}

/**
Expand Down
104 changes: 81 additions & 23 deletions includes/class-wp-job-manager-email-notifications.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class WP_Job_Manager_Email_Notifications {
const EMAIL_SETTING_PREFIX = 'job_manager_email_';
const EMAIL_SETTING_ENABLED = 'enabled';
const EMAIL_SETTING_PLAIN_TEXT = 'plain_text';
const MULTIPART_BOUNDARY = '--jm-boundary';

/**
* Notifications to be scheduled.
Expand Down Expand Up @@ -763,6 +764,8 @@ private static function is_email_notification_valid( $email_class ) {
* @return bool
*/
private static function send_email( $email_notification_key, WP_Job_Manager_Email $email ) {
add_filter( 'wp_mail_content_type', [ __CLASS__, 'mail_content_type' ] );

if ( ! $email->is_valid() ) {
return false;
}
Expand Down Expand Up @@ -792,7 +795,15 @@ private static function send_email( $email_notification_key, WP_Job_Manager_Emai
$sent_count = 0;
foreach ( $send_to as $to_email ) {
$args['to'] = $to_email;
$content = self::get_email_content( $email_notification_key, $args );

$is_plain_text_only = self::send_as_plain_text( $email_notification_key, $args );

$content_plain = self::get_email_content( $email_notification_key, $args, true );
$content_html = null;

if ( ! $is_plain_text_only ) {
$content_html = self::get_email_content( $email_notification_key, $args, false );
}

/**
* Filter all email arguments for job manager notifications.
Expand All @@ -813,9 +824,7 @@ private static function send_email( $email_notification_key, WP_Job_Manager_Emai
$headers[] = 'CC: ' . $args['cc'];
}

if ( ! self::send_as_plain_text( $email_notification_key, $args ) ) {
$headers[] = 'Content-Type: text/html';
}
$multipart_body = self::get_multipart_body( $content_html, $content_plain );

/**
* Allows for short-circuiting the actual sending of email notifications.
Expand All @@ -828,29 +837,46 @@ private static function send_email( $email_notification_key, WP_Job_Manager_Emai
* @param string $content Email content.
* @param array $headers Email headers.
*/
if ( ! apply_filters( 'job_manager_email_do_send_notification', true, $email, $args, $content, $headers ) ) {
if ( ! apply_filters( 'job_manager_email_do_send_notification', true, $email, $args, $multipart_body, $headers ) ) {
continue;
}

if ( wp_mail( $to_email, $args['subject'], $content, $headers, $args['attachments'] ) ) {
if ( wp_mail( $to_email, $args['subject'], $multipart_body, $headers, $args['attachments'] ) ) {
$sent_count++;
}
}

remove_filter( 'wp_mail_content_type', [ __CLASS__, 'mail_content_type' ] );
$job_manager_doing_email = false;

return $sent_count > 0;
}

/**
* Set the "Content Type" header of the e-mail to multipart/alternative.
*
* @access private
*
* @since $$next-version$$
*
* @return string
*/
public static function mail_content_type() {
return 'multipart/alternative; boundary="' . self::MULTIPART_BOUNDARY . '"';
}

/**
* Generates the content for an email.
*
* @access private
*
* @param string $email_notification_key Unique email notification key.
* @param array $args Arguments passed for generating email.
* @param array $args Arguments passed for generating email.
* @param bool $is_plain_text Whether to generate plain text or rich text content.
*
* @return string
*/
private static function get_email_content( $email_notification_key, $args ) {
$plain_text = self::send_as_plain_text( $email_notification_key, $args );
private static function get_email_content( $email_notification_key, $args, $is_plain_text ) {

ob_start();

Expand All @@ -860,15 +886,16 @@ private static function get_email_content( $email_notification_key, $args ) {
* @since 1.31.0
*
* @param string $email_notification_key Unique email notification key.
* @param array $args Arguments passed for generating email.
* @param bool $plain_text True if sending plain text email.
* @param array $args Arguments passed for generating email.
* @param bool $is_plain_text True if sending plain text email.
*/
do_action( 'job_manager_email_header', $email_notification_key, $args, $plain_text );
do_action( 'job_manager_email_header', $email_notification_key, $args, $is_plain_text );

if ( $plain_text ) {
echo wp_kses_post( html_entity_decode( wptexturize( $args['plain_content'] ) ) );
if ( $is_plain_text ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Plain text e-mail.
echo wp_specialchars_decode( wp_strip_all_tags( $args['plain_content'] ) );
} else {
echo wp_kses_post( wpautop( wptexturize( $args['rich_content'] ) ) );
echo wp_kses_post( ( $args['rich_content'] ) );
}

/**
Expand All @@ -877,13 +904,13 @@ private static function get_email_content( $email_notification_key, $args ) {
* @since 1.31.0
*
* @param string $email_notification_key Unique email notification key.
* @param array $args Arguments passed for generating email.
* @param bool $plain_text True if sending plain text email.
* @param array $args Arguments passed for generating email.
* @param bool $is_plain_text True if sending plain text email.
*/
do_action( 'job_manager_email_footer', $email_notification_key, $args, $plain_text );
do_action( 'job_manager_email_footer', $email_notification_key, $args, $is_plain_text );

$content = ob_get_clean();
if ( ! $plain_text ) {
if ( ! $is_plain_text ) {
$content = self::inject_styles( $content );
}

Expand All @@ -892,12 +919,12 @@ private static function get_email_content( $email_notification_key, $args ) {
*
* @since 1.31.0
*
* @param string $content Email content.
* @param string $content Email content.
* @param string $email_notification_key Unique email notification key.
* @param array $args Arguments passed for generating email.
* @param bool $plain_text True if sending plain text email.
* @param array $args Arguments passed for generating email.
* @param bool $is_plain_text True if sending plain text email.
*/
return apply_filters( 'job_manager_email_content', $content, $email_notification_key, $args, $plain_text );
return apply_filters( 'job_manager_email_content', $content, $email_notification_key, $args, $is_plain_text );
}

/**
Expand Down Expand Up @@ -933,4 +960,35 @@ private static function get_styles() {
return ob_get_clean();
}

/**
* Assemble multipart e-mail body.
*
* @param string $content_html
* @param string $content_plain
*
* @return string
*/
private static function get_multipart_body( string $content_html, string $content_plain ): string {
$multipart_body = '';

if ( ! empty( $content_html ) ) {
$multipart_body .= '
--' . self::MULTIPART_BOUNDARY . '
Content-Type: text/html; charset="utf-8"
' . $content_html;
}

if ( ! empty( $content_plain ) ) {

$multipart_body .= '
--' . self::MULTIPART_BOUNDARY . '
Content-Type: text/plain; charset="utf-8"
' . $content_plain;
}

return $multipart_body;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function test_send_deferred_notifications_valid_email() {
$this->assertEquals( 'Test Subject', $sent_email->subject );
$this->assertStringContainsString( '<strong>test</strong>', $sent_email->body );
$this->assertStringContainsString( 'From: From Name <[email protected]>', $sent_email->header );
$this->assertStringContainsString( 'Content-Type: text/html;', $sent_email->header );
$this->assertStringContainsString( 'Content-Type: text/html;', $sent_email->body );
}

/**
Expand Down

0 comments on commit b8388b7

Please sign in to comment.