PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.3
MailPoet – Newsletters, Email Marketing, and Automation v5.34.3
5.36.1 5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / pelago / emogrifier / src / HtmlProcessor / CssToAttributeConverter.php
mailpoet / vendor-prefixed / pelago / emogrifier / src / HtmlProcessor Last commit date
AbstractHtmlProcessor.php 2 years ago CssToAttributeConverter.php 2 years ago HtmlNormalizer.php 2 years ago HtmlPruner.php 2 years ago index.php 2 years ago
CssToAttributeConverter.php
141 lines
1 <?php
2 declare (strict_types=1);
3 namespace MailPoetVendor\Pelago\Emogrifier\HtmlProcessor;
4 if (!defined('ABSPATH')) exit;
5 class CssToAttributeConverter extends AbstractHtmlProcessor
6 {
7 private $cssToHtmlMap = ['background-color' => ['attribute' => 'bgcolor'], 'text-align' => ['attribute' => 'align', 'nodes' => ['p', 'div', 'td', 'th'], 'values' => ['left', 'right', 'center', 'justify']], 'float' => ['attribute' => 'align', 'nodes' => ['table', 'img'], 'values' => ['left', 'right']], 'border-spacing' => ['attribute' => 'cellspacing', 'nodes' => ['table']]];
8 private static $parsedCssCache = [];
9 public function convertCssToVisualAttributes() : self
10 {
11 foreach ($this->getAllNodesWithStyleAttribute() as $node) {
12 $inlineStyleDeclarations = $this->parseCssDeclarationsBlock($node->getAttribute('style'));
13 $this->mapCssToHtmlAttributes($inlineStyleDeclarations, $node);
14 }
15 return $this;
16 }
17 private function getAllNodesWithStyleAttribute() : \DOMNodeList
18 {
19 return $this->getXPath()->query('//*[@style]');
20 }
21 private function parseCssDeclarationsBlock(string $cssDeclarationsBlock) : array
22 {
23 if (isset(self::$parsedCssCache[$cssDeclarationsBlock])) {
24 return self::$parsedCssCache[$cssDeclarationsBlock];
25 }
26 $properties = [];
27 foreach (\preg_split('/;(?!base64|charset)/', $cssDeclarationsBlock) as $declaration) {
28 $matches = [];
29 if (!\preg_match('/^([A-Za-z\\-]+)\\s*:\\s*(.+)$/s', \trim($declaration), $matches)) {
30 continue;
31 }
32 $propertyName = \strtolower($matches[1]);
33 $propertyValue = $matches[2];
34 $properties[$propertyName] = $propertyValue;
35 }
36 self::$parsedCssCache[$cssDeclarationsBlock] = $properties;
37 return $properties;
38 }
39 private function mapCssToHtmlAttributes(array $styles, \DOMElement $node) : void
40 {
41 foreach ($styles as $property => $value) {
42 // Strip !important indicator
43 $value = \trim(\str_replace('!important', '', $value));
44 $this->mapCssToHtmlAttribute($property, $value, $node);
45 }
46 }
47 private function mapCssToHtmlAttribute(string $property, string $value, \DOMElement $node) : void
48 {
49 if (!$this->mapSimpleCssProperty($property, $value, $node)) {
50 $this->mapComplexCssProperty($property, $value, $node);
51 }
52 }
53 private function mapSimpleCssProperty(string $property, string $value, \DOMElement $node) : bool
54 {
55 if (!isset($this->cssToHtmlMap[$property])) {
56 return \false;
57 }
58 $mapping = $this->cssToHtmlMap[$property];
59 $nodesMatch = !isset($mapping['nodes']) || \in_array($node->nodeName, $mapping['nodes'], \true);
60 $valuesMatch = !isset($mapping['values']) || \in_array($value, $mapping['values'], \true);
61 $canBeMapped = $nodesMatch && $valuesMatch;
62 if ($canBeMapped) {
63 $node->setAttribute($mapping['attribute'], $value);
64 }
65 return $canBeMapped;
66 }
67 private function mapComplexCssProperty(string $property, string $value, \DOMElement $node) : void
68 {
69 switch ($property) {
70 case 'background':
71 $this->mapBackgroundProperty($node, $value);
72 break;
73 case 'width':
74 // intentional fall-through
75 case 'height':
76 $this->mapWidthOrHeightProperty($node, $value, $property);
77 break;
78 case 'margin':
79 $this->mapMarginProperty($node, $value);
80 break;
81 case 'border':
82 $this->mapBorderProperty($node, $value);
83 break;
84 default:
85 }
86 }
87 private function mapBackgroundProperty(\DOMElement $node, string $value) : void
88 {
89 // parse out the color, if any
90 $styles = \explode(' ', $value, 2);
91 $first = $styles[0];
92 if (\is_numeric($first[0]) || \strncmp($first, 'url', 3) === 0) {
93 return;
94 }
95 // as this is not a position or image, assume it's a color
96 $node->setAttribute('bgcolor', $first);
97 }
98 private function mapWidthOrHeightProperty(\DOMElement $node, string $value, string $property) : void
99 {
100 // only parse values in px and %, but not values like "auto"
101 if (!\preg_match('/^(\\d+)(\\.(\\d+))?(px|%)$/', $value)) {
102 return;
103 }
104 $number = \preg_replace('/[^0-9.%]/', '', $value);
105 $node->setAttribute($property, $number);
106 }
107 private function mapMarginProperty(\DOMElement $node, string $value) : void
108 {
109 if (!$this->isTableOrImageNode($node)) {
110 return;
111 }
112 $margins = $this->parseCssShorthandValue($value);
113 if ($margins['left'] === 'auto' && $margins['right'] === 'auto') {
114 $node->setAttribute('align', 'center');
115 }
116 }
117 private function mapBorderProperty(\DOMElement $node, string $value) : void
118 {
119 if (!$this->isTableOrImageNode($node)) {
120 return;
121 }
122 if ($value === 'none' || $value === '0') {
123 $node->setAttribute('border', '0');
124 }
125 }
126 private function isTableOrImageNode(\DOMElement $node) : bool
127 {
128 return $node->nodeName === 'table' || $node->nodeName === 'img';
129 }
130 private function parseCssShorthandValue(string $value) : array
131 {
132 $values = \preg_split('/\\s+/', $value);
133 $css = [];
134 $css['top'] = $values[0];
135 $css['right'] = \count($values) > 1 ? $values[1] : $css['top'];
136 $css['bottom'] = \count($values) > 2 ? $values[2] : $css['top'];
137 $css['left'] = \count($values) > 3 ? $values[3] : $css['right'];
138 return $css;
139 }
140 }
141