Skip to content

Commit 8a60f1a

Browse files
committed
Merge branch 'MC-928-background-attributes-row' into cms-team-1-delivery
2 parents 1693f2a + b4bfec2 commit 8a60f1a

30 files changed

+1333
-1255
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PageBuilder\Plugin\Filter;
9+
10+
/**
11+
* Plugin to the template filter to process any background images added by Page Builder
12+
*/
13+
class TemplatePlugin
14+
{
15+
const DATA_BACKGROUND_IMAGE = 'data-background-images';
16+
17+
/**
18+
* @var \Magento\Framework\View\ConfigInterface
19+
*/
20+
private $viewConfig;
21+
22+
/**
23+
* @var \Psr\Log\LoggerInterface
24+
*/
25+
private $logger;
26+
27+
/**
28+
* @param \Psr\Log\LoggerInterface $logger
29+
* @param \Magento\Framework\View\ConfigInterface $viewConfig
30+
*/
31+
public function __construct(
32+
\Psr\Log\LoggerInterface $logger,
33+
\Magento\Framework\View\ConfigInterface $viewConfig
34+
) {
35+
$this->logger = $logger;
36+
$this->viewConfig = $viewConfig;
37+
}
38+
39+
/**
40+
* @param \Magento\Framework\Filter\Template $subject
41+
* @param string $result
42+
*
43+
* @return string
44+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
45+
*/
46+
public function afterFilter(\Magento\Framework\Filter\Template $subject, string $result) : string
47+
{
48+
// Validate if the filtered result requires background image processing
49+
if (strpos($result, self::DATA_BACKGROUND_IMAGE) !== false) {
50+
$domDocument = new \DOMDocument('1.0', 'UTF-8');
51+
set_error_handler(
52+
function ($errorNumber, $errorString) {
53+
throw new \Exception($errorString, $errorNumber);
54+
}
55+
);
56+
$string = mb_convert_encoding($result, 'HTML-ENTITIES', 'UTF-8');
57+
$wrapperElementId = uniqid();
58+
try {
59+
libxml_use_internal_errors(true);
60+
$domDocument->loadHTML(
61+
'<html><body id="' . $wrapperElementId . '">' . $string . '</body></html>'
62+
);
63+
libxml_clear_errors();
64+
} catch (\Exception $e) {
65+
restore_error_handler();
66+
$this->logger->critical($e);
67+
}
68+
restore_error_handler();
69+
$xpath = new \DOMXPath($domDocument);
70+
71+
$this->generateBackgroundImageStyles($xpath);
72+
73+
// Match the contents of the body from our generated document
74+
preg_match(
75+
'/<body id="' . $wrapperElementId . '">(.+)<\/body><\/html>$/si',
76+
mb_convert_encoding($domDocument->saveHTML(), 'UTF-8', 'HTML-ENTITIES'),
77+
$matches
78+
);
79+
80+
return !empty($matches) ? $matches[1] : $result;
81+
}
82+
return $result;
83+
}
84+
85+
/**
86+
* Generate the CSS for any background images on the page
87+
*
88+
* @param \DOMXPath $xpath
89+
*/
90+
private function generateBackgroundImageStyles(\DOMXPath $xpath) : void
91+
{
92+
$nodes = $xpath->query('//*[@' . self:: DATA_BACKGROUND_IMAGE . ']');
93+
foreach ($nodes as $node) {
94+
/* @var \DOMElement $node */
95+
$backgroundImages = $node->attributes->getNamedItem(self:: DATA_BACKGROUND_IMAGE);
96+
if ($backgroundImages->nodeValue !== '') {
97+
$elementClass = uniqid('background-image-');
98+
$images = json_decode(stripslashes($backgroundImages->nodeValue), true);
99+
if (count($images) > 0) {
100+
$style = $xpath->document->createElement(
101+
'style',
102+
$this->generateCssFromImages($elementClass, $images)
103+
);
104+
$style->setAttribute("type", "text/css");
105+
$node->parentNode->appendChild($style);
106+
107+
// Append our new class to the DOM element
108+
$classes = '';
109+
if ($node->attributes->getNamedItem('class')) {
110+
$classes = $node->attributes->getNamedItem('class')->nodeValue . ' ';
111+
}
112+
$node->setAttribute('class', $classes . $elementClass);
113+
}
114+
}
115+
}
116+
}
117+
118+
/**
119+
* Generate CSS based on the images array from our attribute
120+
*
121+
* @param string $elementClass
122+
* @param array $images
123+
*
124+
* @return string
125+
*/
126+
private function generateCssFromImages(string $elementClass, array $images) : string
127+
{
128+
$css = [];
129+
if (isset($images['desktop_image'])) {
130+
$css['.' . $elementClass] = [
131+
'background-image' => 'url(' . $images['desktop_image'] . ')',
132+
];
133+
}
134+
if (isset($images['mobile_image']) && $this->getMobileMediaQuery()) {
135+
$css[$this->getMobileMediaQuery()]['.' . $elementClass] = [
136+
'background-image' => 'url(' . $images['mobile_image'] . ')',
137+
];
138+
}
139+
return $this->cssFromArray($css);
140+
}
141+
142+
/**
143+
* Generate a CSS string from an array
144+
*
145+
* @param array $css
146+
*
147+
* @return string
148+
*/
149+
private function cssFromArray(array $css) : string
150+
{
151+
$output = "";
152+
foreach ($css as $selector => $body) {
153+
if (is_array($body)) {
154+
$output .= $selector . " {";
155+
$output .= $this->cssFromArray($body);
156+
$output .= "}";
157+
} else {
158+
$output .= $selector . ': ' . $body . ";";
159+
}
160+
}
161+
return $output;
162+
}
163+
164+
/**
165+
* Generate the mobile media query from view configuration
166+
*
167+
* @return null|string
168+
*/
169+
private function getMobileMediaQuery() : ?string
170+
{
171+
$breakpoints = $this->viewConfig->getViewConfig()->getVarValue(
172+
'Magento_PageBuilder',
173+
'breakpoints/mobile/conditions'
174+
);
175+
if ($breakpoints && count($breakpoints) > 0) {
176+
$mobileBreakpoint = "@media only screen ";
177+
foreach ($breakpoints as $key => $value) {
178+
$mobileBreakpoint .= "and (" . $key . ": " . $value . ") ";
179+
}
180+
return rtrim($mobileBreakpoint);
181+
}
182+
return null;
183+
}
184+
}

