Gửi email nhiều phần (văn bản / html) qua wp_mail () có thể sẽ khiến tên miền của bạn bị cấm


37

Tóm lược

Bởi vì một lỗi trong WP Core, gửi nhiều phần dữ liệu email (html / text) với wp_mail () (để giảm cơ hội của email kết thúc trong thư mục spam) sẽ trớ trêu thay cho kết quả với tên miền của bạn bị chặn bởi Hotmail (và email khác của Microsoft).

Đây là một vấn đề phức tạp mà tôi sẽ nhắm đến để phân tích rất chi tiết trong nỗ lực giúp ai đó tìm ra giải pháp khả thi mà cuối cùng có thể được thực hiện trong cốt lõi.

Đây sẽ là một bài đọc bổ ích. Hãy bắt đầu nào...

Con bọ

Lời khuyên phổ biến nhất để tránh việc email bản tin của bạn kết thúc trong các thư mục spam là gửi tin nhắn nhiều phần.

Đa phần (mime) đề cập đến việc gửi cả phần HTML và văn bản của một thông điệp email trong một email. Khi khách hàng nhận được một tin nhắn nhiều phần, nó chấp nhận phiên bản HTML nếu nó có thể hiển thị HTML, nếu không, nó thể hiện phiên bản văn bản thuần túy.

Điều này được chứng minh là có tác dụng. Khi gửi đến gmail, tất cả các email của chúng tôi đều rơi vào các thư mục spam cho đến khi chúng tôi thay đổi thư thành nhiều phần khi chúng được chuyển đến hộp thư đến chính. Công cụ tuyệt vời.

Bây giờ, khi gửi tin nhắn nhiều phần qua wp_mail (), nó sẽ xuất Loại Nội dung (bội / *) hai lần, một lần với ranh giới (nếu được đặt tùy chỉnh) và một lần không có. Hành vi này dẫn đến việc email được hiển thị dưới dạng tin nhắn thô và không phải là nhiều phần trên một số email, bao gồm tất cả Microsoft (Hotmail, Outlook, v.v.)

Microsoft sẽ gắn cờ tin nhắn này là rác và một vài tin nhắn đi qua sẽ được người nhận gắn cờ theo cách thủ công. Thật không may , địa chỉ email của Microsoft được sử dụng rộng rãi. 40% thuê bao của chúng tôi sử dụng nó.

Điều này được Microsoft xác nhận thông qua trao đổi email mà chúng tôi đã có gần đây.

Việc gắn cờ các tin nhắn sẽ dẫn đến việc tên miền bị chặn hoàn toàn . Điều này có nghĩa là tin nhắn sẽ không được gửi đến thư mục thư rác, chúng thậm chí sẽ không được gửi đến người nhận.

Chúng tôi đã có tên miền chính của chúng tôi bị chặn 3 lần cho đến nay.

Vì đây là một lỗi trong lõi WP, nên mọi miền gửi tin nhắn nhiều phần đều bị chặn. Vấn đề là hầu hết các quản trị web không biết tại sao. Tôi đã xác nhận điều này khi thực hiện nghiên cứu của mình và thấy những người dùng khác thảo luận về vấn đề này trên các diễn đàn, v.v. Nó đòi hỏi phải đi sâu vào mã thô và có kiến ​​thức tốt về cách thức các loại email này hoạt động, mà chúng ta sẽ tiếp tục ...

Hãy chia nó thành mã

Tạo một tài khoản hotmail / triển vọng. Sau đó, chạy mã sau đây:

// Set $to to an hotmail.com or outlook.com email
$to = "YourEmail@hotmail.com";

$subject = 'wp_mail testing multipart';

$message = '------=_Part_18243133_1346573420.1408991447668
Content-Type: text/plain; charset=UTF-8

Hello world! This is plain text...


------=_Part_18243133_1346573420.1408991447668
Content-Type: text/html; charset=UTF-8

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<p>Hello World! This is HTML...</p> 

</body>
</html>


------=_Part_18243133_1346573420.1408991447668--';

