Wysyłanie e-maili wieloczęściowych (tekstowych / html) za pośrednictwem wp_mail () prawdopodobnie zablokuje twoją domenę


37

Podsumowanie

Z powodu błędu w WP Core wysyłanie wieloczęściowych wiadomości e-mail (html / text) za pomocą wp_mail () (w celu zmniejszenia prawdopodobieństwa, że ​​wiadomości e-mail znajdą się w folderach ze spamem) spowoduje ironiczne zablokowanie domeny przez Hotmail (i inne wiadomości e-mail Microsoft).

Jest to złożony problem, który postaram się szczegółowo wyjaśnić, próbując pomóc komuś znaleźć praktyczne rozwiązanie, które ostatecznie może zostać wdrożone w jądrze.

To będzie satysfakcjonująca lektura. Zaczynajmy...

Błąd

Najczęstszą wskazówką, aby uniknąć sytuacji, w której wiadomości e-mail biuletynu znajdą się w folderach ze spamem, jest wysyłanie wiadomości wieloczęściowych.

Wieloczęściowy (mime) odnosi się do wysyłania zarówno części HTML, jak i TEXT wiadomości e-mail w jednym e-mailu. Gdy klient otrzymuje wiadomość wieloczęściową, akceptuje wersję HTML, jeśli może renderować HTML, w przeciwnym razie wyświetla wersję zwykłego tekstu.

Udowodniono, że to działa. Podczas wysyłania do Gmaila wszystkie nasze e-maile trafiały do ​​folderów spamu, dopóki nie zmieniliśmy wiadomości na wieloczęściowe, gdy dotarły do ​​głównej skrzynki odbiorczej. Świetna sprawa.

Teraz, wysyłając wiadomości wieloczęściowe za pośrednictwem wp_mail (), wysyła Typ treści (wieloczęściowy / *) dwa razy, raz z granicą (jeśli jest to ustawione niestandardowo) i raz bez. To zachowanie powoduje, że wiadomość e-mail jest wyświetlana jako nieprzetworzona wiadomość, a nie wieloczęściowa w niektórych wiadomościach e-mail, w tym we wszystkich Microsoft (Hotmail, Outlook itp.)

Microsoft oznaczy tę wiadomość jako śmieci, a kilka wiadomości, które przejdą, zostanie oflagowane ręcznie przez odbiorcę. Niestety adresy e-mail Microsoft są powszechnie używane. Korzysta z niego 40% naszych subskrybentów.

Potwierdza to Microsoft za pośrednictwem niedawno przeprowadzonej wymiany wiadomości e-mail.

Oznaczenie wiadomości spowoduje całkowite zablokowanie domeny . Oznacza to, że wiadomość nie zostanie wysłana do folderu ze spamem, a nawet nie zostanie dostarczona do odbiorcy.

Do tej pory nasza główna domena była blokowana 3 razy.

Ponieważ jest to błąd w rdzeniu WP, każda domena, która wysyła wiadomości wieloczęściowe, jest blokowana. Problem polega na tym, że większość webmasterów nie wie dlaczego. Potwierdziłem to, przeprowadzając moje badania i widząc, jak inni użytkownicy dyskutują o tym na forach itp. Wymaga to zagłębienia się w surowy kod i dobrej wiedzy o tym, jak działają tego rodzaju wiadomości e-mail, które przechodzimy dalej ...

Podzielmy to na kod

Utwórz konto Hotmail / Outlook. Następnie uruchom następujący kod:

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

A jeśli chcesz zmienić domyślny typ zawartości , użyj:

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

Spowoduje to wysłanie wiadomości wieloczęściowej.

Jeśli więc sprawdzisz pełne nieprzetworzone źródło wiadomości, zauważysz, że typ zawartości jest dodawany dwukrotnie, raz bez granic:

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

To jest problem.

Źródłem problemu jest pluggable.php- jeśli spojrzymy gdzieś tutaj:

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

Potencjalne rozwiązania

