Fehlende Zeilenumbrüche in HTML-Mails

Immer wieder ist mir aufgefallen, dass manche WordPress-Websites die E-Mails als HTML versenden und dabei sämtliche Zeilenumbrüche kaputtgegangen sind. Ich hatte aber nie Zeit mir mal im Detail anzuschauen, woran das lag und wie man es reparieren kann. Nun bin ich endlich dazu gekommen. Schauen wir uns also mal an, was da jeweils schiefgelaufen ist …

Mein erster Blick ging auf den Quelltext der E-Mail und hier zeigte sich bei allen betroffenen Websites dasselbe Bild. Es betraf immer nur HTML-E-Mails und da in einer HTML-Mail die Zeilenumbrüche manuell gesetzt werden müssen, dies hier aber fehlte, wurde alles in einer einzelnen langen Zeile ausgegeben.

Jetzt hatte ich zwar das Problem besser verstanden, aber der Lösung war ich noch nicht näher gekommen.

Mein erster Website-Kandidat zeigte das Problem nur in CF7-Mails, die auf HTML umgestellt waren, also schaute ich den Code von Contact Form 7 näher an und stieß auf eine interessante Sache:

Der Filter wpcf7_autop_or_not hat den Standardwert true (via Konstante WPCF7_AUTOP) und ist in dieser Datei definiert: /includes/functions.php#L212-L217

/** 
  * Returns true if wpcf7_autop() is applied. 
  */ 
 function wpcf7_autop_or_not() { 
 	return (bool) apply_filters( 'wpcf7_autop_or_not', WPCF7_AUTOP ); 
 } 

Aber er wird an zwei Stellen benutzt!

Dem Mail-Inhalt in /includes/mail.php#L5-L14

/** 
  * Filter callback that applies auto-p to HTML email message body. 
  */ 
 function wpcf7_mail_html_body_autop( $body ) { 
 	if ( wpcf7_autop_or_not() ) { 
 		$body = wpcf7_autop( $body ); 
 	} 
  
 	return $body; 
 } 

Und bei der Anzeige des Formulars selbst: /includes/contact-form.php#L865-L884

/** 
 	 * Replaces all form-tags in the form template with corresponding HTML. 
 	 * 
 	 * @return string Replaced form content. 
 	 */ 
 	public function replace_all_form_tags() { 
 		$manager = WPCF7_FormTagsManager::get_instance(); 
 		$form = $this->prop( 'form' ); 
  
 		if ( wpcf7_autop_or_not() ) { 
 			$form = $manager->replace_with_placeholders( $form ); 
 			$form = wpcf7_autop( $form ); 
 			$form = $manager->restore_from_placeholders( $form ); 
 		} 
  
 		$form = $manager->replace_all( $form ); 
 		$this->scanned_form_tags = $manager->get_scanned_tags(); 
  
 		return $form; 
 	} 

Es gibt einige Plugins (21 um genau zu sein), die diesen Filter auf false setzen. Ich denke, das liegt daran, dass sie ihn entweder für die E-Mail ODER das Formular deaktivieren wollen, aber nicht für beide. Dadurch entstehen natürlich unerwünschte Nebeneffekte.

In meinem konkreten Fall war es sogar noch versteckter. Da war der Code inzwischen deprecated und konnte in Neu-Installation gar nicht mehr aktiviert werden. Daher habe ich selbst bei einem Test mit dem gleichen Plugin das Problem nicht reproduzieren können. Die problematische Zeile war diese hier:

add_filter('wpcf7_autop_or_not', '__return_false');

Hier findest du alle Plugins aus dem offiziellen Verzeichnis, die diesen Filter auf false setzen:
https://wpdirectory.net/search/01HJ0SSW2M68K12YT21JYB4K9Y

Meine Empfehlung an den CF7-Entwickler Takayuki Miyoshi war, diesen Filter aufzuteilen und einen nur für die E-Mail und einen für das Formular zu haben, um Nebenwirkungen zu vermeiden, wenn nur eine Stelle angesprochen werden soll.

