Skip to content

Commit 97df424

Browse files
authored
Merge branch '2.4-develop' into fix-28388
2 parents 62e4866 + 7198c76 commit 97df424

File tree

438 files changed

+16344
-3015
lines changed

Some content is hidden

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

438 files changed

+16344
-3015
lines changed

app/code/Magento/Backend/Block/Widget/Grid/Export.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Magento\Framework\App\Filesystem\DirectoryList;
1010

1111
/**
12+
* Class Export for exporting grid data as CSV file or MS Excel 2003 XML Document file
13+
*
1214
* @api
1315
* @deprecated 100.2.0 in favour of UI component implementation
1416
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -69,6 +71,8 @@ public function __construct(
6971
}
7072

7173
/**
74+
* Internal constructor, that is called from real constructor
75+
*
7276
* @return void
7377
* @throws \Magento\Framework\Exception\LocalizedException
7478
*/
@@ -242,6 +246,7 @@ protected function _getExportTotals()
242246

243247
/**
244248
* Iterate collection and call callback method per item
249+
*
245250
* For callback method first argument always is item object
246251
*
247252
* @param string $callback
@@ -273,7 +278,12 @@ public function _exportIterateCollection($callback, array $args)
273278

274279
$collection = $this->_getRowCollection($originalCollection);
275280
foreach ($collection as $item) {
276-
call_user_func_array([$this, $callback], array_merge([$item], $args));
281+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
282+
call_user_func_array(
283+
[$this, $callback],
284+
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
285+
array_merge([$item], $args)
286+
);
277287
}
278288
}
279289
}
@@ -307,7 +317,7 @@ protected function _exportCsvItem(
307317
*/
308318
public function getCsvFile()
309319
{
310-
$name = md5(microtime());
320+
$name = hash('sha256', microtime());
311321
$file = $this->_path . '/' . $name . '.csv';
312322

313323
$this->_directory->create($this->_path);
@@ -432,11 +442,11 @@ public function getRowRecord(\Magento\Framework\DataObject $data)
432442
*/
433443
public function getExcelFile($sheetName = '')
434444
{
435-
$collection = $this->_getRowCollection();
445+
$collection = $this->_getPreparedCollection();
436446

437447
$convert = new \Magento\Framework\Convert\Excel($collection->getIterator(), [$this, 'getRowRecord']);
438448

439-
$name = md5(microtime());
449+
$name = hash('sha256', microtime());
440450
$file = $this->_path . '/' . $name . '.xml';
441451

442452
$this->_directory->create($this->_path);
@@ -551,6 +561,8 @@ public function _getPreparedCollection()
551561
}
552562

553563
/**
564+
* Get export page size
565+
*
554566
* @return int
555567
*/
556568
public function getExportPageSize()

app/code/Magento/Backend/Block/Widget/Grid/Extended.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Magento\Framework\App\Filesystem\DirectoryList;
99

1010
/**
11+
* Extended Grid Widget
12+
*
1113
* @api
1214
* @deprecated 100.2.0 in favour of UI component implementation
1315
* @SuppressWarnings(PHPMD.ExcessivePublicCount)
@@ -177,7 +179,10 @@ class Extended extends \Magento\Backend\Block\Widget\Grid implements \Magento\Ba
177179
protected $_path = 'export';
178180

179181
/**
182+
* Initialization
183+
*
180184
* @return void
185+
* @throws \Magento\Framework\Exception\FileSystemException
181186
*/
182187
protected function _construct()
183188
{
@@ -297,6 +302,7 @@ public function addColumn($columnId, $column)
297302
);
298303
$this->getColumnSet()->getChildBlock($columnId)->setGrid($this);
299304
} else {
305+
// phpcs:ignore Magento2.Exceptions.DirectThrow
300306
throw new \Exception(__('Please correct the column format and try again.'));
301307
}
302308

