Sommario
A causa di un bug in WP Core, l'invio di e-mail multipart (html / text) con wp_mail () (per ridurre la possibilità che le e-mail finiscano nelle cartelle spam) risulterà ironicamente con il blocco del dominio da parte di Hotmail (e altre e-mail Microsoft).
Questo è un problema complesso che mi proporrò di analizzare dettagliatamente nel tentativo di aiutare qualcuno a trovare una soluzione praticabile che alla fine potrebbe essere implementata nel core.
Sarà una lettura gratificante. Cominciamo...
Il bug
Il consiglio più comune per evitare che le e-mail della newsletter finiscano nelle cartelle spam è di inviare messaggi multipart.
Multi-part (mime) si riferisce all'invio di una parte HTML e TEXT di un messaggio e-mail in un'unica e-mail. Quando un client riceve un messaggio multipart, accetta la versione HTML se può eseguire il rendering HTML, altrimenti presenta la versione in testo normale.
Questo ha dimostrato di funzionare. Durante l'invio a Gmail, tutte le nostre e-mail sono finite in cartelle spam fino a quando non abbiamo cambiato i messaggi in multipart quando sono arrivati alla posta in arrivo principale. Roba fantastica.
Ora, quando si inviano messaggi multipart tramite wp_mail (), viene emesso il Tipo di contenuto (multipart / *) due volte, una volta con limite (se impostato su misura) e una volta senza. Questo comportamento comporta che l'email viene visualizzata come messaggio non elaborato e non multipart su alcune e-mail, incluse tutte Microsoft (Hotmail, Outlook, ecc ...)
Microsoft contrassegnerà questo messaggio come spazzatura e i pochi messaggi che arrivano verranno contrassegnati manualmente dal destinatario. Sfortunatamente , gli indirizzi di posta elettronica Microsoft sono ampiamente utilizzati. Il 40% dei nostri abbonati lo utilizza.
Ciò è confermato da Microsoft tramite uno scambio di email che abbiamo avuto di recente.
La segnalazione dei messaggi comporterà il blocco completo del dominio . Ciò significa che il messaggio non verrà inviato alla cartella spam, non verrà nemmeno recapitato al destinatario.
Finora abbiamo bloccato il nostro dominio principale 3 volte.
Poiché si tratta di un bug nel core WP, ogni dominio che invia messaggi multipart viene bloccato. Il problema è che la maggior parte dei webmaster non sa perché. Ho confermato questo quando faccio le mie ricerche e vedo altri utenti che ne discutono nei forum ecc. Richiede approfondire il codice non elaborato e avere una buona conoscenza di come funzionano questi tipi di messaggi e-mail, che andremo al prossimo ...
Dividiamolo in codice
Crea un account hotmail / outlook. Quindi, esegui il seguente codice:
// 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 );
E se vuoi cambiare il tipo di contenuto predefinito , usa:
add_filter( 'wp_mail_content_type', 'set_content_type' );
function set_content_type( $content_type ) {
return 'multipart/alternative';
}
Questo invierà un messaggio multipart.
Quindi, se controlli la fonte raw completa del messaggio, noterai che il tipo di contenuto viene aggiunto due volte, una volta senza limiti:
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="====f230673f9d7c359a81ffebccb88e5d61=="
MIME-Version: 1.0
Content-Type: multipart/alternative; charset=
Questo è il problema.
La fonte del problema sta nel pluggable.php
: se guardiamo da qualche parte qui:
// 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;
}
}
}
Potenziali soluzioni
Quindi ti stai chiedendo, perché non l'hai segnalato a trac ? L'ho già fatto . Con mia grande sorpresa, 5 anni fa è stato creato un biglietto diverso che delineava lo stesso problema.
Ammettiamolo, è stata una mezza decade. Negli anni di Internet, è più simile a 30. Il problema è stato chiaramente abbandonato e sostanzialmente non verrà mai risolto (... a meno che non lo risolviamo qui).
Ho trovato un ottimo thread qui che offre una soluzione, ma mentre la sua soluzione funziona, rompe le email che non hanno un $headers
set personalizzato .
Ecco dove ci schiantiamo ogni volta. O la versione multipart funziona bene e i normali $headers
messaggi non impostati non funzionano, o viceversa.
La soluzione che abbiamo trovato è stata:
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 ) );
}
}
Sì, lo so, la modifica dei file core è un tabù, siediti ... questa è stata una soluzione disperata e uno scarso tentativo di fornire una soluzione per core.
Il problema con la nostra correzione è che le e-mail predefinite come nuove registrazioni, commenti, reimpostazione della password ecc. Verranno recapitate come messaggi vuoti. Quindi abbiamo uno script wp_mail () funzionante che invierà messaggi multipart ma nient'altro.
Cosa fare
Lo scopo qui è quello di trovare un modo per inviare sia messaggi normali (testo normale) che multipart utilizzando la funzione core wp_mail () (non una funzione sendmail personalizzata).
Quando tenterai di risolvere questo problema, il problema principale che incontrerai è la quantità di tempo che impiegherai per inviare messaggi fittizi, controllando se sono ricevuti e fondamentalmente aprendo una scatola di aspirina e imprecando contro Microsoft perché sei abituato al loro IE problemi mentre il gremlin qui è purtroppo WordPress.
Aggiornare
La soluzione pubblicata da @bonger consente $message
di essere un array contenente alternative con chiave di tipo contenuto. Ho confermato che funziona in tutti gli scenari.
Consentiremo che questa domanda rimanga aperta fino a quando non si esaurirà la ricompensa per aumentare la consapevolezza del problema, forse a un livello in cui verrà risolto nel nucleo. Sentiti libero di pubblicare una soluzione alternativa dove $message
può essere una stringa.
wp_mail
è collegabile . Copia la funzione originale in un plugin, modificala come ti serve e attiva il plugin. WordPress utilizzerà la tua funzione modificata anziché quella originale, senza bisogno di modificare il core.
wp_mail()
funzione è innestabile non sta definendo il tuo sostituto come un plugin da usare (in wp-content / mu-plugins) non è una buona soluzione per te (e per tutti gli altri, mancante correzione del core)? In tal caso, non spostare il controllo multipart / boundary dopo che l'impostazione$phpmailer->ContentType = $content_type;
(anziché elsing) non funziona?