Wenn Sie mehrteilige (Text- / HTML-) E-Mails über wp_mail () senden, wird Ihre Domain wahrscheinlich gesperrt


37

Zusammenfassung

Aufgrund eines Fehlers in WP Core führt das Senden von mehrteiligen E-Mails (HTML / Text) mit wp_mail () (um die Wahrscheinlichkeit zu verringern, dass E-Mails in Spam-Ordnern landen) ironischerweise dazu, dass Ihre Domain von Hotmail (und anderen Microsoft-E-Mails) blockiert wird.

Dies ist ein komplexes Problem, das ich detailliert aufschlüsseln möchte, um jemandem zu helfen, eine praktikable Lösung zu finden, die eventuell im Kern implementiert wird.

Es wird eine lohnende Lektüre. Lass uns anfangen...

Der Käfer

Der häufigste Rat, um zu vermeiden, dass Ihre Newsletter-E-Mails in Spam-Ordnern landen, ist das Senden von mehrteiligen Nachrichten.

Mehrteilig (MIME) bezeichnet das Senden eines HTML- und eines TEXT-Teils einer E-Mail-Nachricht in einer einzigen E-Mail. Wenn ein Client eine mehrteilige Nachricht empfängt, akzeptiert er die HTML-Version, wenn er HTML rendern kann, andernfalls wird die Nur-Text-Version angezeigt.

Dies hat sich bewährt. Beim Senden an Google Mail landeten alle unsere E-Mails in Spam-Ordnern, bis wir die Nachrichten in mehrteilig geändert haben, als sie in den Haupteingang eingingen. Tolles Zeug.

Wenn Sie jetzt mehrteilige Nachrichten über wp_mail () senden, wird der Inhaltstyp (multipart / *) zweimal ausgegeben, einmal mit Begrenzung (falls benutzerdefiniert festgelegt) und einmal ohne Begrenzung. Dieses Verhalten führt dazu, dass die E-Mail in einigen E-Mails, einschließlich aller Microsoft- E-Mails (Hotmail, Outlook usw.) , als unformatierte Nachricht und nicht mehrteilig angezeigt wird.

Microsoft kennzeichnet diese Nachricht als Junk und die wenigen eingehenden Nachrichten werden vom Empfänger manuell gekennzeichnet. Leider sind Microsoft-E-Mail-Adressen weit verbreitet. 40% unserer Abonnenten nutzen es.

Dies wird von Microsoft über einen kürzlich durchgeführten E-Mail-Austausch bestätigt.

Das Markieren der Nachrichten führt dazu, dass die Domain vollständig blockiert wird . Dies bedeutet, dass die Nachricht nicht an den Spam-Ordner gesendet wird, sondern überhaupt nicht an den Empfänger übermittelt wird .

Wir haben unsere Hauptdomain bisher dreimal blockieren lassen.

Da dies ein Fehler im WP-Core ist, wird jede Domäne, die mehrteilige Nachrichten sendet, blockiert. Das Problem ist, dass die meisten Webmaster nicht wissen warum. Ich habe dies bestätigt, als ich meine Nachforschungen anstellte und gesehen habe, wie andere Benutzer dies in Foren usw. diskutierten. Dazu muss ich mich mit dem Rohcode befassen und über die Funktionsweise dieser Art von E-Mail-Nachrichten Bescheid wissen, die wir als nächstes behandeln werden.

Lassen Sie es uns in Code zerlegen

Erstellen Sie ein Hotmail- / Outlook-Konto. Führen Sie dann den folgenden Code aus:

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

Und wenn Sie den Standardinhaltstyp ändern möchten , verwenden Sie:

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

Dadurch wird eine mehrteilige Nachricht gesendet.

Wenn Sie also die vollständige Rohdatenquelle der Nachricht überprüfen, werden Sie feststellen, dass der Inhaltstyp zweimal hinzugefügt wird, einmal ohne Begrenzung:

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

Das ist das Problem.

Die Ursache des Problems liegt in pluggable.php- wenn wir hier nachsehen:

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

Potentielle Lösungen

Sie fragen sich also, warum Sie dies nicht bei trac gemeldet haben ? Habe ich schon . Zu meiner großen Überraschung wurde vor 5 Jahren ein anderes Ticket erstellt, das das gleiche Problem beschreibt.

