Skip to content

Commit c697bc9

Browse files
committed
Merge remote-tracking branches 'local/ACP2E-1168', 'local/ACP2E-1180' and 'local/ACP2E-1207' into PR_20_SEP_2022
4 parents 65c71f2 + 4644333 + 7d1254d + 50ffac2 commit c697bc9

File tree

15 files changed

+655
-80
lines changed

15 files changed

+655
-80
lines changed

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

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@
88
namespace Magento\Catalog\Model\Product\Gallery;
99

1010
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
1112
use Magento\Catalog\Model\Product;
13+
use Magento\Catalog\Model\Product\Media\Config;
14+
use Magento\Catalog\Model\ResourceModel\Product\Gallery;
1215
use Magento\Framework\App\Filesystem\DirectoryList;
1316
use Magento\Framework\App\ObjectManager;
17+
use Magento\Framework\EntityManager\MetadataPool;
1418
use Magento\Framework\EntityManager\Operation\ExtensionInterface;
19+
use Magento\Framework\Exception\FileSystemException;
20+
use Magento\Framework\Filesystem;
21+
use Magento\Framework\Json\Helper\Data;
22+
use Magento\MediaStorage\Helper\File\Storage\Database;
1523
use Magento\MediaStorage\Model\File\Uploader as FileUploader;
1624
use Magento\Store\Model\Store;
1725
use Magento\Store\Model\StoreManagerInterface;
@@ -89,6 +97,11 @@ class CreateHandler implements ExtensionInterface
8997
*/
9098
private $storeManager;
9199

100+
/**
101+
* @var DeleteValidator
102+
*/
103+
private $deleteValidator;
104+
92105
/**
93106
* @var string[]
94107
*/
@@ -99,25 +112,27 @@ class CreateHandler implements ExtensionInterface
99112
];
100113

101114
/**
102-
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
103-
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
104-
* @param \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel
105-
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
106-
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
107-
* @param \Magento\Framework\Filesystem $filesystem
108-
* @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb
109-
* @param \Magento\Store\Model\StoreManagerInterface|null $storeManager
110-
* @throws \Magento\Framework\Exception\FileSystemException
115+
* @param MetadataPool $metadataPool
116+
* @param ProductAttributeRepositoryInterface $attributeRepository
117+
* @param Gallery $resourceModel
118+
* @param Data $jsonHelper
119+
* @param Config $mediaConfig
120+
* @param Filesystem $filesystem
121+
* @param Database $fileStorageDb
122+
* @param StoreManagerInterface|null $storeManager
123+
* @param DeleteValidator|null $deleteValidator
124+
* @throws FileSystemException
111125
*/
112126
public function __construct(
113-
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
114-
\Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
115-
\Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel,
116-
\Magento\Framework\Json\Helper\Data $jsonHelper,
117-
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
118-
\Magento\Framework\Filesystem $filesystem,
119-
\Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb,
120-
\Magento\Store\Model\StoreManagerInterface $storeManager = null
127+
MetadataPool $metadataPool,
128+
ProductAttributeRepositoryInterface $attributeRepository,
129+
Gallery $resourceModel,
130+
Data $jsonHelper,
131+
Config $mediaConfig,
132+
Filesystem $filesystem,
133+
Database $fileStorageDb,
134+
StoreManagerInterface $storeManager = null,
135+
?DeleteValidator $deleteValidator = null
121136
) {
122137
$this->metadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
123138
$this->attributeRepository = $attributeRepository;
@@ -127,6 +142,7 @@ public function __construct(
127142
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
128143
$this->fileStorageDb = $fileStorageDb;
129144
$this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
145+
$this->deleteValidator = $deleteValidator ?: ObjectManager::getInstance()->get(DeleteValidator::class);
130146
}
131147

132148
/**
@@ -165,7 +181,7 @@ public function execute($product, $arguments = [])
165181

166182
if ($product->getIsDuplicate() != true) {
167183
foreach ($value['images'] as &$image) {
168-
if (!empty($image['removed']) && !$this->canRemoveImage($product, $image['file'])) {
184+
if (!empty($image['removed']) && $this->deleteValidator->validate($product, $image['file'])) {
169185
$image['removed'] = '';
170186
}
171187

@@ -184,7 +200,7 @@ public function execute($product, $arguments = [])
184200
// For duplicating we need copy original images.
185201
$duplicate = [];
186202
foreach ($value['images'] as &$image) {
187-
if (!empty($image['removed']) && !$this->canRemoveImage($product, $image['file'])) {
203+
if (!empty($image['removed']) && $this->deleteValidator->validate($product, $image['file'])) {
188204
$image['removed'] = '';
189205
}
190206

@@ -608,41 +624,6 @@ private function getImagesForAllStores(ProductInterface $product)
608624
return $this->imagesGallery;
609625
}
610626

611-
/**
612-
* Check possibility to remove image
613-
*
614-
* @param ProductInterface $product
615-
* @param string $imageFile
616-
* @return bool
617-
*/
618-
private function canRemoveImage(ProductInterface $product, string $imageFile) :bool
619-
{
620-
$canRemoveImage = true;
621-
$gallery = $this->getImagesForAllStores($product);
622-
$storeId = $product->getStoreId();
623-
$storeIds = [];
624-
$storeIds[] = 0;
625-
$websiteIds = array_map('intval', $product->getWebsiteIds() ?? []);
626-
foreach ($this->storeManager->getStores() as $store) {
627-
if (in_array((int) $store->getWebsiteId(), $websiteIds, true)) {
628-
$storeIds[] = (int) $store->getId();
629-
}
630-
}
631-
632-
if (!empty($gallery)) {
633-
foreach ($gallery as $image) {
634-
if (in_array((int) $image['store_id'], $storeIds)
635-
&& $image['filepath'] === $imageFile
636-
&& (int) $image['store_id'] !== $storeId
637-
) {
638-
$canRemoveImage = false;
639-
}
640-
}
641-
}
642-
643-
return $canRemoveImage;
644-
}
645-
646627
/**
647628
* Get media attribute value for store view
648629
*
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product\Gallery;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Model\ResourceModel\Product\Gallery;
12+
use Magento\Framework\Phrase;
13+
use Magento\Store\Model\Store;
14+
15+
/**
16+
* Validates media image for removal
17+
*/
18+
class DeleteValidator
19+
{
20+
/**
21+
* @var Gallery
22+
*/
23+
private Gallery $resourceModel;
24+
25+
/**
26+
* @var ProductInterface|null
27+
*/
28+
private ?ProductInterface $product = null;
29+
30+
/**
31+
* @var array|null
32+
*/
33+
private ?array $imagesWithRolesInOtherStoresCache = null;
34+
35+
/**
36+
* @param Gallery $resourceModel
37+
*/
38+
public function __construct(
39+
Gallery $resourceModel
40+
) {
41+
$this->resourceModel = $resourceModel;
42+
}
43+
44+
/**
45+
* Validates media image for removal
46+
*
47+
* @param ProductInterface $product
48+
* @param string $imageFile
49+
* @return Phrase[]
50+
*/
51+
public function validate(ProductInterface $product, string $imageFile): array
52+
{
53+
$errors = [];
54+
if (count($product->getStoreIds()) > 1) {
55+
if (in_array($imageFile, $this->getImagesWithRolesInOtherStores($product))) {
56+
$errors[] = __('The image cannot be removed as it has been assigned to the other image role');
57+
}
58+
}
59+
60+
return $errors;
61+
}
62+
63+
/**
64+
* Returns all images that are assigned to a role in store views other than the current store view
65+
*
66+
* @param ProductInterface $product
67+
* @return array
68+
*/
69+
private function getImagesWithRolesInOtherStores(ProductInterface $product): array
70+
{
71+
if ($this->product !== $product || !$this->imagesWithRolesInOtherStoresCache) {
72+
$this->product = $product;
73+
$storeIds = array_diff(
74+
array_merge($product->getStoreIds(), [Store::DEFAULT_STORE_ID]),
75+
[$product->getStoreId()]
76+
);
77+
$this->imagesWithRolesInOtherStoresCache = array_column(
78+
$this->resourceModel->getProductImages($product, $storeIds),
79+
'filepath'
80+
);
81+
}
82+
return $this->imagesWithRolesInOtherStoresCache;
83+
}
84+
}

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
namespace Magento\Catalog\Model\Product\Gallery;
88

99
use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface;
10+
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Framework\App\ObjectManager;
1013
use Magento\Framework\Exception\InputException;
1114
use Magento\Framework\Exception\NoSuchEntityException;
1215
use Magento\Framework\Exception\StateException;
@@ -32,17 +35,34 @@ class GalleryManagement implements \Magento\Catalog\Api\ProductAttributeMediaGal
3235
protected $contentValidator;
3336

3437
/**
35-
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
38+
* @var ProductInterfaceFactory
39+
*/
40+
private $productInterfaceFactory;
41+
42+
/**
43+
* @var DeleteValidator
44+
*/
45+
private $deleteValidator;
46+
47+
/**
48+
* @param ProductRepositoryInterface $productRepository
3649
* @param ImageContentValidatorInterface $contentValidator
37-
*
50+
* @param ProductInterfaceFactory|null $productInterfaceFactory
51+
* @param DeleteValidator|null $deleteValidator
3852
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
3953
*/
4054
public function __construct(
41-
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
42-
ImageContentValidatorInterface $contentValidator
55+
ProductRepositoryInterface $productRepository,
56+
ImageContentValidatorInterface $contentValidator,
57+
?ProductInterfaceFactory $productInterfaceFactory = null,
58+
?DeleteValidator $deleteValidator = null
4359
) {
4460
$this->productRepository = $productRepository;
4561
$this->contentValidator = $contentValidator;
62+
$this->productInterfaceFactory = $productInterfaceFactory
63+
?? ObjectManager::getInstance()->get(ProductInterfaceFactory::class);
64+
$this->deleteValidator = $deleteValidator
65+
?? ObjectManager::getInstance()->get(DeleteValidator::class);
4666
}
4767

4868
/**
@@ -72,6 +92,8 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry)
7292
}
7393
$existingMediaGalleryEntries[] = $entry;
7494
}
95+
$product = $this->productInterfaceFactory->create();
96+
$product->setSku($sku);
7597
$product->setMediaGalleryEntries($existingMediaGalleryEntries);
7698
try {
7799
$product = $this->productRepository->save($product);
@@ -119,6 +141,8 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry)
119141
__('No image with the provided ID was found. Verify the ID and try again.')
120142
);
121143
}
144+
$product = $this->productInterfaceFactory->create();
145+
$product->setSku($sku);
122146
$product->setMediaGalleryEntries($existingMediaGalleryEntries);
123147

124148
try {
@@ -145,6 +169,10 @@ public function remove($sku, $entryId)
145169
foreach ($existingMediaGalleryEntries as $key => $entry) {
146170
if ($entry->getId() == $entryId) {
147171
unset($existingMediaGalleryEntries[$key]);
172+
$errors = $this->deleteValidator->validate($product, $entry->getFile());
173+
if (!empty($errors)) {
174+
throw new StateException($errors[0]);
175+
}
148176
$found = true;
149177
break;
150178
}
@@ -154,6 +182,8 @@ public function remove($sku, $entryId)
154182
__('No image with the provided ID was found. Verify the ID and try again.')
155183
);
156184
}
185+
$product = $this->productInterfaceFactory->create();
186+
$product->setSku($sku);
157187
$product->setMediaGalleryEntries($existingMediaGalleryEntries);
158188
$this->productRepository->save($product);
159189
return true;

app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88
namespace Magento\Catalog\Model\ProductRepository;
99

1010
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Model\Product\Gallery\DeleteValidator;
1112
use Magento\Catalog\Model\Product\Gallery\Processor;
1213
use Magento\Catalog\Model\Product\Media\Config;
14+
use Magento\Catalog\Model\ResourceModel\Product\Gallery;
1315
use Magento\Framework\Api\Data\ImageContentInterface;
1416
use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
1517
use Magento\Framework\Api\ImageProcessorInterface;
18+
use Magento\Framework\App\ObjectManager;
1619
use Magento\Framework\Exception\InputException;
1720
use Magento\Framework\Exception\LocalizedException;
1821
use Magento\Framework\Exception\StateException;
22+
use Magento\Store\Model\Store;
1923

2024
/**
2125
* Process Media gallery data for ProductRepository before save product.
@@ -37,25 +41,31 @@ class MediaGalleryProcessor
3741
private $contentFactory;
3842

3943
/**
40-
* Image processor.
41-
*
4244
* @var ImageProcessorInterface
4345
*/
4446
private $imageProcessor;
4547

48+
/**
49+
* @var DeleteValidator
50+
*/
51+
private $deleteValidator;
52+
4653
/**
4754
* @param Processor $processor
4855
* @param ImageContentInterfaceFactory $contentFactory
4956
* @param ImageProcessorInterface $imageProcessor
57+
* @param DeleteValidator|null $deleteValidator
5058
*/
5159
public function __construct(
5260
Processor $processor,
5361
ImageContentInterfaceFactory $contentFactory,
54-
ImageProcessorInterface $imageProcessor
62+
ImageProcessorInterface $imageProcessor,
63+
?DeleteValidator $deleteValidator = null
5564
) {
5665
$this->processor = $processor;
5766
$this->contentFactory = $contentFactory;
5867
$this->imageProcessor = $imageProcessor;
68+
$this->deleteValidator = $deleteValidator ?? ObjectManager::getInstance()->get(DeleteValidator::class);
5969
}
6070

6171
/**
@@ -103,7 +113,7 @@ public function processMediaGallery(ProductInterface $product, array $mediaGalle
103113
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
104114
$existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
105115
}
106-
} else {
116+
} elseif ($this->canRemoveImage($product, $existingEntry)) {
107117
//set the removed flag
108118
$existingEntry['removed'] = true;
109119
}
@@ -254,4 +264,17 @@ private function processMediaAttributes(ProductInterface $product, array $images
254264
}
255265
}
256266
}
267+
268+
/**
269+
* Check whether the image can be removed
270+
*
271+
* @param ProductInterface $product
272+
* @param array $image
273+
* @return bool
274+
*/
275+
private function canRemoveImage(ProductInterface $product, array $image): bool
276+
{
277+
return !isset($image['file'])
278+
|| !$this->deleteValidator->validate($product, $image['file']);
279+
}
257280
}

0 commit comments

Comments
 (0)