Erfreulicherweise hat er das inzwischen tatsächlich aufgenommen. Wobei er zwecks Rückwärtskompatibilität einfach eine Variable als Parameter für den Filter hinzugefügt hat, um den jeweiligen Kontext anzugeben:

Hier ist das Issue: https://github.com/rocklobster-in/contact-form-7/issues/1340
Und der finale Pull Request von ihm: https://github.com/rocklobster-in/contact-form-7/pull/1344

Das schuldige Plugin deaktivierte mit der oben zitierten Zeile die automatische Umwandlung für Zeilenumbrüche, da es ein Grid-System für die Ausgabe des Formulars hinzufügt. Wenn über Shortcodes Zeilen und Spalten definiert werden können, dann sind die Zeilenumbrüche eher hinderlich und erzeugen noch mehr Abstände, die nicht gewollt sind. Nebeneffekt war jedoch, dass die Mails nun in einer Zeile ohne Zeilenumbrüche ausgegeben wurde. Es gab aber keine einfache Möglichkeit im Filter zu erkennen, in welchem Kontext wir gerade sind. Mit der kommenden Version 5.8.6 wird das nun möglich sein 🙂

Wer das Problem auch hat und eine schnelle Lösung benötigt: Die einfachste Möglichkeit ist die Zeilenumbrüche manuell via <br> in die HTML-Mail von CF7 einzubauen.


Nun hatte ich noch eine andere Website identifiziert, die das Problem ebenfalls zeigt und da war weder das Plugin noch Contact Form 7 beteiligt. Also machte ich mich weiter auf die Suche. Um ein grundlegendes Problem mit dem Formular selbst auszuschließen, installierte ich das offizielle Health-Check-Plugin und nutzte den einfachen Mail-Test, der unter den Werkzeugen zu finden ist.

Siehe da, auch hier war der Fehler vorhanden!

Damit ersparte ich mir viel unnötige Sucharbeit, denn jetzt war es ein Problem, dass die PHPMailer-Komponente selbst hatte. Ich musste also nur noch herausfinden, was das auslöst.

Eine (sehr) kurze Google-Suche später wusste ich, dass der Inhalt einer HTML-Mail selbst mit manuellen Zeilenumbrüchen ausgestattet werden muss. Dafür gibt es z.B. nl2br in PHP.

Da das Problem selbst im Core vorhanden war, schaute ich mir nun die Funktionen des Cores genau an:
https://developer.wordpress.org/reference/functions/wp_mail/ und
https://developer.wordpress.org/reference/hooks/wp_mail_content_type/

Die Dokumentation selbst war nicht so hilfreich, aber die Contributed Notes lösten das Problem dann recht schnell. Sofern der Content-Type nicht direkt über einen Header umgestellt wird, sondern über den offiziellen Filter, dann ist die Empfehlung von Lead-Developer Helen Hou-Sandí den Standardwert auf HTML zu ändern und nach dem Versenden wieder zurück auf den Standardwert zu stellen (in diesem Fall durch das Entfernen des Filters).

Danach checkte ich alle Plugins, ob dieser best practice auch gefolgt wurde und fand schnell das schuldige Plugin, dass also den Content Type auf text/html änderte, aber nicht zurückstellte, was auslöste, dass nun alle Mails als HTML versendet wurden, auch wenn es Plain-Text-Mails waren. Und so lernte ich, dass selbst den besten Kollegen auch mal so banale Fehler passieren können 😉

Das sind also schon mal zwei mögliche Ursachen für fehlende Zeilenumbrüche in HTML-Mails bei WordPress. Kennst du noch mehr?

Wer das schuldige Plugin bei sich nicht findet, kann mich gerne für das Debugging buchen. Falls du den Fehler schon gefunden hast und es eine andere Erklärung ist, also im Artikel erwähnt, dann gerne ab damit in die Kommentare!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert