diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml
index 050115087c26e..b9ebeb227b764 100644
--- a/app/code/Magento/Backend/etc/adminhtml/di.xml
+++ b/app/code/Magento/Backend/etc/adminhtml/di.xml
@@ -145,6 +145,9 @@
- Magento\Config\Model\Config\Structure\ElementVisibilityInterface::HIDDEN
- Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
index 3ffc02a33254c..f6d85b57ab0ce 100644
--- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
@@ -533,11 +533,12 @@ protected function getMediaGallery(array $productIds)
]
)->joinLeft(
['mgv' => $this->_resourceModel->getTableName('catalog_product_entity_media_gallery_value')],
- '(mg.value_id = mgv.value_id AND mgv.store_id = 0)',
+ '(mg.value_id = mgv.value_id)',
[
'mgv.label',
'mgv.position',
- 'mgv.disabled'
+ 'mgv.disabled',
+ 'mgv.store_id',
]
)->where(
"mgvte.{$this->getProductEntityLinkField()} IN (?)",
@@ -553,6 +554,7 @@ protected function getMediaGallery(array $productIds)
'_media_label' => $mediaRow['label'],
'_media_position' => $mediaRow['position'],
'_media_is_disabled' => $mediaRow['disabled'],
+ '_media_store_id' => $mediaRow['store_id'],
];
}
@@ -1001,12 +1003,10 @@ protected function collectRawData()
unset($data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES]);
}
- if (!empty($data[$itemId][$storeId]) || $this->hasMultiselectData($item, $storeId)) {
- $attrSetId = $item->getAttributeSetId();
- $data[$itemId][$storeId][self::COL_STORE] = $storeCode;
- $data[$itemId][$storeId][self::COL_ATTR_SET] = $this->_attrSetIdToName[$attrSetId];
- $data[$itemId][$storeId][self::COL_TYPE] = $item->getTypeId();
- }
+ $attrSetId = $item->getAttributeSetId();
+ $data[$itemId][$storeId][self::COL_STORE] = $storeCode;
+ $data[$itemId][$storeId][self::COL_ATTR_SET] = $this->_attrSetIdToName[$attrSetId];
+ $data[$itemId][$storeId][self::COL_TYPE] = $item->getTypeId();
$data[$itemId][$storeId][self::COL_SKU] = htmlspecialchars_decode($item->getSku());
$data[$itemId][$storeId]['store_id'] = $storeId;
$data[$itemId][$storeId]['product_id'] = $itemId;
@@ -1082,6 +1082,7 @@ protected function collectMultirawData()
* @param \Magento\Catalog\Model\Product $item
* @param int $storeId
* @return bool
+ * @deprecated
*/
protected function hasMultiselectData($item, $storeId)
{
@@ -1140,20 +1141,24 @@ protected function isValidAttributeValue($code, $value)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
- private function appendMultirowData(&$dataRow, &$multiRawData)
+ private function appendMultirowData(&$dataRow, $multiRawData)
{
$productId = $dataRow['product_id'];
$productLinkId = $dataRow['product_link_id'];
$storeId = $dataRow['store_id'];
$sku = $dataRow[self::COL_SKU];
+ $type = $dataRow[self::COL_TYPE];
+ $attributeSet = $dataRow[self::COL_ATTR_SET];
unset($dataRow['product_id']);
unset($dataRow['product_link_id']);
unset($dataRow['store_id']);
unset($dataRow[self::COL_SKU]);
+ unset($dataRow[self::COL_STORE]);
+ unset($dataRow[self::COL_ATTR_SET]);
+ unset($dataRow[self::COL_TYPE]);
if (Store::DEFAULT_STORE_ID == $storeId) {
- unset($dataRow[self::COL_STORE]);
$this->updateDataWithCategoryColumns($dataRow, $multiRawData['rowCategories'], $productId);
if (!empty($multiRawData['rowWebsites'][$productId])) {
$websiteCodes = [];
@@ -1169,11 +1174,13 @@ private function appendMultirowData(&$dataRow, &$multiRawData)
$additionalImageLabels = [];
$additionalImageIsDisabled = [];
foreach ($multiRawData['mediaGalery'][$productLinkId] as $mediaItem) {
- $additionalImages[] = $mediaItem['_media_image'];
- $additionalImageLabels[] = $mediaItem['_media_label'];
+ if ((int)$mediaItem['_media_store_id'] === Store::DEFAULT_STORE_ID) {
+ $additionalImages[] = $mediaItem['_media_image'];
+ $additionalImageLabels[] = $mediaItem['_media_label'];
- if ($mediaItem['_media_is_disabled'] == true) {
- $additionalImageIsDisabled[] = $mediaItem['_media_image'];
+ if ($mediaItem['_media_is_disabled'] == true) {
+ $additionalImageIsDisabled[] = $mediaItem['_media_image'];
+ }
}
}
$dataRow['additional_images'] =
@@ -1207,6 +1214,21 @@ private function appendMultirowData(&$dataRow, &$multiRawData)
}
}
$dataRow = $this->rowCustomizer->addData($dataRow, $productId);
+ } else {
+ $additionalImageIsDisabled = [];
+ if (!empty($multiRawData['mediaGalery'][$productLinkId])) {
+ foreach ($multiRawData['mediaGalery'][$productLinkId] as $mediaItem) {
+ if ((int)$mediaItem['_media_store_id'] === $storeId) {
+ if ($mediaItem['_media_is_disabled'] == true) {
+ $additionalImageIsDisabled[] = $mediaItem['_media_image'];
+ }
+ }
+ }
+ }
+ if ($additionalImageIsDisabled) {
+ $dataRow['hide_from_product_page'] =
+ implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalImageIsDisabled);
+ }
}
if (!empty($this->collectedMultiselectsData[$storeId][$productId])) {
@@ -1234,6 +1256,9 @@ private function appendMultirowData(&$dataRow, &$multiRawData)
$dataRow[self::COL_STORE] = $this->_storeIdToCode[$storeId];
}
$dataRow[self::COL_SKU] = $sku;
+ $dataRow[self::COL_ATTR_SET] = $attributeSet;
+ $dataRow[self::COL_TYPE] = $type;
+
return $dataRow;
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index fa3ac817f1dfb..d924d0a906d30 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -7,6 +7,7 @@
use Magento\Catalog\Model\Config as CatalogConfig;
use Magento\Catalog\Model\Product\Visibility;
+use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor;
use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor;
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
@@ -18,6 +19,7 @@
use Magento\ImportExport\Model\Import\Entity\AbstractEntity;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
+use Magento\Store\Model\Store;
/**
* Import entity product model
@@ -704,6 +706,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
*/
private $imageTypeProcessor;
+ /**
+ * Provide ability to process and save images during import.
+ *
+ * @var MediaGalleryProcessor
+ */
+ private $mediaProcessor;
+
/**
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -744,6 +753,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
* @param array $dateAttrCodes
* @param CatalogConfig $catalogConfig
* @param ImageTypeProcessor $imageTypeProcessor
+ * @param MediaGalleryProcessor $mediaProcessor
* @throws \Magento\Framework\Exception\LocalizedException
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
@@ -788,7 +798,8 @@ public function __construct(
array $data = [],
array $dateAttrCodes = [],
CatalogConfig $catalogConfig = null,
- ImageTypeProcessor $imageTypeProcessor = null
+ ImageTypeProcessor $imageTypeProcessor = null,
+ MediaGalleryProcessor $mediaProcessor = null
) {
$this->_eventManager = $eventManager;
$this->stockRegistry = $stockRegistry;
@@ -823,6 +834,8 @@ public function __construct(
->get(CatalogConfig::class);
$this->imageTypeProcessor = $imageTypeProcessor ?: \Magento\Framework\App\ObjectManager::getInstance()
->get(ImageTypeProcessor::class);
+ $this->mediaProcessor = $mediaProcessor ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(MediaGalleryProcessor::class);
parent::__construct(
$jsonHelper,
@@ -1473,6 +1486,7 @@ private function getNewSkuFieldsForSelect()
* Init media gallery resources
* @return void
* @since 100.0.4
+ * @deprecated
*/
protected function initMediaGalleryResources()
{
@@ -1496,48 +1510,7 @@ protected function initMediaGalleryResources()
*/
protected function getExistingImages($bunch)
{
- $result = [];
- if ($this->getErrorAggregator()->hasToBeTerminated()) {
- return $result;
- }
-
- $this->initMediaGalleryResources();
- $productSKUs = array_map('strval', array_column($bunch, self::COL_SKU));
- $select = $this->_connection->select()->from(
- ['mg' => $this->mediaGalleryTableName],
- ['value' => 'mg.value']
- )->joinInner(
- ['mgvte' => $this->mediaGalleryEntityToValueTableName],
- '(mg.value_id = mgvte.value_id)',
- [
- $this->getProductEntityLinkField() => 'mgvte.' . $this->getProductEntityLinkField(),
- 'value_id' => 'mgvte.value_id'
- ]
- )->joinLeft(
- ['mgv' => $this->mediaGalleryValueTableName],
- sprintf(
- '(mg.value_id = mgv.value_id AND mgv.%s = mgvte.%s AND mgv.store_id = %d)',
- $this->getProductEntityLinkField(),
- $this->getProductEntityLinkField(),
- \Magento\Store\Model\Store::DEFAULT_STORE_ID
- ),
- [
- 'label' => 'mgv.label'
- ]
- )->joinInner(
- ['pe' => $this->productEntityTableName],
- "(mgvte.{$this->getProductEntityLinkField()} = pe.{$this->getProductEntityLinkField()})",
- ['sku' => 'pe.sku']
- )->where(
- 'pe.sku IN (?)',
- $productSKUs
- );
-
- foreach ($this->_connection->fetchAll($select) as $image) {
- $result[$image['sku']][$image['value']] = $image;
- }
-
- return $result;
+ return $this->mediaProcessor->getExistingImages($bunch);
}
/**
@@ -1716,10 +1689,18 @@ protected function _saveProducts()
// 5. Media gallery phase
$disabledImages = [];
list($rowImages, $rowLabels) = $this->getImagesFromRow($rowData);
+ $storeId = !empty($rowData[self::COL_STORE])
+ ? $this->getStoreIdByCode($rowData[self::COL_STORE])
+ : Store::DEFAULT_STORE_ID;
if (isset($rowData['_media_is_disabled'])) {
$disabledImages = array_flip(
explode($this->getMultipleValueSeparator(), $rowData['_media_is_disabled'])
);
+ if (empty($rowImages)) {
+ foreach (array_keys($disabledImages) as $disabledImage) {
+ $rowImages[self::COL_MEDIA_IMAGE][] = $disabledImage;
+ }
+ }
}
$rowData[self::COL_MEDIA_IMAGE] = [];
@@ -1752,7 +1733,7 @@ protected function _saveProducts()
$rowData[$column] = $uploadedFile;
}
- if ($uploadedFile && !isset($mediaGallery[$rowSku][$uploadedFile])) {
+ if ($uploadedFile && !isset($mediaGallery[$storeId][$rowSku][$uploadedFile])) {
if (isset($existingImages[$rowSku][$uploadedFile])) {
if (isset($rowLabels[$column][$columnImageKey])
&& $rowLabels[$column][$columnImageKey] !=
@@ -1767,7 +1748,7 @@ protected function _saveProducts()
if ($column == self::COL_MEDIA_IMAGE) {
$rowData[$column][] = $uploadedFile;
}
- $mediaGallery[$rowSku][$uploadedFile] = [
+ $mediaGallery[$storeId][$rowSku][$uploadedFile] = [
'attribute_id' => $this->getMediaGalleryAttributeId(),
'label' => isset($rowLabels[$column][$columnImageKey])
? $rowLabels[$column][$columnImageKey]
@@ -2089,95 +2070,14 @@ private function getSystemFile($fileName)
*
* @param array $mediaGalleryData
* @return $this
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
*/
protected function _saveMediaGallery(array $mediaGalleryData)
{
if (empty($mediaGalleryData)) {
return $this;
}
- $this->initMediaGalleryResources();
- $productIds = [];
- $imageNames = [];
- $multiInsertData = [];
- $valueToProductId = [];
- foreach ($mediaGalleryData as $productSku => $mediaGalleryRows) {
- $productId = $this->skuProcessor->getNewSku($productSku)[$this->getProductEntityLinkField()];
- $productIds[] = $productId;
- $insertedGalleryImgs = [];
- foreach ($mediaGalleryRows as $insertValue) {
- if (!in_array($insertValue['value'], $insertedGalleryImgs)) {
- $valueArr = [
- 'attribute_id' => $insertValue['attribute_id'],
- 'value' => $insertValue['value'],
- ];
- $valueToProductId[$insertValue['value']][] = $productId;
- $imageNames[] = $insertValue['value'];
- $multiInsertData[] = $valueArr;
- $insertedGalleryImgs[] = $insertValue['value'];
- }
- }
- }
- $oldMediaValues = $this->_connection->fetchAssoc(
- $this->_connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
- ->where('value IN (?)', $imageNames)
- );
- $this->_connection->insertOnDuplicate($this->mediaGalleryTableName, $multiInsertData, []);
- $multiInsertData = [];
- $newMediaSelect = $this->_connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
- ->where('value IN (?)', $imageNames);
- if (array_keys($oldMediaValues)) {
- $newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues));
- }
-
- $dataForSkinnyTable = [];
- $newMediaValues = $this->_connection->fetchAssoc($newMediaSelect);
- foreach ($mediaGalleryData as $productSku => $mediaGalleryRows) {
- foreach ($mediaGalleryRows as $insertValue) {
- foreach ($newMediaValues as $value_id => $values) {
- if ($values['value'] == $insertValue['value']) {
- $insertValue['value_id'] = $value_id;
- $insertValue[$this->getProductEntityLinkField()]
- = array_shift($valueToProductId[$values['value']]);
- unset($newMediaValues[$value_id]);
- break;
- }
- }
- if (isset($insertValue['value_id'])) {
- $valueArr = [
- 'value_id' => $insertValue['value_id'],
- 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID,
- $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()],
- 'label' => $insertValue['label'],
- 'position' => $insertValue['position'],
- 'disabled' => $insertValue['disabled'],
- ];
- $multiInsertData[] = $valueArr;
- $dataForSkinnyTable[] = [
- 'value_id' => $insertValue['value_id'],
- $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()],
- ];
- }
- }
- }
- try {
- $this->_connection->insertOnDuplicate(
- $this->mediaGalleryValueTableName,
- $multiInsertData,
- ['value_id', 'store_id', $this->getProductEntityLinkField(), 'label', 'position', 'disabled']
- );
- $this->_connection->insertOnDuplicate(
- $this->mediaGalleryEntityToValueTableName,
- $dataForSkinnyTable,
- ['value_id']
- );
- } catch (\Exception $e) {
- $this->_connection->delete(
- $this->mediaGalleryTableName,
- $this->_connection->quoteInto('value_id IN (?)', $newMediaValues)
- );
- }
+ $this->mediaProcessor->saveMediaGallery($mediaGalleryData);
+
return $this;
}
@@ -2923,41 +2823,8 @@ private function getProductIdentifierField()
*/
private function updateMediaGalleryLabels(array $labels)
{
- if (empty($labels)) {
- return;
- }
-
- $insertData = [];
- foreach ($labels as $label) {
- $imageData = $label['imageData'];
-
- if ($imageData['label'] === null) {
- $insertData[] = [
- 'label' => $label['label'],
- $this->getProductEntityLinkField() => $imageData[$this->getProductEntityLinkField()],
- 'value_id' => $imageData['value_id'],
- 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID
- ];
- } else {
- $this->_connection->update(
- $this->mediaGalleryValueTableName,
- [
- 'label' => $label['label']
- ],
- [
- $this->getProductEntityLinkField() . ' = ?' => $imageData[$this->getProductEntityLinkField()],
- 'value_id = ?' => $imageData['value_id'],
- 'store_id = ?' => \Magento\Store\Model\Store::DEFAULT_STORE_ID
- ]
- );
- }
- }
-
- if (!empty($insertData)) {
- $this->_connection->insertMultiple(
- $this->mediaGalleryValueTableName,
- $insertData
- );
+ if (!empty($labels)) {
+ $this->mediaProcessor->updateMediaGalleryLabels($labels);
}
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php
new file mode 100644
index 0000000000000..ec7c6a1172996
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php
@@ -0,0 +1,352 @@
+skuProcessor = $skuProcessor;
+ $this->metadataPool = $metadataPool;
+ $this->connection = $resourceConnection->getConnection();
+ $this->resourceFactory = $resourceModelFactory;
+ $this->errorAggregator = $errorAggregator;
+ }
+
+ /**
+ * Save product media gallery.
+ *
+ * @param $mediaGalleryData
+ * @return void
+ */
+ public function saveMediaGallery(array $mediaGalleryData)
+ {
+ $this->initMediaGalleryResources();
+ $mediaGalleryDataGlobal = array_replace_recursive(...$mediaGalleryData);
+ $imageNames = [];
+ $multiInsertData = [];
+ $valueToProductId = [];
+ foreach ($mediaGalleryDataGlobal as $productSku => $mediaGalleryRows) {
+ $productId = $this->skuProcessor->getNewSku($productSku)[$this->getProductEntityLinkField()];
+ $insertedGalleryImgs = [];
+ foreach ($mediaGalleryRows as $insertValue) {
+ if (!in_array($insertValue['value'], $insertedGalleryImgs)) {
+ $valueArr = [
+ 'attribute_id' => $insertValue['attribute_id'],
+ 'value' => $insertValue['value'],
+ ];
+ $valueToProductId[$insertValue['value']][] = $productId;
+ $imageNames[] = $insertValue['value'];
+ $multiInsertData[] = $valueArr;
+ $insertedGalleryImgs[] = $insertValue['value'];
+ }
+ }
+ }
+ $oldMediaValues = $this->connection->fetchAssoc(
+ $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
+ ->where('value IN (?)', $imageNames)
+ );
+ $this->connection->insertOnDuplicate($this->mediaGalleryTableName, $multiInsertData);
+ $newMediaSelect = $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
+ ->where('value IN (?)', $imageNames);
+ if (array_keys($oldMediaValues)) {
+ $newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues));
+ }
+ $newMediaValues = $this->connection->fetchAssoc($newMediaSelect);
+ foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) {
+ $this->processMediaPerStore((int)$storeId, $storeMediaGalleryData, $newMediaValues, $valueToProductId);
+ }
+ }
+
+ /**
+ * Update media gallery labels.
+ *
+ * @param array $labels
+ * @return void
+ */
+ public function updateMediaGalleryLabels(array $labels)
+ {
+ $insertData = [];
+ foreach ($labels as $label) {
+ $imageData = $label['imageData'];
+
+ if ($imageData['label'] === null) {
+ $insertData[] = [
+ 'label' => $label['label'],
+ $this->getProductEntityLinkField() => $imageData[$this->getProductEntityLinkField()],
+ 'value_id' => $imageData['value_id'],
+ 'store_id' => Store::DEFAULT_STORE_ID,
+ ];
+ } else {
+ $this->connection->update(
+ $this->mediaGalleryValueTableName,
+ [
+ 'label' => $label['label'],
+ ],
+ [
+ $this->getProductEntityLinkField() . ' = ?' => $imageData[$this->getProductEntityLinkField()],
+ 'value_id = ?' => $imageData['value_id'],
+ 'store_id = ?' => Store::DEFAULT_STORE_ID,
+ ]
+ );
+ }
+ }
+
+ if (!empty($insertData)) {
+ $this->connection->insertMultiple(
+ $this->mediaGalleryValueTableName,
+ $insertData
+ );
+ }
+ }
+
+ /**
+ * Get existing images for current bunch.
+ *
+ * @param array $bunch
+ * @return array
+ */
+ public function getExistingImages(array $bunch)
+ {
+ $result = [];
+ if ($this->errorAggregator->hasToBeTerminated()) {
+ return $result;
+ }
+ $this->initMediaGalleryResources();
+ $productSKUs = array_map(
+ 'strval',
+ array_column($bunch, Product::COL_SKU)
+ );
+ $select = $this->connection->select()->from(
+ ['mg' => $this->mediaGalleryTableName],
+ ['value' => 'mg.value']
+ )->joinInner(
+ ['mgvte' => $this->mediaGalleryEntityToValueTableName],
+ '(mg.value_id = mgvte.value_id)',
+ [
+ $this->getProductEntityLinkField() => 'mgvte.' . $this->getProductEntityLinkField(),
+ 'value_id' => 'mgvte.value_id',
+ ]
+ )->joinLeft(
+ ['mgv' => $this->mediaGalleryValueTableName],
+ sprintf(
+ '(mg.value_id = mgv.value_id AND mgv.%s = mgvte.%s AND mgv.store_id = %d)',
+ $this->getProductEntityLinkField(),
+ $this->getProductEntityLinkField(),
+ Store::DEFAULT_STORE_ID
+ ),
+ [
+ 'label' => 'mgv.label',
+ ]
+ )->joinInner(
+ ['pe' => $this->productEntityTableName],
+ "(mgvte.{$this->getProductEntityLinkField()} = pe.{$this->getProductEntityLinkField()})",
+ ['sku' => 'pe.sku']
+ )->where(
+ 'pe.sku IN (?)',
+ $productSKUs
+ );
+
+ foreach ($this->connection->fetchAll($select) as $image) {
+ $result[$image['sku']][$image['value']] = $image;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Init media gallery resources.
+ *
+ * @return void
+ */
+ private function initMediaGalleryResources()
+ {
+ if (null == $this->mediaGalleryTableName) {
+ $this->productEntityTableName = $this->getResource()->getTable('catalog_product_entity');
+ $this->mediaGalleryTableName = $this->getResource()->getTable('catalog_product_entity_media_gallery');
+ $this->mediaGalleryValueTableName = $this->getResource()->getTable(
+ 'catalog_product_entity_media_gallery_value'
+ );
+ $this->mediaGalleryEntityToValueTableName = $this->getResource()->getTable(
+ 'catalog_product_entity_media_gallery_value_to_entity'
+ );
+ }
+ }
+
+ /**
+ * Save media gallery data per store.
+ *
+ * @param $storeId
+ * @param array $mediaGalleryData
+ * @param array $newMediaValues
+ * @param array $valueToProductId
+ * @return void
+ */
+ private function processMediaPerStore(
+ int $storeId,
+ array $mediaGalleryData,
+ array $newMediaValues,
+ array $valueToProductId
+ ) {
+ $multiInsertData = [];
+ $dataForSkinnyTable = [];
+ foreach ($mediaGalleryData as $mediaGalleryRows) {
+ foreach ($mediaGalleryRows as $insertValue) {
+ foreach ($newMediaValues as $value_id => $values) {
+ if ($values['value'] == $insertValue['value']) {
+ $insertValue['value_id'] = $value_id;
+ $insertValue[$this->getProductEntityLinkField()]
+ = array_shift($valueToProductId[$values['value']]);
+ unset($newMediaValues[$value_id]);
+ break;
+ }
+ }
+ if (isset($insertValue['value_id'])) {
+ $valueArr = [
+ 'value_id' => $insertValue['value_id'],
+ 'store_id' => $storeId,
+ $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()],
+ 'label' => $insertValue['label'],
+ 'position' => $insertValue['position'],
+ 'disabled' => $insertValue['disabled'],
+ ];
+ $multiInsertData[] = $valueArr;
+ $dataForSkinnyTable[] = [
+ 'value_id' => $insertValue['value_id'],
+ $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()],
+ ];
+ }
+ }
+ }
+ try {
+ $this->connection->insertOnDuplicate(
+ $this->mediaGalleryValueTableName,
+ $multiInsertData,
+ ['value_id', 'store_id', $this->getProductEntityLinkField(), 'label', 'position', 'disabled']
+ );
+ $this->connection->insertOnDuplicate(
+ $this->mediaGalleryEntityToValueTableName,
+ $dataForSkinnyTable,
+ ['value_id']
+ );
+ } catch (\Exception $e) {
+ $this->connection->delete(
+ $this->mediaGalleryTableName,
+ $this->connection->quoteInto('value_id IN (?)', $newMediaValues)
+ );
+ }
+ }
+
+ /**
+ * Get product entity link field.
+ *
+ * @return string
+ */
+ private function getProductEntityLinkField()
+ {
+ if (!$this->productEntityLinkField) {
+ $this->productEntityLinkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
+ }
+
+ return $this->productEntityLinkField;
+ }
+
+ /**
+ * @return \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModel
+ */
+ private function getResource()
+ {
+ if (!$this->resourceModel) {
+ $this->resourceModel = $this->resourceFactory->create();
+ }
+
+ return $this->resourceModel;
+ }
+}
diff --git a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
index 115a372e6150a..92bc61b3d65e5 100644
--- a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
+++ b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
@@ -42,14 +42,36 @@ class ConcealInProductionConfigList implements ElementVisibilityInterface
*/
private $state;
+ /**
+ *
+ * The list of form element paths which ignore visibility status.
+ *
+ * E.g.
+ *
+ * ```php
+ * [
+ * 'general/country/default' => '',
+ * ];
+ * ```
+ *
+ * It means that:
+ * - field 'default' in group Country Options (in section General) will be showed, even if all group(section)
+ * will be hidden.
+ *
+ * @var array
+ */
+ private $exemptions = [];
+
/**
* @param State $state The object that has information about the state of the system
* @param array $configs The list of form element paths with concrete visibility status.
+ * @param array $exemptions The list of form element paths which ignore visibility status.
*/
- public function __construct(State $state, array $configs = [])
+ public function __construct(State $state, array $configs = [], array $exemptions = [])
{
$this->state = $state;
$this->configs = $configs;
+ $this->exemptions = $exemptions;
}
/**
@@ -58,10 +80,21 @@ public function __construct(State $state, array $configs = [])
*/
public function isHidden($path)
{
+ $result = false;
$path = $this->normalizePath($path);
- return $this->state->getMode() === State::MODE_PRODUCTION
- && !empty($this->configs[$path])
- && $this->configs[$path] === static::HIDDEN;
+ if ($this->state->getMode() === State::MODE_PRODUCTION
+ && preg_match('/(?(?.*?)\/.*?)\/.*?/', $path, $match)) {
+ $group = $match['group'];
+ $section = $match['section'];
+ $exemptions = array_keys($this->exemptions);
+ foreach ($this->configs as $configPath => $value) {
+ if ($value === static::HIDDEN && strpos($path, $configPath) !==false) {
+ $result = empty(array_intersect([$section, $group, $path], $exemptions));
+ }
+ }
+ }
+
+ return $result;
}
/**
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
index 5cad923264e00..fa78d5dde652c 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
@@ -33,9 +33,13 @@ protected function setUp()
'third/path' => 'no',
'third/path/field' => ConcealInProductionConfigList::DISABLED,
'first/path/field' => 'no',
+ 'fourth' => ConcealInProductionConfigList::HIDDEN,
+ ];
+ $exemptions = [
+ 'fourth/path/value' => '',
];
- $this->model = new ConcealInProductionConfigList($this->stateMock, $configs);
+ $this->model = new ConcealInProductionConfigList($this->stateMock, $configs, $exemptions);
}
/**
@@ -96,8 +100,10 @@ public function hiddenDataProvider()
['first/path', State::MODE_PRODUCTION, false],
['first/path', State::MODE_DEFAULT, false],
['some/path', State::MODE_PRODUCTION, false],
- ['second/path', State::MODE_PRODUCTION, true],
+ ['second/path/field', State::MODE_PRODUCTION, true],
['second/path', State::MODE_DEVELOPER, false],
+ ['fourth/path/value', State::MODE_PRODUCTION, false],
+ ['fourth/path/test', State::MODE_PRODUCTION, true],
];
}
}
diff --git a/app/code/Magento/Deploy/App/Mode/ConfigProvider.php b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php
index 142e3fe819438..900908a1f158f 100644
--- a/app/code/Magento/Deploy/App/Mode/ConfigProvider.php
+++ b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php
@@ -16,7 +16,7 @@ class ConfigProvider
* [
* 'developer' => [
* 'production' => [
- * {{setting_path}} => {{setting_value}}
+ * {{setting_path}} => ['value' => {{setting_value}}, 'lock' => {{lock_value}}]
* ]
* ]
* ]
@@ -41,7 +41,7 @@ public function __construct(array $config = [])
* need to turn off 'dev/debug/debug_logging' setting in this case method
* will return array
* [
- * {{setting_path}} => {{setting_value}}
+ * {{setting_path}} => ['value' => {{setting_value}}, 'lock' => {{lock_value}}]
* ]
*
* @param string $currentMode
diff --git a/app/code/Magento/Deploy/Model/Mode.php b/app/code/Magento/Deploy/Model/Mode.php
index 990d119e92ee0..576b447b63594 100644
--- a/app/code/Magento/Deploy/Model/Mode.php
+++ b/app/code/Magento/Deploy/Model/Mode.php
@@ -224,17 +224,17 @@ protected function setStoreMode($mode)
private function saveAppConfigs($mode)
{
$configs = $this->configProvider->getConfigs($this->getMode(), $mode);
- foreach ($configs as $path => $value) {
- $this->emulatedAreaProcessor->process(function () use ($path, $value) {
+ foreach ($configs as $path => $item) {
+ $this->emulatedAreaProcessor->process(function () use ($path, $item) {
$this->processorFacadeFactory->create()->process(
$path,
- $value,
+ $item['value'],
ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
null,
- true
+ $item['lock']
);
});
- $this->output->writeln('Config "' . $path . ' = ' . $value . '" has been saved.');
+ $this->output->writeln('Config "' . $path . ' = ' . $item['value'] . '" has been saved.');
}
}
diff --git a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php
index f80c6cb69f1a9..3db2023e12f40 100644
--- a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php
+++ b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php
@@ -226,7 +226,7 @@ public function testEnableProductionModeMinimal()
->method('getConfigs')
->with('developer', 'production')
->willReturn([
- 'dev/debug/debug_logging' => 0
+ 'dev/debug/debug_logging' => ['value' => 0, 'lock' => false]
]);
$this->emulatedAreaProcessor->expects($this->once())
->method('process')
@@ -245,7 +245,7 @@ public function testEnableProductionModeMinimal()
0,
ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
null,
- true
+ false
);
$this->outputMock->expects($this->once())
->method('writeln')
diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml
index ce7c84c95538a..9e936f70c9986 100644
--- a/app/code/Magento/Deploy/etc/di.xml
+++ b/app/code/Magento/Deploy/etc/di.xml
@@ -76,7 +76,32 @@
-
-
-
- 0
+ -
+
- 0
+ - false
+
+
+
+ -
+
-
+
-
+
- 1
+ - false
+
+
+
+ -
+
-
+
-
+
- 0
+ - false
+
+
+ -
+
-
+
- 1
+ - false
+
diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml
index 9663cff72bc9d..0166814d889c2 100644
--- a/app/code/Magento/Developer/etc/adminhtml/system.xml
+++ b/app/code/Magento/Developer/etc/adminhtml/system.xml
@@ -28,7 +28,6 @@
- Not available in production mode.
Magento\Config\Model\Config\Source\Yesno
diff --git a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
index 42f2745060026..eb708ab8b6320 100644
--- a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
+++ b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js
@@ -51,10 +51,9 @@ define([
if (config.pageTrackingData.isAnonymizedIpActive) {
ga('set', 'anonymizeIp', true);
}
- ga('send', 'pageview' + config.pageTrackingData.optPageUrl);
// Process orders data
- if (config.ordersTrackingData) {
+ if (config.ordersTrackingData.length) {
ga('require', 'ec', 'ec.js');
ga('set', 'currencyCode', config.ordersTrackingData.currency);
@@ -74,6 +73,9 @@ define([
}
ga('send', 'pageview');
+ } else {
+ // Process Data if not orders
+ ga('send', 'pageview' + config.pageTrackingData.optPageUrl);
}
}
}
diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php
index 569aec83dfaf3..12f34955f81f0 100644
--- a/app/code/Magento/ImportExport/Model/Import.php
+++ b/app/code/Magento/ImportExport/Model/Import.php
@@ -567,7 +567,6 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource
ProcessingError::ERROR_LEVEL_CRITICAL,
null,
null,
- null,
$e->getMessage()
);
}
diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php
index f33144b5787e3..dcb6341a7a8dd 100644
--- a/app/code/Magento/Newsletter/Model/Subscriber.php
+++ b/app/code/Magento/Newsletter/Model/Subscriber.php
@@ -604,14 +604,20 @@ protected function _updateCustomerSubscription($customerId, $subscribe)
$this->save();
$sendSubscription = $sendInformationEmail;
- if ($sendSubscription === null xor $sendSubscription) {
+ if ($sendSubscription === null xor $sendSubscription && $this->isStatusChanged()) {
try {
- if ($isConfirmNeed) {
- $this->sendConfirmationRequestEmail();
- } elseif ($this->isStatusChanged() && $status == self::STATUS_UNSUBSCRIBED) {
- $this->sendUnsubscriptionEmail();
- } elseif ($this->isStatusChanged() && $status == self::STATUS_SUBSCRIBED) {
- $this->sendConfirmationSuccessEmail();
+ switch ($status) {
+ case self::STATUS_UNSUBSCRIBED:
+ $this->sendUnsubscriptionEmail();
+ break;
+ case self::STATUS_SUBSCRIBED:
+ $this->sendConfirmationSuccessEmail();
+ break;
+ case self::STATUS_NOT_ACTIVE:
+ if ($isConfirmNeed) {
+ $this->sendConfirmationRequestEmail();
+ }
+ break;
}
} catch (MailException $e) {
// If we are not able to send a new account email, this should be ignored
diff --git a/app/code/Magento/Newsletter/Setup/UpgradeSchema.php b/app/code/Magento/Newsletter/Setup/UpgradeSchema.php
new file mode 100644
index 0000000000000..e7ce898de83a3
--- /dev/null
+++ b/app/code/Magento/Newsletter/Setup/UpgradeSchema.php
@@ -0,0 +1,36 @@
+startSetup();
+
+ if (version_compare($context->getVersion(), '2.0.1', '<')) {
+ $connection = $setup->getConnection();
+
+ $connection->addIndex(
+ $setup->getTable('newsletter_subscriber'),
+ $setup->getIdxName('newsletter_subscriber', ['subscriber_email']),
+ ['subscriber_email']
+ );
+ }
+
+ $setup->endSetup();
+ }
+}
diff --git a/app/code/Magento/Newsletter/etc/module.xml b/app/code/Magento/Newsletter/etc/module.xml
index f338445225222..5da16a9a3e9ba 100644
--- a/app/code/Magento/Newsletter/etc/module.xml
+++ b/app/code/Magento/Newsletter/etc/module.xml
@@ -6,7 +6,7 @@
*/
-->
-
+
diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php
index febeffb5c9c2b..b2cd613ba46a1 100644
--- a/app/code/Magento/Quote/Model/QuoteManagement.php
+++ b/app/code/Magento/Quote/Model/QuoteManagement.php
@@ -232,6 +232,7 @@ public function createEmptyCart()
$quote->setShippingAddress($this->quoteAddressFactory->create());
try {
+ $quote->getShippingAddress()->setCollectShippingRates(true);
$this->quoteRepository->save($quote);
} catch (\Exception $e) {
throw new CouldNotSaveException(__('Cannot create quote'));
diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php
index fb4fe954de94c..2ca7613796b07 100644
--- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php
+++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php
@@ -246,11 +246,15 @@ public function testCreateEmptyCartAnonymous()
$quoteId = 2311;
$quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class);
-
- $quoteAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class);
+ $quoteAddress = $this->createPartialMock(
+ \Magento\Quote\Model\Quote\Address::class,
+ ['setCollectShippingRates']
+ );
+ $quoteAddress->expects($this->once())->method('setCollectShippingRates')->with(true);
$quoteMock->expects($this->any())->method('setBillingAddress')->with($quoteAddress)->willReturnSelf();
$quoteMock->expects($this->any())->method('setShippingAddress')->with($quoteAddress)->willReturnSelf();
+ $quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($quoteAddress);
$this->quoteAddressFactory->expects($this->any())->method('create')->willReturn($quoteAddress);
diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
index 5520bae0e26a8..4804efcc76ecc 100644
--- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
+++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
@@ -108,6 +108,7 @@ public function execute()
}
$isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label'];
+ $responseAjax = new \Magento\Framework\DataObject();
try {
$this->shipmentLoader->setOrderId($this->getRequest()->getParam('order_id'));
@@ -143,7 +144,6 @@ public function execute()
$shipment->register();
$shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));
- $responseAjax = new \Magento\Framework\DataObject();
if ($isNeedCreateLabel) {
$this->labelGenerator->create($shipment, $this->_request);
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
index 61a39a441c973..31fadc2cf4f85 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php
@@ -60,17 +60,7 @@ class Form extends Block
*/
public function getGroup($tabName, $groupName)
{
- $this->baseUrl = $this->getBrowserUrl();
- if (substr($this->baseUrl, -1) !== '/') {
- $this->baseUrl = $this->baseUrl . '/';
- }
-
- $tabUrl = $this->getTabUrl($tabName);
-
- if ($this->getBrowserUrl() !== $tabUrl) {
- $this->browser->open($tabUrl);
- }
- $this->waitForElementNotVisible($this->tabReadiness);
+ $this->openTab($tabName);
$groupElement = $this->_rootElement->find(
sprintf($this->groupBlock, $tabName, $groupName),
@@ -95,6 +85,24 @@ public function getGroup($tabName, $groupName)
return $blockFactory->getMagentoBackendSystemConfigFormGroup($groupElement);
}
+ /**
+ * Check whether specified group presented on page.
+ *
+ * @param string $tabName
+ * @param string $groupName
+ *
+ * @return bool
+ */
+ public function isGroupVisible(string $tabName, string $groupName)
+ {
+ $this->openTab($tabName);
+
+ return $this->_rootElement->find(
+ sprintf($this->groupBlockLink, $tabName, $groupName),
+ Locator::SELECTOR_CSS
+ )->isVisible();
+ }
+
/**
* Retrieve url associated with the form.
*/
@@ -137,4 +145,24 @@ private function getTabUrl($tabName)
return $tabUrl;
}
+
+ /**
+ * Open specified tab.
+ *
+ * @param string $tabName
+ * @return void
+ */
+ private function openTab(string $tabName)
+ {
+ $this->baseUrl = $this->getBrowserUrl();
+ if (substr($this->baseUrl, -1) !== '/') {
+ $this->baseUrl = $this->baseUrl . '/';
+ }
+ $tabUrl = $this->getTabUrl($tabName);
+
+ if ($this->getBrowserUrl() !== $tabUrl) {
+ $this->browser->open($tabUrl);
+ }
+ $this->waitForElementNotVisible($this->tabReadiness);
+ }
}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php
index 1e73bb4867e2e..e443ef5a205e4 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php
@@ -9,27 +9,57 @@
use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit;
/**
- * Assert that Developer section is not present in production mode.
+ * Assert that all groups in Developer section is not present in production mode except debug group "Log to File" field.
*/
class AssertDeveloperSectionVisibility extends AbstractConstraint
{
/**
- * Assert Developer section is not present in production mode.
+ * List of groups not visible in production mode.
+ *
+ * @var array
+ */
+ private $groups = [
+ 'front_end_development_workflow',
+ 'restrict',
+ 'template',
+ 'translate_inline',
+ 'js',
+ 'css',
+ 'image',
+ 'static',
+ 'grid',
+ ];
+
+ /**
+ * Assert all groups in Developer section is not present in production mode except debug group "Log to File" field.
*
* @param SystemConfigEdit $configEdit
* @return void
*/
public function processAssert(SystemConfigEdit $configEdit)
{
+ $configEdit->open();
if ($_ENV['mage_mode'] === 'production') {
- \PHPUnit_Framework_Assert::assertFalse(
- in_array('Developer', $configEdit->getTabs()->getSubTabsNames('Advanced')),
- 'Developer section should be hidden in production mode.'
+ foreach ($this->groups as $group) {
+ \PHPUnit_Framework_Assert::assertFalse(
+ $configEdit->getForm()->isGroupVisible('dev', $group),
+ sprintf('%s group should be hidden in production mode.', $group)
+ );
+ }
+ \PHPUnit_Framework_Assert::assertTrue(
+ $configEdit->getForm()->getGroup('dev', 'debug')->isFieldVisible('dev', 'debug_debug', 'logging'),
+ '"Log to File" should be presented in production mode.'
);
} else {
+ foreach ($this->groups as $group) {
+ \PHPUnit_Framework_Assert::assertTrue(
+ $configEdit->getForm()->isGroupVisible('dev', $group),
+ sprintf('%s group should be visible in developer mode.', $group)
+ );
+ }
\PHPUnit_Framework_Assert::assertTrue(
- in_array('Developer', $configEdit->getTabs()->getSubTabsNames('Advanced')),
- 'Developer section should be not hidden in developer or default mode.'
+ $configEdit->getForm()->isGroupVisible('dev', 'debug'),
+ 'Debug group should be visible in developer mode.'
);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore.php
index 764189ae6200b..7f4a5130aea90 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore.php
@@ -19,7 +19,8 @@
1
)->setAttributeSetId(
4
-)->setStoreId(
+)->setCustomAttribute(
+ 'tax_class_id',
1
)->setCustomAttribute(
'tax_class_id',
@@ -45,8 +46,7 @@
)->save();
$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
-$product->setStoreId(1)
- ->load(1)
+$product->load(1)
->setStoreId($store->getId())
->setName('StoreTitle')
->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 6cdca3e6e46af..bdf894b3a094c 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
@@ -9,6 +9,8 @@
* @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php
* @magentoAppIsolation enabled
* @magentoDbIsolation enabled
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ProductTest extends \PHPUnit\Framework\TestCase
{
@@ -289,4 +291,36 @@ public function testCategoryIdsFilter()
$this->assertNotContains('Simple Product Two', $exportData);
$this->assertNotContains('Simple Product Not Visible On Storefront', $exportData);
}
+
+ /**
+ * Test 'hide from product page' export for non-default store.
+ *
+ * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_images.php
+ */
+ public function testExportWithMedia()
+ {
+ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+ $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ $product = $productRepository->get('simple', 1);
+ $mediaGallery = $product->getData('media_gallery');
+ $image = array_shift($mediaGallery['images']);
+ $this->model->setWriter(
+ $this->objectManager->create(
+ \Magento\ImportExport\Model\Export\Adapter\Csv::class
+ )
+ );
+ $exportData = $this->model->export();
+ /** @var $varDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */
+ $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class)
+ ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR);
+ $varDirectory->writeFile('test_product_with_image.csv', $exportData);
+ /** @var \Magento\Framework\File\Csv $csv */
+ $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class);
+ $data = $csv->getData($varDirectory->getAbsolutePath('test_product_with_image.csv'));
+ foreach ($data[0] as $columnNumber => $columnName) {
+ if ($columnName === 'hide_from_product_page') {
+ self::assertSame($image['file'], $data[2][$columnNumber]);
+ }
+ }
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
index e1eb639c6d371..5cd28031a997a 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -20,8 +20,8 @@
use Magento\Framework\App\Bootstrap;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\ObjectManager;
-use Magento\Framework\Registry;
use Magento\Framework\Filesystem;
+use Magento\Framework\Registry;
use Magento\ImportExport\Model\Import;
use Magento\Store\Model\Store;
use Psr\Log\LoggerInterface;
@@ -32,6 +32,7 @@
* @magentoDbIsolation enabled
* @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*/
class ProductTest extends \Magento\TestFramework\Indexer\TestCase
{
@@ -1970,4 +1971,23 @@ public function testImportWithDifferentSkuCase()
);
}
}
+
+ /**
+ * Test that product import with images for non-default store works properly.
+ *
+ * @magentoDataIsolation enabled
+ * @magentoDataFixture mediaImportImageFixture
+ * @magentoAppIsolation enabled
+ */
+ public function testImportImageForNonDefaultStore()
+ {
+ $this->importDataForMediaTest('import_media_two_stores.csv');
+ $product = $this->getProductBySku('simple_with_images');
+ $mediaGallery = $product->getData('media_gallery');
+ foreach ($mediaGallery['images'] as $image) {
+ $image['file'] === '/m/a/magento_image.jpg'
+ ? self::assertSame('1', $image['disabled'])
+ : self::assertSame('0', $image['disabled']);
+ }
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_two_stores.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_two_stores.csv
new file mode 100644
index 0000000000000..e276a527b0003
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_two_stores.csv
@@ -0,0 +1,3 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus
+simple_with_images,,Default,simple,Default Category,base,Simple Product,Description with html tag,Short description,1,1,0,"Catalog, Search",10,,,,simple-product,meta title,meta keyword,meta description,magento_image.jpg,,magento_image.jpg,,magento_image.jpg,,,,"11/1/17, 1:41 AM","11/1/17, 1:41 AM",,,Block after Info Column,,,,,,,,,,,,,,100,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,0,,,,,,,magento_image.jpg,Image Alt Text,,"name=Test Select,type=drop_down,required=1,price=3.0000,price_type=fixed,sku=3-1-select,file_extension=,image_size_x=,image_size_y=,option_title=Option 1|name=Test Select,type=drop_down,required=1,price=3.0000,price_type=fixed,sku=3-2-select,file_extension=,image_size_x=,image_size_y=,option_title=Option 2|name=Test Field,type=field,required=1,price=1.0000,price_type=fixed,sku=1-text,max_characters=100,file_extension=,image_size_x=,image_size_y=|name=Test Radio,type=radio,required=1,price=3.0000,price_type=fixed,sku=4-1-radio,file_extension=,image_size_x=,image_size_y=,option_title=Option 1|name=Test Radio,type=radio,required=1,price=3.0000,price_type=fixed,sku=4-2-radio,file_extension=,image_size_x=,image_size_y=,option_title=Option 2|name=Test Date and Time,type=date_time,required=1,price=2.0000,price_type=fixed,sku=2-date,file_extension=,image_size_x=,image_size_y=",,,,,,,,,
+simple_with_images,default,Default,simple,,,,,,,,,,,,,,,,,,,Image Alt Text,,Image Alt Text,,Image Alt Text,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,magento_image.jpg,,,,,,,,,,
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images.php
new file mode 100644
index 0000000000000..da456767257e1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images.php
@@ -0,0 +1,47 @@
+create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+$product = $productRepository->get('simple');
+$product->setStoreId(0)
+ ->setImage('/m/a/magento_image.jpg')
+ ->setSmallImage('/m/a/magento_image.jpg')
+ ->setThumbnail('/m/a/magento_image.jpg')
+ ->setData(
+ 'media_gallery',
+ [
+ 'images' => [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'position' => 1,
+ 'label' => 'Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image',
+ ],
+ ],
+ ]
+ )->save();
+$image = array_shift($product->getData('media_gallery')['images']);
+$product = $productRepository->get('simple', false, 1, true);
+$product->setData(
+ 'media_gallery',
+ [
+ 'images' => [
+ [
+ 'value_id' => $image['value_id'],
+ 'file' => $image['file'],
+ 'disabled' => 1,
+ 'media_type' => 'image',
+ ],
+ ],
+ ]
+);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images_rollback.php
new file mode 100644
index 0000000000000..d7a52465ead4a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_images_rollback.php
@@ -0,0 +1,8 @@
+mode->enableDeveloperMode();
- $this->enableDebugging();
if (file_exists($this->getDebuggerLogPath())) {
unlink($this->getDebuggerLogPath());
}
diff --git a/lib/web/magnifier/magnifier.js b/lib/web/magnifier/magnifier.js
index 958af0e96641b..c475364368922 100644
--- a/lib/web/magnifier/magnifier.js
+++ b/lib/web/magnifier/magnifier.js
@@ -588,7 +588,7 @@
_init($box, gOptions);
});
- $(document).on('mousemove', onMousemove);
+ $box.on('mousemove', onMousemove);
_init($box, gOptions);
}