Seien wir ehrlich, es ist ein halbes Jahrzehnt her. In Internetjahren ist das eher 30. Das Problem wurde eindeutig aufgegeben und wird im Grunde nie behoben (... es sei denn, wir lösen es hier).

Ich habe hier einen tollen Thread gefunden , der eine Lösung anbietet, aber während seine Lösung funktioniert, werden E-Mails ohne benutzerdefinierten $headersSatz unterbrochen .

Dort stürzen wir jedes Mal ab. Entweder funktioniert die mehrteilige Version einwandfrei, und normale nicht festgelegte $headersNachrichten funktionieren nicht, oder Sie lesen einen Vers.

Die Lösung, die wir gefunden haben, war:

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

}

Ja, ich weiß, das Bearbeiten von Core-Dateien ist tabu, setzen Sie sich zurück ... Dies war eine verzweifelte Korrektur und ein schlechter Versuch, eine Korrektur für Core bereitzustellen.

Das Problem mit unserem Fix ist, dass Standard-E-Mails wie Neuanmeldungen, Kommentare, Zurücksetzen von Passwörtern usw. als leere Nachrichten zugestellt werden. Wir haben also ein funktionierendes wp_mail () Skript, das mehrteilige Nachrichten sendet, aber sonst nichts.

Was ist zu tun

Ziel ist es, mit der Kernfunktion wp_mail () (keine benutzerdefinierte sendmail-Funktion) sowohl normale ( Nur- Text-) als auch mehrteilige Nachrichten zu senden .

Wenn Sie versuchen, dieses Problem zu lösen, wird das Hauptproblem die Zeit sein, die Sie für das Senden von Dummy-Nachrichten, das Überprüfen des Empfangs und das Öffnen einer Schachtel mit Aspirin und das Anfluchen von Microsoft aufwenden müssen, weil Sie an deren gewöhnt sind IE Probleme, während der Gremlin hier leider WordPress ist.

Aktualisieren

Die von @bonger bereitgestellte Lösung kann $messageein Array sein, das Alternativen mit Inhaltstyp enthält. Ich habe bestätigt, dass es in allen Szenarien funktioniert.

Wir werden zulassen, dass diese Frage offen bleibt, bis das Kopfgeld aufgebraucht ist, um das Bewusstsein für das Problem zu schärfen, möglicherweise bis zu einem Punkt, an dem es im Kern behoben wird. Fühlen Sie sich frei, eine alternative Lösung zu posten, bei der es $messagesich um eine Zeichenfolge handeln kann.


1
Da die wp_mail()Funktion steckbar ist, wird Ihr Ersatz nicht als ein Muss-Plugin definiert (in wp-content / mu-plugins). Dies ist keine gute Lösung für Sie (und alle anderen, die Core Fix nicht ausführen). In welchem ​​Fall würde es $phpmailer->ContentType = $content_type;nicht funktionieren, wenn die Multipart- / Boundary-Prüfung nach dem Festlegen (und nicht nach dem Elsing) nicht verschoben würde?
Bonger

@bonger Kannst du bitte eine Antwort auf deine Lösung schreiben?
Christine Cooper

1
Yo braucht nicht zu bearbeiten Kern, weil das wp_mailist steckbar . Kopiere die ursprüngliche Funktion in ein Plugin, bearbeite sie nach Bedarf und aktiviere das Plugin. WordPress verwendet Ihre bearbeitete Funktion anstelle der ursprünglichen, ohne dass Sie den Kern bearbeiten müssen.
gmazzap

@ChristineCooper Ich zögere, dies zu tun, da Sie sagen, dass das Testen solch ein königlicher Schmerz ist, aber wenn Sie sich den von @ rmccue / @ MattyRob in trac vorgeschlagenen Patch core.trac.wordpress.org/ticket/15448 ansehen, sieht das wirklich gut aus Ich werde eine ungetestete Antwort auf dieser Grundlage veröffentlichen ...
Bonger

2
@ChristineCooper Wenn Sie sich einfach in phpmailer einhängen und den Textkörper in $ phpmailer-> AltBody setzen, passiert der gleiche Fehler?
Chifliiiii

Antworten:


15