Zastanawiasz się więc, dlaczego nie zgłosiłeś tego w trac ? I już . Ku mojemu wielkiemu zdziwieniu, 5 lat temu powstał inny bilet opisujący ten sam problem.

Spójrzmy prawdzie w oczy, minęło pół dekady. W latach internetowych jest to więcej niż 30. Problem wyraźnie został porzucony i zasadniczo nigdy nie zostanie rozwiązany (... chyba że rozwiążemy go tutaj).

Znalazłem tutaj świetny wątek oferujący rozwiązanie, ale chociaż jego rozwiązanie działa, łamie wiadomości e-mail, które nie mają niestandardowego $headerszestawu.

Tam właśnie za każdym razem padamy. Albo wersja wieloczęściowa działa dobrze, a normalne nieuzbrojone $headerswiadomości nie działają, lub odwrotnie.

Rozwiązaniem, które wymyśliliśmy było:

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

}

Tak, wiem, edytowanie podstawowych plików to tabu, usiądźcie ... to była desperacka poprawka i kiepska próba dostarczenia poprawki dla rdzenia.

Problem z naszą poprawką polega na tym, że domyślne wiadomości e-mail, takie jak nowe rejestracje, komentarze, resetowanie hasła itp. Będą dostarczane jako puste wiadomości. Mamy więc działający skrypt wp_mail (), który będzie wysyłać wiadomości wieloczęściowe, ale nic więcej.

Co robić

Celem jest znalezienie sposobu wysyłania zarówno zwykłych (zwykły tekst), jak i wieloczęściowych wiadomości za pomocą podstawowej funkcji wp_mail () (nie jest to niestandardowa funkcja sendmail).

Podczas próby rozwiązania tego, głównym problemem, jaki napotkasz, jest ilość czasu, którą poświęcisz na wysyłanie fałszywych wiadomości, sprawdzanie, czy zostały odebrane, i po prostu otwieranie pudełka z aspiryną i przeklinanie w Microsoft, ponieważ jesteś przyzwyczajony do ich Problemy z IE, podczas gdy gremlin tutaj jest niestety WordPress.

Aktualizacja

Rozwiązanie opublikowane przez @bonger pozwala $messagebyć tablicą zawierającą zamienniki z kluczem zawartości. Potwierdziłem, że działa we wszystkich scenariuszach.

Pozwolimy, aby pytanie pozostało otwarte, dopóki nie skończy się nagroda, aby podnieść świadomość problemu, być może do poziomu, w którym zostanie on naprawiony w rdzeniu. Zapraszam do opublikowania alternatywnego rozwiązania, w którym $messagemoże być ciąg.


1
Ponieważ wp_mail()funkcja jest wtykowa, to nie definiowanie zamiennika jako wtyczki, której należy używać (w treściach wt-wp / wtyczek mu), nie jest dobrym rozwiązaniem dla ciebie (i dla wszystkich innych, nie udaje się naprawić rdzenia)? W którym przypadku nie $phpmailer->ContentType = $content_type;działałoby przeniesienie kontroli wieloczęściowej / granicznej po ustawieniu (zamiast elsowania)?
bonger

@bonger Czy możesz napisać odpowiedź szczegółowo opisującą swoje rozwiązanie?
Christine Cooper

1
Nie musisz edytować rdzenia, ponieważ wp_mailmożna go podłączyć . Skopiuj oryginalną funkcję do wtyczki, edytuj ją tak, jak potrzebujesz i aktywuj wtyczkę. WordPress użyje edytowanej funkcji zamiast oryginalnej, bez potrzeby edycji rdzenia.
gmazzap

@ChristineCooper waham się, czy to zrobić, ponieważ, jak mówisz, testowanie jest tak królewskim bólem, ale patrząc na łatę core.trac.wordpress.org/ticket/15448 sugerowaną przez @ rmccue / @ MattyRob, która wygląda naprawdę fajnie idź, więc opublikuję nieprzetestowaną odpowiedź na podstawie tego ...
bonger