$headers = "MIME-Version: 1.0\r\n";
$headers .= "From: Foo <foo@bar.com>\r\n";
$headers .= 'Content-Type: multipart/alternative;boundary="----=_Part_18243133_1346573420.1408991447668"';


// send email
wp_mail( $to, $subject, $message, $headers );

Và nếu bạn muốn thay đổi loại nội dung mặc định , hãy sử dụng:

add_filter( 'wp_mail_content_type', 'set_content_type' );
function set_content_type( $content_type ) {
    return 'multipart/alternative';
}

Điều này sẽ gửi một tin nhắn nhiều phần.

Vì vậy, nếu bạn kiểm tra toàn bộ nguồn thô của tin nhắn, bạn sẽ nhận thấy rằng loại nội dung được thêm hai lần, một lần không có ranh giới:

MIME-Version: 1.0
Content-Type: multipart/alternative;
         boundary="====f230673f9d7c359a81ffebccb88e5d61=="
MIME-Version: 1.0
Content-Type: multipart/alternative; charset=

Đó là vấn đề.

Nguồn gốc của vấn đề nằm ở pluggable.php- nếu chúng ta tìm kiếm ở đâu đó ở đây:

// 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';

    /**
     * Filter 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 );

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );

    // Set the content-type and charset

    /**
     * Filter the default wp_mail() charset.
     *
     * @since 2.3.0
     *
     * @param string $charset Default email charset.
     */
    $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 ) );
        }

        if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
            $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
    }

    if ( !empty( $attachments ) ) {
        foreach ( $attachments as $attachment ) {
            try {
                $phpmailer->AddAttachment($attachment);
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

Các giải pháp tiềm năng

Vì vậy, bạn đang tự hỏi, tại sao bạn không báo cáo điều này tại trac ? Tôi đã có . Thật ngạc nhiên, một vé khác đã được tạo ra 5 năm trước phác thảo cùng một vấn đề.

Hãy đối mặt với nó, đó là một nửa thập kỷ. Trong những năm qua, nó giống như 30. Vấn đề rõ ràng đã bị bỏ qua và về cơ bản sẽ không bao giờ được khắc phục (... trừ khi chúng tôi giải quyết nó ở đây).

Tôi đã tìm thấy một chủ đề tuyệt vời ở đây cung cấp một giải pháp, nhưng trong khi giải pháp của anh ấy hoạt động, nó phá vỡ các email không có $headersbộ tùy chỉnh .

Đó là nơi chúng ta gặp sự cố mỗi lần. Phiên bản nhiều trang hoạt động tốt, và $headerscác thông báo bỏ đặt bình thường không, hoặc câu thơ vise.

Giải pháp chúng tôi đưa ra là:

if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) {
    $phpmailer->ContentType = $content_type . "; boundary=" . $boundary;
}
else {

        $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 );

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );
}

// Set the content-type and charset

/**
 * Filter the default wp_mail() charset.
 *
 * @since 2.3.0
 *
 * @param string $charset Default email charset.
 */
$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 ) );
    }

}

Vâng, tôi biết, chỉnh sửa các tệp lõi là điều cấm kỵ, ngồi xuống ... đây là một sửa chữa tuyệt vọng và một nỗ lực kém để cung cấp một sửa chữa cho lõi.

Vấn đề với cách khắc phục của chúng tôi là các email mặc định như đăng ký mới, nhận xét, đặt lại mật khẩu, v.v. sẽ được gửi dưới dạng tin nhắn trống. Vì vậy, chúng tôi có một tập lệnh wp_mail () đang hoạt động sẽ gửi tin nhắn nhiều phần nhưng không có gì khác.

Phải làm gì

Mục đích ở đây là tìm cách gửi cả tin nhắn thông thường (văn bản thuần túy) và tin nhắn nhiều phần bằng cách sử dụng hàm wp_mail () lõi (không phải là chức năng gửi thư tùy chỉnh).

Khi cố gắng giải quyết vấn đề này, vấn đề chính bạn sẽ gặp phải là lượng thời gian bạn sẽ dành để gửi tin nhắn giả, kiểm tra xem chúng có nhận được không và về cơ bản là mở hộp aspirin và chửi bới tại Microsoft vì bạn đã quen với chúng Vấn đề IE trong khi gremlin ở đây không may là WordPress.

