Skip to content

Commit f3b4d00

Browse files
merge magento/2.4.0-develop into magento-borg/MC-22963
2 parents 0b2c211 + f165007 commit f3b4d00

File tree

90 files changed

+2506
-698
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2506
-698
lines changed

app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminDeleteRoleActionGroup.xml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,32 @@
1010
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
1111
<actionGroup name="AdminDeleteRoleActionGroup">
1212
<annotations>
13-
<description>Deletes a User Role that contains the text 'Role'. PLEASE NOTE: The Action Group values are Hardcoded.</description>
13+
<description>Deletes a User Role.</description>
1414
</annotations>
1515
<arguments>
1616
<argument name="role" defaultValue=""/>
1717
</arguments>
1818

19-
<click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.theRole}}"/>
19+
<click stepKey="clickResetFilterButtonBefore" selector="{{AdminRoleGridSection.resetButton}}"/>
20+
<waitForPageLoad stepKey="waitForRolesGridFilterResetBefore" time="10"/>
21+
<fillField stepKey="TypeRoleFilter" selector="{{AdminRoleGridSection.roleNameFilterTextField}}" userInput="{{role.name}}"/>
22+
<waitForElementVisible stepKey="waitForFilterSearchButtonBefore" selector="{{AdminRoleGridSection.searchButton}}" time="10"/>
23+
<click stepKey="clickFilterSearchButton" selector="{{AdminRoleGridSection.searchButton}}"/>
24+
<waitForPageLoad stepKey="waitForUserRoleFilter" time="10"/>
25+
<waitForElementVisible stepKey="waitForRoleInRoleGrid" selector="{{AdminDeleteRoleSection.role(role.name)}}" time="10"/>
26+
<click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.role(role.name)}}"/>
27+
<waitForPageLoad stepKey="waitForRolePageToLoad" time="10"/>
2028
<fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/>
29+
<waitForElementVisible stepKey="waitForDeleteRoleButton" selector="{{AdminDeleteRoleSection.delete}}" time="10"/>
2130
<click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/>
22-
<waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/>
31+
<waitForPageLoad stepKey="waitForDeleteConfirmationPopup" time="5"/>
32+
<waitForElementVisible stepKey="waitForConfirmButton" selector="{{AdminDeleteRoleSection.confirm}}" time="10"/>
2333
<click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/>
2434
<waitForPageLoad stepKey="waitForPageLoad" time="10"/>
2535
<see stepKey="seeSuccessMessage" userInput="You deleted the role."/>
36+
<waitForPageLoad stepKey="waitForRolesGridLoad" />
37+
<waitForElementVisible stepKey="waitForResetFilterButtonAfter" selector="{{AdminRoleGridSection.resetButton}}" time="10"/>
38+
<click stepKey="clickResetFilterButtonAfter" selector="{{AdminRoleGridSection.resetButton}}"/>
39+
<waitForPageLoad stepKey="waitForRolesGridFilterResetAfter" time="10"/>
2640
</actionGroup>
2741
</actionGroups>

app/code/Magento/CardinalCommerce/Model/JwtManagement.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\CardinalCommerce\Model;
99

1010
use Magento\Framework\Serialize\Serializer\Json;
11+
use Magento\Framework\Encryption\Helper\Security;
1112

