Skip to content

Commit

Permalink
Merge pull request #2377 from magento-panda/PANDA-FIXES-2.2
Browse files Browse the repository at this point in the history
### Bug
* [MAGETWO-84942](https://jira.corp.magento.com/browse/MAGETWO-84942) [Magento cloud] Custom options should be cleared if custom options in import file is empty
* [MAGETWO-89281](https://jira.corp.magento.com/browse/MAGETWO-89281) Files and folders symlinked in pub/media cannot be deleted from media gallery browser
  • Loading branch information
duhon committed Apr 13, 2018
2 parents b208fc4 + d64ec75 commit 565edc9
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 105 deletions.
103 changes: 69 additions & 34 deletions app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -797,15 +797,15 @@ protected function _addRowsErrors($errorCode, array $errorNumbers)
protected function _validateMainRow(array $rowData, $rowNumber)
{
if (!empty($rowData[self::COLUMN_STORE]) && !array_key_exists(
$rowData[self::COLUMN_STORE],
$this->_storeCodeToId
)
$rowData[self::COLUMN_STORE],
$this->_storeCodeToId
)
) {
$this->_productEntity->addRowError(self::ERROR_INVALID_STORE, $rowNumber);
} elseif (!empty($rowData[self::COLUMN_TYPE]) && !array_key_exists(
$rowData[self::COLUMN_TYPE],
$this->_specificTypes
)
$rowData[self::COLUMN_TYPE],
$this->_specificTypes
)
) {
// type
$this->_productEntity->addRowError(self::ERROR_INVALID_TYPE, $rowNumber);
Expand Down Expand Up @@ -910,9 +910,9 @@ protected function _saveNewOptionData(array $rowData, $rowNumber)
protected function _validateSecondaryRow(array $rowData, $rowNumber)
{
if (!empty($rowData[self::COLUMN_STORE]) && !array_key_exists(
$rowData[self::COLUMN_STORE],
$this->_storeCodeToId
)
$rowData[self::COLUMN_STORE],
$this->_storeCodeToId
)
) {
$this->_productEntity->addRowError(self::ERROR_INVALID_STORE, $rowNumber);
} elseif (!empty($rowData[self::COLUMN_ROW_PRICE]) && !is_numeric(rtrim($rowData[self::COLUMN_ROW_PRICE], '%'))
Expand Down Expand Up @@ -1211,6 +1211,7 @@ protected function _importData()
$typeTitles = [];
$parentCount = [];
$childCount = [];
$optionsToRemove = [];

foreach ($bunch as $rowNumber => $rowData) {
if (isset($optionId, $valueId) && empty($rowData[PRODUCT::COL_STORE_VIEW_CODE])) {
Expand All @@ -1220,6 +1221,12 @@ protected function _importData()
$optionId = $nextOptionId;
$valueId = $nextValueId;
$multiRowData = $this->_getMultiRowFormat($rowData);
if (!empty($rowData[self::COLUMN_SKU]) && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) {
$this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]];
if (array_key_exists('custom_options', $rowData) && trim($rowData['custom_options']) === "") {
$optionsToRemove[] = $this->_rowProductId;
}
}
foreach ($multiRowData as $optionData) {
$combinedData = array_merge($rowData, $optionData);

Expand Down Expand Up @@ -1253,32 +1260,23 @@ protected function _importData()
}
}

// Save prepared custom options data !!!
// Remove all existing options if import behaviour is APPEND
// in other case remove options for products with empty "custom_options" row only
if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
$this->_deleteEntities(array_keys($products));
} elseif (!empty($optionsToRemove)) {
// Remove options for products with empty "custom_options" row
$this->_deleteEntities($optionsToRemove);
}

// Save prepared custom options data
if ($this->_isReadyForSaving($options, $titles, $typeValues)) {
if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
$this->_compareOptionsWithExisting($options, $titles, $prices, $typeValues);
$this->restoreOriginalOptionTypeIds($typeValues, $typePrices, $typeTitles);
}

$this->_saveOptions(
$options
)->_saveTitles(
$titles
)->_savePrices(
$prices
)->_saveSpecificTypeValues(
$typeValues
)->_saveSpecificTypePrices(
$typePrices
)->_saveSpecificTypeTitles(
$typeTitles
)->_updateProducts(
$products
);
$types = [
'values' => $typeValues,
'prices' => $typePrices,
'titles' => $typeTitles
];
$this->savePreparedCustomOptions($products, $options, $titles, $prices, $types);
}
}

