Skip to content

Commit

Permalink
[BUGFIX] Apply CSS attribute names regardles of case, outputting lowe…
Browse files Browse the repository at this point in the history
…rcase attributes and selectors

\#myID P { margin: 0px; } was not being applied correctly from style block
p { MARGIN: 0PX; } was not being applied correctly from style block
all inlined css from passed in css and parsed out <style> blocks is now lowercase

[BUGFIX]Prevent incorrectly capitalized CSS selectors from being stripped

Some wysiwyg editors seem to generate terrible CSS such as:
P {
    PADDING: 0PX;
    MARGIN: 0PX;
}
which due to the capitalized P was not being applied inline.

[BUGFIX] Apply CSS attribute names regardles of case, outputting lowercase attributes and selectors

p { MARGIN: 0PX; } was not being applied correctly from style block
all inlined css from passed in css and parsed out <style> blocks is now lowercase

Prevent incorrectly capitalized CSS selectors from being stripped

Some wysiwyg editors seem to generate terrible CSS such as:
P {
    PADDING: 0PX;
    MARGIN: 0PX;
}
which due to the capitalized P was not being applied inline.

fix my dumb

replace deprecated preg_replace /e with preg_replace_callback to pass php 5.6 travis tests

fix my dumb

replace deprecated preg_replace /e with preg_replace_callback to pass php 5.6 travis tests

Change undercase behavior to not change the case of the vss attribute value.  Font names, content property (http://www.w3schools.com/cssref/pr_gen_content.asp) should be left alone.
This fix exposes an inconsistency in the output CSS.  Now all code paths output the css with no spacing around the : or ; which is breaking approx 25 unit tests that will be updated for the next commit.

update the unit tests to expect no spaces in all resulting CSS instead of the mixed ouput based on what code path created it.
update the regex to not skip when uppercase chars were present, added todo for possibly removing preg_match for less expensive explode.
removed a few extra var_dumps I had left behind

ran the .travis stuff manually to verify formatting / unit tests / lint and fixed formatting a bit, removed TODO but left comment in order to avoid generating a warning
  • Loading branch information
Jared Chmielecki committed Jun 18, 2014
1 parent 0e85318 commit ccd31b7
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 59 deletions.
60 changes: 36 additions & 24 deletions Classes/Emogrifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,42 +315,27 @@ function (array $m) {

foreach ($this->caches[self::CACHE_KEY_CSS][$cssKey] as $value) {
// query the body for the xpath selector
$nodesMatchingCssSelectors = $xpath->query($this->translateCssToXpath(trim($value['selector'])));
$nodesMatchingCssSelectors = $xpath->query($this->translateCssToXpath($value['selector']));

/** @var $node \DOMNode */
foreach ($nodesMatchingCssSelectors as $node) {
// if it has a style attribute, get it, process it, and append (overwrite) new stuff
if ($node->hasAttribute('style')) {
// break it up into an associative array
$oldStyleDeclarations = $this->parseCssDeclarationBlock($node->getAttribute('style'));
$newStyleDeclarations = $this->parseCssDeclarationBlock($value['attributes']);

// new styles overwrite the old styles (not technically accurate, but close enough)
$combinedArray = array_merge($oldStyleDeclarations, $newStyleDeclarations);
$style = '';
foreach ($combinedArray as $attributeName => $attributeValue) {
$style .= (strtolower($attributeName) . ':' . $attributeValue . ';');
}
} else {
// otherwise create a new style
$style = trim($value['attributes']);
$oldStyleDeclarations = array();
}
$node->setAttribute('style', $style);
$newStyleDeclarations = $this->parseCssDeclarationBlock($value['attributes']);
$node->setAttribute('style', $this->generateStyleStringFromDeclarationsArrays($oldStyleDeclarations, $newStyleDeclarations));
}
}

// now iterate through the nodes that contained inline styles in the original HTML
foreach ($this->styleAttributesForNodes as $nodePath => $styleAttributesForNode) {
$node = $this->visitedNodes[$nodePath];
$currentStyleAttributes = $this->parseCssDeclarationBlock($node->getAttribute('style'));

$combinedArray = array_merge($currentStyleAttributes, $styleAttributesForNode);
$style = '';
foreach ($combinedArray as $attributeName => $attributeValue) {
$style .= (strtolower($attributeName) . ':' . $attributeValue . ';');
}

$node->setAttribute('style', $style);
$node->setAttribute('style', $this->generateStyleStringFromDeclarationsArrays($currentStyleAttributes, $styleAttributesForNode));
}

// This removes styles from your email that contain display:none.
Expand Down Expand Up @@ -378,6 +363,26 @@ function (array $m) {
}
}


/**
* This method merges old or existing name/value array with new name/value array
* and then generates a string of the combined style suitable for placing inline.
* This becomes the single point for CSS string generation allowing for consistent
* CSS output no matter where the CSS originally came from.
* @param array $oldStyles
* @param array $newStyles
* @return string
*/
private function generateStyleStringFromDeclarationsArrays(array $oldStyles, array $newStyles) {
$combinedStyles = array_merge($oldStyles, $newStyles);
$style = '';
foreach ($combinedStyles as $attributeName => $attributeValue) {
$style .= (strtolower(trim($attributeName)) . ': ' . trim($attributeValue) . '; ');
}
return trim($style);
}


/**
* Copies the media part from CSS array parts to $xmlDocument.
*
Expand Down Expand Up @@ -591,11 +596,18 @@ private function getCssSelectorPrecedence($selector) {
*
* @see http://plasmasturm.org/log/444/
*
* @param string $cssSelector
* @param string $paramCssSelector
*
* @return string
*/
private function translateCssToXpath($cssSelector) {
private function translateCssToXpath($paramCssSelector) {
$cssSelector = ' ' . $paramCssSelector . ' ';
$cssSelector = preg_replace_callback('/\s+\w+\s+/',
function(array $matches) {
return strtolower($matches[0]);
},
$cssSelector
);
$cssSelector = trim($cssSelector);
$xpathKey = md5($cssSelector);
if (!isset($this->caches[self::CACHE_KEY_XPATH][$xpathKey])) {
Expand Down Expand Up @@ -779,10 +791,10 @@ private function parseCssDeclarationBlock($cssDeclarationBlock) {
$declarations = explode(';', $cssDeclarationBlock);
foreach ($declarations as $declaration) {
$matches = array();
if (!preg_match('/ *([a-z\\-]+) *: *([^;]+) */', $declaration, $matches)) {
if (!preg_match('/ *([A-Za-z\\-]+) *: *([^;]+) */', $declaration, $matches)) {
continue;
}
$propertyName = $matches[1];
$propertyName = strtolower($matches[1]);
$propertyValue = $matches[2];
$properties[$propertyName] = $propertyValue;
}
Expand Down
Loading

0 comments on commit ccd31b7

Please sign in to comment.