Die folgende Version von enthält wp_mail()den Patch von @ rmccue / @ MattyRob im Ticket https://core.trac.wordpress.org/ticket/15448 , der für 4.2.2 aktualisiert wurde $messageund ein Array mit dem Inhaltstyp sein kann getastete Alternativen:

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

Wenn Sie das also in Ihre Datei "wp-content / mu-plugins / functions.php" einfügen, wird die WP-Version überschrieben. Es hat eine nette Verwendung, ohne mit Überschriften herumzuspielen, zB:

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

Bitte beachten Sie, dass ich dies nicht mit aktuellen E-Mails getestet habe ...


Ich habe dies hinzugefügt, um Plugins zu haben, und den Testcode ausgeführt. es funktionierte. Ich habe die Standardkernbenachrichtigungen (Benachrichtigung für neue Benutzer usw.) getestet und es hat auch funktioniert. Ich werde dieses Wochenende weiterhin Tests durchführen und sehen, wie Plugins damit funktionieren und ob im Grunde alles funktioniert. Ich werde speziell die Rohdaten der Nachricht durchsehen. Dies wird eine sehr zeitaufwendige Aufgabe sein, aber seien Sie versichert, ich melde mich, wenn Sie fertig sind. Wenn es ein Szenario gibt, in dem wp_mail () nicht funktioniert (wenn es sonst sollte), lassen Sie es mich bitte wissen. Vielen Dank für diese Antwort.
Christine Cooper

Gutes Zeug, ich habe die Ausgabe in die Augen geschlossen und sie sieht gut aus - in der Tat bewirkt der Patch, dass wp_mail im Falle des Übergebens eines Arrays nur PHPMailers Standard-Solid-Verarbeitung verwendet, und ansonsten standardmäßig das zweifelhafte WP-Zeug (aus Gründen der Abwärtskompatibilität). so sollte es gut sein (offensichtlich ein großes Lob an die Patch-Autoren) ... Ich werde es von nun an verwenden (und irgendwann nachrüsten) - und danke zurück für die Information, dass wir beide html / plain verwenden Reduzieren Sie die Wahrscheinlichkeit, als Spam geteert zu werden ...
Bonger

1
Wir haben es in allen möglichen Szenarien getestet und es funktioniert großartig. Wir werden morgen einen Newsletter veröffentlichen und sehen, ob wir Beschwerden von Nutzern erhalten. Die einzigen geringfügigen Änderungen, die wir vornehmen mussten, waren die Bereinigung / Aufhebung der Bereinigung des Arrays beim Einfügen in die Datenbank (Nachrichten in einer Warteschlange in der Datenbank, in der ein Cron sie in kleinen Mengen sendet). Ich werde zulassen, dass diese Frage offen bleibt und noch offen ist, bis das Kopfgeld aufgebraucht ist, damit wir auf dieses Problem aufmerksam machen können. Hoffentlich wird dieser Patch oder eine Alternative zu Core hinzugefügt. Oder noch wichtiger, warum nicht. Was denken sie?
Christine Cooper

Ich habe zufällig bemerkt, dass Sie ein Update für das verknüpfte Trac-Ticket durchgeführt haben. Handelt es sich um eine Aktualisierung dieses Codes? Wenn ja, können Sie dieses Update bitte veröffentlichen, indem Sie Ihre Antwort ebenfalls hier bearbeiten, damit diese Antwort auf dem neuesten Stand bleibt? Vielen Dank.
Christine Cooper

Hallo, nein, es war nur eine Aktualisierung des Patches gegen den aktuellen Trunk, so dass er konfliktfrei verschmilzt (in der Hoffnung, dass ihm etwas Aufmerksamkeit geschenkt wird), der Code ist genau der gleiche ...
Bonger

4

Dies ist überhaupt kein WordPress-Fehler, sondern ein Fehler, bei dem phpmailerbenutzerdefinierte Header nicht berücksichtigt werden ... wenn Sie sich Folgendes ansehen 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;
    }

Sie können sehen, dass der fehlerhafte Standardfall die Ausgabe der zusätzlichen Kopfzeile mit Zeichensatz und ohne Begrenzung ist. Das Setzen des Inhaltstyps per Filter löst dies nicht von selbst, nur weil der althier eingestellte Fall message_typedurch Aktivieren AltBodynicht leer ist, sondern der Inhaltstyp.

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