app/code/Magento/PageBuilder/Test/Mftf/ActionGroup/BackgroundConfigurationActionGroup.xml

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,25 @@
88

99
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1010
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="validateBackgroundAttributes">
12+
<arguments>
13+
<argument name="section"/>
14+
<argument name="backgroundColor"/>
15+
<argument name="backgroundImage"/>
16+
<argument name="backgroundPosition" defaultValue="PageBuilderBackgroundPosition_Default"/>
17+
<argument name="backgroundSize" defaultValue="PageBuilderBackgroundSize_Default"/>
18+
<argument name="backgroundRepeat" defaultValue="PageBuilderBackgroundRepeat_Default"/>
19+
<argument name="backgroundAttachment" defaultValue="PageBuilderBackgroundAttachment_Default"/>
20+
<argument name="index" defaultValue="1" type="string"/>
21+
</arguments>
22+
<waitForElementVisible selector="{{section.base(index)}}" stepKey="waitForBaseElement"/>
23+
<waitForElementVisible selector="{{section.backgroundColor(index, backgroundColor.rgb)}}" stepKey="waitForBackgroundColor"/>
24+
<waitForElementVisible selector="{{section.backgroundImage(index, backgroundImage.fileName)}}" stepKey="waitForBackgroundImage"/>
25+
<waitForElementVisible selector="{{section.backgroundPosition(index, backgroundPosition.styleValue)}}" stepKey="waitForBackgroundPosition"/>
26+
<waitForElementVisible selector="{{section.backgroundSize(index, backgroundSize.value)}}" stepKey="waitForBackgroundSize"/>
27+
<waitForElementVisible selector="{{section.backgroundRepeat(index, backgroundRepeat.styleValue)}}" stepKey="waitForBackgroundRepeat"/>
28+
<waitForElementVisible selector="{{section.backgroundAttachment(index, backgroundAttachment.value)}}" stepKey="waitForBackgroundAttachment"/>
29+
</actionGroup>
1130
<actionGroup name="validateBackgroundAttributesWithNoImageOrColor">
1231
<arguments>
1332
<argument name="section"/>
@@ -139,7 +158,29 @@
139158
<executeJS function="return '{$baseURL}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexBaseURL"/>
140159
<executeJS function="return '{{backgroundImage.path}}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexFilePath"/>
141160
<assertRegExp stepKey="assertBackgroundImageContainsFileInformation">
142-
<expectedResult type="string">/{$regexBaseURL}\/pub\/media.*{$regexFilePath}\/{{backgroundImage.fileName}}(_\d+)?\.{{backgroundImage.extension}}/</expectedResult>
161+
<expectedResult type="string">/{$regexBaseURL}\/(pub\/|\/)?media.*{$regexFilePath}\/{{backgroundImage.fileName}}(_\d+)?\.{{backgroundImage.extension}}/</expectedResult>
162+
<actualResult type="variable">$backgroundImageValue</actualResult>
163+
</assertRegExp>
164+
</actionGroup>
165+
<actionGroup name="validateBackgroundImageContainer">
166+
<arguments>
167+
<argument name="section"/>
168+
<argument name="pageNamePrefix" defaultValue="admin" type="string"/>
169+
<argument name="backgroundImage"/>
170+
<argument name="index" defaultValue="1" type="string"/>
171+
</arguments>
172+
<comment userInput="validateBackgroundImage" stepKey="comment"/>
173+
<waitForElementVisible selector="{{section.base(index)}}" stepKey="waitForBaseElement"/>
174+
<!-- Retrieve background image from computed styles -->
175+
<executeJS function="return window.getComputedStyle({{section.baseJS(index)}}).backgroundImage" stepKey="backgroundImageValue"/>
176+
<!-- Grab base URL -->
177+
<executeJS function="return '{{pageNamePrefix}}'" stepKey="pageNamePrefix"/>
178+
<grabFromCurrentUrl regex="/(^.+(?=\/$pageNamePrefix))/" stepKey="baseURL"/>
179+
<!-- Convert all characters in string variables to regex friendly strings -->
180+
<executeJS function="return '{$baseURL}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexBaseURL"/>
181+
<executeJS function="return '{{backgroundImage.path}}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexFilePath"/>
182+
<assertRegExp stepKey="assertBackgroundImageContainsFileInformation">
183+
<expectedResult type="string">/{$regexBaseURL}\/(pub\/|\/)?media.*{$regexFilePath}\/{{backgroundImage.fileName}}(_\d+)?\.{{backgroundImage.extension}}/</expectedResult>
143184
<actualResult type="variable">$backgroundImageValue</actualResult>
144185
</assertRegExp>
145186
</actionGroup>
@@ -164,7 +205,7 @@
164205
<executeJS function="return '{$baseURL}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexBaseURL"/>
165206
<executeJS function="return '{{backgroundMobileImage.path}}'.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\\\^\\$\|]/g, '\\\\$&amp;')" stepKey="regexFilePath"/>
166207
<assertRegExp stepKey="assertBackgroundMobileImageContainsFileInformation">
167-
<expectedResult type="string">/{$regexBaseURL}\/pub\/media.*{$regexFilePath}\/{{backgroundMobileImage.fileName}}(_\d+)?\.{{backgroundMobileImage.extension}}/</expectedResult>
208+
<expectedResult type="string">/{$regexBaseURL}\/(pub\/|\/)?media.*{$regexFilePath}\/{{backgroundMobileImage.fileName}}(_\d+)?\.{{backgroundMobileImage.extension}}/</expectedResult>
168209
<actualResult type="variable">$backgroundMobileImageValue</actualResult>
169210
</assertRegExp>
170211
<resizeWindow width="767" height="1000" stepKey="resizeWindowToMobile"/>

