web-dev-qa-db-fra.com

L'envoi d'e-mails en plusieurs parties (texte/html) via wp_mail () entraînera probablement l'interdiction de votre domaine.

Résumé

En raison d’un bogue dans WP Core, l’envoi de multipart emails (html/text) avec wp_mail () (pour réduire le risque que des courriels se retrouvent dans des dossiers de courrier indésirable) sera ironiquement résultat avec votre domaine bloqué par Hotmail (et d’autres emails Microsoft).

C’est un problème complexe que je vais tenter d’exploiter de manière très détaillée pour tenter d’aider une personne à trouver une solution viable pouvant éventuellement être mise en œuvre de manière centralisée.

Ça va être une lecture enrichissante. Commençons ...

L'insecte

Le conseil le plus courant pour éviter que vos e-mails de bulletin d'information finissent dans des dossiers de courrier indésirable est d'envoyer des messages en plusieurs parties.

Multi-part (mime) fait référence à l'envoi simultané d'une partie HTML et d'une partie TEXT d'un message électronique. Lorsqu'un client reçoit un message en plusieurs parties, il accepte la version HTML s'il peut rendre le code HTML. Sinon, il présente la version en texte brut.

Ceci est prouvé pour fonctionner. Lors de l'envoi à gmail, tous nos courriels se retrouvaient dans des dossiers de spam jusqu'à ce que nous changions les messages en plusieurs parties lorsqu'ils arrivaient dans la boîte de réception principale. Super truc.

