diff --git a/app/code/Magento/Catalog/Model/ImageExtractor.php b/app/code/Magento/Catalog/Model/ImageExtractor.php
index c12db4dba49d7..7888d8de1c2ff 100644
--- a/app/code/Magento/Catalog/Model/ImageExtractor.php
+++ b/app/code/Magento/Catalog/Model/ImageExtractor.php
@@ -5,10 +5,11 @@
*/
namespace Magento\Catalog\Model;
-use Magento\Catalog\Model\Product\Attribute\Backend\Media\ImageEntryConverter;
use Magento\Catalog\Helper\Image;
+use Magento\Catalog\Model\Product\Attribute\Backend\Media\ImageEntryConverter;
+use Magento\Framework\View\Xsd\Media\TypeDataExtractorInterface;
-class ImageExtractor implements \Magento\Framework\View\Xsd\Media\TypeDataExtractorInterface
+class ImageExtractor implements TypeDataExtractorInterface
{
/**
* Extract configuration data of images from the DOM structure
@@ -30,8 +31,11 @@ public function process(\DOMElement $mediaNode, $mediaParentTag)
if ($attribute->nodeType != XML_ELEMENT_NODE) {
continue;
}
- if ($attribute->tagName == 'background') {
+ $attributeTagName = $attribute->tagName;
+ if ($attributeTagName === 'background') {
$nodeValue = $this->processImageBackground($attribute->nodeValue);
+ } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') {
+ $nodeValue = intval($attribute->nodeValue);
} else {
$nodeValue = $attribute->nodeValue;
}
@@ -55,6 +59,7 @@ private function processImageBackground($backgroundString)
$backgroundArray = [];
if (preg_match($pattern, $backgroundString, $backgroundArray)) {
array_shift($backgroundArray);
+ $backgroundArray = array_map('intval', $backgroundArray);
}
return $backgroundArray;
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ImageExtractorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ImageExtractorTest.php
index 05ed780fc1e3d..3b0262aa99e4f 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ImageExtractorTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ImageExtractorTest.php
@@ -23,7 +23,7 @@ protected function setUp()
public function testProcess()
{
$expectedArray = include(__DIR__ . '/_files/converted_view.php');
- $this->assertEquals($expectedArray, $this->model->process($this->getDomElement(), 'media'));
+ $this->assertSame($expectedArray, $this->model->process($this->getDomElement(), 'media'));
}
/**
diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
index 6f9dda090914a..97a0e6aaebf4f 100644
--- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
@@ -5,6 +5,7 @@
*/
namespace Magento\CatalogImportExport\Model\Export;
+use Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor;
use Magento\ImportExport\Model\Import;
use \Magento\Store\Model\Store;
use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct;
@@ -438,11 +439,12 @@ protected function initCategories()
if ($pathSize > 1) {
$path = [];
for ($i = 1; $i < $pathSize; $i++) {
- $path[] = $collection->getItemById($structure[$i])->getName();
+ $name = $collection->getItemById($structure[$i])->getName();
+ $path[] = $this->quoteCategoryDelimiter($name);
}
$this->_rootCategories[$category->getId()] = array_shift($path);
if ($pathSize > 2) {
- $this->_categories[$category->getId()] = implode('/', $path);
+ $this->_categories[$category->getId()] = implode(CategoryProcessor::DELIMITER_CATEGORY, $path);
}
}
}
@@ -1470,4 +1472,19 @@ protected function getProductEntityLinkField()
}
return $this->productEntityLinkField;
}
+
+ /**
+ * Quoting category delimiter character in string.
+ *
+ * @param string $string
+ * @return string
+ */
+ private function quoteCategoryDelimiter($string)
+ {
+ return str_replace(
+ CategoryProcessor::DELIMITER_CATEGORY,
+ '\\' . CategoryProcessor::DELIMITER_CATEGORY,
+ $string
+ );
+ }
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
index 81c83c38bb546..788623d822e31 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
@@ -84,7 +84,8 @@ protected function initCategories()
if ($pathSize > 1) {
$path = [];
for ($i = 1; $i < $pathSize; $i++) {
- $path[] = $collection->getItemById((int)$structure[$i])->getName();
+ $name = $collection->getItemById((int)$structure[$i])->getName();
+ $path[] = $this->quoteDelimiter($name);
}
/** @var string $index */
$index = $this->standardizeString(
@@ -114,7 +115,7 @@ protected function createCategory($name, $parentId)
}
$category->setPath($parentCategory->getPath());
$category->setParentId($parentId);
- $category->setName($name);
+ $category->setName($this->unquoteDelimiter($name));
$category->setIsActive(true);
$category->setIncludeInMenu(true);
$category->setAttributeSetId($category->getDefaultAttributeSetId());
@@ -137,7 +138,7 @@ protected function upsertCategory($categoryPath)
$index = $this->standardizeString($categoryPath);
if (!isset($this->categories[$index])) {
- $pathParts = explode(self::DELIMITER_CATEGORY, $categoryPath);
+ $pathParts = preg_split('~(?
- ^[0-9]{4}\s?[a-zA-Z]{2}$
+ ^[1-9][0-9]{3}\s?[a-zA-Z]{2}$
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_slash.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_slash.php
new file mode 100644
index 0000000000000..59f8df2c736d3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_slash.php
@@ -0,0 +1,29 @@
+create(\Magento\Catalog\Model\Category::class);
+$category->isObjectNew(true);
+$category->setId(
+ 3331
+)->setCreatedAt(
+ '2017-06-23 09:50:07'
+)->setName(
+ 'Category with slash/ symbol'
+)->setParentId(
+ 2
+)->setPath(
+ '1/2/3331'
+)->setLevel(
+ 2
+)->setAvailableSortBy(
+ ['position', 'name']
+)->setDefaultSortBy(
+ 'name'
+)->setIsActive(
+ true
+)->setPosition(
+ 1
+)->save();
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
index 085d6fac0e00b..6cdca3e6e46af 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
@@ -104,6 +104,7 @@ public function testExportSpecialChars()
);
$exportData = $this->model->export();
$this->assertContains('simple ""1""', $exportData);
+ $this->assertContains('Category with slash\/ symbol', $exportData);
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv
index c0deb9b289fd3..4885522f5a363 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_custom_separator.csv
@@ -1,2 +1,2 @@
sku,product_type,store_view_code,name,price,attribute_set_code,categories,product_websites
-simple1,simple,,"simple 2",25,Default,"Default Category/Category 1|Default Category/Category 2",base
+simple1,simple,,"simple 2",25,Default,"Default Category/Category 1|Default Category/Category\/ 2",base
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv
index 9bf687dec4632..80ed257f25078 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_new_categories_default_separator.csv
@@ -1,2 +1,2 @@
sku,product_type,store_view_code,name,price,attribute_set_code,categories,product_websites,url_key
-simple1,simple,,"simple 1",25,Default,"Default Category/Category 1,Default Category/Category 2",base,simple1-ds
+simple1,simple,,"simple 1",25,Default,"Default Category/Category 1,Default Category/Category\/ 2",base,simple1-ds
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php
index 06a686d9d7830..8c7e0ab151410 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php
@@ -5,6 +5,8 @@
*/
/** Create category */
require dirname(dirname(__DIR__)) . '/Catalog/_files/category.php';
+/** Create category with special chars */
+require dirname(dirname(__DIR__)) . '/Catalog/_files/catalog_category_with_slash.php';
/** Create fixture store */
require dirname(dirname(__DIR__)) . '/Store/_files/second_store.php';
/** Create product with multiselect attribute and values */
@@ -28,10 +30,8 @@
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setWebsiteIds([1])
- ->setCateroryIds([])
->setStockData(['qty' => 100, 'is_in_stock' => 1])
->setCanSaveCustomOptions(true)
- ->setCategoryIds([333])
- ->setUpSellLinkData([$product->getId() => ['position' => 1]]);
+ ->setCategoryIds([333, 3331]);
$productModel->setOptions([])->save();
diff --git a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd
index 33a7d71e0e7db..f830f41b53255 100644
--- a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd
+++ b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd
@@ -9,6 +9,7 @@
+
@@ -45,6 +46,7 @@
+