Skip to content

Commit e30633f

Browse files
committed
Merge branch 'MC-15022' into cms-team-1-delivery
2 parents c095644 + b4b06be commit e30633f

21 files changed

+253
-21
lines changed

app/code/Magento/PageBuilder/Plugin/Filter/TemplatePlugin.php

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,24 @@ class TemplatePlugin
3131
*/
3232
private $domDocument;
3333

34+
/**
35+
* @var \Magento\Framework\Math\Random
36+
*/
37+
private $mathRandom;
38+
3439
/**
3540
* @param \Psr\Log\LoggerInterface $logger
3641
* @param \Magento\Framework\View\ConfigInterface $viewConfig
42+
* @param \Magento\Framework\Math\Random $mathRandom
3743
*/
3844
public function __construct(
3945
\Psr\Log\LoggerInterface $logger,
40-
\Magento\Framework\View\ConfigInterface $viewConfig
46+
\Magento\Framework\View\ConfigInterface $viewConfig,
47+
\Magento\Framework\Math\Random $mathRandom
4148
) {
4249
$this->logger = $logger;
4350
$this->viewConfig = $viewConfig;
51+
$this->mathRandom = $mathRandom;
4452
}
4553

4654
/**
@@ -65,7 +73,7 @@ public function afterFilter(\Magento\Framework\Filter\Template $subject, string
6573
// Process any HTML content types, they need to be decoded on the front-end
6674
if (preg_match(self::HTML_CONTENT_TYPE_PATTERN, $result)) {
6775
$document = $this->getDomDocument($result);
68-
$this->decodeHtmlContentTypes($document);
76+
$uniqueNodeNameToDecodedOuterHtmlMap = $this->generateDecodedHtmlPlaceholderMappingInDocument($document);
6977
}
7078

7179
// If a document was retrieved we've modified the output so need to retrieve it from within the document
@@ -77,7 +85,21 @@ public function afterFilter(\Magento\Framework\Filter\Template $subject, string
7785
$matches
7886
);
7987

80-
return !empty($matches) ? $matches[1] : $result;
88+
if (!empty($matches)) {
89+
$docHtml = $matches[1];
90+
91+
if (isset($uniqueNodeNameToDecodedOuterHtmlMap)) {
92+
foreach ($uniqueNodeNameToDecodedOuterHtmlMap as $uniqueNodeName => $decodedOuterHtml) {
93+
$docHtml = str_replace(
94+
'<' . $uniqueNodeName . '>' . '</' . $uniqueNodeName . '>',
95+
$decodedOuterHtml,
96+
$docHtml
97+
);
98+
}
99+
}
100+
101+
$result = $docHtml;
102+
}
81103
}
82104

83105
return $result;
@@ -131,31 +153,48 @@ function ($errorNumber, $errorString) {
131153
}
132154

133155
/**
134-
* Decode the contents of any HTML content types for the store front
156+
* Convert encoded HTML content types to placeholders and generate decoded outer html map for future replacement
135157
*
136158
* @param \DOMDocument $document
159+
* @return array - map of unique node name to decoded html
137160
*/
138-
private function decodeHtmlContentTypes(\DOMDocument $document): void
161+
private function generateDecodedHtmlPlaceholderMappingInDocument(\DOMDocument $document): array
139162
{
140163
$xpath = new \DOMXPath($document);
141-
$nodes = $xpath->query('//*[@data-content-type="html" and not(@data-decoded="true")]');
142-
foreach ($nodes as $node) {
143-
if (strlen(trim($node->nodeValue)) > 0) {
144-
/* @var \DOMElement $node */
145-
$fragment = $document->createDocumentFragment();
146-
$fragment->appendXML($node->nodeValue);
147-
// Store a decoded attribute on the element so we don't double decode
148-
$node->setAttribute("data-decoded", "true");
149-
$node->nodeValue = "";
150-
151-
// If the HTML code in the content type is invalid it may throw
152-
try {
153-
$node->appendChild($fragment);
154-
} catch (\Exception $e) {
155-
$this->logger->critical($e);
156-
}
164+
165+
// construct xpath query to fetch top-level ancestor html content type nodes
166+
/** @var $htmlContentTypeNodes \DOMNode[] */
167+
$htmlContentTypeNodes = $xpath->query(
168+
'//*[@data-content-type="html" and not(@data-decoded="true")]' .
169+
'[not(ancestor::*[@data-content-type="html"])]'
170+
);
171+
172+
$uniqueNodeNameToDecodedOuterHtmlMap = [];
173+
174+
foreach ($htmlContentTypeNodes as $htmlContentTypeNode) {
175+
// Set decoded attribute on all encoded html content types so we don't double decode;
176+
$htmlContentTypeNode->setAttribute('data-decoded', 'true');
177+
178+
// if nothing exists inside the node, continue
179+
if (!strlen(trim($htmlContentTypeNode->nodeValue))) {
180+
continue;
157181
}
182+
183+
$preDecodedOuterHtml = $document->saveHTML($htmlContentTypeNode);
184+
$decodedOuterHtml = html_entity_decode($preDecodedOuterHtml);
185+
186+
// generate unique node name element to replace with decoded html contents at end of processing;
187+
// goal is to create a document as few times as possible to prevent inadvertent parsing of contents as html
188+
// by the dom library
189+
$uniqueNodeName = $this->mathRandom->getRandomString(32, $this->mathRandom::CHARS_LOWERS);
190+
191+
$uniqueNode = new \DOMElement($uniqueNodeName);
192+
$htmlContentTypeNode->parentNode->replaceChild($uniqueNode, $htmlContentTypeNode);
193+
194+
$uniqueNodeNameToDecodedOuterHtmlMap[$uniqueNodeName] = $decodedOuterHtml;
158195
}
196+
197+
return $uniqueNodeNameToDecodedOuterHtmlMap;
159198
}
160199

161200
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
// @codingStandardsIgnoreFile
8+
9+
?>
10+
<div data-content-type="html">
11+
&lt;img src=&quot;http://example.com&quot;&gt;
12+
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/owsfdh4gxyc&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
13+
&amp;amp;
14+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
// @codingStandardsIgnoreFile
8+
9+
?>
10+
<div>
11+
<img src="http://example.com/block">
12+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
// @codingStandardsIgnoreFile
8+
9+
?>
10+
Hello world
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\PageBuilder\Plugin\Filter;
10+
11+
use Magento\Widget\Model\Template\Filter as TemplateFilter;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
14+
/**
15+
* @magentoAppArea frontend
16+
*/
17+
class TemplatePluginTest extends \PHPUnit\Framework\TestCase
18+
{
19+
/**
20+
* @var \Magento\Framework\ObjectManagerInterface
21+
*/
22+
private $objectManager;
23+
24+
/**
25+
* @var TemplateFilter
26+
*/
27+
private $templateFilter;
28+
29+
protected function setUp()
30+
{
31+
$this->objectManager = Bootstrap::getObjectManager();
32+
$this->templateFilter = $this->objectManager->get(TemplateFilter::class);
33+
}
34+
35+
/**
36+
* @param string $preFiltered
37+
* @param string $postFiltered
38+
* @param string $preFilteredBasename
39+
* @dataProvider filterDataProvider
40+
*/
41+
public function testFiltering(string $preFiltered, string $postFiltered, string $preFilteredBasename)
42+
{
43+
$this->assertEquals(
44+
$postFiltered,
45+
$this->templateFilter->filter($preFiltered),
46+
"Failed asserting that two strings are equal after filtering $preFilteredBasename"
47+
);
48+
}
49+
50+
/**
51+
* @return array
52+
*/
53+
public function filterDataProvider(): array
54+
{
55+
$preFilteredFiles = glob(__DIR__ . '/../../_files/template_plugin/*pre_filter*');
56+
57+
$dataProviderArgs = [];
58+
59+
foreach ($preFilteredFiles as $preFilteredFile) {
60+
$preFilteredBasename = basename($preFilteredFile);
61+
$postFilteredFile = pathinfo($preFilteredFile, PATHINFO_DIRNAME) . '/' . str_replace(
62+
'pre_filter',
63+
'post_filter',
64+
$preFilteredBasename
65+
);
66+
67+
$dataProviderArgs[] = [
68+
file_get_contents($preFilteredFile),
69+
file_get_contents($postFilteredFile),
70+
$preFilteredBasename
71+
];
72+
}
73+
74+
return $dataProviderArgs;
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div data-content-type="html" data-decoded="true">&amp;<img src="http://example.com"><iframe width="560" height="315" src="https://www.youtube.com/embed/owsfdh4gxyc" frameborder="0" allowfullscreen></iframe></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div data-content-type="html" data-decoded="true">&amp;<img src="http://example.com"><iframe width="560" height="315" src="https://www.youtube.com/embed/owsfdh4gxyc" frameborder="0" allowfullscreen></iframe></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<div data-content-type="html" data-appearance="default" data-element="main" style="border-style: none; border-width: 1px; border-radius: 0px; margin: 0px; padding: 0px;" data-decoded="true"><div id="fb-root"></div>
2+
<script async defer src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.2&appId=1&autoLogAppEvents=1"></script>
3+
4+
<div class="fb-page" data-href="https://www.facebook.com/facebook" data-tabs="timeline" data-small-header="false"
5+
data-adapt-container-width="true" data-hide-cover="false" data-show-facepile="true"><blockquote
6+
cite="https://www.facebook.com/facebook" class="fb-xfbml-parse-ignore"><a
7+
href="https://www.facebook.com/facebook">Facebook</a></blockquote></div>
8+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div data-content-type="html" data-appearance="default" data-element="main"
2+
style="border-style: none; border-width: 1px; border-radius: 0px; margin: 0px; padding: 0px;">&lt;div id="fb-root"&gt;&lt;/div&gt;
3+
&lt;script async defer src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&amp;version=v3.2&amp;appId=1&amp;autoLogAppEvents=1"&gt;&lt;/script&gt;
4+
5+
&lt;div class="fb-page" data-href="https://www.facebook.com/facebook" data-tabs="timeline" data-small-header="false"
6+
data-adapt-container-width="true" data-hide-cover="false" data-show-facepile="true"&gt;&lt;blockquote
7+
cite="https://www.facebook.com/facebook" class="fb-xfbml-parse-ignore"&gt;&lt;a
8+
href="https://www.facebook.com/facebook"&gt;Facebook&lt;/a&gt;&lt;/blockquote&gt;&lt;/div&gt;
9+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div data-content-type="html" data-decoded="true">
2+
3 < 4 > 2
3+
</div>

0 commit comments

Comments
 (0)