Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[FEATURE] Allow user to define media types to keep #200

Merged
merged 1 commit into from
Jul 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions Classes/Emogrifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class Emogrifier
*/
private $unprocessableHtmlTags = array('wbr');

/**
* @var bool[]
*/
private $allowedMediaTypes = array('all' => true, 'screen' => true, 'print' => true);

/**
* @var array[]
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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];
},
Expand All @@ -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);
}

/**
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 37 additions & 2 deletions Tests/Unit/EmogrifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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;}}'),
);
}
Expand All @@ -962,6 +962,42 @@ public function emogrifyKeepsMediaRules($css)
);
}

/**
* @test
*/
public function removeAllowedMediaTypeRemovesStylesForTheGivenMediaType()
{
$css = '@media screen { html {} }';

$html = $this->html5DocumentType . self::LF . '<html></html>';
$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 . '<html></html>';
$this->subject->setHtml($html);
$this->subject->setCss($css);
$this->subject->addAllowedMediaType('braille');

self::assertContains(
$css,
$this->subject->emogrify()
);
}

/**
* @test
*/
Expand Down Expand Up @@ -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; } }'),
Expand Down