Skip to content

Commit 0c94c18

Browse files
committed
Merge branch '2.4-develop' of https://github.com/magento-commerce/magento2ce into PR-03-11-2024
2 parents 821dce8 + 39d54c2 commit 0c94c18

File tree

13 files changed

+419
-26
lines changed

13 files changed

+419
-26
lines changed

app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,11 @@ public function update(array $prices)
157157
{
158158
$prices = $this->retrieveValidPrices($prices);
159159
$formattedPrices = [];
160+
$productIds = [];
160161

161162
foreach ($prices as $price) {
162163
$ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]);
164+
$productIds[] = $ids[key($ids)];
163165
foreach ($ids as $id) {
164166
$formattedPrices[] = [
165167
'store_id' => $price->getStoreId(),
@@ -182,6 +184,7 @@ public function update(array $prices)
182184
}
183185

184186
$this->getPricePersistence()->update($formattedPrices);
187+
$this->getPricePersistence()->updateLastUpdatedAt($productIds);
185188

186189
return $this->validationResult->getFailedItems();
187190
}

app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
1111
use Magento\Catalog\Model\ProductIdLocatorInterface;
1212
use Magento\Catalog\Model\ResourceModel\Attribute;
13+
use Magento\Framework\App\ObjectManager;
1314
use Magento\Framework\EntityManager\MetadataPool;
1415
use Magento\Framework\Exception\CouldNotDeleteException;
1516
use Magento\Framework\Exception\CouldNotSaveException;
17+
use Magento\Framework\Stdlib\DateTime\DateTime;
1618

1719
/**
1820
* Class responsibly for persistence of prices.
21+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1922
*/
2023
class PricePersistence
2124
{
@@ -42,33 +45,40 @@ class PricePersistence
4245
private $productIdLocator;
4346

4447
/**
45-
* Metadata pool.
48+
* Metadata pool property to get a metadata.
4649
*
4750
* @var MetadataPool
4851
*/
4952
private $metadataPool;
5053

5154
/**
52-
* Attribute code.
55+
* Attribute code attribute to get the attribute id.
5356
*
5457
* @var string
5558
*/
5659
private $attributeCode;
5760

5861
/**
59-
* Attribute ID.
62+
* Attribute ID property to store the attribute id.
6063
*
6164
* @var int
6265
*/
6366
private $attributeId;
6467

6568
/**
66-
* Items per operation.
69+
* Items per operation to chunk the array in a batch.
6770
*
6871
* @var int
6972
*/
7073
private $itemsPerOperation = 500;
7174

75+
/**
76+
* Date time property to get the gm date.
77+
*
78+
* @var DateTime
79+
*/
80+
private $dateTime;
81+
7282
/**
7383
* PricePersistence constructor.
7484
*
@@ -77,19 +87,23 @@ class PricePersistence
7787
* @param ProductIdLocatorInterface $productIdLocator
7888
* @param MetadataPool $metadataPool
7989
* @param string $attributeCode
90+
* @param DateTime|null $dateTime
8091
*/
8192
public function __construct(
8293
Attribute $attributeResource,
8394
ProductAttributeRepositoryInterface $attributeRepository,
8495
ProductIdLocatorInterface $productIdLocator,
8596
MetadataPool $metadataPool,
86-
$attributeCode = ''
97+
$attributeCode = '',
98+
?DateTime $dateTime = null
8799
) {
88100
$this->attributeResource = $attributeResource;
89101
$this->attributeRepository = $attributeRepository;
90102
$this->attributeCode = $attributeCode;
91103
$this->productIdLocator = $productIdLocator;
92104
$this->metadataPool = $metadataPool;
105+
$this->dateTime = $dateTime ?: ObjectManager::getInstance()
106+
->get(DateTime::class);
93107
}
94108

95109
/**
@@ -233,4 +247,27 @@ public function getEntityLinkField()
233247
return $this->metadataPool->getMetadata(ProductInterface::class)
234248
->getLinkField();
235249
}
250+
251+
/**
252+
* Update last updated date.
253+
*
254+
* @param array $productIds
255+
* @return void
256+
* @throws CouldNotSaveException
257+
*/
258+
public function updateLastUpdatedAt(array $productIds): void
259+
{
260+
try {
261+
$this->attributeResource->getConnection()->update(
262+
$this->attributeResource->getTable('catalog_product_entity'),
263+
[ProductInterface::UPDATED_AT => $this->dateTime->gmtDate()],
264+
[$this->getEntityLinkField(). ' IN(?)' => $productIds]
265+
);
266+
} catch (\Exception $e) {
267+
throw new CouldNotSaveException(
268+
__("The attribute can't be saved."),
269+
$e
270+
);
271+
}
272+
}
236273
}

