wake-up-neo.net

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

Zusammenfassung

Wegen eines Fehlers in WP Core wird das Senden von mehrteiligen E-Mails (HTML/Text) mit wp_mail () (um die Wahrscheinlichkeit zu verringern, dass E-Mails in Spam-Ordnern landen) ironischerweise führt 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 sein. Fangen wir an ...

Der Fehler

Der häufigste Rat, um zu vermeiden, dass Ihre Newsletter-E-Mails in Spam-Ordnern landen, ist das Senden mehrteiliger 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 als Rohnachricht und nicht mehrteilig angezeigt wird, einschließlich all Microsoft (Hotmail, Outlook usw.).

Microsoft kennzeichnet diese Nachricht als Junk und die wenigen eingehenden Nachrichten werden vom Empfänger manuell gekennzeichnet. Leider, Microsoft-E-Mail-Adressen sind 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 gesperrt ist. Dies bedeutet, dass die Nachricht nicht an den Spam-Ordner gesendet wird, sie werden nicht einmal zugestellt an den Empfänger.

Wir haben unsere Hauptdomain bisher dreimal blockieren lassen.

Da dies ein Fehler im WP -Kern ist, werden alle Domänen, die mehrteilige Nachrichten senden, 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.

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 = "[email protected]";

$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 <[email protected]>\r\n";
$headers .= 'Content-Type: multipart/alternative;boundary="----=_Part_18243133_1346573420.1408991447668"';


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

Und wenn Sie den Standard-Inhaltstyp ä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 haben Sie dies nicht bei trac gemeldet? Ich habe bereits . Zu meiner großen Überraschung wurde vor 5 Jahren ein anderes Ticket erstellt, das das gleiche Problem umreißt.

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 $headers unterbrochen.

Dort stürzen wir jedes Mal ab. Entweder funktioniert die mehrteilige Version einwandfrei und normale nicht festgelegte $headers-Nachrichten nicht oder Verse.

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 Lösung und ein schlechter Versuch, eine Lösung 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, einen Weg zu finden, um sowohl normale (Klartext) als auch mehrteilige Nachrichten zu senden mit der Kernfunktion wp_mail () (keine benutzerdefinierte sendmail-Funktion).

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 Beschimpfen von Microsoft aufwenden müssen, weil Sie an deren gewöhnt sind IE Probleme, während der Gremlin hier leider WordPress ist.

Aktualisieren

Mit der von @bonger bereitgestellten Lösung kann $message ein 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. Sie können auch eine alternative Lösung posten, bei der $message eine Zeichenfolge sein kann.

36

Die folgende Version von wp_mail() enthält den Patch von @ rmccue/@ MattyRob im Ticket https://core.trac.wordpress.org/ticket/15448 , der für 4.2.2 aktualisiert wurde und $message ermöglicht Ein Array, das Alternativen mit Inhaltstyp enthält:

/**
 * 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 protected]>' 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 [email protected]$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 = '[email protected]' . $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 <[email protected]>"
            $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 <[email protected]>"
                $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 <[email protected]>"
                $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 zB "wp-content/mu-plugins/functions.php" -Datei 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 = "[email protected]";

$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 '[email protected]'; } );
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 ...

14
bonger

Dies ist überhaupt kein WordPress-Fehler, sondern eine phpmailer, die keine benutzerdefinierten Header zulässt ... wenn Sie sich class-phpmailer.php ansehen:

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 Festlegen des Inhaltstyps nach Filter löst dies nicht von selbst, nur weil der alt-Fall hier auf message_type gesetzt ist, indem AltBody nicht leer ist und nicht 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, dass der Fehler umgangen werden sollte, sobald Sie eine Datei oder ein Inline-Bild anhängen oder die AltBody festlegen. Dies bedeutet auch, dass der Inhaltstyp nicht explizit festgelegt werden muss, da er von AltBody auf multipart/alternative gesetzt wird, sobald eine phpmailer vorhanden ist.

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 Klasse phpmailer geschützt. Andernfalls besteht eine gültige Alternative darin, die Eigenschaft MIMEHeaders vor dem Senden einfach über den Hook phpmailer_init zu überprüfen und zu überschreiben.

3
majick

Ich habe gerade ein Plugin veröffentlicht damit Benutzer HTML-Vorlagen für WordPress verwenden können und ich gerade mit der Dev-Version spiele 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
chifliiiii

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 ein Gist über.

2
ewake

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 bestimmten altbody (d. h. plaintext) setzen, anstatt mich auf einige umwandlungen/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'];
        }
    }   
}
1
Olly

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

<?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 einen Inhalt in die Eigenschaft AltBody der PHPMailer-Klasse einfügen, wird der Standard-Inhaltstyp automatisch auf multipart/alternative gesetzt.

0
Joshua Reyes

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

Der alternative Textkörper wird wiederverwendet für verschiedene aufeinanderfolgende E-Mails, die gesendet werden, verwendet, sofern Sie ihn nicht manuell löschen! 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 wird den meisten Benutzern von HTML-fähigen E-Mail-Clients die Textversion nicht angezeigt, es handelt sich jedoch im Grunde genommen immer noch um ein Sicherheitsproblem.

Zum Glück gibt es eine einfache Lösung. Dies beinhaltet das Altbody-Ersatzbit; Beachten Sie, dass Sie die Bibliothek Html2Text PHP 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 mit dem oben genannten Hook nicht kommentieren, um sie davor zu warnen, da ich noch nicht genug Repräsentanten habe, um sie zu kommentieren.

0
Tanuki