Skip to content

Commit 64b86ca

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into ft-pr-new
2 parents b5d295a + 1552780 commit 64b86ca

File tree

91 files changed

+2781
-415
lines changed

Some content is hidden

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

91 files changed

+2781
-415
lines changed

app/code/Magento/Backend/Block/Widget/Form/Container.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ class Container extends \Magento\Backend\Block\Widget\Container
3737
* @var string
3838
*/
3939
protected $_blockGroup = 'Magento_Backend';
40+
41+
/**
42+
* @var string
43+
*/
44+
const PARAM_BLOCK_GROUP = 'block_group';
45+
46+
/**
47+
* @var string
48+
*/
49+
const PARAM_MODE = 'mode';
4050

4151
/**
4252
* @var string
@@ -49,6 +59,12 @@ class Container extends \Magento\Backend\Block\Widget\Container
4959
protected function _construct()
5060
{
5161
parent::_construct();
62+
if ($this->hasData(self::PARAM_BLOCK_GROUP)) {
63+
$this->_blockGroup = $this->_getData(self::PARAM_BLOCK_GROUP);
64+
}
65+
if ($this->hasData(self::PARAM_MODE)) {
66+
$this->_mode = $this->_getData(self::PARAM_MODE);
67+
}
5268

5369
$this->addButton(
5470
'back',

app/code/Magento/Backend/etc/adminhtml/system.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@
198198
<resource>Magento_Config::config_general</resource>
199199
<group id="country" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
200200
<label>Country Options</label>
201-
<field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
201+
<field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
202202
<label>Allow Countries</label>
203203
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
204204
<can_be_empty>1</can_be_empty>

app/code/Magento/Catalog/Console/Command/ProductAttributesCleanUp.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
101101
$output->writeln("");
102102
$output->writeln("<info>Unused product attributes successfully cleaned up:</info>");
103103
$output->writeln("<comment> " . implode("\n ", $attributeTables) . "</comment>");
104+
return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
104105
} catch (\Exception $exception) {
105106
$this->attributeResource->rollBack();
106107

107108
$output->writeln("");
108109
$output->writeln("<error>{$exception->getMessage()}</error>");
110+
// we must have an exit code higher than zero to indicate something was wrong
111+
return \Magento\Framework\Console\Cli::RETURN_FAILURE;
109112
}
110113
}
111114

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,10 +1909,12 @@ public function addOption(Product\Option $option)
19091909
*/
19101910
public function getOptionById($optionId)
19111911
{
1912-
/** @var \Magento\Catalog\Model\Product\Option $option */
1913-
foreach ($this->getOptions() as $option) {
1914-
if ($option->getId() == $optionId) {
1915-
return $option;
1912+
if (is_array($this->getOptions())) {
1913+
/** @var \Magento\Catalog\Model\Product\Option $option */
1914+
foreach ($this->getOptions() as $option) {
1915+
if ($option->getId() == $optionId) {
1916+
return $option;
1917+
}
19161918
}
19171919
}
19181920

@@ -2272,7 +2274,7 @@ public function getIdentities()
22722274
$identities[] = self::CACHE_PRODUCT_CATEGORY_TAG . '_' . $categoryId;
22732275
}
22742276
}
2275-
2277+
22762278
if (($this->getOrigData('status') != $this->getData('status')) || $this->isStockStatusChanged()) {
22772279
foreach ($this->getCategoryIds() as $categoryId) {
22782280
$identities[] = self::CACHE_PRODUCT_CATEGORY_TAG . '_' . $categoryId;
@@ -2287,7 +2289,7 @@ public function getIdentities()
22872289

22882290
/**
22892291
* Check whether stock status changed
2290-
*
2292+
*
22912293
* @return bool
22922294
*/
22932295
private function isStockStatusChanged()
@@ -2305,7 +2307,7 @@ private function isStockStatusChanged()
23052307
&& ($stockItem->getIsInStock() != $stockData['is_in_stock'])
23062308
);
23072309
}
2308-
2310+
23092311
/**
23102312
* Reload PriceInfo object
23112313
*

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use Magento\Framework\Exception\InputException;
1010
use Magento\Framework\Exception\NoSuchEntityException;
11+
use Magento\Eav\Api\Data\AttributeInterface;
1112

1213
/**
1314
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -117,6 +118,8 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
117118
throw NoSuchEntityException::singleField('attribute_code', $existingModel->getAttributeCode());
118119
}
119120

121+
// Attribute code must not be changed after attribute creation
122+
$attribute->setAttributeCode($existingModel->getAttributeCode());
120123
$attribute->setAttributeId($existingModel->getAttributeId());
121124
$attribute->setIsUserDefined($existingModel->getIsUserDefined());
122125
$attribute->setFrontendInput($existingModel->getFrontendInput());
@@ -176,8 +179,11 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
176179
$attribute->setIsUserDefined(1);
177180
}
178181
$this->attributeResource->save($attribute);
179-
foreach ($attribute->getOptions() as $option) {
180-
$this->getOptionManagement()->add($attribute->getAttributeCode(), $option);
182+
183+
if (!empty($attribute->getData(AttributeInterface::OPTIONS))) {
184+
foreach ($attribute->getOptions() as $option) {
185+
$this->getOptionManagement()->add($attribute->getAttributeCode(), $option);
186+
}
181187
}
182188
return $this->get($attribute->getAttributeCode());
183189
}

app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
namespace Magento\Catalog\Test\Unit\Model\Product\Attribute;
1111

12-
use \Magento\Catalog\Model\Product\Attribute\Repository;
12+
use Magento\Catalog\Model\Product\Attribute\Repository;
13+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
14+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
1315

1416
/**
1517
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -261,4 +263,29 @@ public function testSaveInputExceptionInvalidFieldValue()
261263

262264
$this->model->save($attributeMock);
263265
}
266+
267+
public function testSaveDoesNotSaveAttributeOptionsIfOptionsAreAbsentInPayload()
268+
{
269+
$attributeId = 1;
270+
$attributeCode = 'existing_attribute_code';
271+
$attributeMock = $this->getMock(Attribute::class, [], [], '', false);
272+
$attributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
273+
$attributeMock->expects($this->any())->method('getAttributeId')->willReturn($attributeId);
274+
275+
$existingModelMock = $this->getMock(Attribute::class, [], [], '', false);
276+
$existingModelMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
277+
$existingModelMock->expects($this->any())->method('getAttributeId')->willReturn($attributeId);
278+
279+
$this->eavAttributeRepositoryMock->expects($this->any())
280+
->method('get')
281+
->with(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode)
282+
->willReturn($existingModelMock);
283+
284+
// Attribute code must not be changed after attribute creation
285+
$attributeMock->expects($this->once())->method('setAttributeCode')->with($attributeCode);
286+
$this->attributeResourceMock->expects($this->once())->method('save')->with($attributeMock);
287+
$this->optionManagementMock->expects($this->never())->method('add');
288+
289+
$this->model->save($attributeMock);
290+
}
264291
}

app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,4 +1467,27 @@ public function testGetTypeId()
14671467
$this->model->setTypeId('typeId');
14681468
$this->model->getTypeInstance();
14691469
}
1470+
1471+
public function testGetOptionById()
1472+
{
1473+
$optionId = 100;
1474+
$optionMock = $this->getMock(\Magento\Catalog\Model\Product\Option::class, [], [], '', false);
1475+
$this->model->setOptions([$optionMock]);
1476+
$optionMock->expects($this->once())->method('getId')->willReturn($optionId);
1477+
$this->assertEquals($optionMock, $this->model->getOptionById($optionId));
1478+
}
1479+
1480+
public function testGetOptionByIdWithWrongOptionId()
1481+
{
1482+
$optionId = 100;
1483+
$optionMock = $this->getMock(\Magento\Catalog\Model\Product\Option::class, [], [], '', false);
1484+
$this->model->setOptions([$optionMock]);
1485+
$optionMock->expects($this->once())->method('getId')->willReturn(200);
1486+
$this->assertNull($this->model->getOptionById($optionId));
1487+
}
1488+
1489+
public function testGetOptionByIdForProductWithoutOptions()
1490+
{
1491+
$this->assertNull($this->model->getOptionById(100));
1492+
}
14701493
}

app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Categories;
99
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
1010
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
11+
use Magento\Framework\App\CacheInterface;
1112
use Magento\Framework\DB\Helper as DbHelper;
1213
use Magento\Framework\UrlInterface;
1314
use Magento\Store\Model\Store;
@@ -112,4 +113,38 @@ public function testModifyMeta()
112113

113114
$this->assertArrayHasKey($groupCode, $this->getModel()->modifyMeta($meta));
114115
}
116+
117+
public function testModifyMetaWithCaching()
118+
{
119+
$this->arrayManagerMock->expects($this->exactly(2))
120+
->method('findPath')
121+
->willReturn(true);
122+
$cacheManager = $this->getMockBuilder(CacheInterface::class)
123+
->getMockForAbstractClass();
124+
$cacheManager->expects($this->once())
125+
->method('load')
126+
->with(Categories::CATEGORY_TREE_ID . '_');
127+
$cacheManager->expects($this->once())
128+
->method('save');
129+
130+
$modifier = $this->createModel();
131+
$cacheContextProperty = new \ReflectionProperty(
132+
Categories::class,
133+
'cacheManager'
134+
);
135+
$cacheContextProperty->setAccessible(true);
136+
$cacheContextProperty->setValue($modifier, $cacheManager);
137+
138+
$groupCode = 'test_group_code';
139+
$meta = [
140+
$groupCode => [
141+
'children' => [
142+
'category_ids' => [
143+
'sortOrder' => 10,
144+
],
145+
],
146+
],
147+
];
148+
$modifier->modifyMeta($meta);
149+
}
115150
}

app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,25 @@
77

88
use Magento\Catalog\Model\Locator\LocatorInterface;
99
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
10+
use Magento\Framework\App\ObjectManager;
11+
use Magento\Framework\App\CacheInterface;
1012
use Magento\Framework\DB\Helper as DbHelper;
1113
use Magento\Catalog\Model\Category as CategoryModel;
1214
use Magento\Framework\UrlInterface;
1315
use Magento\Framework\Stdlib\ArrayManager;
1416

1517
/**
1618
* Data provider for categories field of product page
19+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1720
*/
1821
class Categories extends AbstractModifier
1922
{
23+
/**#@+
24+
* Category tree cache id
25+
*/
26+
const CATEGORY_TREE_ID = 'CATALOG_PRODUCT_CATEGORY_TREE';
27+
/**#@-*/
28+
2029
/**
2130
* @var CategoryCollectionFactory
2231
*/
@@ -29,6 +38,7 @@ class Categories extends AbstractModifier
2938

3039
/**
3140
* @var array
41+
* @deprecated
3242
*/
3343
protected $categoriesTrees = [];
3444

@@ -47,6 +57,11 @@ class Categories extends AbstractModifier
4757
*/
4858
protected $arrayManager;
4959

60+
/**
61+
* @var CacheInterface
62+
*/
63+
private $cacheManager;
64+
5065
/**
5166
* @param LocatorInterface $locator
5267
* @param CategoryCollectionFactory $categoryCollectionFactory
@@ -68,6 +83,21 @@ public function __construct(
6883
$this->arrayManager = $arrayManager;
6984
}
7085

86+
/**
87+
* Retrieve cache interface
88+
*
89+
* @return CacheInterface
90+
* @deprecated
91+
*/
92+
private function getCacheManager()
93+
{
94+
if (!$this->cacheManager) {
95+
$this->cacheManager = ObjectManager::getInstance()
96+
->get(CacheInterface::class);
97+
}
98+
return $this->cacheManager;
99+
}
100+
71101
/**
72102
* {@inheritdoc}
73103
*/
@@ -254,8 +284,9 @@ protected function customizeCategoriesField(array $meta)
254284
*/
255285
protected function getCategoriesTree($filter = null)
256286
{
257-
if (isset($this->categoriesTrees[$filter])) {
258-
return $this->categoriesTrees[$filter];
287+
$categoryTree = $this->getCacheManager()->load(self::CATEGORY_TREE_ID . '_' . $filter);
288+
if ($categoryTree) {
289+
return unserialize($categoryTree);
259290
}
260291

261292
$storeId = $this->locator->getStore()->getId();
@@ -307,9 +338,16 @@ protected function getCategoriesTree($filter = null)
307338
$categoryById[$category->getId()]['label'] = $category->getName();
308339
$categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()];
309340
}
341+
342+
$this->getCacheManager()->save(
343+
serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']),
344+
self::CATEGORY_TREE_ID . '_' . $filter,
345+
[
346+
\Magento\Catalog\Model\Category::CACHE_TAG,
347+
\Magento\Framework\App\Cache\Type\Block::CACHE_TAG
348+
]
349+
);
310350

311-
$this->categoriesTrees[$filter] = $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
312-
313-
return $this->categoriesTrees[$filter];
351+
return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
314352
}
315353
}

app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,13 @@
313313
},
314314

315315
save : function() {
316+
var block;
317+
316318
if ($('messages')) {
317319
$('messages').update();
320+
} else {
321+
block = jQuery('<div/>').attr('id', 'messages');
322+
jQuery('.page-main-actions').after(block);
318323
}
319324
TreePanels.rebuildTrees();
320325
if(!jQuery('#set-prop-form').valid()) {

0 commit comments

Comments
 (0)