Skip to content

Commit 79fa2d8

Browse files
committed
MC-15391: Delete Products Image In case of multiple stores test is deleting the image without any notification
1 parent db0dce6 commit 79fa2d8

File tree

11 files changed

+502
-28
lines changed

11 files changed

+502
-28
lines changed

app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
7+
68
namespace Magento\Catalog\Model\Product\Gallery;
79

10+
use Magento\Catalog\Api\Data\ProductInterface;
811
use Magento\Framework\App\Filesystem\DirectoryList;
12+
use Magento\Framework\App\ObjectManager;
913
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
1014
use Magento\MediaStorage\Model\File\Uploader as FileUploader;
15+
use Magento\Store\Model\StoreManagerInterface;
1116

1217
/**
1318
* Create handler for catalog product gallery
@@ -74,6 +79,16 @@ class CreateHandler implements ExtensionInterface
7479
*/
7580
private $mediaAttributeCodes;
7681

82+
/**
83+
* @var array
84+
*/
85+
private $imagesGallery;
86+
87+
/**
88+
* @var \Magento\Store\Model\StoreManagerInterface
89+
*/
90+
private $storeManager;
91+
7792
/**
7893
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
7994
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
@@ -82,6 +97,8 @@ class CreateHandler implements ExtensionInterface
8297
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
8398
* @param \Magento\Framework\Filesystem $filesystem
8499
* @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb
100+
* @param \Magento\Store\Model\StoreManagerInterface|null $storeManager
101+
* @throws \Magento\Framework\Exception\FileSystemException
85102
*/
86103
public function __construct(
87104
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
@@ -90,7 +107,8 @@ public function __construct(
90107
\Magento\Framework\Json\Helper\Data $jsonHelper,
91108
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
92109
\Magento\Framework\Filesystem $filesystem,
93-
\Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb
110+
\Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb,
111+
\Magento\Store\Model\StoreManagerInterface $storeManager = null
94112
) {
95113
$this->metadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
96114
$this->attributeRepository = $attributeRepository;
@@ -99,6 +117,7 @@ public function __construct(
99117
$this->mediaConfig = $mediaConfig;
100118
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
101119
$this->fileStorageDb = $fileStorageDb;
120+
$this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
102121
}
103122

104123
/**
@@ -137,6 +156,10 @@ public function execute($product, $arguments = [])
137156

138157
if ($product->getIsDuplicate() != true) {
139158
foreach ($value['images'] as &$image) {
159+
if (!empty($image['removed']) && !$this->canRemoveImage($product, $image['file'])) {
160+
$image['removed'] = '';
161+
}
162+
140163
if (!empty($image['removed'])) {
141164
$clearImages[] = $image['file'];
142165
} elseif (empty($image['value_id'])) {
@@ -152,6 +175,10 @@ public function execute($product, $arguments = [])
152175
// For duplicating we need copy original images.
153176
$duplicate = [];
154177
foreach ($value['images'] as &$image) {
178+
if (!empty($image['removed']) && !$this->canRemoveImage($product, $image['file'])) {
179+
$image['removed'] = '';
180+
}
181+
155182
if (empty($image['value_id']) || !empty($image['removed'])) {
156183
continue;
157184
}
@@ -538,4 +565,46 @@ private function processMediaAttributeLabel(
538565
);
539566
}
540567
}
568+
569+
/**
570+
* Get product images for all stores
571+
*
572+
* @param ProductInterface $product
573+
* @return array
574+
*/
575+
private function getImagesForAllStores(ProductInterface $product)
576+
{
577+
if ($this->imagesGallery === null) {
578+
$storeIds = array_keys($this->storeManager->getStores());
579+
$storeIds[] = 0;
580+
581+
$this->imagesGallery = $this->resourceModel->getProductImages($product, $storeIds);
582+
}
583+
584+
return $this->imagesGallery;
585+
}
586+
587+
/**
588+
* Check possibility to remove image
589+
*
590+
* @param ProductInterface $product
591+
* @param string $imageFile
592+
* @return bool
593+
*/
594+
private function canRemoveImage(ProductInterface $product, string $imageFile) :bool
595+
{
596+
$canRemoveImage = true;
597+
$gallery = $this->getImagesForAllStores($product);
598+
$storeId = $product->getStoreId();
599+
600+
if (!empty($gallery)) {
601+
foreach ($gallery as $image) {
602+
if ($image['filepath'] === $imageFile && (int) $image['store_id'] !== $storeId) {
603+
$canRemoveImage = false;
604+
}
605+
}
606+
}
607+
608+
return $canRemoveImage;
609+
}
541610
}

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignImageRolesActionGroup.xml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
<arguments>
1616
<argument name="image"/>
1717
</arguments>
18-
19-
<conditionalClick selector="{{AdminProductImagesSection.productImagesToggleState('closed')}}" dependentSelector="{{AdminProductImagesSection.productImagesToggleState('open')}}" visible="false" stepKey="clickSectionImage"/>
18+
<conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageFile(image.fileName)}}" visible="false" stepKey="expandImages"/>
19+
<waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="seeProductImageName"/>
2020
<click selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="clickProductImage"/>
2121
<waitForElementVisible selector="{{AdminProductImagesSection.altText}}" stepKey="seeAltTextSection"/>
2222
<checkOption selector="{{AdminProductImagesSection.roleBase}}" stepKey="checkRoleBase"/>
@@ -25,4 +25,14 @@
2525
<checkOption selector="{{AdminProductImagesSection.roleSwatch}}" stepKey="checkRoleSwatch"/>
2626
<click selector="{{AdminSlideOutDialogSection.closeButton}}" stepKey="clickCloseButton"/>
2727
</actionGroup>
28+
<actionGroup name="AdminAssignImageRolesIfUnassignedActionGroup" extends="AdminAssignImageRolesActionGroup">
29+
<annotations>
30+
<description>Requires the navigation to the Product Creation page. Assign the Base, Small, Thumbnail, and Swatch Roles to image.</description>
31+
</annotations>
32+
33+
<conditionalClick selector="{{AdminProductImagesSection.roleBase}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Base')}}" visible="false" stepKey="checkRoleBase"/>
34+
<conditionalClick selector="{{AdminProductImagesSection.roleSmall}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Small')}}" visible="false" stepKey="checkRoleSmall"/>
35+
<conditionalClick selector="{{AdminProductImagesSection.roleThumbnail}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Thumbnail')}}" visible="false" stepKey="checkRoleThumbnail"/>
36+
<conditionalClick selector="{{AdminProductImagesSection.roleSwatch}}" dependentSelector="{{AdminProductImagesSection.isRoleChecked('Swatch')}}" visible="false" stepKey="checkRoleSwatch"/>
37+
</actionGroup>
2838
</actionGroups>

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<seeInCurrentUrl url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, product.type_id)}}" stepKey="seeNewProductUrl"/>
2525
<see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/>
2626
</actionGroup>
27-
27+
2828
<!--Navigate to create product page directly via ID-->
2929
<actionGroup name="goToProductPageViaID">
3030
<annotations>
@@ -184,6 +184,18 @@
184184
<click selector="{{AdminProductImagesSection.removeImageButton}}" stepKey="clickRemoveImage"/>
185185
</actionGroup>
186186

187+
<!--Remove Product image by name-->
188+
<actionGroup name="RemoveProductImageByName" extends="removeProductImage">
189+
<annotations>
190+
<description>Removes a Product Image on the Admin Products creation/edit page by name.</description>
191+
</annotations>
192+
193+
<arguments>
194+
<argument name="image" defaultValue="ProductImage"/>
195+
</arguments>
196+
<click selector="{{AdminProductImagesSection.removeImageButtonForExactImage(image.fileName)}}" stepKey="clickRemoveImage"/>
197+
</actionGroup>
198+
187199
<!-- Assert product image in Admin Product page -->
188200
<actionGroup name="assertProductImageAdminProductPage">
189201
<annotations>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
11+
<entity name="ProductFormMessages" type="message">
12+
<data key="remove_image_notice">The image cannot be removed as it has been assigned to the other image role</data>
13+
<data key="save_success">You saved the product.</data>
14+
</entity>
15+
</entities>

app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMainActionsSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<element name="DeleteButton" type="button" selector=".page-actions-inner #delete" timeout="30"/>
1414
<element name="CategoryStoreViewDropdownToggle" type="button" selector="#store-change-button"/>
1515
<element name="CategoryStoreViewOption" type="button" selector="//div[contains(@class, 'store-switcher')]//a[normalize-space()='{{store}}']" parameterized="true"/>
16+
<element name="CategoryStoreViewOptionSelected" type="button" selector="//div[contains(@class, 'store-switcher')]//div[contains(@class,'actions')]//button[contains(text(),'{{store}}')]" parameterized="true"/>
1617
<element name="CategoryStoreViewModalAccept" type="button" selector=".modal-popup.confirm._show .action-accept"/>
1718
<element name="allStoreViews" type="button" selector=".store-switcher .store-switcher-all" timeout="30"/>
1819
<element name="storeSwitcher" type="text" selector=".store-switcher"/>

app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagesSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<element name="imageUploadButton" type="button" selector="div.image div.fileinput-button"/>
1515
<element name="imageFile" type="text" selector="//*[@id='media_gallery_content']//img[contains(@src, '{{url}}')]" parameterized="true"/>
1616
<element name="removeImageButton" type="button" selector=".action-remove"/>
17+
<element name="removeImageButtonForExactImage" type="button" selector="[id='media_gallery_content'] img[src*='{{imageName}}'] + div[class='actions'] button[class='action-remove']" parameterized="true"/>
1718
<element name="modalOkBtn" type="button" selector="button.action-primary.action-accept"/>
1819
<element name="uploadProgressBar" type="text" selector=".uploader .file-row"/>
1920
<element name="productImagesToggleState" type="button" selector="[data-index='gallery'] > [data-state-collapsible='{{status}}']" parameterized="true"/>
@@ -27,6 +28,7 @@
2728
<element name="roleSmall" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li/label[normalize-space(.) = 'Small']"/>
2829
<element name="roleThumbnail" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li/label[normalize-space(.) = 'Thumbnail']"/>
2930
<element name="roleSwatch" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li/label[normalize-space(.) = 'Swatch']"/>
31+
<element name="isRoleChecked" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li/label[normalize-space(.) = '{{role}}']/parent::li[contains(@class,'selected')]" parameterized="true"/>
3032

3133
<element name="isBaseSelected" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li[contains(@class, 'selected')]/label[normalize-space(.) = 'Base']"/>
3234
<element name="isSmallSelected" type="button" selector="//div[contains(@class, 'field-image-role')]//ul/li[contains(@class, 'selected')]/label[normalize-space(.) = 'Small']"/>

0 commit comments

Comments
 (0)