Cập nhật

Giải pháp được đăng bởi @bonger cho phép $messagelà một mảng chứa các lựa chọn thay thế kiểu nội dung. Tôi đã xác nhận rằng nó hoạt động trong tất cả các kịch bản.

Chúng tôi sẽ cho phép câu hỏi này vẫn mở cho đến khi hết tiền thưởng để nâng cao nhận thức về vấn đề, có thể đến một mức độ mà nó sẽ được khắc phục trong cốt lõi. Hãy đăng một giải pháp thay thế trong đó $messagecó thể là một chuỗi.


1
wp_mail()chức năng có thể cắm được nên không xác định thay thế của bạn là plugin phải sử dụng (trong wp-content / mu-plugin) không phải là giải pháp tốt cho bạn (và mọi người khác, không khắc phục được lỗi)? Trong trường hợp nào sẽ không di chuyển kiểm tra nhiều phần / ranh giới đến sau khi cài đặt $phpmailer->ContentType = $content_type;(chứ không phải elsing) không hoạt động?
bonger

@bonger Bạn có thể vui lòng viết lên một câu trả lời chi tiết về giải pháp của bạn không?
Christine Cooper

1
Bạn không cần phải chỉnh sửa lõi, vì wp_mailcó thể cắm được . Sao chép chức năng ban đầu trong một plugin, chỉnh sửa nó như bạn cần và kích hoạt plugin. WordPress sẽ sử dụng chức năng chỉnh sửa của bạn thay vì chức năng ban đầu, không cần chỉnh sửa lõi.
gmazzap

@ChristineCooper Tôi ngần ngại làm điều này vì bạn nói rằng thử nghiệm là một nỗi đau của hoàng gia, nhưng nhìn vào bản vá core.trac.wordpress.org/ticket/15448 được đề xuất trong trac bởi @ rmccue / @ MattyRob có vẻ rất hay vì vậy tôi sẽ đăng một câu trả lời chưa được kiểm tra dựa trên điều đó ...
bonger

2
@ChristineCooper nếu bạn đơn giản móc vào phpmailer và đặt phần thân văn bản trong $ phpmailer-> AltBody có xảy ra lỗi tương tự không?
chifliiiii

Câu trả lời:


15

Phiên bản sau đây wp_mail()là với bản vá được áp dụng của @ rmccue / @ MattyRob trong vé https://core.trac.wordpress.org/ticket/15448 , được làm mới cho 4.2.2, cho phép $messagelà một mảng chứa loại nội dung khóa thay thế:

/**
 * Send mail, similar to PHP's mail
 *
 * A true return value does not automatically mean that the user received the
 * email successfully. It just only means that the method used was able to
 * process the request without any errors.
 *
 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from
 * creating a from address like 'Name <email@address.com>' when both are set. If
 * just 'wp_mail_from' is set, then just the email address will be used with no
 * name.
 *
 * The default content type is 'text/plain' which does not allow using HTML.
 * However, you can set the content type of the email by using the
 * '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 'wp_mail_charset' filter.
 *
 * @since 1.2.1
 *
 * @uses PHPMailer
 *
 * @param string|array $to Array or comma-separated list of email addresses to send message.
 * @param string $subject Email subject
 * @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.
 */
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
    // Compact the input, apply the filters, and extract them back out

    /**
     * Filter the wp_mail() arguments.
     *
     * @since 2.2.0
     *
     * @param array $args A compacted array of wp_mail() arguments, including the "to" email,
     *                    subject, message, headers, and attachments values.
     */
    $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );

    if ( isset( $atts['to'] ) ) {
        $to = $atts['to'];
    }

    if ( isset( $atts['subject'] ) ) {
        $subject = $atts['subject'];
    }

    if ( isset( $atts['message'] ) ) {
        $message = $atts['message'];
    }

    if ( isset( $atts['headers'] ) ) {
        $headers = $atts['headers'];
    }

    if ( isset( $atts['attachments'] ) ) {
        $attachments = $atts['attachments'];
    }

    if ( ! is_array( $attachments ) ) {
        $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
    }
    global $phpmailer;

    // (Re)create it, if it's gone missing
    if ( ! ( $phpmailer instanceof PHPMailer ) ) {
        require_once ABSPATH . WPINC . '/class-phpmailer.php';
        require_once ABSPATH . WPINC . '/class-smtp.php';
        $phpmailer = new PHPMailer( true );
    }

    // Headers
    if ( empty( $headers ) ) {
        $headers = array();
    } else {
        if ( !is_array( $headers ) ) {
            // Explode the headers out, so this function can take both
            // string headers and an array of headers.
            $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
        } else {
            $tempheaders = $headers;
        }
        $headers = array();
        $cc = array();
        $bcc = array();

        // If it's actually got contents
        if ( !empty( $tempheaders ) ) {
            // Iterate through the raw headers
            foreach ( (array) $tempheaders as $header ) {
                if ( strpos($header, ':') === false ) {
                    if ( false !== stripos( $header, 'boundary=' ) ) {
                        $parts = preg_split('/boundary=/i', trim( $header ) );
                        $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
                    }
                    continue;
                }
                // Explode them out
                list( $name, $content ) = explode( ':', trim( $header ), 2 );

                // Cleanup crew
                $name    = trim( $name    );
                $content = trim( $content );

                switch ( strtolower( $name ) ) {
                    // Mainly for legacy -- process a From: header if it's there
                    case 'from':
                        $bracket_pos = strpos( $content, '<' );
                        if ( $bracket_pos !== false ) {
                            // Text before the bracketed email is the "From" name.
                            if ( $bracket_pos > 0 ) {
                                $from_name = substr( $content, 0, $bracket_pos - 1 );
                                $from_name = str_replace( '"', '', $from_name );
                                $from_name = trim( $from_name );
                            }

                            $from_email = substr( $content, $bracket_pos + 1 );
                            $from_email = str_replace( '>', '', $from_email );
                            $from_email = trim( $from_email );

                        // Avoid setting an empty $from_email.
                        } elseif ( '' !== trim( $content ) ) {
                            $from_email = trim( $content );
                        }
                        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 );
                            if ( false !== stripos( $charset_content, 'charset=' ) ) {
                                $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
                            } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
                                $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
                                $charset = '';
                            }

                        // Avoid setting an empty $content_type.
                        } elseif ( '' !== trim( $content ) ) {
                            $content_type = trim( $content );
                        }
                        break;
                    case 'cc':
                        $cc = array_merge( (array) $cc, explode( ',', $content ) );
                        break;
                    case 'bcc':
                        $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
                        break;
                    default:
                        // Add it to our grand headers array
                        $headers[trim( $name )] = trim( $content );
                        break;
                }
            }
        }
    }

    // Empty out the values that may be set
    $phpmailer->ClearAllRecipients();
    $phpmailer->ClearAttachments();
    $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 ) )
        $from_name = 'WordPress';

    /* If we don't have an email from the input headers default to wordpress@$sitename
     * Some hosts will block outgoing mail from this address if it doesn't exist but
     * there's no easy alternative. Defaulting to admin_email might appear to be another
     * option but some hosts may refuse to relay mail from an unknown domain. See
     * https://core.trac.wordpress.org/ticket/5007.
     */

    if ( !isset( $from_email ) ) {
        // Get the site domain and get rid of www.
        $sitename = strtolower( $_SERVER['SERVER_NAME'] );
        if ( substr( $sitename, 0, 4 ) == 'www.' ) {
            $sitename = substr( $sitename, 4 );
        }

        $from_email = 'wordpress@' . $sitename;
    }

    /**
     * Filter the email address to send from.
     *
     * @since 2.2.0
     *
     * @param string $from_email Email address to send from.
     */
    $phpmailer->From = apply_filters( 'wp_mail_from', $from_email );

    /**
     * Filter the name to associate with the "from" email address.
     *
     * @since 2.3.0
     *
     * @param string $from_name Name associated with the "from" email address.
     */
    $phpmailer->FromName = apply_filters( 'wp_mail_from_name', $from_name );

    // Set destination addresses
    if ( !is_array( $to ) )
        $to = explode( ',', $to );

    foreach ( (array) $to as $recipient ) {
        try {
            // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
            $recipient_name = '';
            if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                if ( count( $matches ) == 3 ) {
                    $recipient_name = $matches[1];
                    $recipient = $matches[2];
                }
            }
            $phpmailer->AddAddress( $recipient, $recipient_name);
        } catch ( phpmailerException $e ) {
            continue;
        }
    }

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );

    // Set the content-type and charset

    /**
     * Filter the default wp_mail() charset.
     *
     * @since 2.3.0
     *
     * @param string $charset Default email charset.
     */
    $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );

    // Set mail's subject and body
    $phpmailer->Subject = $subject;

    if ( is_string($message) ) {
        $phpmailer->Body = $message;

        // 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';

        /**
         * Filter 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 ($type === 'text/html') {
                    $phpmailer->Body = $body;
                }
                elseif ($type === 'text/plain') {
                    $phpmailer->AltBody = $body;
                }
                else {
                    $phpmailer->AddAttachment($body, '', 'base64', $type);
                }
            }
        }
    }

    // Add any CC and BCC recipients
    if ( !empty( $cc ) ) {
        foreach ( (array) $cc as $recipient ) {
            try {
                // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
                $recipient_name = '';
                if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                    if ( count( $matches ) == 3 ) {
                        $recipient_name = $matches[1];
                        $recipient = $matches[2];
                    }
                }
                $phpmailer->AddCc( $recipient, $recipient_name );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    if ( !empty( $bcc ) ) {
        foreach ( (array) $bcc as $recipient) {
            try {
                // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
                $recipient_name = '';
                if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                    if ( count( $matches ) == 3 ) {
                        $recipient_name = $matches[1];
                        $recipient = $matches[2];
                    }
                }
                $phpmailer->AddBcc( $recipient, $recipient_name );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    // 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 ) ) {
        foreach ( $attachments as $attachment ) {
            try {
                $phpmailer->AddAttachment($attachment);
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    /**
     * Fires after PHPMailer is initialized.
     *
     * @since 2.2.0
     *
     * @param PHPMailer &$phpmailer The PHPMailer instance, passed by reference.
     */
    do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );

    // Send!
    try {
        return $phpmailer->Send();
    } catch ( phpmailerException $e ) {
        return false;
    }
}