Expand Down Expand Up @@ -1523,12 +1521,9 @@ private function getExistingOptionTypeId($optionId, $storeId, $optionTypeTitle)
*/
protected function _parseRequiredData(array $rowData)
{
if ($rowData[self::COLUMN_SKU] != '' && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) {
$this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]];
} elseif (!isset($this->_rowProductId)) {
if (!isset($this->_rowProductId)) {
return false;
}

// Init store
if (!empty($rowData[self::COLUMN_STORE])) {
if (!isset($this->_storeCodeToId[$rowData[self::COLUMN_STORE]])) {
Expand Down Expand Up @@ -1982,4 +1977,44 @@ private function getProductIdentifierField()
}
return $this->productEntityIdentifierField;
}

/**
* Save prepared custom options
*
* @param array $products
* @param array $options
* @param array $titles
* @param array $prices
* @param array $types
*
* @return void
*/
private function savePreparedCustomOptions(
array $products,
array $options,
array $titles,
array $prices,
array $types
) {
if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
$this->_compareOptionsWithExisting($options, $titles, $prices, $types['values']);
$this->restoreOriginalOptionTypeIds($types['values'], $types['prices'], $types['titles']);
}

$this->_saveOptions(
$options
)->_saveTitles(
$titles
)->_savePrices(
$prices
)->_saveSpecificTypeValues(
$types['values']
)->_saveSpecificTypePrices(
$types['prices']
)->_saveSpecificTypeTitles(
$types['titles']
)->_updateProducts(
$products
);
}
}
2 changes: 1 addition & 1 deletion app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ protected function _validatePath($path)
*/
protected function _sanitizePath($path)
{
return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPath($path)), '/');
return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPathSafety($path)), '/');
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,9 @@ class StorageTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
$this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class);
$this->driverMock = $this->getMockForAbstractClass(
\Magento\Framework\Filesystem\DriverInterface::class,
[],
'',
false,
false,
true,
['getRealPath']
);
$this->driverMock->expects($this->any())->method('getRealPath')->will($this->returnArgument(0));
$this->driverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class)
->setMethods(['getRealPathSafety'])
->getMockForAbstractClass();

$this->directoryMock = $this->createPartialMock(
\Magento\Framework\Filesystem\Directory\Write::class,
Expand Down Expand Up @@ -243,6 +236,7 @@ public function testDeleteDirectoryOverRoot()
\Magento\Framework\Exception\LocalizedException::class,
sprintf('Directory %s is not under storage root path.', self::INVALID_DIRECTORY_OVER_ROOT)
);
$this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0));
$this->imagesStorage->deleteDirectory(self::INVALID_DIRECTORY_OVER_ROOT);
}