2
@ChristineCooper, jeśli po prostu podpiszesz phpmailer i ustawisz treść w $ phpmailer-> AltBody, ten sam błąd się zdarza?
chifliiiii

Odpowiedzi:


15

Następująca wersja wp_mail()jest z poprawką @ rmccue / @ MattyRob w bilecie https://core.trac.wordpress.org/ticket/15448 , odświeżona do 4.2.2, która pozwala $messagebyć tablicą zawierającą typ zawartości kluczowane alternatywy:

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

Więc jeśli umieścisz to w swoim pliku np. „Wp-content / mu-plugins / functions.php”, to zastąpi wersję WP. Ma ładne zastosowanie bez żadnych bałaganu przy nagłówkach, np .:

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

Pamiętaj, że nie testowałem tego z rzeczywistymi wiadomościami e-mail ...


Dodałem to, aby mieć wtyczki i uruchomiłem kod testowy; zadziałało. Przetestowałem domyślne powiadomienia podstawowe (powiadomienia o nowych użytkownikach itp.), A także zadziałało. Będę nadal przeprowadzać testy w ten weekend i zobaczę, jak wtyczki będą z tym działały, i w zasadzie jeśli wszystko będzie działać. W szczególności przejrzę nieprzetworzone dane wiadomości. To będzie bardzo czasochłonne zadanie, ale zapewniam, że po zakończeniu zgłosię się. Jeśli istnieje scenariusz, w którym wp_mail () nie będzie działał (kiedy powinno inaczej), daj mi znać. Dziękuję za tę odpowiedź.
Christine Cooper

Dobre rzeczy, zwróciłem uwagę na wynik i wygląda dobrze - w rzeczywistości łatka po prostu sprawia, że ​​wp_mail używa standardowego solidnego przetwarzania PHPMailera w przypadku przekazywania tablicy, a poza tym domyślnie przyjmuje podejrzane rzeczy WP (dla kompatybilności wstecznej) więc powinno być dobrze (oczywiście kudo tutaj trafia do autorów łatek) ... Będę go używać od teraz (i w końcu dostosuję) - i dziękuję za informacje dotyczące ponownego użycia zarówno HTMLa / zwykłego do zmniejszyć szanse, że zostaniesz uznany za spam ...
bonger

1
Przetestowaliśmy to we wszystkich możliwych scenariuszach i działa świetnie. Jutro wystrzelimy biuletyn i zobaczymy, czy otrzymamy jakieś skargi od użytkowników. Jedyne niewielkie zmiany, które musieliśmy wprowadzić, to odkażenie / odkażenie tablicy, gdy jest ona wstawiana do bazy danych (wiadomości w kolejce w bazie danych, gdzie cron wysyła ją małymi zbiorami). Pozwolę, aby pytanie pozostało otwarte i oczekiwane, dopóki nagroda się nie skończy, abyśmy mogli uświadomić sobie ten problem. Mamy nadzieję, że ta łatka lub alternatywa zostaną dodane do rdzenia. A co ważniejsze, dlaczego nie. Co oni sobie myślą!
Christine Cooper

Losowo zauważyłem, że wykonałeś aktualizację połączonego biletu trac. Czy to aktualizacja tego kodu? Jeśli tak, czy możesz uprzejmie opublikować tę aktualizację, edytując tutaj swoją odpowiedź, aby ta odpowiedź była aktualna? Dziękuję Ci bardzo.
Christine Cooper

Cześć, nie, to tylko odświeżenie łaty do obecnego tułowia, aby łączyło się bez konfliktów (w nadziei, że
zyska

4

To wcale nie jest błąd WordPressa, phpmailernie pozwala na niestandardowe nagłówki ... jeśli spojrzysz na 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;
    }

Możesz zobaczyć, że obrażająca domyślna wielkość jest tym, co wypisuje dodatkową linię nagłówka z charset i bez granicy. Ustawienie typu zawartości według filtru nie rozwiązuje tego samodzielnie, tylko dlatego, że altprzypadek jest ustawiony message_typeprzez sprawdzenie, że AltBodynie jest pusty, a nie typ zawartości.

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

