Sumário
Devido a um erro no WP Core, o envio de emails com várias partes (html / text) com wp_mail () (para reduzir a chance de emails terminarem em pastas de spam) ironicamente resultará em seu domínio ser bloqueado pelo Hotmail (e outros emails da Microsoft).
Esse é um problema complexo que tentarei detalhar em detalhes, na tentativa de ajudar alguém a encontrar uma solução viável que possa eventualmente ser implementada no núcleo.
Será uma leitura gratificante. Vamos começar...
O inseto
O conselho mais comum para evitar que os emails de seu boletim acabem nas pastas de spam é enviar mensagens com várias partes.
Múltiplas partes (mime) refere-se ao envio de uma parte HTML e TEXT de uma mensagem de email em um único email. Quando um cliente recebe uma mensagem com várias partes, ele aceita a versão HTML se puder renderizar HTML; caso contrário, apresenta a versão em texto sem formatação.
Está provado que isso funciona. Ao enviar para o gmail, todos os nossos emails foram parar em pastas de spam até mudarmos as mensagens para multipartes quando chegaram à caixa de entrada principal. Coisas boas.
Agora, ao enviar mensagens com várias partes via wp_mail (), ele gera o Tipo de conteúdo (multipart / *) duas vezes, uma com limite (se definido de maneira personalizada) e uma vez sem. Esse comportamento resulta com o email sendo exibido como uma mensagem bruta e não com várias partes em alguns emails, incluindo todos os Microsoft (Hotmail, Outlook, etc ...)
A Microsoft sinalizará esta mensagem como lixo eletrônico e as poucas mensagens enviadas serão sinalizadas manualmente pelo destinatário. Infelizmente , os endereços de email da Microsoft são amplamente usados. 40% dos nossos assinantes usam.
Isso é confirmado pela Microsoft por meio de uma troca de e-mails que tivemos recentemente.
A sinalização das mensagens resultará com o domínio sendo completamente bloqueado . Isso significa que a mensagem não será enviada para a pasta de spam, nem será entregue ao destinatário.
Até agora, nosso domínio principal foi bloqueado três vezes.
Como esse é um bug no núcleo do WP, todos os domínios que enviam mensagens com várias partes estão sendo bloqueados. O problema é que a maioria dos webmasters não sabe o porquê. Confirmei isso ao fazer minha pesquisa e ver outros usuários discutindo isso em fóruns, etc. Isso exige que você se aprofunde no código bruto e tenha um bom conhecimento de como esse tipo de mensagem de email funciona, o que vamos seguir ...
Vamos dividi-lo em código
Crie uma conta do hotmail / outlook. Em seguida, execute o seguinte código:
// 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 );
E se você deseja alterar o tipo de conteúdo padrão , use:
add_filter( 'wp_mail_content_type', 'set_content_type' );
function set_content_type( $content_type ) {
return 'multipart/alternative';
}
Isso enviará uma mensagem com várias partes.
Portanto, se você verificar a fonte bruta completa da mensagem, notará que o tipo de conteúdo é adicionado duas vezes, uma vez sem limite:
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="====f230673f9d7c359a81ffebccb88e5d61=="
MIME-Version: 1.0
Content-Type: multipart/alternative; charset=
Essa é a questão.
A fonte do problema está pluggable.php
- se olharmos para algum lugar aqui:
// 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;
}
}
}
Soluções potenciais
Então você está se perguntando, por que você não relatou isso no trac ? Eu já tenho . Para minha grande surpresa, um bilhete diferente foi criado há 5 anos, descrevendo o mesmo problema.
Vamos ser sinceros, já faz meia década. Nos anos da internet, isso é mais ou menos 30. O problema foi claramente abandonado e basicamente nunca será resolvido (... a menos que se o resolvamos aqui).
Encontrei um ótimo tópico aqui oferecendo uma solução, mas enquanto a solução funciona, ele quebra e-mails que não têm um $headers
conjunto personalizado .
É aí que batemos toda vez. A versão multipartes funciona bem, e as $headers
mensagens não configuradas normais não funcionam ou vice-versa.
A solução que encontramos foi:
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 ) );
}
}
Sim, eu sei, editar arquivos principais é um tabu, sente-se ... essa foi uma correção desesperada e uma tentativa pobre de fornecer uma correção para o núcleo.
O problema com nossa correção é que os emails padrão, como novos registros, comentários, redefinição de senha, etc. serão entregues como mensagens em branco. Portanto, temos um script wp_mail () que envia mensagens com várias partes, mas nada mais.
O que fazer
O objetivo aqui é encontrar uma maneira de enviar mensagens normais (texto sem formatação) e multipartes usando a função principal wp_mail () (não uma função personalizada do sendmail).
Ao tentar resolver isso, o principal problema que você encontrará é a quantidade de tempo que você gastará no envio de mensagens falsas, verificando se elas são recebidas e basicamente abrindo uma caixa de aspirina e xingando na Microsoft porque você está acostumado a isso. Problemas do IE enquanto o gremlin aqui é infelizmente o WordPress.
Atualizar
A solução publicada por @bonger permite $message
ser uma matriz que contém alternativas com chave do tipo de conteúdo. Eu confirmei que funciona em todos os cenários.
Permitiremos que essa questão permaneça em aberto até que a recompensa acabe, para aumentar a conscientização sobre o problema, talvez até um nível em que ele seja fixado no núcleo. Sinta-se à vontade para postar uma solução alternativa onde $message
possa ser uma string.
wp_mail()
função é conectável, a definição de sua substituição como um plug-in obrigatório (no wp-content / mu-plugins) não é uma boa solução para você (e para todos os outros, com falha na correção do núcleo)? Nesse caso, não mover a verificação multipartes / limite para depois da configuração$phpmailer->ContentType = $content_type;
(em vez de excluir) não funcionaria?wp_mail
é plugável . Copie a função original em um plugin, edite-a conforme necessário e ative o plugin. O WordPress usará sua função editada em vez da original, sem a necessidade de editar o núcleo.Respostas:
A seguinte versão do
wp_mail()
é com o patch aplicado de @ rmccue / @ MattyRob no ticket https://core.trac.wordpress.org/ticket/15448 , atualizado para 4.2.2, que permite$message
ser uma matriz que contém o tipo de conteúdo suplentes com chave:Portanto, se você colocar isso no seu arquivo, por exemplo, "wp-content / mu-plugins / functions.php", ele substituirá a versão do WP. Ele tem um bom uso, sem mexer nos cabeçalhos, por exemplo:
Observe que não testei isso com e-mails reais ...
fonte
Este não é realmente um bug do WordPress, ele
phpmailer
não permite cabeçalhos personalizados ... se você olhar paraclass-phpmailer.php
:Você pode ver que o caso padrão ofensivo é o que está gerando a linha de cabeçalho extra com charset e sem limite. Definir o tipo de conteúdo por filtro não resolve isso por si só, apenas porque o
alt
caso aqui é ativadomessage_type
pela verificaçãoAltBody
não está vazio, e não o tipo de conteúdo.No final, o que isso significa é que, assim que você anexa um arquivo ou imagem embutida, ou define o
AltBody
erro, o bug incorreto deve ser ignorado. Isso também significa que não há necessidade de definir explicitamente o tipo de conteúdo, porque assim que houver um,AltBody
ele será definidomultipart/alternative
porphpmailer
.Portanto, a resposta simples é:
Então você não precisa definir os cabeçalhos explicitamente, basta:
Infelizmente, muitas das funções e propriedades da
phpmailer
classe estão protegidas, se não fosse por isso, uma alternativa válida seria simplesmente verificar e substituir aMIMEHeaders
propriedade pelophpmailer_init
gancho antes de enviar.fonte
Acabei de lançar um plug - in para permitir que os usuários usem modelos html no WordPress e estou jogando agora na versão dev para adicionar um fallback de texto simples. Fiz o seguinte e, em meus testes, vejo apenas um limite adicionado e os emails estão chegando bem ao Hotmail.
Então, basicamente, o que faço aqui é modificar o objeto phpmailer, carregar a mensagem dentro de um modelo html e configurá-lo para a propriedade Body. Também peguei a mensagem original e defina a propriedade AltBody.
fonte
Minha solução simples é usar o html2text https://github.com/soundasleep/html2text desta maneira:
Aqui também https://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 .
fonte
Para quem estiver usando o gancho 'phpmailer_init' para adicionar seu próprio 'AltBody':
O corpo do texto alternativo é reutilizado para diferentes e-mails consecutivos enviados, a menos que você o limpe manualmente! O WordPress não o limpa em wp_mail () porque não espera que essa propriedade seja usada.
Isso resulta em destinatários potencialmente recebendo e-mails não destinados a eles. Felizmente, a maioria das pessoas que usa clientes de email habilitados para HTML não vê a versão em texto, mas ainda é basicamente um problema de segurança.
Felizmente, há uma solução fácil. Isso inclui o bit de substituição do altbody; Observe que você precisa da biblioteca PHP Html2Text:
Aqui está também uma síntese de um plugin WP que modifiquei para corrigir esse problema: https://gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661
Infelizmente, não posso comentar sobre as outras soluções usando o gancho mencionado acima, para avisá-las disso, pois ainda não tenho representante suficiente para comentar.
fonte
isso pode não ser uma resposta exata para a postagem inicial aqui, mas é uma alternativa para algumas das soluções aqui fornecidas, relacionadas à configuração de um corpo alternativo
essencialmente, eu precisava (queria) definir um altbody distinto (ou seja, texto simples) adicionalmente à parte html, em vez de depender de algumas conversões / striptags e outros enfeites. então eu vim com isso que parece funcionar muito bem
fonte
Se você não deseja criar nenhum conflito de código no núcleo do Wordpress, acho que a solução alternativa ou mais simples é adicionar uma ação
phpmailer_init
que será feita antes do envio real do e-mail nowp_mail
. Para simplificar minha explicação, veja o exemplo de código abaixo:Se você adicionar um conteúdo na
AltBody
propriedade da classe PHPMailer , o tipo de conteúdo padrão será automaticamente definido comomultipart/alternative
.fonte