Expand All @@ -255,6 +249,7 @@ public function testDeleteRootDirectory()
\Magento\Framework\Exception\LocalizedException::class,
sprintf('We can\'t delete root directory %s right now.', self::STORAGE_ROOT_DIR)
);
$this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0));
$this->imagesStorage->deleteDirectory(self::STORAGE_ROOT_DIR);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,10 @@ public function testStockState()
* @dataProvider getBehaviorDataProvider
* @param string $importFile
* @param string $sku
* @param int $expectedOptionsQty
* @magentoAppIsolation enabled
*/
public function testSaveCustomOptions($importFile, $sku)
public function testSaveCustomOptions($importFile, $sku, $expectedOptionsQty)
{
$pathToFile = __DIR__ . '/_files/' . $importFile;
$importModel = $this->createImportModel($pathToFile);
Expand Down Expand Up @@ -314,6 +315,7 @@ public function testSaveCustomOptions($importFile, $sku)
// assert of options data
$this->assertCount(count($expectedData['data']), $actualData['data']);
$this->assertCount(count($expectedData['values']), $actualData['values']);
$this->assertCount($expectedOptionsQty, $actualData['options']);
foreach ($expectedData['options'] as $expectedId => $expectedOption) {
$elementExist = false;
// find value in actual options and values
Expand Down Expand Up @@ -418,12 +420,19 @@ public function getBehaviorDataProvider()
{
return [
'Append behavior with existing product' => [
'$importFile' => 'product_with_custom_options.csv',
'$sku' => 'simple',
'importFile' => 'product_with_custom_options.csv',
'sku' => 'simple',
'expectedOptionsQty' => 6
],
'Append behavior with existing product and without options in import file' => [
'importFile' => 'product_without_custom_options.csv',
'sku' => 'simple',
'expectedOptionsQty' => 0
],
'Append behavior with new product' => [
'$importFile' => 'product_with_custom_options_new.csv',
'$sku' => 'simple_new',
'importFile' => 'product_with_custom_options_new.csv',
'sku' => 'simple_new',
'expectedOptionsQty' => 4
]
];
}
Expand Down Expand Up @@ -574,6 +583,7 @@ protected function getExpectedOptionsData($pathToFile, $storeCode = '')
break;
}
}
if (!empty($productData['data'][$storeRowId]['custom_options'])) {
foreach (explode('|', $productData['data'][$storeRowId]['custom_options']) as $optionData) {
$option = array_values(
array_map(
Expand All @@ -595,7 +605,7 @@ function ($input) {
$expectedData[$expectedOptionId] = [];
foreach ($this->_assertOptions as $assertKey => $assertFieldName) {
if (array_key_exists($assertFieldName, $option)
&& !(($assertFieldName == 'price' || $assertFieldName == 'sku')
&& !(($assertFieldName == 'price' || $assertFieldName == 'sku')
&& in_array($option['type'], $this->specificTypes))
) {
$expectedData[$expectedOptionId][$assertKey] = $option[$assertFieldName];
Expand All @@ -613,6 +623,7 @@ function ($input) {
$expectedValues[$expectedOptionId][] = $optionValue;
}
}
}
return [
'id' => $expectedOptionId,
'options' => $expectedOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sku,website_code,store_view_code,attribute_set_code,product_type,name,description,short_description,weight,product_online,visibility,product_websites,categories,price,special_price,special_price_from_date,special_price_to_date,tax_class_name,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,additional_images,additional_image_labels,configurable_variation_labels,configurable_variations,bundle_price_type,bundle_sku_type,bundle_weight_type,bundle_values,downloadble_samples,downloadble_links,associated_skus,related_skus,crosssell_skus,upsell_skus,custom_options,additional_attributes,manage_stock,is_in_stock,qty,out_of_stock_qty,is_qty_decimal,allow_backorders,min_cart_qty,max_cart_qty,notify_on_stock_below,qty_increments,enable_qty_increments,is_decimal_divided,new_from_date,new_to_date,gift_message_available,created_at,updated_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_price,msrp_display_actual_price_type,map_enabled
simple,base,,Default,simple,New Product,,,9,1,"Catalog, Search",base,,10,,,,Taxable Goods,new-product,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,,
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,29 @@ class DeleteFilesTest extends \PHPUnit\Framework\TestCase
/**
* @var string
*/
private $fullDirectoryPath;
private $fileName = 'magento_small_image.jpg';

/**
* @var string
* @var \Magento\Framework\Filesystem
*/
private $fileName = 'magento_small_image.jpg';
private $filesystem;

/**
* @var \Magento\Framework\ObjectManagerInterface
*/
private $objectManager;

/**
* @inheritdoc
*/
protected function setUp()
{
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$directoryName = 'directory1';
$filesystem = $objectManager->get(\Magento\Framework\Filesystem::class);
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class);
/** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */
$this->imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->fullDirectoryPath = $this->imagesHelper->getStorageRoot() . '/' . $directoryName;
$this->mediaDirectory->create($this->mediaDirectory->getRelativePath($this->fullDirectoryPath));
$filePath = $this->fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName;
$fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files');
copy($fixtureDir . '/' . $this->fileName, $filePath);
$this->model = $objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::class);
$this->imagesHelper = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
$this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->model = $this->objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::class);
}

/**
Expand All @@ -65,17 +64,48 @@ protected function setUp()
*/
public function testExecute()
{
$directoryName = 'directory1';
$fullDirectoryPath = $this->imagesHelper->getStorageRoot() . '/' . $directoryName;
$this->mediaDirectory->create($this->mediaDirectory->getRelativePath($fullDirectoryPath));
$filePath = $fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName;
$fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files');
copy($fixtureDir . '/' . $this->fileName, $filePath);

$this->model->getRequest()->setMethod('POST')
->setPostValue('files', [$this->imagesHelper->idEncode($this->fileName)]);
$this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath);
$this->model->getStorage()->getSession()->setCurrentPath($fullDirectoryPath);
$this->model->execute();
$this->assertFalse(
$this->mediaDirectory->isExist(
$this->mediaDirectory->getRelativePath($this->fullDirectoryPath . '/' . $this->fileName)
$this->mediaDirectory->getRelativePath($fullDirectoryPath . '/' . $this->fileName)
)
);
}

/**
* Execute method with correct directory path and file name to check that files under linked media directory
* can be removed.
*
* @return void
* @magentoDataFixture Magento/Cms/_files/linked_media.php
*/
public function testExecuteWithLinkedMedia()
{
$directoryName = 'linked_media';
$fullDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB)
->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName;
$filePath = $fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName;
$fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files');
copy($fixtureDir . '/' . $this->fileName, $filePath);

$wysiwygDir = $this->mediaDirectory->getAbsolutePath() . '/wysiwyg';
$this->model->getRequest()->setMethod('POST')
->setPostValue('files', [$this->imagesHelper->idEncode($this->fileName)]);
$this->model->getStorage()->getSession()->setCurrentPath($wysiwygDir);
$this->model->execute();
$this->assertFalse(is_file($fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName));
}

/**
* @inheritdoc
*/
Expand Down

0 comments on commit 565edc9

Please sign in to comment.