Vì vậy, nếu bạn đặt nó trong tệp "wp-content / mu-plugins / tests.php" của bạn, thì nó sẽ ghi đè lên phiên bản WP. Nó có một cách sử dụng tốt mà không có bất kỳ sự lộn xộn nào với các tiêu đề, ví dụ:

// Set $to to an hotmail.com or outlook.com email
$to = "YourEmail@hotmail.com";

$subject = 'wp_mail testing multipart';

$message['text/plain'] = 'Hello world! This is plain text...';
$message['text/html'] = '<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<p>Hello World! This is HTML...</p> 

</body>
</html>';

add_filter( 'wp_mail_from', $from_func = function ( $from_email ) { return 'foo@bar.com'; } );
add_filter( 'wp_mail_from_name', $from_name_func = function ( $from_name ) { return 'Foo'; } );

// send email
wp_mail( $to, $subject, $message );

remove_filter( 'wp_mail_from', $from_func );
remove_filter( 'wp_mail_from_name', $from_name_func );

Xin lưu ý rằng tôi đã không kiểm tra điều này với các email thực tế ...


Tôi đã thêm cái này để có plugin và chạy mã kiểm tra; nó đã làm việc. Tôi đã thử nghiệm các thông báo lõi mặc định (thông báo người dùng mới, v.v.) và nó cũng hoạt động. Tôi sẽ tiếp tục thực hiện các thử nghiệm vào cuối tuần này và xem các plugin sẽ hoạt động như thế nào và về cơ bản nếu mọi thứ hoạt động. Tôi sẽ xem xét cụ thể dữ liệu thô của tin nhắn. Đây sẽ là một nhiệm vụ rất tốn thời gian nhưng yên tâm, tôi sẽ báo cáo lại khi hoàn thành. Nếu có một kịch bản trong đó wp_mail () sẽ không hoạt động (khi không nên), vui lòng cho tôi biết. Cảm ơn bạn cho câu trả lời này.
Christine Cooper