Ostatecznie oznacza to, jak tylko załączysz plik lub obraz wbudowany, lub ustawisz AltBody, winny błąd powinien zostać ominięty. Oznacza to również, że nie ma potrzeby jawnego ustawiania typu zawartości, ponieważ tak szybko, jak to AltBodymożliwe, jest to ustawiane multipart/alternativeprzez phpmailer.

Prosta odpowiedź brzmi:

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

Następnie nie musisz jawnie ustawiać nagłówków, możesz po prostu zrobić:

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

Na nieszczęście wiele funkcji i właściwości w phpmailerklasie jest chronionych, gdyby nie to, poprawną alternatywą byłoby po prostu sprawdzenie i przesłonięcie MIMEHeaderswłaściwości za pomocą phpmailer_inithaka przed wysłaniem.


2

Właśnie wydałem wtyczkę, która pozwala użytkownikom korzystać z szablonów HTML na WordPress i Im teraz odtwarzam wersję dev, aby dodać prosty tekst zastępczy. Wykonałem następujące czynności i podczas testów widzę tylko jedną dodaną granicę, a wiadomości e-mail docierają do 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 );
}

Więc w zasadzie to, co tu robię, to zmodyfikowanie obiektu phpmailer, załadowanie wiadomości do szablonu HTML i ustawienie jej na właściwość Body. Również wziąłem oryginalną wiadomość i ustawiłem właściwość AltBody.


2

Moim prostym rozwiązaniem jest użycie html2text https://github.com/soundasleep/html2text w następujący sposób:

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

Tutaj https://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 także sedno.


2

Dla każdego, kto używa haka „phpmailer_init”, aby dodać własne „AltBody”:

Alternatywna treść tekstu jest ponownie wykorzystywana do wysyłania kolejnych wiadomości e-mail, chyba że usuniesz ją ręcznie! WordPress nie usuwa tego w wp_mail (), ponieważ nie oczekuje, że ta właściwość zostanie użyta.

Powoduje to, że odbiorcy mogą otrzymywać wiadomości, które nie są dla nich przeznaczone. Na szczęście większość osób korzystających z klientów pocztowych z obsługą HTML nie widzi wersji tekstowej, ale nadal jest to w zasadzie problem z bezpieczeństwem.

Na szczęście jest łatwa naprawa. Obejmuje to bit zastępczy altbody; zwróć uwagę, że potrzebujesz biblioteki PHP HTML2Text:

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

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

  // Return untouched atts
  return $wp_mail_atts;
}

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

Oto także streszczenie wtyczki WP, którą zmodyfikowałem w celu rozwiązania tego problemu: https://gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

Niestety nie mogę komentować innych rozwiązań za pomocą wyżej wspomnianego haka, aby ich ostrzec, ponieważ nie mam jeszcze wystarczającej liczby przedstawicieli, aby skomentować.


1

to może nie być dokładna odpowiedź na początkowy post tutaj, ale jest to alternatywa dla niektórych tutaj przedstawionych rozwiązań dotyczących ustawiania alt body

Zasadniczo, musiałem (chciałem) ustawić odrębne altbody (tj. tekst jawny) dodatkowo do części HTML zamiast polegać na niektórych konwersjach / striptagach i tak dalej. więc wymyśliłem to, co wydaje się działać dobrze

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

Jeśli nie chcesz tworzyć konfliktu kodu w rdzeniu Wordpress, myślę, że alternatywnym lub najprostszym rozwiązaniem jest dodanie akcji, phpmailer_initktóra zrobi to przed faktycznym wysłaniem poczty w wp_mail. Aby uprościć moje wyjaśnienie, zobacz poniższy przykład kodu:

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

Jeśli dodasz zawartość do AltBodywłaściwości klasy PHPMailer, domyślny typ zawartości zostanie automatycznie ustawiony na multipart/alternative.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.