@@ -471,10 +477,6 @@ protected function _prepareMassactionColumn()
471477
protected function _prepareCollection()
472478
{
473479
if ($this->getCollection()) {
474-
if ($this->getCollection()->isLoaded()) {
475-
$this->getCollection()->clear();
476-
}
477-
478480
parent::_prepareCollection();
479481

480482
if (!$this->_isExport) {
@@ -663,6 +665,7 @@ public function setEmptyCellLabel($label)
663665
*/
664666
public function getRowUrl($item)
665667
{
668+
// phpstan:ignore "Call to an undefined static method"
666669
$res = parent::getRowUrl($item);
667670
return $res ? $res : '#';
668671
}
@@ -680,6 +683,7 @@ public function getMultipleRows($item)
680683

681684
/**
682685
* Retrieve columns for multiple rows
686+
*
683687
* @return array
684688
*/
685689
public function getMultipleRowColumns()
@@ -943,6 +947,7 @@ protected function _getExportTotals()
943947

944948
/**
945949
* Iterate collection and call callback method per item
950+
*
946951
* For callback method first argument always is item object
947952
*
948953
* @param string $callback
@@ -972,7 +977,12 @@ public function _exportIterateCollection($callback, array $args)
972977
$page++;
973978

974979
foreach ($collection as $item) {
975-
call_user_func_array([$this, $callback], array_merge([$item], $args));
980+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
981+
call_user_func_array(
982+
[$this, $callback],
983+
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
984+
array_merge([$item], $args)
985+
);
976986
}
977987
}
978988
}
@@ -1009,6 +1019,7 @@ public function getCsvFile()
10091019
$this->_isExport = true;
10101020
$this->_prepareGrid();
10111021

1022+
// phpcs:ignore Magento2.Security.InsecureFunction
10121023
$name = md5(microtime());
10131024
$file = $this->_path . '/' . $name . '.csv';
10141025

@@ -1153,6 +1164,7 @@ public function getExcelFile($sheetName = '')
11531164
[$this, 'getRowRecord']
11541165
);
11551166

1167+
// phpcs:ignore Magento2.Security.InsecureFunction
11561168
$name = md5(microtime());
11571169
$file = $this->_path . '/' . $name . '.xml';
11581170

@@ -1244,7 +1256,7 @@ public function setCollection($collection)
12441256
}
12451257

12461258
/**
1247-
* get collection object
1259+
* Get collection object
12481260
*
12491261
* @return \Magento\Framework\Data\Collection
12501262
*/

app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/ExtendedTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public function testPrepareLoadedCollection()
4141
$layout->expects($this->any())->method('getBlock')->willReturn($columnSet);
4242

4343
$collection = $this->createMock(Collection::class);
44-
$collection->expects($this->atLeastOnce())->method('isLoaded')->willReturn(true);
45-
$collection->expects($this->atLeastOnce())->method('clear');
44+
$collection->expects($this->never())->method('isLoaded');
45+
$collection->expects($this->never())->method('clear');
4646
$collection->expects($this->atLeastOnce())->method('load');
4747

4848
/** @var Extended $block */

app/code/Magento/Bundle/Model/Product/Type.php

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\Bundle\Model\Product;
88

9+
use Magento\Bundle\Model\Option;
10+
use Magento\Bundle\Model\ResourceModel\Option\Collection;
911
use Magento\Bundle\Model\ResourceModel\Selection\Collection as Selections;
1012
use Magento\Bundle\Model\ResourceModel\Selection\Collection\FilterApplier as SelectionCollectionFilterApplier;
1113
use Magento\Catalog\Api\ProductRepositoryInterface;
@@ -414,16 +416,13 @@ public function beforeSave($product)
414416
if ($product->getCanSaveBundleSelections()) {
415417
$product->canAffectOptions(true);
416418
$selections = $product->getBundleSelectionsData();
417-
if ($selections && !empty($selections)) {
418-
$options = $product->getBundleOptionsData();
419-
if ($options) {
420-
foreach ($options as $option) {
421-
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422-
$product->setTypeHasOptions(true);
423-
if (1 == (int)$option['required']) {
424-
$product->setTypeHasRequiredOptions(true);
425-
break;
426-
}
419+
if (!empty($selections) && $options = $product->getBundleOptionsData()) {
420+
foreach ($options as $option) {
421+
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422+
$product->setTypeHasOptions(true);
423+
if (1 == (int)$option['required']) {
424+
$product->setTypeHasRequiredOptions(true);
425+
break;
427426
}
428427
}
429428
}
@@ -464,7 +463,7 @@ public function getOptionsIds($product)
464463
public function getOptionsCollection($product)
465464
{
466465
if (!$product->hasData($this->_keyOptionsCollection)) {
467-
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
466+
/** @var Collection $optionsCollection */
468467
$optionsCollection = $this->_bundleOption->create()
469468
->getResourceCollection();
470469
$optionsCollection->setProductIdFilter($product->getEntityId());
@@ -530,10 +529,10 @@ public function getSelectionsCollection($optionIds, $product)
530529
* Example: the catalog inventory validation of decimal qty can change qty to int,
531530
* so need to change quote item qty option value too.
532531
*
533-
* @param array $options
534-
* @param \Magento\Framework\DataObject $option
535-
* @param mixed $value
536-
* @param \Magento\Catalog\Model\Product $product
532+
* @param array $options
533+
* @param \Magento\Framework\DataObject $option
534+
* @param mixed $value
535+
* @param \Magento\Catalog\Model\Product $product
537536
* @return $this
538537
*/
539538
public function updateQtyOption($options, \Magento\Framework\DataObject $option, $value, $product)
@@ -682,6 +681,11 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
682681
$options
683682
);
684683

684+
$this->validateRadioAndSelectOptions(
685+
$optionsCollection,
686+
$options
687+
);
688+
685689
$selectionIds = array_values($this->arrayUtility->flatten($options));
686690
// If product has not been configured yet then $selections array should be empty
687691
if (!empty($selectionIds)) {
@@ -1184,9 +1188,11 @@ public function canConfigure($product)
11841188
* @return void
11851189
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
11861190
*/
1191+
// @codingStandardsIgnoreStart
11871192
public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11881193
{
11891194
}
1195+
// @codingStandardsIgnoreEnd
11901196

11911197
/**
11921198
* Return array of specific to type product entities
@@ -1196,18 +1202,19 @@ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11961202
*/
11971203
public function getIdentities(\Magento\Catalog\Model\Product $product)
11981204
{
1199-
$identities = parent::getIdentities($product);
1205+
$identities = [];
1206+
$identities[] = parent::getIdentities($product);
12001207
/** @var \Magento\Bundle\Model\Option $option */
12011208
foreach ($this->getOptions($product) as $option) {
12021209
if ($option->getSelections()) {
12031210
/** @var \Magento\Catalog\Model\Product $selection */
12041211
foreach ($option->getSelections() as $selection) {
1205-
$identities = array_merge($identities, $selection->getIdentities());
1212+
$identities[] = $selection->getIdentities();
12061213
}
12071214
}
12081215
}
12091216

1210-
return $identities;
1217+
return array_merge([], ...$identities);
12111218
}
12121219

12131220
/**
@@ -1272,6 +1279,53 @@ protected function checkIsAllRequiredOptions($product, $isStrictProcessMode, $op
12721279
}
12731280
}
12741281

1282+
/**
1283+
* Validate Options for Radio and Select input types
1284+
*
1285+
* @param Collection $optionsCollection
1286+
* @param int[] $options
1287+
* @return void
1288+
* @throws \Magento\Framework\Exception\LocalizedException
1289+
*/
1290+
private function validateRadioAndSelectOptions($optionsCollection, $options): void
1291+
{
1292+
$errorTypes = [];
1293+
1294+
if (is_array($optionsCollection->getItems())) {
1295+
foreach ($optionsCollection->getItems() as $option) {
1296+
if ($this->isSelectedOptionValid($option, $options)) {
1297+
$errorTypes[] = $option->getType();
1298+
}
1299+
}
1300+
}
1301+
1302+
if (!empty($errorTypes)) {
1303+
throw new \Magento\Framework\Exception\LocalizedException(
1304+
__(
1305+
'Option type (%types) should have only one element.',
1306+
['types' => implode(", ", $errorTypes)]
1307+
)
1308+
);
1309+
}
1310+
}
1311+
1312+
/**
1313+
* Check if selected option is valid
1314+
*
1315+
* @param Option $option
1316+
* @param array $options
1317+
* @return bool
1318+
*/
1319+
private function isSelectedOptionValid($option, $options): bool
1320+
{
1321+
return (
1322+
($option->getType() == 'radio' || $option->getType() == 'select') &&
1323+
isset($options[$option->getOptionId()]) &&
1324+
is_array($options[$option->getOptionId()]) &&
1325+
count($options[$option->getOptionId()]) > 1
1326+
);
1327+
}
1328+
12751329
/**
12761330
* Check if selection is salable
12771331
*
@@ -1333,16 +1387,18 @@ protected function checkIsResult($_result)
13331387
*/
13341388
protected function mergeSelectionsWithOptions($options, $selections)
13351389
{
1390+
$selections = [];
1391+
13361392
foreach ($options as $option) {
13371393
$optionSelections = $option->getSelections();
13381394
if ($option->getRequired() && is_array($optionSelections) && count($optionSelections) == 1) {
1339-
$selections = array_merge($selections, $optionSelections);
1395+
$selections[] = $optionSelections;
13401396
} else {
13411397
$selections = [];
13421398
break;
13431399
}
13441400
}
13451401

1346-
return $selections;
1402+
return array_merge([], ...$selections);
13471403
}
13481404
}

app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,9 @@
6868
<actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName">
6969
<argument name="product" value="BundleProduct"/>
7070
</actionGroup>
71-
<click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/>
72-
<waitForPageLoad stepKey="loading2"/>
7371

7472
<!--Delete-->
75-
<click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/>
76-
<click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/>
77-
<click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/>
78-
<waitForPageLoad stepKey="loading3"/>
73+
<actionGroup ref="AdminDeleteAllProductsFromGridActionGroup" stepKey="selectAndDeleteProducts"/>
7974

8075
<!--Locating delete message-->
8176
<seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/>

app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,7 @@
118118
<actionGroup ref="BundleProductFilter" stepKey="FilterForOnlyBundleProducts"/>
119119

120120
<!--Delete-->
121-
<click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/>
122-
<waitForPageLoad stepKey="loading"/>
123-
<click selector="{{AdminProductFiltersSection.actions}}" stepKey="ClickOnActionsChangingView"/>
124-
<click selector="{{AdminProductFiltersSection.delete}}" stepKey="ClickDelete"/>
125-
<click selector="//button[@class='action-primary action-accept']" stepKey="ConfirmDelete"/>
126-
<waitForPageLoad stepKey="loading3"/>
121+
<actionGroup ref="AdminDeleteAllProductsFromGridActionGroup" stepKey="selectAndDeleteProducts"/>
127122

128123
<!--Locating delete message-->
129124
<seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="deleteMessage"/>

0 commit comments

Comments
 (0)