Công cụ tốt, tôi đã chú ý đến đầu ra và nó có vẻ tốt - trên thực tế, bản vá chỉ khiến wp_mail sử dụng quy trình xử lý khối đá tiêu chuẩn của PHPMailer trong trường hợp chuyển một mảng và mặc định là mặc định cho công cụ WP tinh ranh (để tương thích ngược) vì vậy nó sẽ tốt (rõ ràng là kudos ở đây sẽ đến với các tác giả bản vá) ... Tôi sẽ sử dụng nó từ bây giờ (và cuối cùng là phù hợp với retro) - và cảm ơn vì đã cung cấp lại thông tin bằng cách sử dụng cả html / plain giảm khả năng bị truy quét là thư rác ...
bonger

1
Chúng tôi đã thử nghiệm nó trong tất cả các kịch bản có thể và nó hoạt động rất tốt. Chúng tôi sẽ bắn ra một bản tin vào ngày mai và chúng tôi sẽ xem liệu chúng tôi có nhận được bất kỳ khiếu nại nào từ người dùng không. Những thay đổi nhỏ duy nhất chúng ta cần thực hiện là vệ sinh / hủy kích hoạt mảng khi nó được chèn vào db (có các thông báo trong một hàng đợi trong db nơi một cron gửi nó ra trong các khối nhỏ). Tôi sẽ cho phép câu hỏi này vẫn mở và chờ xử lý cho đến khi hết tiền thưởng để chúng tôi có thể mang lại nhận thức về vấn đề này. Hy vọng, bản vá này, hoặc một sự thay thế sẽ được thêm vào cốt lõi. Hoặc quan trọng hơn, tại sao không. Họ đang nghĩ gì vậy!
Christine Cooper

Tôi ngẫu nhiên nhận thấy rằng bạn đã thực hiện cập nhật cho vé trac được liên kết. Đây có phải là một bản cập nhật cho mã này? Nếu vậy, bạn có thể vui lòng đăng cập nhật này bằng cách chỉnh sửa câu trả lời của bạn ở đây để câu trả lời này vẫn tiếp tục không? Cảm ơn nhiều.
Christine Cooper

Xin chào, không, nó chỉ là một bản làm mới của bản vá với thân cây hiện tại để nó hợp nhất mà không có xung đột (với hy vọng nó sẽ được chú ý), mã này hoàn toàn giống nhau ...
bonger

4

Đây thực sự không phải là một lỗi WordPress, nó là một phpmailerlỗi không cho phép các tiêu đề tùy chỉnh ... nếu bạn nhìn vào class-phpmailer.php:

public function getMailMIME()
{
    $result = '';
    $ismultipart = true;
    switch ($this->message_type) {
        case 'inline':
            $result .= $this->headerLine('Content-Type', 'multipart/related;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        case 'attach':
        case 'inline_attach':
        case 'alt_attach':
        case 'alt_inline_attach':
            $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        case 'alt':
        case 'alt_inline':
            $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        default:
            // Catches case 'plain': and case '':
            $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
            $ismultipart = false;
            break;
    }

Bạn có thể thấy trường hợp mặc định vi phạm là những gì đang xuất ra dòng tiêu đề bổ sung với bộ ký tự và không có ranh giới. Thiết lập các kiểu nội dung bởi bộ lọc không giải quyết điều này bằng cách riêng của mình chỉ vì những alttrường hợp ở đây được thiết lập trên message_typebằng cách kiểm tra AltBodykhông có sản phẩm nào chứ không phải là loại nội dung.

protected function setMessageType()
{
    $type = array();
    if ($this->alternativeExists()) {
        $type[] = 'alt';
    }
    if ($this->inlineImageExists()) {
        $type[] = 'inline';
    }
    if ($this->attachmentExists()) {
        $type[] = 'attach';
    }
    $this->message_type = implode('_', $type);
    if ($this->message_type == '') {
        $this->message_type = 'plain';
    }
}

public function alternativeExists()
{
    return !empty($this->AltBody);
}

Cuối cùng, điều này có nghĩa là ngay khi bạn đính kèm tệp hoặc hình ảnh nội tuyến hoặc đặt AltBodylỗi, lỗi vi phạm sẽ được bỏ qua. Nó cũng có nghĩa là không cần phải thiết lập một cách rõ ràng kiểu nội dung vì càng sớm càng có một AltBodynó được thiết lập để multipart/alternativebằng phpmailer.

Vì vậy, câu trả lời đơn giản là:

add_action('phpmailer_init','wp_mail_set_text_body');
function wp_mail_set_text_body($phpmailer) {
     if (empty($phpmailer->AltBody)) {$phpmailer->AltBody = strip_tags($phpmailer->Body);}
}

Sau đó, bạn không cần phải đặt tiêu đề một cách rõ ràng, bạn chỉ cần làm:

 $message ='<html>
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 </head>
 <body>
     <p>Hello World! This is HTML...</p> 
 </body>
 </html>';

 wp_mail($to,$subject,$message);

Thật không may, nhiều chức năng và thuộc tính trong phpmailerlớp được bảo vệ, nếu không, điều đó thay thế hợp lệ sẽ chỉ đơn giản là kiểm tra và ghi đè lên thuộc MIMEHeaderstính thông qua phpmailer_inithook trước khi gửi.


2

Tôi vừa phát hành một plugin để cho phép người dùng sử dụng các mẫu html trên WordPress và tôi đang chơi ngay trên phiên bản dev để thêm một dự phòng văn bản đơn giản. Tôi đã làm như sau và trong các thử nghiệm của mình, tôi chỉ thấy một ranh giới được thêm vào và email đang chuyển đến Hotmail.

add_action( 'phpmailer_init', array($this->mailer, 'send_email' ) );

/**
* Modify php mailer body with final email
*
* @since 1.0.0
* @param object $phpmailer
*/
function send_email( $phpmailer ) {

    $message            =  $this->add_template( apply_filters( 'mailtpl/email_content', $phpmailer->Body ) );
    $phpmailer->AltBody =  $this->replace_placeholders( strip_tags($phpmailer->Body) );
    $phpmailer->Body    =  $this->replace_placeholders( $message );
}

Vì vậy, về cơ bản những gì tôi làm ở đây là sửa đổi đối tượng phpmailer, tải thông báo bên trong một mẫu html và đặt nó vào thuộc tính Body. Ngoài ra tôi lấy thông điệp ban đầu và đặt thuộc tính AltBody.


2

Giải pháp đơn giản của tôi là sử dụng html2text https://github.com/soundas ngủ / html2text theo cách này:

add_action( 'phpmailer_init', 'phpmailer_init' );

//http://wordpress.stackexchange.com/a/191974
//http://stackoverflow.com/a/2564472
function phpmailer_init( $phpmailer )
{
  if( $phpmailer->ContentType == 'text/html' ) {
    $phpmailer->AltBody = Html2Text\Html2Text::convert( $phpmailer->Body );
  }
}

Tại đây https://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 cũng là một ý chính về.


2

Đối với bất kỳ ai đang sử dụng hook 'phpmailer_init' để thêm 'AltBody' của riêng họ:

Phần thân văn bản thay thế được sử dụng lại cho các thư liên tiếp khác nhau được gửi, trừ khi bạn xóa thủ công! WordPress không xóa nó trong wp_mail () vì nó không mong muốn thuộc tính này được sử dụng.

Điều này dẫn đến việc người nhận có khả năng nhận thư không dành cho họ. May mắn thay, hầu hết mọi người sử dụng ứng dụng thư khách hỗ trợ HTML sẽ không thấy phiên bản văn bản, nhưng về cơ bản, đây vẫn là một vấn đề bảo mật.

May mắn thay, có một sửa chữa dễ dàng. Điều này bao gồm bit thay thế altbody; lưu ý rằng bạn cần thư viện PHP Html2Text:

add_filter( 'wp_mail', 'wpse191923_force_phpmailer_reinit_for_multiple_mails', -1 );
function wpse191923_force_phpmailer_reinit_for_multiple_mails( $wp_mail_atts ) {
  global $phpmailer;

  if ( $phpmailer instanceof PHPMailer && $phpmailer->alternativeExists() ) {
    // AltBody property is set, so WordPress must already have used this
    // $phpmailer object just now to send mail, so let's
    // clear the AltBody property
    $phpmailer->AltBody = '';
  }

  // Return untouched atts
  return $wp_mail_atts;
}

add_action( 'phpmailer_init', 'wpse191923_phpmailer_init_altbody', 1000, 1 );
function wpse191923_phpmailer_init_altbody( $phpmailer ) {
  if ( ( $phpmailer->ContentType == 'text/html' ) && empty( $phpmailer->AltBody ) ) {
    if ( ! class_exists( 'Html2Text\Html2Text' ) ) {
      require_once( 'Html2Text.php' );
    }
    if ( ! class_exists( 'Html2Text\Html2TextException' ) ) {
      require_once( 'Html2TextException.php' );
    }
    $phpmailer->AltBody = Html2Text\Html2Text::convert( $phpmailer->Body );
  }
}

Đây cũng là một ý chính cho một plugin WP tôi đã sửa đổi để khắc phục vấn đề này: https://gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

Thật không may, tôi không thể nhận xét về các giải pháp khác bằng cách sử dụng móc nói trên, để cảnh báo cho họ về điều này, vì tôi chưa có đủ đại diện để nhận xét.


1

đây có thể không phải là một câu trả lời chính xác cho bài viết ban đầu ở đây, nhưng nó là một giải pháp thay thế cho một số giải pháp ở đây được cung cấp liên quan đến việc đặt thân máy thay thế

về cơ bản, tôi cần (muốn) thiết lập một altbody riêng biệt (ví dụ như văn bản gốc) bổ sung cho phần html thay vì dựa vào một số chuyển đổi / thoát y và không có gì. Vì vậy, tôi đã đưa ra điều này dường như chỉ hoạt động tốt

/* setting the message parts for wp_mail()*/
$markup = array();
$markup['html'] = '<html>some html</html>';
$markup['plaintext'] = 'some plaintext';
/* message we are sending */    
$message = maybe_serialize($markup);


/* setting alt body distinctly */
add_action('phpmailer_init', array($this, 'set_alt_mail_body'));

function set_alt_mail_body($phpmailer){
    if( $phpmailer->ContentType == 'text/html' ) {
        $body_parts = maybe_unserialize($phpmailer->Body);

        if(!empty($body_parts['html'])){
            $phpmailer->MsgHTML($body_parts['html']);
        }

        if(!empty($body_parts['plaintext'])){
            $phpmailer->AltBody = $body_parts['plaintext'];
        }
    }   
}

0

Nếu bạn không muốn tạo bất kỳ xung đột mã nào trong lõi Wordpress, tôi nghĩ giải pháp thay thế hoặc đơn giản nhất là thêm hành động vào phpmailer_initđó sẽ thực hiện trước khi gửi thư thực tế trong wp_mail. Để đơn giản hóa lời giải thích của tôi, xem ví dụ mã dưới đây:

<?php 

$to = '';
$subject = '';
$from = '';
$body = 'The text html content, <html>...';

$headers = "FROM: {$from}";

add_action( 'phpmailer_init', function ( $phpmailer ) {
    $phpmailer->AltBody = 'The text plain content of your original text html content.';
} );

wp_mail($to, $subject, $body, $headers);

Nếu bạn thêm một nội dung trong thuộc tính lớp PHPMailer AltBodythì loại nội dung mặc định sẽ tự động được đặt thành multipart/alternative.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.