wp_mail()を介してマルチパート(テキスト/ html)メールを送信すると、ドメインが禁止される可能性があります


37

概要

そのためWPコアのバグのため、送信マルチパートで電子メール(HTML /テキスト))(wp_mail(スパムフォルダで終わるメールの可能性を減らすために)します皮肉なことに、あなたのドメインがホットメール(およびその他のMicrosoftの電子メール)によってブロックされているとなります。

これは複雑な問題であり、最終的にコアに実装される可能性のある実行可能なソリューションを誰かが見つけられるように、詳細に分析することを目指します。

やりがいのある読み物になるでしょう。さぁ、始めよう...

不具合

ニュースレターの電子メールがスパムフォルダーになってしまうことを避けるための最も一般的なアドバイスは、マルチパートメッセージを送信することです。

マルチパート(MIME)は、1つの電子メールで電子メールメッセージのHTML部分とTEXT部分の両方を送信することを指します。クライアントがマルチパートメッセージを受信すると、HTMLをレンダリングできる場合はHTMLバージョンを受け入れ、そうでない場合はプレーンテキストバージョンを提示します。

これは機能することが証明されています。Gmailに送信すると、メインの受信トレイに到達したときにメッセージをマルチパートに変更するまで、すべての電子メールがスパムフォルダーに到着しました。素晴らしいもの。

現在、wp_mail()を介してマルチパートメッセージを送信する場合、コンテンツタイプ(multipart / *)を2回出力します。この動作は、すべての Microsoft(Hotmail、Outlookなど)を含む一部の電子メールでは生のメッセージとして表示され、マルチパートではない電子メールで発生します

Microsoftはこのメッセージに迷惑メールのフラグを付け、受信したいくつかのメッセージには受信者が手動でフラグを付けます。残念ながら、Microsoftの電子メールアドレスは広く使用されています。サブスクライバーの40%が使用しています。

これは、Microsoftが最近行ったメール交換で確認されています。

ドメインが完全にブロックされると、メッセージにフラグが立てられます。つまり、メッセージはスパムフォルダーに送信されず、受信者にも配信されません

これまでにメインドメインを3回ブロックしました。

これはWPコアのバグであるため、マルチパートメッセージを送信するすべてのドメインがブロックされています。問題は、ほとんどのウェブマスターが理由を知らないことです。私は調査を行い、他のユーザーがフォーラムなどでこれを議論しているのを見て、これを確認しました。それは生のコードを掘り下げ、これらのタイプの電子メールメッセージがどのように機能するかについての十分な知識が必要です。

コードに分解しましょう

hotmail / outlookアカウントを作成します。次に、次のコードを実行します。

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

デフォルトのコンテンツタイプを変更する場合は、次を使用します。

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

これにより、マルチパートメッセージが送信されます。

したがって、メッセージの未加工のソース全体を確認すると、コンテンツタイプが2回追加され、境界なしで1回追加されていることがわかります。

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

それが問題です。

問題の原因はpluggable.php次のとおりです。ここを参照してください。

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

潜在的なソリューション

あなたは不思議に思っていますなぜあなたはtracでこれを報告しなかったのですか?私はすでに持っています。驚いたことに、同じ問題の概要を説明する別のチケットが5年前に作成されました。

それに直面しましょう、それは半十年です。インターネットの年では、それは30に近いです。問題は明らかに放棄されており、基本的に修正されることはありません(...ここで解決しない限り)。

私はここで解決策を提供するすばらしいスレッドを見つけましたが、彼の解決策は機能しますが、カスタム$headersセットを持たない電子メールを中断します。

そこが毎回クラッシュする場所です。マルチパートバージョンは正常に$headers機能し、通常の未設定メッセージは機能しないか、またはその逆です。

私たちが思いついた解決策は:

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

}

はい、私は知っています、コアファイルの編集はタブーで、座ってください...これは必死の修正であり、コアの修正を提供する試みは貧弱でした。

修正の問題は、新規登録、コメント、パスワードのリセットなどのデフォルトのメールが空白のメッセージとして配信されることです。したがって、マルチパートメッセージを送信するがそれ以外は送信しないwp_mail()スクリプトが動作します。

何をすべきか