Désormais, lors de l’envoi de messages multipart via wp_mail (), le type de contenu (multipart/*) est généré deux fois, une fois avec une limite (si défini de manière personnalisée) et une fois sans. Ce comportement a pour résultat que l'e-mail est affiché en tant que message brut et non en plusieurs parties sur certains courriels, y compris all Microsoft (Hotmail, Outlook, etc.).

Microsoft signalera ce message comme courrier indésirable et les quelques messages reçus seront signalés manuellement par le destinataire. _ {Malheureusement} _, les adresses électroniques de Microsoft sont largement utilisées. 40% de nos abonnés l'utilisent.

Ceci est confirmé par Microsoft via un échange de courrier électronique que nous avons eu récemment.

Le signalement des messages aura pour résultat que le domaine est complètement bloqué . Cela signifie que le message ne sera pas envoyé dans le dossier spam, ils ne seront même pas remis au destinataire.

Nous avons eu notre domaine principal bloqué 3 fois jusqu'à présent.

Comme il s’agit d’un bogue dans le noyau WP, chaque domaine qui envoie des messages en plusieurs parties est bloqué. Le problème est que la plupart des webmasters ne savent pas pourquoi. Je l'ai confirmé lors de mes recherches et lorsque j'ai vu d'autres utilisateurs en discuter sur des forums, etc. Cela nécessite de fouiller dans le code brut et de bien connaître le fonctionnement de ce type de messages électroniques, ce que nous passons ensuite à ...

Décomposons en code

Créez un compte hotmail/Outlook. Ensuite, exécutez le code suivant:

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

Et si vous souhaitez modifier le type de contenu par défaut , utilisez:

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

Cela enverra un message en plusieurs parties.

Ainsi, si vous vérifiez la source brute complète du message, vous remarquerez que le type de contenu est ajouté deux fois, une fois sans limite:

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

C'est le problème.

La source du problème réside dans pluggable.php - si nous regardons quelque part ici:

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

Solutions potentielles

Alors vous vous demandez, pourquoi n’avez-vous pas signalé cela à trac ? I ai déjà . À ma grande surprise, un billet différent a été créé il y a 5 ans, soulignant le même problème.

Regardons les choses en face, cela fait une demi-décennie. Dans les années Internet, cela correspond plus à 30 ans. Le problème a clairement été abandonné et ne sera fondamentalement jamais résolu (... sauf si nous le résolvons ici).

J'ai trouvé un bon fil ici offrant une solution, mais bien que sa solution fonctionne, elle casse les courriels qui n'ont pas défini $headers personnalisé.

C'est là que nous nous écrasons à chaque fois. Soit la version en plusieurs parties fonctionne bien, et les messages $headers non définis normaux ne fonctionnent pas, soit un verset.

La solution que nous avons trouvée était:

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

}

Oui, je sais, éditer des fichiers de base est tabou, asseyez-vous… c’était un correctif désespéré et une tentative médiocre de fournir un correctif pour le cœur.

Le problème avec notre solution est que les emails par défaut tels que les nouvelles inscriptions, les commentaires, la réinitialisation de mot de passe, etc. seront livrés sous forme de messages vierges. Nous avons donc un script de travail wp_mail () qui envoie des messages en plusieurs parties, mais rien d’autre.

Que faire

L'objectif ici est de trouver un moyen d'envoyer à la fois des messages normaux (texte brut) et des messages en plusieurs parties en utilisant la fonction centrale wp_mail () (pas une fonction sendmail personnalisée).

Lorsque vous tentez de résoudre ce problème, le principal problème que vous rencontrerez est le temps que vous passerez à envoyer des messages factices, à vérifier s'ils sont reçus et à ouvrir une boîte d'aspirine et à maudire Microsoft parce que vous êtes habitué à leurs messages. IE problèmes alors que le gremlin est malheureusement WordPress.

Mettre à jour

La solution publiée par @bonger permet à $message d’être un tableau contenant des substituts avec une clé de type de contenu. J'ai confirmé que cela fonctionne dans tous les scénarios.

Nous laisserons cette question rester ouverte jusqu'à épuisement des primes pour sensibiliser le public au problème, peut-être à un niveau où elles seront résolues de manière centrale. N'hésitez pas à poster une solution alternative dans laquelle $message peut être une chaîne.

36
Christine Cooper

La version suivante de wp_mail() contient le correctif appliqué de @ rmccue/@ MattyRob dans le ticket https://core.trac.wordpress.org/ticket/15448 , actualisé pour la version 4.2.2, permettant à $message d’être un tableau contenant des substituts avec une clé de type de contenu:

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

Donc, si vous mettez cela dans votre fichier "wp-content/mu-plugins/functions.php", il remplacera la version WP. Il a une utilisation agréable sans déconner avec les en-têtes, par exemple:

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

Veuillez noter que je n'ai pas testé cela avec les emails actuels ...

14
bonger

Ce n’est pas vraiment un bug de WordPress, c’est un phpmailer ne permettant pas d’en-têtes personnalisés ... si vous regardez 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;
    }

Vous pouvez voir que le cas par défaut incriminé est la sortie de la ligne d’en-tête supplémentaire avec charset et aucune limite. La définition du type de contenu par filtre ne résout pas le problème en soi, car le cas alt est défini ici sur message_type en vérifiant que AltBody n'est pas vide plutôt que le type de contenu.

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

En fin de compte, cela signifie que dès que vous joignez un fichier ou une image en ligne, ou définissez la variable AltBody, le bogue incriminé doit être ignoré. Cela signifie également qu'il n'est pas nécessaire de définir explicitement le type de contenu car dès qu'il existe une variable AltBody, celle-ci est définie sur multipart/alternative par phpmailer.

Donc, la réponse simple est:

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

Ensuite, vous n'avez pas besoin de définir explicitement les en-têtes, vous pouvez simplement faire:

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

Malheureusement, de nombreuses fonctions et propriétés de la classe phpmailer sont protégées. Sinon, une alternative valable serait simplement de vérifier et de surcharger la propriété MIMEHeaders via le hook phpmailer_init avant de l'envoyer.

3
majick

Je viens de publier un plugin pour permettre aux utilisateurs d'utiliser des modèles HTML sur WordPress et Im en ce moment sur le version dev pour ajouter un simple repli de texte. J'ai fait ce qui suit et dans mes tests, je ne vois qu'une limite ajoutée et les courriels arrivent bien à 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 );
}

Donc, fondamentalement, ce que je fais ici est de modifier l'objet phpmailer, de charger le message dans un modèle html et de le définir sur la propriété Body. De plus, j'ai pris le message d'origine et défini la propriété AltBody.

2
chifliiiii

Ma solution simple consiste à utiliser html2text https://github.com/soundasleep/html2text de cette manière:

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

Ici https://Gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 aussi un sujet de base.

2
ewake

ce n'est peut-être pas une réponse exacte au message initial ici, mais c'est une alternative à certaines des solutions proposées ici concernant la définition d'un corps alt

essentiellement, je devais (je voulais) définir un altbody distinct (par exemple, le texte en clair) en plus de la partie html au lieu de compter sur des conversions/striptags et autres. donc je suis venu avec ce qui semble fonctionner très bien

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

Si vous ne voulez pas créer de conflit de code dans le noyau Wordpress, je pense que la solution la plus simple ou la solution la plus simple consiste à ajouter une action à phpmailer_init à faire avant l'envoi effectif du courrier dans le wp_mail. Pour simplifier mon explication, voir l'exemple de code ci-dessous:

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

Si vous ajoutez un contenu dans la propriété AltBody de la classe PHPMailailer, le type de contenu par défaut sera automatiquement défini sur multipart/alternative.

0
Joshua Reyes

Pour ceux qui utilisent le hook 'phpmailer_init' pour ajouter leur propre 'AltBody':

Le corps de texte alternatif est réutilisé pour différents courriers consécutifs envoyés, à moins que vous ne le supprimiez manuellement! WordPress ne l'efface pas dans wp_mail () car il ne s'attend pas à ce que cette propriété soit utilisée.

Cela a pour conséquence que les destinataires reçoivent potentiellement des courriers qui ne leur sont pas destinés. Heureusement, la plupart des utilisateurs de clients de messagerie utilisant HTML ne voient pas la version texte, mais il s’agit toujours d’un problème de sécurité.

Heureusement, il y a une solution facile. Cela inclut le bit de remplacement altbody; notez que vous avez besoin de la bibliothèque 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 );
  }
}

Voici également un résumé pour un plug-in WP que j'ai modifié pour résoudre ce problème: https://Gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

_ {Malheureusement, je ne peux pas commenter les autres solutions utilisant le hook susmentionné, pour les en avertir, car je n'ai pas encore assez de représentants pour commenter.}

0
Tanuki