1213
/**
1314
* JSON Web Token management.
@@ -62,7 +63,8 @@ public function decode(string $jwt, string $key): array
6263
$payload = $this->json->unserialize($payloadJson);
6364

6465
$signature = $this->urlSafeB64Decode($signatureB64);
65-
if ($signature !== $this->sign($headB64 . '.' . $payloadB64, $key, $header['alg'])) {
66+
67+
if (!Security::compareStrings($signature, $this->sign($headB64 . '.' . $payloadB64, $key, $header['alg']))) {
6668
throw new \InvalidArgumentException('JWT signature verification failed');
6769
}
6870

app/code/Magento/Catalog/Block/Product/ListProduct.php

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,18 +353,16 @@ public function getIdentities()
353353

354354
$category = $this->getLayer()->getCurrentCategory();
355355
if ($category) {
356-
$identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $category->getId();
356+
$identities[] = [Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $category->getId()];
357357
}
358358

359359
//Check if category page shows only static block (No products)
360-
if ($category->getData('display_mode') == Category::DM_PAGE) {
361-
return $identities;
362-
}
363-
364-
foreach ($this->_getProductCollection() as $item) {
365-
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
366-
$identities = array_merge($identities, $item->getIdentities());
360+
if ($category->getData('display_mode') != Category::DM_PAGE) {
361+
foreach ($this->_getProductCollection() as $item) {
362+
$identities[] = $item->getIdentities();
363+
}
367364
}
365+
$identities = array_merge(...$identities);
368366

369367
return $identities;
370368
}
@@ -377,7 +375,7 @@ public function getIdentities()
377375
*/
378376
public function getAddToCartPostParams(Product $product)
379377
{
380-
$url = $this->getAddToCartUrl($product);
378+
$url = $this->getAddToCartUrl($product, ['_escape' => false]);
381379
return [
382380
'action' => $url,
383381
'data' => [

app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77
namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute;
88

99
use Magento\AsynchronousOperations\Api\Data\OperationInterface;
10+
use Magento\Catalog\Model\ProductFactory;
1011
use Magento\Catalog\Api\Data\ProductAttributeInterface;
1112
use Magento\Eav\Model\Config;
1213
use Magento\Framework\App\Action\HttpPostActionInterface;
1314
use Magento\Backend\App\Action;
1415
use Magento\Framework\App\ObjectManager;
16+
use Magento\Framework\Exception\LocalizedException;
1517
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
1618

1719
/**
18-
* Class used for saving mass updated products attributes.
20+
* Class responsible for saving product attributes.
1921
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2022
*/
2123
class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface
@@ -60,6 +62,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
6062
*/
6163
private $eavConfig;
6264

65+
/**
66+
* @var ProductFactory
67+
*/
68+
private $productFactory;
69+
6370
/**
6471
* @param Action\Context $context
6572
* @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper
@@ -71,6 +78,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
7178
* @param int $bulkSize
7279
* @param TimezoneInterface $timezone
7380
* @param Config $eavConfig
81+
* @param ProductFactory $productFactory
7482
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
7583
*/
7684
public function __construct(
@@ -83,7 +91,8 @@ public function __construct(
8391
\Magento\Authorization\Model\UserContextInterface $userContext,
8492
int $bulkSize = 100,
8593
TimezoneInterface $timezone = null,
86-
Config $eavConfig = null
94+
Config $eavConfig = null,
95+
ProductFactory $productFactory = null
8796
) {
8897
parent::__construct($context, $attributeHelper);
8998
$this->bulkManagement = $bulkManagement;
@@ -96,6 +105,7 @@ public function __construct(
96105
->get(TimezoneInterface::class);
97106
$this->eavConfig = $eavConfig ?: ObjectManager::getInstance()
98107
->get(Config::class);
108+
$this->productFactory = $productFactory ?? ObjectManager::getInstance()->get(ProductFactory::class);
99109
}
100110

101111
/**
@@ -121,9 +131,10 @@ public function execute()
121131
$attributesData = $this->sanitizeProductAttributes($attributesData);
122132

123133
try {
134+
$this->validateProductAttributes($attributesData);
124135
$this->publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds);
125136
$this->messageManager->addSuccessMessage(__('Message is added to queue'));
126-
} catch (\Magento\Framework\Exception\LocalizedException $e) {
137+
} catch (LocalizedException $e) {
127138
$this->messageManager->addErrorMessage($e->getMessage());
128139
} catch (\Exception $e) {
129140
$this->messageManager->addExceptionMessage(
@@ -152,10 +163,12 @@ private function sanitizeProductAttributes($attributesData)
152163
}
153164

154165
$attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
166+
155167
if (!$attribute->getAttributeId()) {
156168
unset($attributesData[$attributeCode]);
157169
continue;
158170
}
171+
159172
if ($attribute->getBackendType() === 'datetime') {
160173
if (!empty($value)) {
161174
$filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]);
@@ -183,6 +196,25 @@ private function sanitizeProductAttributes($attributesData)
183196
return $attributesData;
184197
}
185198

199+
/**
200+
* Validate product attributes data.
201+
*
202+
* @param array $attributesData
203+
*
204+
* @return void
205+
* @throws LocalizedException
206+
*/
207+
private function validateProductAttributes(array $attributesData): void
208+
{
209+
$product = $this->productFactory->create();
210+
$product->setData($attributesData);
211+
212+
foreach (array_keys($attributesData) as $attributeCode) {
213+
$attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
214+
$attribute->getBackend()->validate($product);
215+
}
216+
}
217+
186218
/**
187219
* Schedule new bulk
188220
*
@@ -192,7 +224,7 @@ private function sanitizeProductAttributes($attributesData)
192224
* @param int $storeId
193225
* @param int $websiteId
194226
* @param array $productIds
195-
* @throws \Magento\Framework\Exception\LocalizedException
227+
* @throws LocalizedException
196228
*
197229
* @return void
198230
*/
@@ -246,7 +278,7 @@ private function publish(
246278
$this->userContext->getUserId()
247279
);
248280
if (!$result) {
249-
throw new \Magento\Framework\Exception\LocalizedException(
281+
throw new LocalizedException(
250282
__('Something went wrong while processing the request.')
251283
);
252284
}

app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public function execute()
221221
return $this->returnResult('catalog/*/', [], ['error' => true]);
222222
}
223223
// entity type check
224-
if ($model->getEntityTypeId() != $this->_entityTypeId) {
224+
if ($model->getEntityTypeId() != $this->_entityTypeId || array_key_exists('backend_model', $data)) {
225225
$this->messageManager->addErrorMessage(__('We can\'t update the attribute.'));
226226
$this->_session->setAttributeData($data);
227227
return $this->returnResult('catalog/*/', [], ['error' => true]);
@@ -258,6 +258,8 @@ public function execute()
258258
unset($data['apply_to']);
259259
}
260260

261+
unset($data['entity_type_id']);
262+
261263
$model->addData($data);
262264

263265
if (!$attributeId) {

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
7373
const STORE_ID = 'store_id';
7474

7575
/**
76-
* @var string
76+
* @var string|bool
7777
*/
78-
protected $_cacheTag = self::CACHE_TAG;
78+
protected $_cacheTag = false;
7979

8080
/**
8181
* @var string
@@ -878,7 +878,6 @@ public function getAttributes($groupId = null, $skipSuper = false)
878878
*/
879879
public function beforeSave()
880880
{
881-
$this->cleanCache();
882881
$this->setTypeHasOptions(false);
883882
$this->setTypeHasRequiredOptions(false);
884883
$this->setHasOptions(false);

app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public function testGetAddToCartPostParams()
213213
->will($this->returnValue(true));
214214
$this->cartHelperMock->expects($this->any())
215215
->method('getAddUrl')
216-
->with($this->equalTo($this->productMock), $this->equalTo([]))
216+
->with($this->equalTo($this->productMock), $this->equalTo(['_escape' => false]))
217217
->will($this->returnValue($url));
218218
$this->productMock->expects($this->once())
219219
->method('getEntityId')

app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
use Magento\Framework\Exception\LocalizedException;
1818
use Psr\Log\LoggerInterface;
1919

20+
/**
21+
* Controller for removing quote item from shopping cart.
22+
*/
2023
class RemoveItem implements HttpPostActionInterface
2124
{
2225
/**
@@ -91,6 +94,9 @@ public function execute()
9194
$this->sidebar->removeQuoteItem($itemId);
9295
} catch (LocalizedException $e) {
9396
$error = $e->getMessage();
97+
} catch (\Zend_Db_Exception $e) {
98+
$this->logger->critical($e);
99+
$error = __('An unspecified error occurred. Please contact us for assistance.');
94100
} catch (Exception $e) {
95101
$this->logger->critical($e);
96102
$error = $e->getMessage();

app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,57 @@ public function testExecuteWithException()
221221
$this->assertEquals($resultJson, $this->action->execute());
222222
}
223223

224+
/**
225+
* Test controller when DB exception is thrown.
226+
*
227+
* @return void
228+
*/
229+
public function testExecuteWithDbException(): void
230+
{
231+
$itemId = 1;
232+
$dbError = 'Error';
233+
$message = __('An unspecified error occurred. Please contact us for assistance.');
234+
$responseData = [
235+
'success' => false,
236+
'error_message' => $message,
237+
];
238+
239+
$this->formKeyValidatorMock
240+
->expects($this->once())
241+
->method('validate')
242+
->with($this->requestMock)
243+
->willReturn(true);
244+
$this->requestMock->expects($this->once())
245+
->method('getParam')
246+
->with('item_id')
247+
->willReturn($itemId);
248+
249+
$exception = new \Zend_Db_Exception($dbError);
250+
251+
$this->sidebarMock->expects($this->once())
252+
->method('checkQuoteItem')
253+
->with($itemId)
254+
->willThrowException($exception);
255+
256+
$this->loggerMock->expects($this->once())->method('critical')->with($exception);
257+
258+
$this->sidebarMock->expects($this->once())
259+
->method('getResponseData')
260+
->with($message)
261+
->willReturn($responseData);
262+
263+
$resultJson = $this->createMock(ResultJson::class);
264+
$resultJson->expects($this->once())
265+
->method('setData')
266+
->with($responseData)
267+
->willReturnSelf();
268+
$this->resultJsonFactoryMock->expects($this->once())
269+
->method('create')
270+
->willReturn($resultJson);
271+
272+
$this->action->execute();
273+
}
274+
224275
public function testExecuteWhenFormKeyValidationFailed()
225276
{
226277
$resultRedirect = $this->createMock(ResultRedirect::class);

app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
* Copyright © Magento, Inc. All rights reserved.
55
* See COPYING.txt for license details.
66
*/
7+
8+
declare(strict_types=1);
9+
710
namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images;
811

912
use Magento\Framework\App\Action\HttpPostActionInterface;
@@ -60,13 +63,8 @@ public function execute()
6063
{
6164
try {
6265
$path = $this->getStorage()->getCmsWysiwygImages()->getCurrentPath();
63-
if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) {
64-
throw new \Magento\Framework\Exception\LocalizedException(
65-
__('Directory %1 is not under storage root path.', $path)
66-
);
67-
}
6866
$this->getStorage()->deleteDirectory($path);
69-
67+
7068
return $this->resultRawFactory->create();
7169
} catch (\Exception $e) {
7270
$result = ['error' => true, 'message' => $e->getMessage()];

0 commit comments

Comments
 (0)