ここでの目的は、コアwp_mail()関数(カスタムsendmail関数ではない)を使用して、通常(プレーンテキスト)とマルチパートメッセージの両方を送信する方法を見つけることです。

これを解決しようとすると、遭遇する主な問題は、ダミーメッセージの送信、受信したかどうかの確認、基本的にアスピリンの箱を開けてMicrosoftに呪いをかけることに費やす時間ですIEは、ここのグレムリンは残念ながらWordPressですが、問題があります。

更新

@bongerによって投稿されたソリューションでは、$messageコンテンツタイプのキー付き代替を含む配列を使用できます。すべてのシナリオで機能することを確認しました。

この問題は、問題についての認識を高めるために賞金が尽きるまで、おそらく問題が核心で修正されるレベルまで、この質問を開いたままにしておきます$message文字列にできる代替ソリューションをお気軽に投稿してください。


1
wp_mail()機能はプラグイン可能である(WP-コンテンツ/ MU-プラグインで)必見使用プラグインではない良いあなたのためのソリューション(および他の皆、失敗コアフィックス)として、あなたの交換を定義していませんか?どのような場合、設定後に$phpmailer->ContentType = $content_type;(エルシングではなく)マルチパート/境界チェックが移動しないのですか?
ボンガー

@bongerソリューションの詳細を回答してください。
クリスティーンクーパー

1
wp_mailプラグイン可能であるため、コアを編集する必要はありません。プラグインの元の関数をコピーし、必要に応じて編集し、プラグインをアクティブにします。WordPressは、コアを編集する必要なく、元の関数ではなく編集した関数を使用します。
gmazzap

私はあなたがテストは、このような王室の痛みであると言うが、パッチを見ているとしてこれを行うことを躊躇@ChristineCooper core.trac.wordpress.org/ticket/15448 @ rmccue / @ MattyRobによってtracの中で示唆したルックスは本当に良い方法へのそれに基づいて未テストの回答を投稿します
...-ボンガー

2
@ChristineCooper phpmailerに単純にフックし、$ phpmailer-> AltBodyでテキスト本文を設定すると、同じエラーが発生しますか?
chifliiiii

回答:


15

次のバージョンはwp_mail()、チケットhttps://core.trac.wordpress.org/ticket/15448で@ rmccue / @ MattyRobのパッチが適用されており、4.2.2に更新され$messageており、content-typeを含む配列にすることができますキー付き代替:

/**
 * 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;
    }
}

そのため、たとえば「wp-content / mu-plugins / functions.php」ファイルに配置すると、WPバージョンが上書きされます。ヘッダーをいじることのない便利な使い方があります。例えば:

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

私は実際のメールでこれをテストしていないことに注意してください...


プラグインが必要なためにこれを追加し、テストコードを実行しました。出来た。デフォルトのコア通知(新規ユーザー通知など)をテストしましたが、それも機能しました。今週末も引き続きテストを実行し、プラグインがどのように機能するか、基本的にすべてが機能するかどうかを確認します。具体的には、メッセージの生データを調べます。これは非常に時間のかかる作業になりますが、安心してください。完了したら報告します。wp_mail()が機能しないシナリオがある場合(そうでない場合)、お知らせください。この答えをありがとう。
クリスティーンクーパー

良いもの、私は出力を目で見て見た目が良い-実際、このパッチは配列を渡す場合にwp_mailがPHPMailerの標準的な堅実な処理を使用するようにします。だからそれは良いはずです(明らかにここの称賛はパッチの作者に行きます)...私はこれから使用します(そして最終的にレトロフィットします)-そしてhtml / plainの両方を使用して情報を再利用してくれてありがとう...スパムとしてタールを塗ったことの可能性を減らす
bonger

1
考えられるすべてのシナリオでテストしており、うまく機能しています。明日、ニュースレターを発行し、ユーザーから苦情が寄せられるかどうかを確認します。必要なマイナーな変更は、dbに挿入するときに配列をサニタイズ/サニタイズ解除することだけです(cronが小さなバルクで送信するdbのqueにメッセージがあります)。賞金が尽きるまでこの質問を開いたまま保留にして、この問題を認識できるようにします。うまくいけば、このパッチまたは代替がコアに追加されます。または、もっと重要なのは、なぜですか。彼らは何を考えている!
クリスティーンクーパー

リンクされたtracチケットの更新を実行したことにランダムに気付きました。これはこのコードの更新ですか?もしそうなら、この回答を最新の状態に保つために、ここでも回答を編集して、この更新を親切に投稿してください。どうもありがとうございました。
クリスティーンクーパー

こんにちは、いやそれは...それは(それはいくつかの注目を集めての希望で)競合せずにマージするように、コードはまったく同じである現在のトランクに対するパッチのちょうどリフレッシュした
bonger

4

これは本当に、すべてのWordPressのバグではありませんが、それはphpmailerあなたが見ればカスタムヘッダーのために許可していないに1人が... 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;
    }

問題のあるデフォルトのケースは、charsetを使用して境界のない追加のヘッダー行を出力していることです。フィルタによって、コンテンツタイプを設定するだけであるため、それ自体でこれを解決しないalt場合は、ここで設定されてmessage_typeチェックすることにより、AltBodyコンテンツタイプではなく、空ではありません。

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

最後に、これが意味することは、ファイルまたはインラインイメージを添付するか、を設定するとすぐAltBodyに、問題のあるバグをバイパスする必要があることです。また、そこにあるや否やため、明示的にコンテンツタイプを設定する必要はありませんを意味しAltBody、それが設定されているmultipart/alternativeことではphpmailer

簡単な答えは次のとおりです。

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

次に、ヘッダーを明示的に設定する必要はありません。単純に行うことができます:

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

残念ながら、phpmailerクラス内の多くの関数とプロパティは保護されています。そうでない場合、有効な代替手段はMIMEHeadersphpmailer_init送信する前にフックを介してプロパティを単純にチェックしてオーバーライドすることです。


2

私はプラグインをリリースし、ユーザーが上で今のWordPressとイム演奏上のHTMLテンプレートを使用できるようにするのdevのバージョンの単純なテキストのフォールバックを追加します。私は次のことを行いましたが、テストでは境界が1つしか追加されておらず、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 );
}

基本的にここで行うことは、phpmailerオブジェクトを変更し、htmlテンプレート内にメッセージをロードして、Bodyプロパティに設定することです。また、元のメッセージを取得し、AltBodyプロパティを設定しました。


2

私の簡単な解決策は、この方法でhtml2text https://github.com/soundasleep/html2textを使用することです。

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

ここhttps://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80も要点です。


2

「phpmailer_init」フックを使用して独自の「AltBody」を追加する場合:

代替テキスト本文は、手動でクリアしない限り、送信される連続した異なるメールに再利用されます!WordPressはwp_mail()でこのプロパティを使用しないことを想定していないため、クリアしません。

これにより、受信者は自分宛ではないメールを受信する可能性があります。幸いなことに、HTML対応のメールクライアントを使用しているほとんどの人はテキストバージョンを見ることができませんが、それでも基本的にはセキュリティの問題です。

幸いなことに、簡単な修正があります。これには、altbody交換ビットが含まれます。Html2Text PHPライブラリが必要なことに注意してください。

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

この問題を修正するために修正したWPプラグインの要点もここにありますhttps : //gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

残念ながら、前述のフックを使用して他のソリューションについてコメントすることはできません。これについて警告するには、まだコメントするのに十分な担当者がいないためです。


1

これはここの最初の投稿に対する正確な答えではないかもしれませんが、代替ボディの設定に関してここで提供されているいくつかのソリューションの代替案です

基本的に、私はいくつかの変換/ストリップタグなどに頼るのではなく、html部分に別個のaltbody(すなわちプレーンテキスト)を追加で設定する必要がありました。だから私はこれを思いついたが、うまくいくようだ

/* 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

Wordpressコアでコードの競合を作成したくない場合、代替または最も簡単な解決策はphpmailer_init、実際にメールを送信する前にアクションを追加することだと思いwp_mailます。説明を簡単にするために、以下のコード例を参照してください。

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

PHPMailerクラスAltBodyプロパティにコンテンツを追加すると、デフォルトのコンテンツタイプは自動的にに設定されmultipart/alternativeます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.