From ae7fb74af9400c32e8ddda58b9c2e7a51897e0aa Mon Sep 17 00:00:00 2001 From: Rene Saare Date: Fri, 19 Jun 2015 15:50:03 +0300 Subject: [PATCH] [FEATURE] Allow user to define media types to keep Also keep the print media type by default. Fixes #68 FIxes #178 --- Classes/Emogrifier.php | 44 +++++++++++++++++++++++++++++++---- README.md | 5 ++++ Tests/Unit/EmogrifierTest.php | 39 +++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/Classes/Emogrifier.php b/Classes/Emogrifier.php index 1a97cc70..74c46c73 100644 --- a/Classes/Emogrifier.php +++ b/Classes/Emogrifier.php @@ -78,6 +78,11 @@ class Emogrifier */ private $unprocessableHtmlTags = array('wbr'); + /** + * @var bool[] + */ + private $allowedMediaTypes = array('all' => true, 'screen' => true, 'print' => true); + /** * @var array[] */ @@ -407,6 +412,32 @@ public function removeUnprocessableHtmlTag($tagName) } } + /** + * Marks a media query type to keep. + * + * @param string $mediaName the media type name, e.g., "braille" + * + * @return void + */ + public function addAllowedMediaType($mediaName) + { + $this->allowedMediaTypes[$mediaName] = true; + } + + /** + * Drops a media query type from the allowed list. + * + * @param string $mediaName the tag name, e.g., "braille" + * + * @return void + */ + public function removeAllowedMediaType($mediaName) + { + if (isset($this->allowedMediaTypes[$mediaName])) { + unset($this->allowedMediaTypes[$mediaName]); + } + } + /** * This removes styles from your email that contain display:none. * We need to look for display:none, but we need to do a case-insensitive search. Since DOMDocument only @@ -615,8 +646,13 @@ private function splitCssAndMediaQuery($css) { $media = ''; - $css = preg_replace_callback( - '#@media\\s+(?:only\\s)?(?:[\\s{\\(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#misU', + $mediaTypesExpression = ''; + if (!empty($this->allowedMediaTypes)) { + $mediaTypesExpression = '|' . implode('|', array_keys($this->allowedMediaTypes)); + } + + $cssForAllowedMediaTypes = preg_replace_callback( + '#@media\\s+(?:only\\s)?(?:[\\s{\\(]' . $mediaTypesExpression . ')\\s?[^{]+{.*}\\s*}\\s*#misU', function ($matches) use (&$media) { $media .= $matches[0]; }, @@ -640,9 +676,9 @@ function ($matches) use (&$media) { ); // clean CSS before output - $css = preg_replace($search, $replace, $css); + $cleanedCss = preg_replace($search, $replace, $cssForAllowedMediaTypes); - return array('css' => $css, 'media' => $media); + return array('css' => $cleanedCss, 'media' => $media); } /** diff --git a/README.md b/README.md index bdb88837..e3f6fc98 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,11 @@ calling the `emogrify` method: * `$emogrifier->disableInvisibleNodeRemoval()` - By default, Emogrifier removes elements from the DOM that have the style attribute `display: none;`. If you would like to keep invisible elements in the DOM, use this option. +* `$emogrifier->addAllowedMediaType(string $mediaName)` - By default, Emogrifier + will keep only media types `all`, `screen` and `print`. If you want to keep + some others, you can use this method to define them. +* `$emogrifier->removeAllowedMediaType(string $mediaName)` - You can use this + method to remove media types that Emogrifier keeps. ## Installing with Composer diff --git a/Tests/Unit/EmogrifierTest.php b/Tests/Unit/EmogrifierTest.php index b66a4b29..b723a228 100644 --- a/Tests/Unit/EmogrifierTest.php +++ b/Tests/Unit/EmogrifierTest.php @@ -899,7 +899,6 @@ public function unneededCssThingsDataProvider() 'style in "braille" media type rule' => array('@media braille {p {color: #000;}}', '#000'), 'style in "embossed" media type rule' => array('@media embossed {p {color: #000;}}', '#000'), 'style in "handheld" media type rule' => array('@media handheld {p {color: #000;}}', '#000'), - 'style in "print" media type rule' => array('@media print {p {color: #000;}}', '#000'), 'style in "projection" media type rule' => array('@media projection {p {color: #000;}}', '#000'), 'style in "speech" media type rule' => array('@media speech {p {color: #000;}}', '#000'), 'style in "tty" media type rule' => array('@media tty {p {color: #000;}}', '#000'), @@ -939,6 +938,7 @@ public function mediaRulesDataProvider() 'style in "only screen" media type rule' => array('@media only screen {p {color: #000;}}'), 'style in media type rule' => array('@media {p {color: #000;}}'), 'style in "screen" media type rule' => array('@media screen {p {color: #000;}}'), + 'style in "print" media type rule' => array('@media print {p {color: #000;}}'), 'style in "all" media type rule' => array('@media all {p {color: #000;}}'), ); } @@ -962,6 +962,42 @@ public function emogrifyKeepsMediaRules($css) ); } + /** + * @test + */ + public function removeAllowedMediaTypeRemovesStylesForTheGivenMediaType() + { + $css = '@media screen { html {} }'; + + $html = $this->html5DocumentType . self::LF . ''; + $this->subject->setHtml($html); + $this->subject->setCss($css); + $this->subject->removeAllowedMediaType('screen'); + + self::assertNotContains( + $css, + $this->subject->emogrify() + ); + } + + /** + * @test + */ + public function addAllowedMediaTypeKeepsStylesForTheGivenMediaType() + { + $css = '@media braille { html {} }'; + + $html = $this->html5DocumentType . self::LF . ''; + $this->subject->setHtml($html); + $this->subject->setCss($css); + $this->subject->addAllowedMediaType('braille'); + + self::assertContains( + $css, + $this->subject->emogrify() + ); + } + /** * @test */ @@ -1111,7 +1147,6 @@ public function invalidMediaPreserveDataProvider() 'style in "braille" type rule' => array('@media braille { h1 { color:red; } }'), 'style in "embossed" type rule' => array('@media embossed { h1 { color:red; } }'), 'style in "handheld" type rule' => array('@media handheld { h1 { color:red; } }'), - 'style in "print" type rule' => array('@media print { h1 { color:red; } }'), 'style in "projection" type rule' => array('@media projection { h1 { color:red; } }'), 'style in "speech" type rule' => array('@media speech { h1 { color:red; } }'), 'style in "tty" type rule' => array('@media tty { h1 { color:red; } }'),