app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
use Magento\Framework\Exception\LocalizedException;
1717
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
1818
use Magento\Store\Api\WebsiteRepositoryInterface;
19+
use Magento\Framework\App\ObjectManager;
20+
use Magento\Framework\App\Config\ScopeConfigInterface;
21+
use Magento\Catalog\Helper\Data;
22+
use Magento\Store\Model\ScopeInterface;
23+
use Magento\Framework\Exception\NoSuchEntityException;
1924

2025
/**
2126
* Validate Tier Price and check duplication
@@ -91,6 +96,11 @@ class TierPriceValidator implements ResetAfterRequestInterface
9196
*/
9297
private $productsCacheBySku = [];
9398

99+
/**
100+
* @var ScopeConfigInterface
101+
*/
102+
private $scopeConfig;
103+
94104
/**
95105
* TierPriceValidator constructor.
96106
*
@@ -103,17 +113,20 @@ class TierPriceValidator implements ResetAfterRequestInterface
103113
* @param InvalidSkuProcessor $invalidSkuProcessor
104114
* @param ProductRepositoryInterface $productRepository
105115
* @param array $allowedProductTypes [optional]
116+
* @param ScopeConfigInterface|null $scopeConfig
117+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
106118
*/
107119
public function __construct(
108-
ProductIdLocatorInterface $productIdLocator,
109-
SearchCriteriaBuilder $searchCriteriaBuilder,
110-
FilterBuilder $filterBuilder,
111-
GroupRepositoryInterface $customerGroupRepository,
112-
WebsiteRepositoryInterface $websiteRepository,
113-
Result $validationResult,
114-
InvalidSkuProcessor $invalidSkuProcessor,
120+
ProductIdLocatorInterface $productIdLocator,
121+
SearchCriteriaBuilder $searchCriteriaBuilder,
122+
FilterBuilder $filterBuilder,
123+
GroupRepositoryInterface $customerGroupRepository,
124+
WebsiteRepositoryInterface $websiteRepository,
125+
Result $validationResult,
126+
InvalidSkuProcessor $invalidSkuProcessor,
115127
ProductRepositoryInterface $productRepository,
116-
array $allowedProductTypes = []
128+
array $allowedProductTypes = [],
129+
?ScopeConfigInterface $scopeConfig = null
117130
) {
118131
$this->productIdLocator = $productIdLocator;
119132
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
@@ -124,6 +137,7 @@ public function __construct(
124137
$this->invalidSkuProcessor = $invalidSkuProcessor;
125138
$this->productRepository = $productRepository;
126139
$this->allowedProductTypes = $allowedProductTypes;
140+
$this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
127141
}
128142

129143
/**
@@ -355,10 +369,19 @@ private function checkQuantity(TierPriceInterface $price, $key, Result $validati
355369
* @param Result $validationResult
356370
* @return void
357371
*/
358-
private function checkWebsite(TierPriceInterface $price, $key, Result $validationResult)
372+
private function checkWebsite(TierPriceInterface $price, $key, Result $validationResult): void
359373
{
360374
try {
361375
$this->websiteRepository->getById($price->getWebsiteId());
376+
$isWebsiteScope = $this->scopeConfig
377+
->isSetFlag(
378+
Data::XML_PATH_PRICE_SCOPE,
379+
ScopeInterface::SCOPE_STORE,
380+
ScopeConfigInterface::SCOPE_TYPE_DEFAULT
381+
);
382+
if (!$isWebsiteScope && (int) $this->allWebsitesValue !== $price->getWebsiteId()) {
383+
throw NoSuchEntityException::singleField('website_id', $price->getWebsiteId());
384+
}
362385
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
363386
$validationResult->addFailedItem(
364387
$key,

app/code/Magento/MediaStorage/Model/File/Uploader.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ protected function _validateFile()
205205
parent::_validateFile();
206206

207207
if (!$this->getImageValidator()->isValid($this->_file['tmp_name'])) {
208-
throw new ValidationException(__('File validation failed.'));
208+
throw new ValidationException(
209+
__('File validation failed. Check Image Processing Settings in the Store Configuration.')
210+
);
209211
}
210212
}
211213

app/code/Magento/MediaStorage/i18n/en_US.csv

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@ Synchronize,Synchronize
22
"Synchronizing %1 to %2","Synchronizing %1 to %2"
33
Synchronizing...,Synchronizing...
44
"The timeout limit for response from synchronize process was reached.","The timeout limit for response from synchronize process was reached."
5-
"File %1 does not exist","File %1 does not exist"
5+
"The ""%1"" file doesn't exist. Verify the file and try again.","The ""%1"" file doesn't exist. Verify the file and try again."
66
"File %1 is not readable","File %1 is not readable"
77
"File System","File System"
8-
Database,Database
8+
"Database (Deprecated)","Database (Deprecated)"
9+
"Sorry, something went wrong during image resize. Please see log for details.","Sorry, something went wrong during image resize. Please see log for details."
910
"database ""%1""","database ""%1"""
1011
"Parent directory does not exist: %1","Parent directory does not exist: %1"
1112
"File system","File system"
1213
"Unable to save file ""%1"" at ""%2""","Unable to save file ""%1"" at ""%2"""
1314
"Wrong file info format","Wrong file info format"
15+
"The file you uploaded has no extension.","The file you uploaded has no extension."
16+
"The source file moving process failed.","The source file moving process failed."
17+
"File validation failed. Check Image Processing Settings in the Store Configuration.","File validation failed. Check Image Processing Settings in the Store Configuration."
1418
"Path ""%value%"" is protected and cannot be used.","Path ""%value%"" is protected and cannot be used."
1519
"Path ""%value%"" is not available and cannot be used.","Path ""%value%"" is not available and cannot be used."
16-
"Path ""%value%"" may not include parent directory traversal (""../"", "".."").","Path ""%value%"" may not include parent directory traversal (""../"", "".."")."
20+
"Path ""%value%"" may not include parent directory traversal (""../"", ""..\").","Path ""%value%"" may not include parent directory traversal (""../"", ""..\")."
1721
"Please set available and/or protected paths list(s) before validation.","Please set available and/or protected paths list(s) before validation."
1822
"File with an extension ""%value%"" is protected and cannot be uploaded","File with an extension ""%value%"" is protected and cannot be uploaded"
1923
"Unable to create directory: %1","Unable to create directory: %1"
2024
"Unable to save file: %1","Unable to save file: %1"
25+
"Cannot resize image ""%1"" - original image not found","Cannot resize image ""%1"" - original image not found"
26+
"Cannot resize images - product images not found","Cannot resize images - product images not found"
27+
"Image resize: %1","Image resize: %1"
2128
"Storage Configuration for Media","Storage Configuration for Media"
2229
"Media Storage","Media Storage"
2330
"Select Media Database","Select Media Database"
24-
"After selecting a new media storage location, press the Synchronize button to transfer all media to that location. Media will not be available in the new location until the synchronization process is complete.","After selecting a new media storage location, press the Synchronize button to transfer all media to that location. Media will not be available in the new location until the synchronization process is complete."
31+
"After selecting a new media storage location, press the Synchronize button to transfer all media to that location and then ""Save Config"". Media will not be available in the new location until the synchronization process is complete.","After selecting a new media storage location, press the Synchronize button to transfer all media to that location and then ""Save Config"". Media will not be available in the new location until the synchronization process is complete."
2532
"Environment Update Time","Environment Update Time"

app/code/Magento/Sales/Model/AdminOrder/Create.php

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Customer\Model\Metadata\Form as CustomerForm;
1212
use Magento\Framework\Api\ExtensibleDataObjectConverter;
1313
use Magento\Framework\App\ObjectManager;
14+
use Magento\Framework\Exception\LocalizedException;
1415
use Magento\Quote\Model\Quote\Address;
1516
use Magento\Quote\Model\Quote\Address\CustomAttributeListInterface;
1617
use Magento\Quote\Model\Quote\Item;
@@ -20,6 +21,7 @@
2021
use Magento\Store\Model\StoreManagerInterface;
2122
use Psr\Log\LoggerInterface;
2223
use Magento\Quote\Model\Quote;
24+
use Magento\Framework\App\Request\Http as HttpRequest;
2325

2426
/**
2527
* Order create model
@@ -264,6 +266,11 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\
264266
*/
265267
private $orderRepositoryInterface;
266268

269+
/**
270+
* @var HttpRequest
271+
*/
272+
private $request;
273+
267274
/**
268275
* @param \Magento\Framework\ObjectManagerInterface $objectManager
269276
* @param \Magento\Framework\Event\ManagerInterface $eventManager
@@ -298,6 +305,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\
298305
* @param StoreManagerInterface $storeManager
299306
* @param CustomAttributeListInterface|null $customAttributeList
300307
* @param OrderRepositoryInterface|null $orderRepositoryInterface
308+
* @param HttpRequest|null $request
301309
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
302310
*/
303311
public function __construct(
@@ -333,7 +341,8 @@ public function __construct(
333341
ExtensibleDataObjectConverter $dataObjectConverter = null,
334342
StoreManagerInterface $storeManager = null,
335343
CustomAttributeListInterface $customAttributeList = null,
336-
OrderRepositoryInterface $orderRepositoryInterface = null
344+
OrderRepositoryInterface $orderRepositoryInterface = null,
345+
HttpRequest $request = null
337346
) {
338347
$this->_objectManager = $objectManager;
339348
$this->_eventManager = $eventManager;
@@ -372,6 +381,8 @@ public function __construct(
372381
->get(CustomAttributeListInterface::class);
373382
$this->orderRepositoryInterface = $orderRepositoryInterface ?: ObjectManager::getInstance()
374383
->get(OrderRepositoryInterface::class);
384+
$this->request = $request ?: ObjectManager::getInstance()
385+
->get(HttpRequest::class);
375386
}
376387

377388
/**
@@ -895,15 +906,16 @@ public function moveQuoteItem($item, $moveTo, $qty)
895906
}
896907
$canBeRestored = $this->restoreTransferredItem('cart', $cartItemsToRestore);
897908

909+
$cartItem = $cart->addProduct($product, $info);
898910
if (!$canBeRestored) {
899-
$cartItem = $cart->addProduct($product, $info);
900911
if (is_string($cartItem)) {
901912
throw new \Magento\Framework\Exception\LocalizedException(__($cartItem));
902913
}
903914
$cartItem->setPrice($item->getProduct()->getPrice());
904915
}
905916
$this->_needCollectCart = true;
906917
$removeItem = true;
918+
$this->removeCartTransferredItemsAndUpdateQty($cartItem, $item->getId());
907919
}
908920
break;
909921
case 'wishlist':
@@ -2310,4 +2322,33 @@ private function formattedOptions(\Magento\Catalog\Model\Product $product, $buyR
23102322
}
23112323
return $this;
23122324
}
2325+
2326+
/**
2327+
* Remove cart from transferred items and update the qty.
2328+
*
2329+
* @param int|null|Item $cartItem
2330+
* @param int $itemId
2331+
* @return void
2332+
*/
2333+
private function removeCartTransferredItemsAndUpdateQty(int|null|Item $cartItem, int $itemId)
2334+
{
2335+
$removeCartTransferredItems = $this->getSession()->getTransferredItems() ?? [];
2336+
if (isset($removeCartTransferredItems['cart'])) {
2337+
$removeTransferredItemKey = array_search($cartItem->getId(), $removeCartTransferredItems['cart']);
2338+
if ($removeCartTransferredItems['cart'][$removeTransferredItemKey]) {
2339+
$cartItem->clearMessage();
2340+
$cartItem->setHasError(false);
2341+
if (isset($this->request->get('item')[$itemId]['qty'])) {
2342+
$qty = $this->request->get('item')[$itemId]['qty'];
2343+
$cartItem->setQty($qty);
2344+
}
2345+
2346+
if ($cartItem->getHasError()) {
2347+
throw new LocalizedException(__($cartItem->getMessage()));
2348+
}
2349+
unset($removeCartTransferredItems['cart'][$removeTransferredItemKey]);
2350+
}
2351+
$this->getSession()->setTransferredItems($removeCartTransferredItems);
2352+
}
2353+
}
23132354
}

app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ protected function sanitizeName($name)
8181
preg_match('/\$[:]*{(.)*}/', $name ?: '', $matches);
8282
if (count($matches) > 0) {
8383
$name = $this->escaper->escapeHtml($this->escaper->escapeJs($name));
84+
} elseif (preg_match("/^(?=.*')[a-zA-Z0-9' ]+$/", $name)) {
85+
return $name;
8486
} else {
8587
$name = $this->escaper->escapeHtml($name);
8688
}

0 commit comments

Comments
 (0)