Am Ende bedeutet dies AltBody, dass der Fehler umgangen werden sollte, sobald Sie eine Datei oder ein Inline-Bild anhängen oder das setzen . Dies bedeutet auch, dass der Inhaltstyp nicht explizit festgelegt werden muss, da er, sobald er vorhanden ist AltBody, multipart/alternativevon festgelegt wird phpmailer.

Die einfache Antwort lautet also:

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

Dann müssen Sie die Überschriften nicht explizit setzen, sondern können einfach Folgendes tun:

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

Leider sind viele der Funktionen und Eigenschaften in der phpmailerKlasse geschützt. Wenn dies nicht der Fall ist, besteht eine gültige Alternative darin, die MIMEHeadersEigenschaft phpmailer_initvor dem Senden einfach über den Hook zu überprüfen und zu überschreiben .


2

Ich habe gerade ein Plugin veröffentlicht , mit dem Benutzer HTML-Vorlagen für WordPress verwenden können und ich spiele gerade mit der Dev-Version , um einen einfachen Text-Fallback hinzuzufügen. Ich habe Folgendes ausgeführt und in meinen Tests wurde nur eine Grenze hinzugefügt, und E-Mails gehen einwandfrei bei Hotmail ein.

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

Im Grunde ändere ich hier also das phpmailer-Objekt, lade die Nachricht in eine HTML-Vorlage und setze sie auf die Body-Eigenschaft. Außerdem habe ich die ursprüngliche Nachricht genommen und die AltBody-Eigenschaft festgelegt.


2

Meine einfache Lösung besteht darin, html2text https://github.com/soundasleep/html2text folgendermaßen zu verwenden :

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

Hier https://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 auch eine Zusammenfassung.


2

Für alle, die den 'phpmailer_init'-Hook verwenden, um ihren eigenen' AltBody 'hinzuzufügen:

Der alternative Textkörper wird für verschiedene aufeinanderfolgende E-Mails wiederverwendet , es sei denn, Sie löschen ihn manuell! WordPress löscht es in wp_mail () nicht, da es nicht erwartet, dass diese Eigenschaft verwendet wird.

Dies führt dazu, dass Empfänger möglicherweise E-Mails erhalten, die nicht für sie bestimmt sind. Glücklicherweise können die meisten Leute, die HTML-fähige Mail-Clients verwenden, die Textversion nicht sehen, aber es ist im Grunde immer noch ein Sicherheitsproblem.

Zum Glück gibt es eine einfache Lösung. Dies beinhaltet das Altbody-Ersatzbit; Beachten Sie, dass Sie die Html2Text-PHP-Bibliothek benötigen:

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

Hier ist auch eine Liste für ein WP-Plugin, das ich geändert habe, um dieses Problem zu beheben: https://gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

Leider kann ich die anderen Lösungen, die den oben genannten Haken verwenden, nicht kommentieren, um sie davor zu warnen, da ich noch nicht genug Repräsentanten habe, um Kommentare abzugeben.


1

Dies ist möglicherweise keine exakte Antwort auf den ersten Beitrag hier, aber eine Alternative zu einigen der hier angebotenen Lösungen zum Festlegen eines alternativen Körpers

Im Wesentlichen musste (wollte) ich zusätzlich zum HTML-Teil einen eigenen Altbody (dh Klartext) setzen, anstatt mich auf einige Conversion / Striptags und so weiter zu verlassen. Also habe ich mir das ausgedacht, was gut zu funktionieren scheint

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

Wenn Sie keinen Codekonflikt im Wordpress-Kern erstellen möchten, besteht die alternative oder einfachste Lösung meiner Meinung nach darin, eine Aktion hinzuzufügen phpmailer_init, die vor dem eigentlichen Senden von E-Mails in der ausgeführt wird wp_mail. Um meine Erklärung zu vereinfachen, sehen Sie sich das folgende Codebeispiel an:

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

Wenn Sie der PHPMailer-Klasseneigenschaft einen Inhalt hinzufügen, wird AltBodyder Standard-Inhaltstyp automatisch auf festgelegt multipart/alternative.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.