diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml
new file mode 100644
index 0000000000000..e146506d51a24
--- /dev/null
+++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Fill catalog products list title field.
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml
new file mode 100644
index 0000000000000..4505680424471
--- /dev/null
+++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ Assert widget title on storefront.
+
+
+
+
+
+
+
+ $grabWidgetTitle
+ {{title}}
+
+
+
diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml
index 9b40971611d6f..3d8d5ecc1cda9 100644
--- a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml
+++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml
@@ -19,5 +19,6 @@
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml
new file mode 100644
index 0000000000000..bc379ec424fce
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php
index 195c3f397ff18..b05b70cfcbc71 100644
--- a/app/code/Magento/Widget/Model/Widget.php
+++ b/app/code/Magento/Widget/Model/Widget.php
@@ -5,6 +5,16 @@
*/
namespace Magento\Widget\Model;
+use Magento\Framework\App\Cache\Type\Config;
+use Magento\Framework\DataObject;
+use Magento\Framework\Escaper;
+use Magento\Framework\Math\Random;
+use Magento\Framework\View\Asset\Repository;
+use Magento\Framework\View\Asset\Source;
+use Magento\Framework\View\FileSystem;
+use Magento\Widget\Helper\Conditions;
+use Magento\Widget\Model\Config\Data;
+
/**
* Widget model for different purposes
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -15,32 +25,32 @@
class Widget
{
/**
- * @var \Magento\Widget\Model\Config\Data
+ * @var Data
*/
protected $dataStorage;
/**
- * @var \Magento\Framework\App\Cache\Type\Config
+ * @var Config
*/
protected $configCacheType;
/**
- * @var \Magento\Framework\View\Asset\Repository
+ * @var Repository
*/
protected $assetRepo;
/**
- * @var \Magento\Framework\View\Asset\Source
+ * @var Source
*/
protected $assetSource;
/**
- * @var \Magento\Framework\View\FileSystem
+ * @var FileSystem
*/
protected $viewFileSystem;
/**
- * @var \Magento\Framework\Escaper
+ * @var Escaper
*/
protected $escaper;
@@ -50,30 +60,35 @@ class Widget
protected $widgetsArray = [];
/**
- * @var \Magento\Widget\Helper\Conditions
+ * @var Conditions
*/
protected $conditionsHelper;
/**
- * @var \Magento\Framework\Math\Random
+ * @var Random
*/
private $mathRandom;
/**
- * @param \Magento\Framework\Escaper $escaper
- * @param \Magento\Widget\Model\Config\Data $dataStorage
- * @param \Magento\Framework\View\Asset\Repository $assetRepo
- * @param \Magento\Framework\View\Asset\Source $assetSource
- * @param \Magento\Framework\View\FileSystem $viewFileSystem
- * @param \Magento\Widget\Helper\Conditions $conditionsHelper
+ * @var string[]
+ */
+ private $reservedChars = ['}', '{'];
+
+ /**
+ * @param Escaper $escaper
+ * @param Data $dataStorage
+ * @param Repository $assetRepo
+ * @param Source $assetSource
+ * @param FileSystem $viewFileSystem
+ * @param Conditions $conditionsHelper
*/
public function __construct(
- \Magento\Framework\Escaper $escaper,
- \Magento\Widget\Model\Config\Data $dataStorage,
- \Magento\Framework\View\Asset\Repository $assetRepo,
- \Magento\Framework\View\Asset\Source $assetSource,
- \Magento\Framework\View\FileSystem $viewFileSystem,
- \Magento\Widget\Helper\Conditions $conditionsHelper
+ Escaper $escaper,
+ Data $dataStorage,
+ Repository $assetRepo,
+ Source $assetSource,
+ FileSystem $viewFileSystem,
+ Conditions $conditionsHelper
) {
$this->escaper = $escaper;
$this->dataStorage = $dataStorage;
@@ -110,14 +125,11 @@ public function getWidgetByClassType($type)
$widgets = $this->getWidgets();
/** @var array $widget */
foreach ($widgets as $widget) {
- if (isset($widget['@'])) {
- if (isset($widget['@']['type'])) {
- if ($type === $widget['@']['type']) {
- return $widget;
- }
- }
+ if (isset($widget['@']['type']) && $type === $widget['@']['type']) {
+ return $widget;
}
}
+
return null;
}
@@ -131,6 +143,7 @@ public function getWidgetByClassType($type)
*/
public function getConfigAsXml($type)
{
+ // phpstan:ignore
return $this->getXmlElementByType($type);
}
@@ -296,42 +309,70 @@ public function getWidgetsArray($filters = [])
*/
public function getWidgetDeclaration($type, $params = [], $asIs = true)
{
- $directive = '{{widget type="' . $type . '"';
$widget = $this->getConfigAsObject($type);
+ $params = array_filter($params, function ($value) {
+ return $value !== null && $value !== '';
+ });
+
+ $directiveParams = '';
foreach ($params as $name => $value) {
// Retrieve default option value if pre-configured
- if ($name == 'conditions') {
- $name = 'conditions_encoded';
- $value = $this->conditionsHelper->encode($value);
- } elseif (is_array($value)) {
- $value = implode(',', $value);
- } elseif (trim($value) == '') {
- $parameters = $widget->getParameters();
- if (isset($parameters[$name]) && is_object($parameters[$name])) {
- $value = $parameters[$name]->getValue();
- }
- }
- if (isset($value)) {
- $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false));
- }
+ $directiveParams .= $this->getDirectiveParam($widget, $name, $value);
}
- $directive .= $this->getWidgetPageVarName($params);
-
- $directive .= '}}';
+ $directive = sprintf('{{widget type="%s"%s%s}}', $type, $directiveParams, $this->getWidgetPageVarName($params));
if ($asIs) {
return $directive;
}
- $html = sprintf(
+ return sprintf(
'
',
$this->idEncode($directive),
$this->getPlaceholderImageUrl($type),
$this->escaper->escapeUrl($directive)
);
- return $html;
+ }
+
+ /**
+ * Returns directive param with prepared value
+ *
+ * @param DataObject $widget
+ * @param string $name
+ * @param string|array $value
+ * @return string
+ */
+ private function getDirectiveParam(DataObject $widget, string $name, $value): string
+ {
+ if ($name === 'conditions') {
+ $name = 'conditions_encoded';
+ $value = $this->conditionsHelper->encode($value);
+ } elseif (is_array($value)) {
+ $value = implode(',', $value);
+ } elseif (trim($value) === '') {
+ $parameters = $widget->getParameters();
+ if (isset($parameters[$name]) && is_object($parameters[$name])) {
+ $value = $parameters[$name]->getValue();
+ }
+ } else {
+ $value = $this->getPreparedValue($value);
+ }
+
+ return sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false));
+ }
+
+ /**
+ * Returns encoded value if it contains reserved chars
+ *
+ * @param string $value
+ * @return string
+ */
+ private function getPreparedValue(string $value): string
+ {
+ $pattern = sprintf('/%s/', implode('|', $this->reservedChars));
+
+ return preg_match($pattern, $value) ? rawurlencode($value) : $value;
}
/**