app/code/Magento/PageBuilder/Test/Mftf/Data/RowData.xml

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,6 @@
5959
<data key="section">background</data>
6060
<data key="fieldName">background_image</data>
6161
</entity>
62-
<entity name="PageBuilderRowBackgroundImage_MagentoJpg" type="pagebuilder_row_background_image_property">
63-
<data key="name">Background Image</data>
64-
<data key="section">background</data>
65-
<data key="fieldName">background_image</data>
66-
<data key="value">magento.jpg</data>
67-
<data key="fileName">magento</data>
68-
<data key="extension">jpg</data>
69-
<data key="path">wysiwyg/magento</data>
70-
<data key="url">/(\b(https?|ftp|file):\/\/)[-A-Za-z0-9+&amp;@#/%?=~_|!:,.;]+[-A-Za-z0-9+&amp;@#/%=~_|]/g</data>
71-
</entity>
7262
<!-- Background Position -->
7363
<entity name="PageBuilderRowBackgroundPosition_Default" type="pagebuilder_row_background_position_property">
7464
<data key="name">Background Position</data>
@@ -77,39 +67,13 @@
7767
<data key="value">Top Aligned</data>
7868
<data key="styleValue">left top</data>
7969
</entity>
80-
<entity name="PageBuilderRowBackgroundPosition_TopAligned" type="pagebuilder_row_background_position_property">
81-
<data key="name">Background Position</data>
82-
<data key="section">background</data>
83-
<data key="fieldName">background_position</data>
84-
<data key="value">Top Aligned</data>
85-
<data key="styleValue">left top</data>
86-
</entity>
87-
<entity name="PageBuilderRowBackgroundPosition_Centered" type="pagebuilder_row_background_position_property">
88-
<data key="name">Background Position</data>
89-
<data key="section">background</data>
90-
<data key="fieldName">background_position</data>
91-
<data key="value">Centered</data>
92-
<data key="styleValue">center center</data>
93-
</entity>
9470
<!-- Background Size -->
9571
<entity name="PageBuilderRowBackgroundSize_Default" type="pagebuilder_row_background_size_property">
9672
<data key="name">Background Size</data>
9773
<data key="section">background</data>
9874
<data key="fieldName">background_size</data>
9975
<data key="value">cover</data>
10076
</entity>
101-
<entity name="PageBuilderRowBackgroundSize_Cover" type="pagebuilder_row_background_size_property">
102-
<data key="name">Background Size</data>
103-
<data key="section">background</data>
104-
<data key="fieldName">background_size</data>
105-
<data key="value">cover</data>
106-
</entity>
107-
<entity name="PageBuilderRowBackgroundSize_Contain" type="pagebuilder_row_background_size_property">
108-
<data key="name">Background Size</data>
109-
<data key="section">background</data>
110-
<data key="fieldName">background_size</data>
111-
<data key="value">contain</data>
112-
</entity>
11377
<!-- Background Repeat -->
11478
<entity name="PageBuilderRowBackgroundRepeat_Default" type="pagebuilder_row_background_repeat_property">
11579
<data key="name">Background Repeat</data>
@@ -118,26 +82,13 @@
11882
<data key="value">0</data>
11983
<data key="styleValue">no-repeat</data>
12084
</entity>
121-
<entity name="PageBuilderRowBackgroundRepeat_Enabled" type="pagebuilder_row_background_repeat_property">
122-
<data key="name">Background Repeat</data>
123-
<data key="section">background</data>
124-
<data key="fieldName">background_repeat</data>
125-
<data key="value">1</data>
126-
<data key="styleValue">repeat</data>
127-
</entity>
12885
<!-- Background Attachment -->
12986
<entity name="PageBuilderRowBackgroundAttachment_Default" type="pagebuilder_row_background_attachment_property">
13087
<data key="name">Background Attachment</data>
13188
<data key="section">background</data>
13289
<data key="fieldName">background_attachment</data>
13390
<data key="value">scroll</data>
13491
</entity>
135-
<entity name="PageBuilderRowBackgroundAttachment_Fixed" type="pagebuilder_row_background_attachment_property">
136-
<data key="name">Background Attachment</data>
137-
<data key="section">background</data>
138-
<data key="fieldName">background_attachment</data>
139-
<data key="value">fixed</data>
140-
</entity>
14192
<!-- Parallax Enabled -->
14293
<entity name="PageBuilderRowParallax_Default" type="pagebuilder_row_parallax_property">
14394
<data key="name">Enable Parallax Background</data>

app/code/Magento/PageBuilder/Test/Mftf/Section/PageBuilderColumnSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
</section>
4343
<section name="ColumnOnFrontend">
4444
<element name="base" type="block" selector="(//div[@data-role='column'])[{{arg1}}]" parameterized="true"/>
45+
<element name="baseJS" type="text" selector="document.querySelectorAll('[data-role=column]')[{{arg1}} - 1]" parameterized="true"/>
4546
<element name="columnGroup" type="block" selector=".pagebuilder-column-group"/>
4647
<element name="allColumns" type="block" selector="(//div[@data-role='column'])"/>
4748
<element name="columnX" type="block" selector="[data-role=column]:nth-child({{arg1}})" parameterized="true"/>

0 commit comments

Comments
 (0)