diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml
deleted file mode 100644
index e02c34fd8868e..0000000000000
--- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
index cf12e332be86d..8da2614eb2ced 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
@@ -261,6 +261,10 @@ public function execute()
unset($data['apply_to']);
}
+ if ($model->getBackendType() == 'static' && !$model->getIsUserDefined()) {
+ $data['frontend_class'] = $model->getFrontendClass();
+ }
+
$model->addData($data);
if (!$attributeId) {
diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php
index 77dedb9eb0121..3494fd00a8b6c 100644
--- a/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php
+++ b/app/code/Magento/Catalog/Model/Layer/Filter/Price/Render.php
@@ -72,6 +72,8 @@ public function renderRangeLabel($fromPrice, $toPrice)
}
/**
+ * Prepare range data
+ *
* @param int $range
* @param int[] $dbRanges
* @return array
@@ -81,12 +83,10 @@ public function renderRangeData($range, $dbRanges)
if (empty($dbRanges)) {
return [];
}
- $lastIndex = array_keys($dbRanges);
- $lastIndex = $lastIndex[count($lastIndex) - 1];
foreach ($dbRanges as $index => $count) {
- $fromPrice = $index == 1 ? '' : ($index - 1) * $range;
- $toPrice = $index == $lastIndex ? '' : $index * $range;
+ $fromPrice = $index == 1 ? 0 : ($index - 1) * $range;
+ $toPrice = $index * $range;
$this->itemDataBuilder->addItemData(
$this->renderRangeLabel($fromPrice, $toPrice),
$fromPrice . '-' . $toPrice,
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php
index fab2441db26c9..939f9d354af85 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/AggregateCount.php
@@ -8,11 +8,15 @@
use Magento\Catalog\Model\Category;
/**
+ * Aggregate count for parent category after deleting child category
+ *
* Class AggregateCount
*/
class AggregateCount
{
/**
+ * Reduces children count for parent categories
+ *
* @param Category $category
* @return void
*/
@@ -25,9 +29,7 @@ public function processDelete(Category $category)
*/
$parentIds = $category->getParentIds();
if ($parentIds) {
- $childDecrease = $category->getChildrenCount() + 1;
- // +1 is itself
- $data = ['children_count' => new \Zend_Db_Expr('children_count - ' . $childDecrease)];
+ $data = ['children_count' => new \Zend_Db_Expr('children_count - 1')];
$where = ['entity_id IN(?)' => $parentIds];
$resourceModel->getConnection()->update($resourceModel->getEntityTable(), $data, $where);
}
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminEnableCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminEnableCategoryActionGroup.xml
new file mode 100644
index 0000000000000..bd7eb664819dd
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminEnableCategoryActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Enable the category
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsListedInCategoriesTreeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsListedInCategoriesTreeActionGroup.xml
new file mode 100644
index 0000000000000..3a75b0a3cd361
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsListedInCategoriesTreeActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsNotListedInCategoriesTreeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsNotListedInCategoriesTreeActionGroup.xml
new file mode 100644
index 0000000000000..e0a98a8932d4d
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminCategoryIsNotListedInCategoriesTreeActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchStoreActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchStoreActionGroup.xml
new file mode 100644
index 0000000000000..4a403364a91e3
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchStoreActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Switch the Storefront to the provided Store.
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml
index 192bab7c6d126..2cdec1405e9f9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml
@@ -31,21 +31,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml
index 4b0774d2307dd..e66984dda4427 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml
@@ -21,10 +21,8 @@
-
-
-
+
@@ -37,39 +35,32 @@
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
+
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml
index 40bd3bdcfea20..4979b06a1051e 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryTest.xml
@@ -27,16 +27,19 @@
-
-
-
+
+
+
+
-
-
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml
index fe07360d6b9ca..4310c6f06219a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml
@@ -33,59 +33,48 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
+
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml
index 9b5fa25085e1a..48e6245b011ba 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml
@@ -37,23 +37,26 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
index ca35d49113f41..681cef8489796 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
@@ -7,10 +7,12 @@
namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Attribute;
+use Magento\Backend\Model\Session;
use Magento\Backend\Model\View\Result\Redirect as ResultRedirect;
use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save;
use Magento\Catalog\Helper\Product as ProductHelper;
+use Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype\Presentation;
use Magento\Catalog\Model\Product\AttributeSet\Build;
use Magento\Catalog\Model\Product\AttributeSet\BuildFactory;
use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory;
@@ -31,63 +33,64 @@
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
*/
class SaveTest extends AttributeTest
{
/**
* @var BuildFactory|MockObject
*/
- protected $buildFactoryMock;
+ private $buildFactoryMock;
/**
* @var FilterManager|MockObject
*/
- protected $filterManagerMock;
+ private $filterManagerMock;
/**
* @var ProductHelper|MockObject
*/
- protected $productHelperMock;
+ private $productHelperMock;
/**
* @var AttributeFactory|MockObject
*/
- protected $attributeFactoryMock;
+ private $attributeFactoryMock;
/**
* @var ValidatorFactory|MockObject
*/
- protected $validatorFactoryMock;
+ private $validatorFactoryMock;
/**
* @var CollectionFactory|MockObject
*/
- protected $groupCollectionFactoryMock;
+ private $groupCollectionFactoryMock;
/**
* @var LayoutFactory|MockObject
*/
- protected $layoutFactoryMock;
+ private $layoutFactoryMock;
/**
* @var ResultRedirect|MockObject
*/
- protected $redirectMock;
+ private $redirectMock;
/**
- * @var AttributeSet|MockObject
+ * @var AttributeSetInterface|MockObject
*/
- protected $attributeSetMock;
+ private $attributeSetMock;
/**
* @var Build|MockObject
*/
- protected $builderMock;
+ private $builderMock;
/**
* @var InputTypeValidator|MockObject
*/
- protected $inputTypeValidatorMock;
+ private $inputTypeValidatorMock;
/**
* @var FormData|MockObject
@@ -104,19 +107,34 @@ class SaveTest extends AttributeTest
*/
private $attributeCodeValidatorMock;
+ /**
+ * @var Presentation|MockObject
+ */
+ private $presentationMock;
+
+ /**
+ * @var Session|MockObject
+ */
+
+ private $sessionMock;
+
protected function setUp(): void
{
parent::setUp();
+ $this->filterManagerMock = $this->createMock(FilterManager::class);
+ $this->productHelperMock = $this->createMock(ProductHelper::class);
+ $this->attributeSetMock = $this->createMock(AttributeSetInterface::class);
+ $this->builderMock = $this->createMock(Build::class);
+ $this->inputTypeValidatorMock = $this->createMock(InputTypeValidator::class);
+ $this->formDataSerializerMock = $this->createMock(FormData::class);
+ $this->attributeCodeValidatorMock = $this->createMock(AttributeCodeValidator::class);
+ $this->presentationMock = $this->createMock(Presentation::class);
+ $this->sessionMock = $this->createMock(Session::class);
+ $this->layoutFactoryMock = $this->createMock(LayoutFactory::class);
$this->buildFactoryMock = $this->getMockBuilder(BuildFactory::class)
->setMethods(['create'])
->disableOriginalConstructor()
->getMock();
- $this->filterManagerMock = $this->getMockBuilder(FilterManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->productHelperMock = $this->getMockBuilder(ProductHelper::class)
- ->disableOriginalConstructor()
- ->getMock();
$this->attributeFactoryMock = $this->getMockBuilder(AttributeFactory::class)
->setMethods(['create'])
->disableOriginalConstructor()
@@ -129,32 +147,23 @@ protected function setUp(): void
->setMethods(['create'])
->disableOriginalConstructor()
->getMock();
- $this->layoutFactoryMock = $this->getMockBuilder(LayoutFactory::class)
- ->disableOriginalConstructor()
- ->getMock();
$this->redirectMock = $this->getMockBuilder(ResultRedirect::class)
->setMethods(['setData', 'setPath'])
->disableOriginalConstructor()
->getMock();
- $this->attributeSetMock = $this->getMockBuilder(AttributeSetInterface::class)
- ->disableOriginalConstructor()
- ->getMockForAbstractClass();
- $this->builderMock = $this->getMockBuilder(Build::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->inputTypeValidatorMock = $this->getMockBuilder(InputTypeValidator::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->formDataSerializerMock = $this->getMockBuilder(FormData::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->attributeCodeValidatorMock = $this->getMockBuilder(AttributeCodeValidator::class)
- ->disableOriginalConstructor()
- ->getMock();
$this->productAttributeMock = $this->getMockBuilder(ProductAttributeInterface::class)
- ->setMethods(['getId', 'get'])
- ->getMockForAbstractClass();
-
+ ->setMethods(
+ [
+ 'getId',
+ 'get',
+ 'getBackendTypeByInput',
+ 'getDefaultValueByInput',
+ 'getBackendType',
+ 'getFrontendClass',
+ 'addData',
+ 'save'
+ ]
+ )->getMockForAbstractClass();
$this->buildFactoryMock->expects($this->any())
->method('create')
->willReturn($this->builderMock);
@@ -167,7 +176,7 @@ protected function setUp(): void
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function getModel()
{
@@ -184,7 +193,9 @@ protected function getModel()
'groupCollectionFactory' => $this->groupCollectionFactoryMock,
'layoutFactory' => $this->layoutFactoryMock,
'formDataSerializer' => $this->formDataSerializerMock,
- 'attributeCodeValidator' => $this->attributeCodeValidatorMock
+ 'attributeCodeValidator' => $this->attributeCodeValidatorMock,
+ 'presentation' => $this->presentationMock,
+ '_session' => $this->sessionMock
]);
}
@@ -214,6 +225,67 @@ public function testExecuteWithEmptyData()
$this->assertInstanceOf(ResultRedirect::class, $this->getModel()->execute());
}
+ public function testExecuteSaveFrontendClass()
+ {
+ $data = [
+ 'frontend_input' => 'test_frontend_input',
+ ];
+
+ $this->requestMock->expects($this->any())
+ ->method('getParam')
+ ->willReturnMap([
+ ['isAjax', null, null],
+ ['serialized_options', '[]', ''],
+ ['set', null, 1],
+ ['attribute_code', null, 'test_attribute_code'],
+ ]);
+ $this->formDataSerializerMock
+ ->expects($this->once())
+ ->method('unserialize')
+ ->with('')
+ ->willReturn([]);
+ $this->requestMock->expects($this->once())
+ ->method('getPostValue')
+ ->willReturn($data);
+ $this->inputTypeValidatorMock->expects($this->any())
+ ->method('isValid')
+ ->with($data['frontend_input'])
+ ->willReturn(true);
+ $this->presentationMock->expects($this->once())
+ ->method('convertPresentationDataToInputType')
+ ->willReturn($data);
+ $this->productHelperMock->expects($this->once())
+ ->method('getAttributeSourceModelByInputType')
+ ->with($data['frontend_input'])
+ ->willReturn(null);
+ $this->productHelperMock->expects($this->once())
+ ->method('getAttributeBackendModelByInputType')
+ ->with($data['frontend_input'])
+ ->willReturn(null);
+ $this->productAttributeMock->expects($this->once())
+ ->method('getBackendTypeByInput')
+ ->with($data['frontend_input'])
+ ->willReturnSelf('test_backend_type');
+ $this->productAttributeMock->expects($this->once())
+ ->method('getDefaultValueByInput')
+ ->with($data['frontend_input'])
+ ->willReturn(null);
+ $this->productAttributeMock->expects($this->once())
+ ->method('getBackendType')
+ ->willReturn('static');
+ $this->productAttributeMock->expects($this->once())
+ ->method('getFrontendClass')
+ ->willReturn('static');
+ $this->resultFactoryMock->expects($this->any())
+ ->method('create')
+ ->willReturn($this->redirectMock);
+ $this->redirectMock->expects($this->any())
+ ->method('setPath')
+ ->willReturnSelf();
+
+ $this->assertInstanceOf(ResultRedirect::class, $this->getModel()->execute());
+ }
+
public function testExecute()
{
$data = [
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Category/AggregateCountTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Category/AggregateCountTest.php
new file mode 100644
index 0000000000000..c73e02fb7ecbf
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Category/AggregateCountTest.php
@@ -0,0 +1,91 @@
+categoryMock = $this->createMock(Category::class);
+ $this->resourceCategoryMock = $this->createMock(ResourceCategory::class);
+ $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)
+ ->getMockForAbstractClass();
+ $this->objectManagerHelper = new ObjectManagerHelper($this);
+ $this->aggregateCount = $this->objectManagerHelper->getObject(AggregateCount::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testProcessDelete(): void
+ {
+ $parentIds = 3;
+ $table = 'catalog_category_entity';
+
+ $this->categoryMock->expects($this->once())
+ ->method('getResource')
+ ->willReturn($this->resourceCategoryMock);
+ $this->categoryMock->expects($this->once())
+ ->method('getParentIds')
+ ->willReturn($parentIds);
+ $this->resourceCategoryMock->expects($this->any())
+ ->method('getEntityTable')
+ ->willReturn($table);
+ $this->resourceCategoryMock->expects($this->once())
+ ->method('getConnection')
+ ->willReturn($this->connectionMock);
+ $this->connectionMock->expects($this->once())
+ ->method('update')
+ ->with(
+ $table,
+ ['children_count' => new \Zend_Db_Expr('children_count - 1')],
+ ['entity_id IN(?)' => $parentIds]
+ );
+ $this->aggregateCount->processDelete($this->categoryMock);
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
index 254d893d24584..17318d4207841 100644
--- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
@@ -7,13 +7,16 @@
namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier;
-use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
-use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Categories;
+use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
+use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
use Magento\Framework\AuthorizationInterface;
use Magento\Framework\DB\Helper as DbHelper;
use Magento\Framework\UrlInterface;
use Magento\Store\Model\Store;
+use Magento\Backend\Model\Auth\Session;
+use Magento\Authorization\Model\Role;
+use Magento\User\Model\User;
use PHPUnit\Framework\MockObject\MockObject;
/**
@@ -51,6 +54,11 @@ class CategoriesTest extends AbstractModifierTest
*/
private $authorizationMock;
+ /**
+ * @var Session|MockObject
+ */
+ private $sessionMock;
+
protected function setUp(): void
{
parent::setUp();
@@ -72,7 +80,10 @@ protected function setUp(): void
$this->authorizationMock = $this->getMockBuilder(AuthorizationInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
-
+ $this->sessionMock = $this->getMockBuilder(Session::class)
+ ->setMethods(['getUser'])
+ ->disableOriginalConstructor()
+ ->getMock();
$this->categoryCollectionFactoryMock->expects($this->any())
->method('create')
->willReturn($this->categoryCollectionMock);
@@ -88,6 +99,26 @@ protected function setUp(): void
$this->categoryCollectionMock->expects($this->any())
->method('getIterator')
->willReturn(new \ArrayIterator([]));
+
+ $roleAdmin = $this->getMockBuilder(Role::class)
+ ->setMethods(['getId'])
+ ->disableOriginalConstructor()
+ ->getMock();
+ $roleAdmin->expects($this->any())
+ ->method('getId')
+ ->willReturn(0);
+
+ $userAdmin = $this->getMockBuilder(User::class)
+ ->setMethods(['getRole'])
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userAdmin->expects($this->any())
+ ->method('getRole')
+ ->willReturn($roleAdmin);
+
+ $this->sessionMock->expects($this->any())
+ ->method('getUser')
+ ->willReturn($userAdmin);
}
/**
@@ -101,11 +132,28 @@ protected function createModel()
'locator' => $this->locatorMock,
'categoryCollectionFactory' => $this->categoryCollectionFactoryMock,
'arrayManager' => $this->arrayManagerMock,
- 'authorization' => $this->authorizationMock
+ 'authorization' => $this->authorizationMock,
+ 'session' => $this->sessionMock
]
);
}
+ /**
+ * @param object $object
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ * @throws \ReflectionException
+ */
+ private function invokeMethod($object, $method, $args = [])
+ {
+ $class = new \ReflectionClass(Categories::class);
+ $method = $class->getMethod($method);
+ $method->setAccessible(true);
+
+ return $method->invokeArgs($object, $args);
+ }
+
public function testModifyData()
{
$this->assertSame([], $this->getModel()->modifyData([]));
@@ -176,4 +224,44 @@ public function modifyMetaLockedDataProvider()
{
return [[true], [false]];
}
+
+ /**
+ * Asserts that a user with an ACL role ID of 0 and a user with an ACL role ID of 1 do not have the same cache IDs
+ * Assumes a store ID of 0
+ *
+ * @throws \ReflectionException
+ */
+ public function testAclCacheIds()
+ {
+ $categoriesAdmin = $this->createModel();
+ $cacheIdAdmin = $this->invokeMethod($categoriesAdmin, 'getCategoriesTreeCacheId', [0]);
+
+ $roleAclUser = $this->getMockBuilder(Role::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $roleAclUser->expects($this->any())
+ ->method('getId')
+ ->willReturn(1);
+
+ $userAclUser = $this->getMockBuilder(User::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userAclUser->expects($this->any())
+ ->method('getRole')
+ ->will($this->returnValue($roleAclUser));
+
+ $this->sessionMock = $this->getMockBuilder(Session::class)
+ ->setMethods(['getUser'])
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->sessionMock->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($userAclUser));
+
+ $categoriesAclUser = $this->createModel();
+ $cacheIdAclUser = $this->invokeMethod($categoriesAclUser, 'getCategoriesTreeCacheId', [0]);
+
+ $this->assertNotEquals($cacheIdAdmin, $cacheIdAclUser);
+ }
}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
index 7608173c8edfc..c0d5f0a1af3b8 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php
@@ -18,12 +18,14 @@
use Magento\Framework\UrlInterface;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Framework\AuthorizationInterface;
+use Magento\Backend\Model\Auth\Session;
/**
* Data provider for categories field of product page
*
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
* @since 101.0.0
*/
class Categories extends AbstractModifier
@@ -86,6 +88,11 @@ class Categories extends AbstractModifier
*/
private $authorization;
+ /**
+ * @var Session
+ */
+ private $session;
+
/**
* @param LocatorInterface $locator
* @param CategoryCollectionFactory $categoryCollectionFactory
@@ -94,6 +101,7 @@ class Categories extends AbstractModifier
* @param ArrayManager $arrayManager
* @param SerializerInterface $serializer
* @param AuthorizationInterface $authorization
+ * @param Session $session
*/
public function __construct(
LocatorInterface $locator,
@@ -102,7 +110,8 @@ public function __construct(
UrlInterface $urlBuilder,
ArrayManager $arrayManager,
SerializerInterface $serializer = null,
- AuthorizationInterface $authorization = null
+ AuthorizationInterface $authorization = null,
+ Session $session = null
) {
$this->locator = $locator;
$this->categoryCollectionFactory = $categoryCollectionFactory;
@@ -111,6 +120,7 @@ public function __construct(
$this->arrayManager = $arrayManager;
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
$this->authorization = $authorization ?: ObjectManager::getInstance()->get(AuthorizationInterface::class);
+ $this->session = $session ?: ObjectManager::getInstance()->get(Session::class);
}
/**
@@ -370,10 +380,16 @@ protected function getCategoriesTree($filter = null)
* @param string $filter
* @return string
*/
- private function getCategoriesTreeCacheId(int $storeId, string $filter = '') : string
+ private function getCategoriesTreeCacheId(int $storeId, string $filter = ''): string
{
+ if ($this->session->getUser() !== null) {
+ return self::CATEGORY_TREE_ID
+ . '_' . (string)$storeId
+ . '_' . $this->session->getUser()->getAclRole()
+ . '_' . $filter;
+ }
return self::CATEGORY_TREE_ID
- . '_' . (string) $storeId
+ . '_' . (string)$storeId
. '_' . $filter;
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php
index 69592657241a0..0bfd9d58ec969 100644
--- a/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php
+++ b/app/code/Magento/CatalogGraphQl/Model/AttributesJoiner.php
@@ -8,7 +8,10 @@
namespace Magento\CatalogGraphQl\Model;
use GraphQL\Language\AST\FieldNode;
+use GraphQL\Language\AST\InlineFragmentNode;
+use GraphQL\Language\AST\NodeKind;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Joins attributes for provided field node field names.
@@ -43,11 +46,12 @@ public function __construct(array $fieldToAttributeMap = [])
*
* @param FieldNode $fieldNode
* @param AbstractCollection $collection
+ * @param ResolveInfo $resolveInfo
* @return void
*/
- public function join(FieldNode $fieldNode, AbstractCollection $collection): void
+ public function join(FieldNode $fieldNode, AbstractCollection $collection, ResolveInfo $resolveInfo): void
{
- foreach ($this->getQueryFields($fieldNode) as $field) {
+ foreach ($this->getQueryFields($fieldNode, $resolveInfo) as $field) {
$this->addFieldToCollection($collection, $field);
}
}
@@ -56,26 +60,70 @@ public function join(FieldNode $fieldNode, AbstractCollection $collection): void
* Get an array of queried fields.
*
* @param FieldNode $fieldNode
+ * @param ResolveInfo $resolveInfo
* @return string[]
*/
- public function getQueryFields(FieldNode $fieldNode): array
+ public function getQueryFields(FieldNode $fieldNode, ResolveInfo $resolveInfo): array
{
if (null === $this->getFieldNodeSelections($fieldNode)) {
$query = $fieldNode->selectionSet->selections;
$selectedFields = [];
+ $fragmentFields = [];
/** @var FieldNode $field */
foreach ($query as $field) {
- if ($field->kind === 'InlineFragment') {
- continue;
+ if ($field->kind === NodeKind::INLINE_FRAGMENT) {
+ $fragmentFields[] = $this->addInlineFragmentFields($resolveInfo, $field);
+ } elseif ($field->kind === NodeKind::FRAGMENT_SPREAD &&
+ ($spreadFragmentNode = $resolveInfo->fragments[$field->name->value])) {
+
+ foreach ($spreadFragmentNode->selectionSet->selections as $spreadNode) {
+ if (isset($spreadNode->selectionSet->selections)) {
+ $fragmentFields[] = $this->getQueryFields($spreadNode, $resolveInfo);
+ } else {
+ $selectedFields[] = $spreadNode->name->value;
+ }
+ }
+ } else {
+ $selectedFields[] = $field->name->value;
}
- $selectedFields[] = $field->name->value;
}
- $this->setSelectionsForFieldNode($fieldNode, $selectedFields);
+ if ($fragmentFields) {
+ $selectedFields = array_merge($selectedFields, array_merge(...$fragmentFields));
+ }
+ $this->setSelectionsForFieldNode($fieldNode, array_unique($selectedFields));
}
return $this->getFieldNodeSelections($fieldNode);
}
+ /**
+ * Add fields from inline fragment nodes
+ *
+ * @param ResolveInfo $resolveInfo
+ * @param InlineFragmentNode $inlineFragmentField
+ * @param array $inlineFragmentFields
+ * @return string[]
+ */
+ private function addInlineFragmentFields(
+ ResolveInfo $resolveInfo,
+ InlineFragmentNode $inlineFragmentField,
+ $inlineFragmentFields = []
+ ): array {
+ $query = $inlineFragmentField->selectionSet->selections;
+ /** @var FieldNode $field */
+ foreach ($query as $field) {
+ if ($field->kind === NodeKind::INLINE_FRAGMENT) {
+ $this->addInlineFragmentFields($resolveInfo, $field, $inlineFragmentFields);
+ } elseif (isset($field->selectionSet->selections)) {
+ continue;
+ } else {
+ $inlineFragmentFields[] = $field->name->value;
+ }
+ }
+
+ return array_unique($inlineFragmentFields);
+ }
+
/**
* Add field to collection select
*
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php
index b5d02511da4e7..ab100c7272ba0 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/DepthCalculator.php
@@ -8,6 +8,9 @@
namespace Magento\CatalogGraphQl\Model\Category;
use GraphQL\Language\AST\FieldNode;
+use GraphQL\Language\AST\InlineFragmentNode;
+use GraphQL\Language\AST\NodeKind;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Used for determining the depth information for a requested category tree in a GraphQL request
@@ -17,22 +20,57 @@ class DepthCalculator
/**
* Calculate the total depth of a category tree inside a GraphQL request
*
+ * @param ResolveInfo $resolveInfo
* @param FieldNode $fieldNode
* @return int
*/
- public function calculate(FieldNode $fieldNode) : int
+ public function calculate(ResolveInfo $resolveInfo, FieldNode $fieldNode) : int
{
$selections = $fieldNode->selectionSet->selections ?? [];
$depth = count($selections) ? 1 : 0;
$childrenDepth = [0];
foreach ($selections as $node) {
- if ($node->kind === 'InlineFragment' || null !== $node->alias) {
+ if (isset($node->alias) && null !== $node->alias) {
continue;
}
- $childrenDepth[] = $this->calculate($node);
+ if ($node->kind === NodeKind::INLINE_FRAGMENT) {
+ $childrenDepth[] = $this->addInlineFragmentDepth($resolveInfo, $node);
+ } elseif ($node->kind === NodeKind::FRAGMENT_SPREAD && isset($resolveInfo->fragments[$node->name->value])) {
+ foreach ($resolveInfo->fragments[$node->name->value]->selectionSet->selections as $spreadNode) {
+ $childrenDepth[] = $this->calculate($resolveInfo, $spreadNode);
+ }
+ } else {
+ $childrenDepth[] = $this->calculate($resolveInfo, $node);
+ }
}
return $depth + max($childrenDepth);
}
+
+ /**
+ * Add inline fragment fields into calculating of category depth
+ *
+ * @param ResolveInfo $resolveInfo
+ * @param InlineFragmentNode $inlineFragmentField
+ * @param array $depth
+ * @return int
+ */
+ private function addInlineFragmentDepth(
+ ResolveInfo $resolveInfo,
+ InlineFragmentNode $inlineFragmentField,
+ $depth = []
+ ): int {
+ $selections = $inlineFragmentField->selectionSet->selections;
+ /** @var FieldNode $field */
+ foreach ($selections as $field) {
+ if ($field->kind === NodeKind::INLINE_FRAGMENT) {
+ $depth[] = $this->addInlineFragmentDepth($resolveInfo, $field, $depth);
+ } elseif ($field->selectionSet && $field->selectionSet->selections) {
+ $depth[] = $this->calculate($resolveInfo, $field);
+ }
+ }
+
+ return $depth ? max($depth) : 0;
+ }
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductLinksTypeResolver.php b/app/code/Magento/CatalogGraphQl/Model/ProductLinksTypeResolver.php
index 5a230ceed0ca4..c6de07bdedd19 100644
--- a/app/code/Magento/CatalogGraphQl/Model/ProductLinksTypeResolver.php
+++ b/app/code/Magento/CatalogGraphQl/Model/ProductLinksTypeResolver.php
@@ -10,7 +10,7 @@
use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface;
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
class ProductLinksTypeResolver implements TypeResolverInterface
{
@@ -20,9 +20,9 @@ class ProductLinksTypeResolver implements TypeResolverInterface
private $linkTypes = ['related', 'upsell', 'crosssell'];
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
- public function resolveType(array $data) : string
+ public function resolveType(array $data): string
{
if (isset($data['link_type'])) {
$linkType = $data['link_type'];
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
index 535fe3a80cd25..d7118d71db89b 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
@@ -7,18 +7,18 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
-use Magento\CatalogGraphQl\Model\Resolver\Product\ProductCategories;
-use Magento\Framework\Exception\LocalizedException;
-use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Api\Data\CategoryInterface;
use Magento\Catalog\Model\ResourceModel\Category\Collection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\CatalogGraphQl\Model\AttributesJoiner;
+use Magento\CatalogGraphQl\Model\Category\Hydrator as CategoryHydrator;
+use Magento\CatalogGraphQl\Model\Resolver\Product\ProductCategories;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CustomAttributesFlattener;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
-use Magento\CatalogGraphQl\Model\Category\Hydrator as CategoryHydrator;
+use Magento\Framework\GraphQl\Query\ResolverInterface;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Store\Model\StoreManagerInterface;
/**
@@ -121,7 +121,7 @@ function () use ($that, $categoryIds, $info) {
}
if (!$this->collection->isLoaded()) {
- $that->attributesJoiner->join($info->fieldNodes[0], $this->collection);
+ $that->attributesJoiner->join($info->fieldNodes[0], $this->collection, $info);
$this->collection->addIdFilter($this->categoryIds);
}
/** @var CategoryInterface | \Magento\Catalog\Model\Category $item */
@@ -130,7 +130,7 @@ function () use ($that, $categoryIds, $info) {
// Try to extract all requested fields from the loaded collection data
$categories[$item->getId()] = $this->categoryHydrator->hydrateCategory($item, true);
$categories[$item->getId()]['model'] = $item;
- $requestedFields = $that->attributesJoiner->getQueryFields($info->fieldNodes[0]);
+ $requestedFields = $that->attributesJoiner->getQueryFields($info->fieldNodes[0], $info);
$extractedFields = array_keys($categories[$item->getId()]);
$foundFields = array_intersect($requestedFields, $extractedFields);
if (count($requestedFields) === count($foundFields)) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php
index 14732ecf37c63..187fd05c1001e 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php
@@ -22,7 +22,15 @@ class BatchProductLinks implements BatchServiceContractResolverInterface
/**
* @var string[]
*/
- private static $linkTypes = ['related', 'upsell', 'crosssell'];
+ private $linkTypes;
+
+ /**
+ * @param array $linkTypes
+ */
+ public function __construct(array $linkTypes)
+ {
+ $this->linkTypes = $linkTypes;
+ }
/**
* @inheritDoc
@@ -44,7 +52,7 @@ public function convertToServiceArgument(ResolveRequestInterface $request)
/** @var \Magento\Catalog\Model\Product $product */
$product = $value['model'];
- return new ListCriteria((string)$product->getId(), self::$linkTypes, $product);
+ return new ListCriteria((string)$product->getId(), $this->linkTypes, $product);
}
/**
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php
index 9ddad4e6451fa..3139c35774008 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php
@@ -7,6 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
+use GraphQL\Language\AST\NodeKind;
use Magento\Framework\GraphQl\Query\FieldTranslator;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
@@ -43,9 +44,9 @@ public function getProductFieldsFromInfo(ResolveInfo $info, string $productNodeN
continue;
}
foreach ($node->selectionSet->selections as $selectionNode) {
- if ($selectionNode->kind === 'InlineFragment') {
+ if ($selectionNode->kind === NodeKind::INLINE_FRAGMENT) {
foreach ($selectionNode->selectionSet->selections as $inlineSelection) {
- if ($inlineSelection->kind === 'InlineFragment') {
+ if ($inlineSelection->kind === NodeKind::INLINE_FRAGMENT) {
continue;
}
$fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
index fc5a563c82b4e..c553d4486f9e9 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
@@ -8,15 +8,16 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider;
use GraphQL\Language\AST\FieldNode;
-use Magento\CatalogGraphQl\Model\Category\DepthCalculator;
-use Magento\CatalogGraphQl\Model\Category\LevelCalculator;
-use Magento\Framework\EntityManager\MetadataPool;
-use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use GraphQL\Language\AST\NodeKind;
use Magento\Catalog\Api\Data\CategoryInterface;
+use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\ResourceModel\Category\Collection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\CatalogGraphQl\Model\AttributesJoiner;
-use Magento\Catalog\Model\Category;
+use Magento\CatalogGraphQl\Model\Category\DepthCalculator;
+use Magento\CatalogGraphQl\Model\Category\LevelCalculator;
+use Magento\Framework\EntityManager\MetadataPool;
+use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
* Category tree data provider
@@ -85,8 +86,8 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterato
{
$categoryQuery = $resolveInfo->fieldNodes[0];
$collection = $this->collectionFactory->create();
- $this->joinAttributesRecursively($collection, $categoryQuery);
- $depth = $this->depthCalculator->calculate($categoryQuery);
+ $this->joinAttributesRecursively($collection, $categoryQuery, $resolveInfo);
+ $depth = $this->depthCalculator->calculate($resolveInfo, $categoryQuery);
$level = $this->levelCalculator->calculate($rootCategoryId);
// If root category is being filter, we've to remove first slash
@@ -124,24 +125,27 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterato
*
* @param Collection $collection
* @param FieldNode $fieldNode
+ * @param ResolveInfo $resolveInfo
* @return void
*/
- private function joinAttributesRecursively(Collection $collection, FieldNode $fieldNode) : void
- {
+ private function joinAttributesRecursively(
+ Collection $collection,
+ FieldNode $fieldNode,
+ ResolveInfo $resolveInfo
+ ): void {
if (!isset($fieldNode->selectionSet->selections)) {
return;
}
$subSelection = $fieldNode->selectionSet->selections;
- $this->attributesJoiner->join($fieldNode, $collection);
+ $this->attributesJoiner->join($fieldNode, $collection, $resolveInfo);
/** @var FieldNode $node */
foreach ($subSelection as $node) {
- if ($node->kind === 'InlineFragment') {
+ if ($node->kind === NodeKind::INLINE_FRAGMENT || $node->kind === NodeKind::FRAGMENT_SPREAD) {
continue;
}
-
- $this->joinAttributesRecursively($collection, $node);
+ $this->joinAttributesRecursively($collection, $node, $resolveInfo);
}
}
}
diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml
index 5fec7bfd4fda7..03f9d7ad03f04 100644
--- a/app/code/Magento/CatalogGraphQl/etc/di.xml
+++ b/app/code/Magento/CatalogGraphQl/etc/di.xml
@@ -74,4 +74,14 @@
+
+
+
+
+ - related
+ - upsell
+ - crosssell
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index c5fcac99767bd..189bfa61f2c42 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -1595,6 +1595,7 @@ protected function _saveProducts()
}
$rowSku = $rowData[self::COL_SKU];
+ $rowSkuNormalized = mb_strtolower($rowSku);
if (null === $rowSku) {
$this->getErrorAggregator()->addRowToSkip($rowNum);
@@ -1604,9 +1605,9 @@ protected function _saveProducts()
$storeId = !empty($rowData[self::COL_STORE])
? $this->getStoreIdByCode($rowData[self::COL_STORE])
: Store::DEFAULT_STORE_ID;
- $rowExistingImages = $existingImages[$storeId][$rowSku] ?? [];
+ $rowExistingImages = $existingImages[$storeId][$rowSkuNormalized] ?? [];
$rowStoreMediaGalleryValues = $rowExistingImages;
- $rowExistingImages += $existingImages[Store::DEFAULT_STORE_ID][$rowSku] ?? [];
+ $rowExistingImages += $existingImages[Store::DEFAULT_STORE_ID][$rowSkuNormalized] ?? [];
if (self::SCOPE_STORE == $rowScope) {
// set necessary data from SCOPE_DEFAULT row
@@ -1762,10 +1763,11 @@ protected function _saveProducts()
continue;
}
- if (isset($rowExistingImages[$uploadedFile])) {
- $currentFileData = $rowExistingImages[$uploadedFile];
+ $uploadedFileNormalized = ltrim($uploadedFile, '/\\');
+ if (isset($rowExistingImages[$uploadedFileNormalized])) {
+ $currentFileData = $rowExistingImages[$uploadedFileNormalized];
$currentFileData['store_id'] = $storeId;
- $storeMediaGalleryValueExists = isset($rowStoreMediaGalleryValues[$uploadedFile]);
+ $storeMediaGalleryValueExists = isset($rowStoreMediaGalleryValues[$uploadedFileNormalized]);
if (array_key_exists($uploadedFile, $imageHiddenStates)
&& $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile]
) {
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php
index a94a87a44b32a..d4694b72ba64f 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php
@@ -384,7 +384,9 @@ public function getExistingImages(array $bunch)
foreach ($this->connection->fetchAll($select) as $image) {
$storeId = $image['store_id'];
unset($image['store_id']);
- $result[$storeId][$image['sku']][$image['value']] = $image;
+ $sku = mb_strtolower($image['sku']);
+ $value = ltrim($image['value'], '/\\');
+ $result[$storeId][$sku][$value] = $image;
}
return $result;
diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
index 332bb991bf29f..b2aaa054ebc34 100644
--- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
+++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Price.php
@@ -176,15 +176,16 @@ public function getCurrencyRate()
*
* @param float|string $fromPrice
* @param float|string $toPrice
+ * @param boolean $isLast
* @return float|\Magento\Framework\Phrase
*/
- protected function _renderRangeLabel($fromPrice, $toPrice)
+ protected function _renderRangeLabel($fromPrice, $toPrice, $isLast = false)
{
$fromPrice = empty($fromPrice) ? 0 : $fromPrice * $this->getCurrencyRate();
$toPrice = empty($toPrice) ? $toPrice : $toPrice * $this->getCurrencyRate();
$formattedFromPrice = $this->priceCurrency->format($fromPrice);
- if ($toPrice === '') {
+ if ($isLast) {
return __('%1 and above', $formattedFromPrice);
} elseif ($fromPrice == $toPrice && $this->dataProvider->getOnePriceIntervalValue()) {
return $formattedFromPrice;
@@ -215,12 +216,15 @@ protected function _getItemsData()
$data = [];
if (count($facets) > 1) { // two range minimum
+ $lastFacet = array_key_last($facets);
foreach ($facets as $key => $aggregation) {
$count = $aggregation['count'];
if (strpos($key, '_') === false) {
continue;
}
- $data[] = $this->prepareData($key, $count, $data);
+
+ $isLast = $lastFacet === $key;
+ $data[] = $this->prepareData($key, $count, $isLast);
}
}
@@ -264,18 +268,13 @@ protected function getFrom($from)
*
* @param string $key
* @param int $count
+ * @param boolean $isLast
* @return array
*/
- private function prepareData($key, $count)
+ private function prepareData($key, $count, $isLast = false)
{
- list($from, $to) = explode('_', $key);
- if ($from == '*') {
- $from = $this->getFrom($to);
- }
- if ($to == '*') {
- $to = $this->getTo($to);
- }
- $label = $this->_renderRangeLabel($from, $to);
+ [$from, $to] = explode('_', $key);
+ $label = $this->_renderRangeLabel($from, $to, $isLast);
$value = $from . '-' . $to . $this->dataProvider->getAdditionalRequestData();
$data = [
diff --git a/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/Store/View.php b/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/Store/View.php
index 257f8ba9bfe53..9e360481e8eb3 100644
--- a/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/Store/View.php
+++ b/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/Store/View.php
@@ -7,6 +7,7 @@
namespace Magento\CmsUrlRewrite\Plugin\Cms\Model\Store;
+use Magento\Cms\Api\Data\PageInterface;
use Magento\Cms\Api\PageRepositoryInterface;
use Magento\CmsUrlRewrite\Model\CmsPageUrlRewriteGenerator;
use Magento\Framework\Api\SearchCriteriaBuilder;
@@ -21,6 +22,8 @@
*/
class View
{
+ private const ALL_STORE_VIEWS = '0';
+
/**
* @var UrlPersistInterface
*/
@@ -89,9 +92,8 @@ private function generateCmsPagesUrls(int $storeId): array
{
$rewrites = [];
$urls = [];
- $searchCriteria = $this->searchCriteriaBuilder->create();
- $cmsPagesCollection = $this->pageRepository->getList($searchCriteria)->getItems();
- foreach ($cmsPagesCollection as $page) {
+
+ foreach ($this->getCmsPageItems() as $page) {
$page->setStoreId($storeId);
$rewrites[] = $this->cmsPageUrlRewriteGenerator->generate($page);
}
@@ -99,4 +101,18 @@ private function generateCmsPagesUrls(int $storeId): array
return $urls;
}
+
+ /**
+ * Return cms page items for all store view
+ *
+ * @return PageInterface[]
+ */
+ private function getCmsPageItems(): array
+ {
+ $searchCriteria = $this->searchCriteriaBuilder->addFilter('store_id', self::ALL_STORE_VIEWS)
+ ->create();
+ $list = $this->pageRepository->getList($searchCriteria);
+
+ return $list->getItems();
+ }
}
diff --git a/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php
index 2be340c8ccca4..b4c737f6600bf 100644
--- a/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php
+++ b/app/code/Magento/Customer/Block/DataProviders/AddressAttributeData.php
@@ -52,7 +52,7 @@ public function getFrontendLabel(string $attributeCode): string
{
try {
$attribute = $this->addressMetadata->getAttributeMetadata($attributeCode);
- $frontendLabel = $attribute->getFrontendLabel();
+ $frontendLabel = $attribute->getStoreLabel() ?: $attribute->getFrontendLabel();
} catch (NoSuchEntityException $e) {
$frontendLabel = '';
}
diff --git a/app/code/Magento/Customer/Model/CustomerRegistry.php b/app/code/Magento/Customer/Model/CustomerRegistry.php
index d68904f6d1645..f2868132790cf 100644
--- a/app/code/Magento/Customer/Model/CustomerRegistry.php
+++ b/app/code/Magento/Customer/Model/CustomerRegistry.php
@@ -101,8 +101,10 @@ public function retrieve($customerId)
public function retrieveByEmail($customerEmail, $websiteId = null)
{
if ($websiteId === null) {
- $websiteId = $this->storeManager->getStore()->getWebsiteId();
+ $websiteId = $this->storeManager->getStore()->getWebsiteId()
+ ?: $this->storeManager->getDefaultStoreView()->getWebsiteId();
}
+
$emailKey = $this->getEmailKey($customerEmail, $websiteId);
if (isset($this->customerRegistryByEmail[$emailKey])) {
return $this->customerRegistryByEmail[$emailKey];
diff --git a/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php b/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php
index 9eb9ffb806c9f..b877b2cca67a5 100644
--- a/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php
+++ b/app/code/Magento/Customer/Model/Plugin/CustomerAuthorization.php
@@ -7,7 +7,10 @@
namespace Magento\Customer\Model\Plugin;
use Magento\Authorization\Model\UserContextInterface;
+use Magento\Customer\Model\CustomerFactory;
+use Magento\Customer\Model\ResourceModel\Customer as CustomerResource;
use Magento\Integration\Api\AuthorizationServiceInterface as AuthorizationService;
+use Magento\Store\Model\StoreManagerInterface;
/**
* Plugin around \Magento\Framework\Authorization::isAllowed
@@ -19,16 +22,41 @@ class CustomerAuthorization
/**
* @var UserContextInterface
*/
- protected $userContext;
+ private $userContext;
+
+ /**
+ * @var CustomerFactory
+ */
+ private $customerFactory;
+
+ /**
+ * @var CustomerResource
+ */
+ private $customerResource;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
/**
* Inject dependencies.
*
* @param UserContextInterface $userContext
+ * @param CustomerFactory $customerFactory
+ * @param CustomerResource $customerResource
+ * @param StoreManagerInterface $storeManager
*/
- public function __construct(UserContextInterface $userContext)
- {
+ public function __construct(
+ UserContextInterface $userContext,
+ CustomerFactory $customerFactory,
+ CustomerResource $customerResource,
+ StoreManagerInterface $storeManager
+ ) {
$this->userContext = $userContext;
+ $this->customerFactory = $customerFactory;
+ $this->customerResource = $customerResource;
+ $this->storeManager = $storeManager;
}
/**
@@ -53,9 +81,15 @@ public function aroundIsAllowed(
&& $this->userContext->getUserId()
&& $this->userContext->getUserType() === UserContextInterface::USER_TYPE_CUSTOMER
) {
- return true;
- } else {
- return $proceed($resource, $privilege);
+ $customer = $this->customerFactory->create();
+ $this->customerResource->load($customer, $this->userContext->getUserId());
+ $currentStoreId = $this->storeManager->getStore()->getId();
+ $sharedStoreIds = $customer->getSharedStoreIds();
+ if (in_array($currentStoreId, $sharedStoreIds)) {
+ return true;
+ }
}
+
+ return $proceed($resource, $privilege);
}
}
diff --git a/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php b/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php
new file mode 100644
index 0000000000000..ef3e86788c43f
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Api/ValidateCustomerDataInterface.php
@@ -0,0 +1,24 @@
+getAllowedCustomerAttributes = $getAllowedCustomerAttributes;
$this->emailAddressValidator = $emailAddressValidator;
+ $this->validators = $validators;
}
/**
* Validate customer data
*
* @param array $customerData
- *
- * @return void
- *
* @throws GraphQlInputException
+ * @throws LocalizedException
+ * @throws NoSuchEntityException
*/
- public function execute(array $customerData): void
+ public function execute(array $customerData)
{
- $attributes = $this->getAllowedCustomerAttributes->execute(array_keys($customerData));
- $errorInput = [];
-
- foreach ($attributes as $attributeInfo) {
- if ($attributeInfo->getIsRequired()
- && (!isset($customerData[$attributeInfo->getAttributeCode()])
- || $customerData[$attributeInfo->getAttributeCode()] == '')
- ) {
- $errorInput[] = $attributeInfo->getDefaultFrontendLabel();
- }
- }
-
- if ($errorInput) {
- throw new GraphQlInputException(
- __('Required parameters are missing: %1', [implode(', ', $errorInput)])
- );
- }
-
- if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) {
- throw new GraphQlInputException(
- __('"%1" is not a valid email address.', $customerData['email'])
- );
+ /** @var ValidateCustomerDataInterface $validator */
+ foreach ($this->validators as $validator) {
+ $validator->execute($customerData);
}
}
}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php
new file mode 100644
index 0000000000000..87f8831550f04
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateEmail.php
@@ -0,0 +1,45 @@
+emailAddressValidator = $emailAddressValidator;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(array $customerData): void
+ {
+ if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) {
+ throw new GraphQlInputException(
+ __('"%1" is not a valid email address.', $customerData['email'])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php
new file mode 100644
index 0000000000000..463372a63d8d5
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateGender.php
@@ -0,0 +1,58 @@
+customerMetadata = $customerMetadata;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(array $customerData): void
+ {
+ if (isset($customerData['gender']) && $customerData['gender']) {
+ /** @var AttributeMetadata $genderData */
+ $options = $this->customerMetadata->getAttributeMetadata('gender')->getOptions();
+
+ $isValid = false;
+ foreach ($options as $optionData) {
+ if ($optionData->getValue() && $optionData->getValue() == $customerData['gender']) {
+ $isValid = true;
+ }
+ }
+
+ if (!$isValid) {
+ throw new GraphQlInputException(
+ __('"%1" is not a valid gender value.', $customerData['gender'])
+ );
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php
new file mode 100644
index 0000000000000..fdf4fa1c824f2
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData/ValidateRequiredArguments.php
@@ -0,0 +1,59 @@
+getAllowedCustomerAttributes = $getAllowedCustomerAttributes;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(array $customerData): void
+ {
+ $attributes = $this->getAllowedCustomerAttributes->execute(array_keys($customerData));
+ $errorInput = [];
+
+ foreach ($attributes as $attributeInfo) {
+ if ($attributeInfo->getIsRequired()
+ && (!isset($customerData[$attributeInfo->getAttributeCode()])
+ || $customerData[$attributeInfo->getAttributeCode()] == '')
+ ) {
+ $errorInput[] = $attributeInfo->getDefaultFrontendLabel();
+ }
+ }
+
+ if ($errorInput) {
+ throw new GraphQlInputException(
+ __('Required parameters are missing: %1', [implode(', ', $errorInput)])
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
index 1ba0e457430e0..3ed77a2ad563c 100644
--- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
@@ -29,4 +29,14 @@
+
+
+
+
+ - Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateRequiredArguments
+ - Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateEmail
+ - Magento\CustomerGraphQl\Model\Customer\ValidateCustomerData\ValidateGender
+
+
+
diff --git a/app/code/Magento/Developer/Console/Command/patch_template.php.dist b/app/code/Magento/Developer/Console/Command/patch_template.php.dist
index f4fc25abcb29a..8e14b24bdc933 100644
--- a/app/code/Magento/Developer/Console/Command/patch_template.php.dist
+++ b/app/code/Magento/Developer/Console/Command/patch_template.php.dist
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace %moduleName%\Setup\Patch\%patchType%;
@@ -36,7 +37,7 @@ class %class% implements %implementsInterfaces%
}
%revertFunction%
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getAliases()
{
@@ -44,12 +45,10 @@ class %class% implements %implementsInterfaces%
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getDependencies()
{
- return [
-
- ];
+ return [];
}
}
diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
index 1f6e05c9e02fc..8576d8df0cc95 100644
--- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
+++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
@@ -19,7 +19,7 @@ class Converter implements ConverterInterface
*/
private const ES_DATA_TYPE_TEXT = 'text';
private const ES_DATA_TYPE_KEYWORD = 'keyword';
- private const ES_DATA_TYPE_FLOAT = 'float';
+ private const ES_DATA_TYPE_DOUBLE = 'double';
private const ES_DATA_TYPE_INT = 'integer';
private const ES_DATA_TYPE_DATE = 'date';
/**#@-*/
@@ -32,7 +32,7 @@ class Converter implements ConverterInterface
private $mapping = [
self::INTERNAL_DATA_TYPE_STRING => self::ES_DATA_TYPE_TEXT,
self::INTERNAL_DATA_TYPE_KEYWORD => self::ES_DATA_TYPE_KEYWORD,
- self::INTERNAL_DATA_TYPE_FLOAT => self::ES_DATA_TYPE_FLOAT,
+ self::INTERNAL_DATA_TYPE_FLOAT => self::ES_DATA_TYPE_DOUBLE,
self::INTERNAL_DATA_TYPE_INT => self::ES_DATA_TYPE_INT,
self::INTERNAL_DATA_TYPE_DATE => self::ES_DATA_TYPE_DATE,
];
diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
index bd9a380230652..8d8787a5eff72 100644
--- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
+++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
@@ -276,7 +276,7 @@ public function addFieldsMapping(array $fields, $index, $entityType)
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
index 88dab83698794..2067dcdc7fe9f 100644
--- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
+++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/Converter.php
@@ -16,7 +16,7 @@ class Converter implements ConverterInterface
* Text flags for Elasticsearch field types
*/
private const ES_DATA_TYPE_STRING = 'string';
- private const ES_DATA_TYPE_FLOAT = 'float';
+ private const ES_DATA_TYPE_DOUBLE = 'double';
private const ES_DATA_TYPE_INT = 'integer';
private const ES_DATA_TYPE_DATE = 'date';
/**#@-*/
@@ -29,7 +29,7 @@ class Converter implements ConverterInterface
private $mapping = [
self::INTERNAL_DATA_TYPE_STRING => self::ES_DATA_TYPE_STRING,
self::INTERNAL_DATA_TYPE_KEYWORD => self::ES_DATA_TYPE_STRING,
- self::INTERNAL_DATA_TYPE_FLOAT => self::ES_DATA_TYPE_FLOAT,
+ self::INTERNAL_DATA_TYPE_FLOAT => self::ES_DATA_TYPE_DOUBLE,
self::INTERNAL_DATA_TYPE_INT => self::ES_DATA_TYPE_INT,
self::INTERNAL_DATA_TYPE_DATE => self::ES_DATA_TYPE_DATE,
];
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php
index 1e106023ea00d..548a57e55f3e2 100644
--- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Dynamic.php
@@ -35,7 +35,7 @@ public function __construct(Repository $algorithmRepository, EntityStorageFactor
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function build(
RequestBucketInterface $bucket,
@@ -46,9 +46,7 @@ public function build(
/** @var DynamicBucket $bucket */
$algorithm = $this->algorithmRepository->get($bucket->getMethod(), ['dataProvider' => $dataProvider]);
$data = $algorithm->getItems($bucket, $dimensions, $this->getEntityStorage($queryResult));
- $resultData = $this->prepareData($data);
-
- return $resultData;
+ return $this->prepareData($data);
}
/**
@@ -77,12 +75,9 @@ private function prepareData($data)
{
$resultData = [];
foreach ($data as $value) {
- $from = is_numeric($value['from']) ? $value['from'] : '*';
- $to = is_numeric($value['to']) ? $value['to'] : '*';
- unset($value['from'], $value['to']);
-
- $rangeName = "{$from}_{$to}";
- $resultData[$rangeName] = array_merge(['value' => $rangeName], $value);
+ $rangeName = "{$value['from']}_{$value['to']}";
+ $value['value'] = $rangeName;
+ $resultData[$rangeName] = $value;
}
return $resultData;
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php b/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php
index 496a77e4c5ac3..7bc64b59ffe78 100644
--- a/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Dynamic/DataProvider.php
@@ -235,11 +235,9 @@ public function prepareData($range, array $dbRanges)
{
$data = [];
if (!empty($dbRanges)) {
- $lastIndex = array_keys($dbRanges);
- $lastIndex = $lastIndex[count($lastIndex) - 1];
foreach ($dbRanges as $index => $count) {
- $fromPrice = $index == 1 ? '' : ($index - 1) * $range;
- $toPrice = $index == $lastIndex ? '' : $index * $range;
+ $fromPrice = $index == 1 ? 0 : ($index - 1) * $range;
+ $toPrice = $index * $range;
$data[] = [
'from' => $fromPrice,
'to' => $toPrice,
diff --git a/app/code/Magento/Elasticsearch/Setup/Patch/Data/InvalidateIndex.php b/app/code/Magento/Elasticsearch/Setup/Patch/Data/InvalidateIndex.php
new file mode 100644
index 0000000000000..7cd72c322d647
--- /dev/null
+++ b/app/code/Magento/Elasticsearch/Setup/Patch/Data/InvalidateIndex.php
@@ -0,0 +1,66 @@
+moduleDataSetup = $moduleDataSetup;
+ $this->indexerRegistry = $indexerRegistry;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function apply(): PatchInterface
+ {
+ $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID)->invalidate();
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getDependencies(): array
+ {
+ return [];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAliases(): array
+ {
+ return [];
+ }
+}
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php
index 49a894f1295c7..575a64dc43abd 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php
@@ -329,7 +329,7 @@ public function testAddFieldsMapping()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
@@ -400,7 +400,7 @@ public function testAddFieldsMappingFailure()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicFieldTest.php
index 87f072836544e..a9bcd1a20a1b2 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicFieldTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicFieldTest.php
@@ -246,7 +246,7 @@ function ($type) use ($complexType) {
if ($type === 'string') {
return 'string';
} elseif ($type === 'float') {
- return 'float';
+ return 'double';
} elseif ($type === 'integer') {
return 'integer';
} else {
@@ -281,7 +281,7 @@ public function attributeProvider()
'index' => 'no_index'
],
'price_1_1' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true
]
]
@@ -300,7 +300,7 @@ public function attributeProvider()
'index' => 'no_index'
],
'price_1_1' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true
]
],
@@ -319,7 +319,7 @@ public function attributeProvider()
'index' => 'no_index'
],
'price_1_1' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true
]
]
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/ConverterTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/ConverterTest.php
index 75b79bc43e805..718adf255254f 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/ConverterTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldType/ConverterTest.php
@@ -56,7 +56,7 @@ public function convertProvider()
{
return [
['string', 'string'],
- ['float', 'float'],
+ ['float', 'double'],
['integer', 'integer'],
];
}
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php
index c5b9089acd91c..0595b667f4ee8 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Dynamic/DataProviderTest.php
@@ -390,13 +390,13 @@ public function testPrepareData()
{
$expectedResult = [
[
- 'from' => '',
+ 'from' => 0,
'to' => 10,
'count' => 1,
],
[
'from' => 10,
- 'to' => '',
+ 'to' => 20,
'count' => 1,
],
];
diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php
index 2c1c283c5b24d..0571b075aff28 100644
--- a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php
+++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php
@@ -281,7 +281,7 @@ public function addFieldsMapping(array $fields, $index, $entityType)
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php
index aa0b49400c517..2a7fa2ce8114a 100644
--- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php
+++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php
@@ -439,7 +439,7 @@ public function testAddFieldsMapping()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
@@ -509,7 +509,7 @@ public function testAddFieldsMappingFailure()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php
index feacca8d62804..4b318f987abfe 100644
--- a/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php
+++ b/app/code/Magento/Elasticsearch7/Model/Client/Elasticsearch.php
@@ -289,7 +289,7 @@ public function addFieldsMapping(array $fields, string $index, string $entityTyp
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php
index 593bbd7792f46..091387f844d55 100644
--- a/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php
+++ b/app/code/Magento/Elasticsearch7/Test/Unit/Model/Client/ElasticsearchTest.php
@@ -438,7 +438,7 @@ public function testAddFieldsMapping()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
@@ -509,7 +509,7 @@ public function testAddFieldsMappingFailure()
'match' => 'price_*',
'match_mapping_type' => 'string',
'mapping' => [
- 'type' => 'float',
+ 'type' => 'double',
'store' => true,
],
],
diff --git a/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php
index c317221fb6ef7..2ce51c8bbf19d 100644
--- a/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php
+++ b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php
@@ -66,7 +66,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new GraphQlInputException(__('"model" value must be specified'));
}
$cart = $value['model'];
diff --git a/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/Item/GiftMessage.php b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/Item/GiftMessage.php
new file mode 100644
index 0000000000000..a9a8e682612cc
--- /dev/null
+++ b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/Item/GiftMessage.php
@@ -0,0 +1,97 @@
+itemRepository = $itemRepository;
+ $this->giftMessageHelper = $giftMessageHelper;
+ }
+
+ /**
+ * Return information about Gift message for item cart
+ *
+ * @param Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ *
+ * @return array|Value|mixed
+ * @throws GraphQlInputException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($value['model'])) {
+ throw new GraphQlInputException(__('"model" value must be specified'));
+ }
+
+ $quoteItem = $value['model'];
+
+ if (!$this->giftMessageHelper->isMessagesAllowed('items', $quoteItem)) {
+ return null;
+ }
+
+ if (!$this->giftMessageHelper->isMessagesAllowed('item', $quoteItem)) {
+ return null;
+ }
+
+ try {
+ $giftItemMessage = $this->itemRepository->get($quoteItem->getQuoteId(), $quoteItem->getItemId());
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Can\'t load cart item'));
+ }
+
+ if (!isset($giftItemMessage)) {
+ return null;
+ }
+
+ return [
+ 'to' => $giftItemMessage->getRecipient() ?? '',
+ 'from' => $giftItemMessage->getSender() ?? '',
+ 'message'=> $giftItemMessage->getMessage() ?? ''
+ ];
+ }
+}
diff --git a/app/code/Magento/GiftMessageGraphQl/README.md b/app/code/Magento/GiftMessageGraphQl/README.md
index fa2e02116b66c..d73a058e0db9c 100644
--- a/app/code/Magento/GiftMessageGraphQl/README.md
+++ b/app/code/Magento/GiftMessageGraphQl/README.md
@@ -1,3 +1,3 @@
# GiftMessageGraphQl
-**GiftMessageGraphQl** provides information about gift messages for cart, cart items, order and order items.
+**GiftMessageGraphQl** provides information about gift messages for carts, cart items, orders and order items.
diff --git a/app/code/Magento/GiftMessageGraphQl/etc/graphql/di.xml b/app/code/Magento/GiftMessageGraphQl/etc/graphql/di.xml
new file mode 100644
index 0000000000000..bce5b7063e6b9
--- /dev/null
+++ b/app/code/Magento/GiftMessageGraphQl/etc/graphql/di.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ - sales/gift_options/allow_order
+ - sales/gift_options/allow_items
+
+
+
+
diff --git a/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls b/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls
index f14c812a9a5f3..ad18054abca13 100644
--- a/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls
@@ -1,20 +1,47 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
+type StoreConfig {
+ allow_order : String @doc(description: "The value of the Allow Gift Messages on Order Level option")
+ allow_items : String @doc(description: "The value of the Allow Gift Messages for Order Items option")
+}
+
type Cart {
gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Cart\\GiftMessage") @doc(description: "The entered gift message for the cart")
}
-type SalesItemInterface {
- gift_message: GiftMessage @doc(description: "The entered gift message for the order item")
+type SimpleCartItem {
+ gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Cart\\Item\\GiftMessage") @doc(description: "The entered gift message for the cart item")
}
-type CustomerOrder {
- gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Order\\GiftMessage") @doc(description: "The entered gift message for the order")
+type ConfigurableCartItem {
+ gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Cart\\Item\\GiftMessage") @doc(description: "The entered gift message for the cart item")
+}
+
+type BundleCartItem {
+ gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Cart\\Item\\GiftMessage") @doc(description: "The entered gift message for the cart item")
+}
+
+type GiftMessage @doc(description: "Contains the text of a gift message, its sender, and recipient") {
+ to: String! @doc(description: "Recipient name")
+ from: String! @doc(description: "Sender name")
+ message: String! @doc(description: "Gift message text")
+}
+
+input CartItemUpdateInput {
+ gift_message: GiftMessageInput @doc(description: "Gift message details for the cart item")
}
-type GiftMessage {
- to: String! @doc(description: "Recepient name")
+input GiftMessageInput @doc(description: "Contains the text of a gift message, its sender, and recipient") {
+ to: String! @doc(description: "Recipient name")
from: String! @doc(description: "Sender name")
message: String! @doc(description: "Gift message text")
}
+
+type SalesItemInterface {
+ gift_message: GiftMessage @doc(description: "The entered gift message for the order item")
+}
+
+type CustomerOrder {
+ gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Order\\GiftMessage") @doc(description: "The entered gift message for the order")
+}
diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php
new file mode 100644
index 0000000000000..ba2e995d4f704
--- /dev/null
+++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowCredentialsHeaderProvider.php
@@ -0,0 +1,71 @@
+corsConfiguration = $corsConfiguration;
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * Get name of header
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->headerName;
+ }
+
+ /**
+ * Get value for header
+ *
+ * @return string
+ */
+ public function getValue(): string
+ {
+ return "1";
+ }
+
+ /**
+ * Check if header can be applied
+ *
+ * @return bool
+ */
+ public function canApply(): bool
+ {
+ return $this->corsConfiguration->isEnabled() && $this->corsConfiguration->isCredentialsAllowed();
+ }
+}
diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php
new file mode 100644
index 0000000000000..68760de543daa
--- /dev/null
+++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowHeadersHeaderProvider.php
@@ -0,0 +1,71 @@
+corsConfiguration = $corsConfiguration;
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * Get name of header
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->headerName;
+ }
+
+ /**
+ * Check if header can be applied
+ *
+ * @return bool
+ */
+ public function canApply(): bool
+ {
+ return $this->corsConfiguration->isEnabled() && $this->getValue();
+ }
+
+ /**
+ * Get value for header
+ *
+ * @return string|null
+ */
+ public function getValue(): ?string
+ {
+ return $this->corsConfiguration->getAllowedHeaders();
+ }
+}
diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php
new file mode 100644
index 0000000000000..233839b9deb74
--- /dev/null
+++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowMethodsHeaderProvider.php
@@ -0,0 +1,71 @@
+corsConfiguration = $corsConfiguration;
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * Get name of header
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->headerName;
+ }
+
+ /**
+ * Check if header can be applied
+ *
+ * @return bool
+ */
+ public function canApply(): bool
+ {
+ return $this->corsConfiguration->isEnabled() && $this->getValue();
+ }
+
+ /**
+ * Get value for header
+ *
+ * @return string|null
+ */
+ public function getValue(): ?string
+ {
+ return $this->corsConfiguration->getAllowedMethods();
+ }
+}
diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php
new file mode 100644
index 0000000000000..21850f18db1f2
--- /dev/null
+++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsAllowOriginHeaderProvider.php
@@ -0,0 +1,71 @@
+corsConfiguration = $corsConfiguration;
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * Get name of header
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->headerName;
+ }
+
+ /**
+ * Check if header can be applied
+ *
+ * @return bool
+ */
+ public function canApply(): bool
+ {
+ return $this->corsConfiguration->isEnabled() && $this->getValue();
+ }
+
+ /**
+ * Get value for header
+ *
+ * @return string|null
+ */
+ public function getValue(): ?string
+ {
+ return $this->corsConfiguration->getAllowedOrigins();
+ }
+}
diff --git a/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php
new file mode 100644
index 0000000000000..e30209ae25e68
--- /dev/null
+++ b/app/code/Magento/GraphQl/Controller/HttpResponse/Cors/CorsMaxAgeHeaderProvider.php
@@ -0,0 +1,71 @@
+corsConfiguration = $corsConfiguration;
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * Get name of header
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->headerName;
+ }
+
+ /**
+ * Check if header can be applied
+ *
+ * @return bool
+ */
+ public function canApply(): bool
+ {
+ return $this->corsConfiguration->isEnabled() && $this->getValue();
+ }
+
+ /**
+ * Get value for header
+ *
+ * @return string|null
+ */
+ public function getValue(): ?string
+ {
+ return (string) $this->corsConfiguration->getMaxAge();
+ }
+}
diff --git a/app/code/Magento/GraphQl/Model/Cors/Configuration.php b/app/code/Magento/GraphQl/Model/Cors/Configuration.php
new file mode 100644
index 0000000000000..dd5a0b426e22d
--- /dev/null
+++ b/app/code/Magento/GraphQl/Model/Cors/Configuration.php
@@ -0,0 +1,96 @@
+scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Are CORS headers enabled
+ *
+ * @return bool
+ */
+ public function isEnabled(): bool
+ {
+ return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_HEADERS_ENABLED);
+ }
+
+ /**
+ * Get allowed origins or null if stored configuration is empty
+ *
+ * @return string|null
+ */
+ public function getAllowedOrigins(): ?string
+ {
+ return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_ORIGINS);
+ }
+
+ /**
+ * Get allowed headers or null if stored configuration is empty
+ *
+ * @return string|null
+ */
+ public function getAllowedHeaders(): ?string
+ {
+ return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_HEADERS);
+ }
+
+ /**
+ * Get allowed methods or null if stored configuration is empty
+ *
+ * @return string|null
+ */
+ public function getAllowedMethods(): ?string
+ {
+ return $this->scopeConfig->getValue(self::XML_PATH_CORS_ALLOWED_METHODS);
+ }
+
+ /**
+ * Get max age header value
+ *
+ * @return int
+ */
+ public function getMaxAge(): int
+ {
+ return (int) $this->scopeConfig->getValue(self::XML_PATH_CORS_MAX_AGE);
+ }
+
+ /**
+ * Are credentials allowed
+ *
+ * @return bool
+ */
+ public function isCredentialsAllowed(): bool
+ {
+ return $this->scopeConfig->isSetFlag(self::XML_PATH_CORS_ALLOW_CREDENTIALS);
+ }
+}
diff --git a/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php
new file mode 100644
index 0000000000000..b40b64f48e51f
--- /dev/null
+++ b/app/code/Magento/GraphQl/Model/Cors/ConfigurationInterface.php
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ service
+ Magento_Integration::config_oauth
+
+
+
+
+ Magento\Config\Model\Config\Source\Yesno
+
+
+
+
+ The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin. Fill this field with one or more origins (comma separated) or use '*' to allow access from all origins.
+
+ 1
+
+
+
+
+
+ The Access-Control-Allow-Methods response header specifies the method or methods allowed when accessing the resource in response to a preflight request. Use comma separated methods (e.g. GET,POST)
+
+ 1
+
+
+
+
+
+
+
+ validate-digits
+ The Access-Control-Max-Age response header indicates how long the results of a preflight request (that is the information contained in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can be cached.
+
+ 1
+
+
+
+
+
+ Magento\Config\Model\Config\Source\Yesno
+ The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to frontend code when the request's credentials mode is include.
+
+ 1
+
+
+
+
+
+
diff --git a/app/code/Magento/GraphQl/etc/config.xml b/app/code/Magento/GraphQl/etc/config.xml
new file mode 100644
index 0000000000000..39caacbec42d2
--- /dev/null
+++ b/app/code/Magento/GraphQl/etc/config.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ 0
+
+
+
+ 86400
+ 0
+
+
+
+
diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml
index b356f33c4f4bf..fca6c425e2507 100644
--- a/app/code/Magento/GraphQl/etc/di.xml
+++ b/app/code/Magento/GraphQl/etc/di.xml
@@ -98,4 +98,31 @@
300
+
+
+
+
+ Access-Control-Max-Age
+
+
+
+
+ Access-Control-Allow-Credentials
+
+
+
+
+ Access-Control-Allow-Headers
+
+
+
+
+ Access-Control-Allow-Methods
+
+
+
+
+ Access-Control-Allow-Origin
+
+
diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml
index 77fce336374dd..23d49124d1a02 100644
--- a/app/code/Magento/GraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/GraphQl/etc/graphql/di.xml
@@ -30,4 +30,15 @@
+
+
+
+ - Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowOriginHeaderProvider
+ - Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowHeadersHeaderProvider
+ - Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowMethodsHeaderProvider
+ - Magento\GraphQl\Controller\HttpResponse\Cors\CorsAllowCredentialsHeaderProvider
+ - Magento\GraphQl\Controller\HttpResponse\Cors\CorsMaxAgeHeaderProvider
+
+
+
diff --git a/app/code/Magento/GroupedProductGraphQl/Model/GroupedProductLinksTypeResolver.php b/app/code/Magento/GroupedProductGraphQl/Model/GroupedProductLinksTypeResolver.php
index 92cfb375fea41..29fa2bffabb3b 100644
--- a/app/code/Magento/GroupedProductGraphQl/Model/GroupedProductLinksTypeResolver.php
+++ b/app/code/Magento/GroupedProductGraphQl/Model/GroupedProductLinksTypeResolver.php
@@ -10,7 +10,7 @@
use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface;
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
class GroupedProductLinksTypeResolver implements TypeResolverInterface
{
@@ -20,14 +20,14 @@ class GroupedProductLinksTypeResolver implements TypeResolverInterface
private $linkTypes = ['associated'];
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
- public function resolveType(array $data) : string
+ public function resolveType(array $data): string
{
if (isset($data['link_type'])) {
$linkType = $data['link_type'];
if (in_array($linkType, $this->linkTypes)) {
- return 'GroupedProductLinks';
+ return 'ProductLinks';
}
}
return '';
diff --git a/app/code/Magento/GroupedProductGraphQl/etc/di.xml b/app/code/Magento/GroupedProductGraphQl/etc/di.xml
index 35b63370baf2f..717bc14826f70 100644
--- a/app/code/Magento/GroupedProductGraphQl/etc/di.xml
+++ b/app/code/Magento/GroupedProductGraphQl/etc/di.xml
@@ -13,4 +13,11 @@
+
+
+
+ - associated
+
+
+
diff --git a/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php b/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php
index 09b17371ae4e8..f5b62df9aea2c 100644
--- a/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php
+++ b/app/code/Magento/ImportExport/Model/Export/Adapter/Csv.php
@@ -54,6 +54,19 @@ public function destruct()
{
if (is_object($this->_fileHandler)) {
$this->_fileHandler->close();
+ $this->resolveDestination();
+ }
+ }
+
+ /**
+ * Remove temporary destination
+ *
+ * @return void
+ */
+ private function resolveDestination(): void
+ {
+ // only temporary file located directly in var folder
+ if (strpos($this->_destination, '/') === false) {
$this->_directoryHandle->delete($this->_destination);
}
}
diff --git a/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php b/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php
new file mode 100644
index 0000000000000..1565d455cc43f
--- /dev/null
+++ b/app/code/Magento/MediaContentCatalog/Observer/CategoryDelete.php
@@ -0,0 +1,120 @@
+extractAssetsFromContent = $extractAssetsFromContent;
+ $this->getContent = $getContent;
+ $this->deleteContentAssetLinks = $deleteContentAssetLinks;
+ $this->contentAssetLinkFactory = $contentAssetLinkFactory;
+ $this->contentIdentityFactory = $contentIdentityFactory;
+ $this->fields = $fields;
+ }
+
+ /**
+ * Retrieve the deleted category and remove relation betwen category and asset
+ *
+ * @param Observer $observer
+ * @throws \Exception
+ */
+ public function execute(Observer $observer): void
+ {
+ $category = $observer->getEvent()->getData('category');
+ $contentAssetLinks = [];
+
+ if ($category instanceof CatalogCategory) {
+ foreach ($this->fields as $field) {
+ $contentIdentity = $this->contentIdentityFactory->create(
+ [
+ self::TYPE => self::CONTENT_TYPE,
+ self::FIELD => $field,
+ self::ENTITY_ID => (string) $category->getEntityId(),
+ ]
+ );
+ $content = implode(PHP_EOL, $this->getContent->execute($contentIdentity));
+ $assets = $this->extractAssetsFromContent->execute($content);
+
+ foreach ($assets as $asset) {
+ $contentAssetLinks[] = $this->contentAssetLinkFactory->create(
+ [
+ 'assetId' => $asset->getId(),
+ 'contentIdentity' => $contentIdentity
+ ]
+ );
+ }
+ }
+ if (!empty($contentAssetLinks)) {
+ $this->deleteContentAssetLinks->execute($contentAssetLinks);
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php b/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php
new file mode 100644
index 0000000000000..421bb5a33fa1d
--- /dev/null
+++ b/app/code/Magento/MediaContentCatalog/Observer/ProductDelete.php
@@ -0,0 +1,120 @@
+extractAssetsFromContent = $extractAssetsFromContent;
+ $this->getContent = $getContent;
+ $this->deleteContentAssetLinks = $deleteContentAssetLinks;
+ $this->contentAssetLinkFactory = $contentAssetLinkFactory;
+ $this->contentIdentityFactory = $contentIdentityFactory;
+ $this->fields = $fields;
+ }
+
+ /**
+ * Retrieve the deleted product and remove relation betwen product and asset
+ *
+ * @param Observer $observer
+ * @throws \Exception
+ */
+ public function execute(Observer $observer): void
+ {
+ $product = $observer->getEvent()->getData('product');
+ $contentAssetLinks = [];
+
+ if ($product instanceof CatalogProduct) {
+ foreach ($this->fields as $field) {
+ $contentIdentity = $this->contentIdentityFactory->create(
+ [
+ self::TYPE => self::CONTENT_TYPE,
+ self::FIELD => $field,
+ self::ENTITY_ID => (string) $product->getEntityId(),
+ ]
+ );
+ $productContent = implode(PHP_EOL, $this->getContent->execute($contentIdentity));
+ $assets = $this->extractAssetsFromContent->execute($productContent);
+
+ foreach ($assets as $asset) {
+ $contentAssetLinks[] = $this->contentAssetLinkFactory->create(
+ [
+ 'assetId' => $asset->getId(),
+ 'contentIdentity' => $contentIdentity
+ ]
+ );
+ }
+ }
+ if (!empty($contentAssetLinks)) {
+ $this->deleteContentAssetLinks->execute($contentAssetLinks);
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/MediaContentCatalog/etc/di.xml b/app/code/Magento/MediaContentCatalog/etc/di.xml
index a2d300a2bb208..6b0ee83b30788 100644
--- a/app/code/Magento/MediaContentCatalog/etc/di.xml
+++ b/app/code/Magento/MediaContentCatalog/etc/di.xml
@@ -14,6 +14,22 @@
+
+
+
+ - description
+ - short_description
+
+
+
+
+
+
+ - image
+ - description
+
+
+
diff --git a/app/code/Magento/MediaContentCatalog/etc/events.xml b/app/code/Magento/MediaContentCatalog/etc/events.xml
index f68d66eb3cc40..8ec7a30b961ba 100644
--- a/app/code/Magento/MediaContentCatalog/etc/events.xml
+++ b/app/code/Magento/MediaContentCatalog/etc/events.xml
@@ -9,6 +9,12 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/MediaContentCms/Observer/BlockDelete.php b/app/code/Magento/MediaContentCms/Observer/BlockDelete.php
new file mode 100644
index 0000000000000..582f0a9ec6701
--- /dev/null
+++ b/app/code/Magento/MediaContentCms/Observer/BlockDelete.php
@@ -0,0 +1,119 @@
+extractAssetsFromContent = $extractAssetsFromContent;
+ $this->getContent = $getContent;
+ $this->deleteContentAssetLinks = $deleteContentAssetLinks;
+ $this->contentAssetLinkFactory = $contentAssetLinkFactory;
+ $this->contentIdentityFactory = $contentIdentityFactory;
+ $this->fields = $fields;
+ }
+
+ /**
+ * Retrieve the deleted category and remove relation betwen category and asset
+ *
+ * @param Observer $observer
+ * @throws \Exception
+ */
+ public function execute(Observer $observer): void
+ {
+ $block = $observer->getEvent()->getData('object');
+ $contentAssetLinks = [];
+
+ if ($block instanceof CmsBlock) {
+ foreach ($this->fields as $field) {
+ $contentIdentity = $this->contentIdentityFactory->create(
+ [
+ self::TYPE => self::CONTENT_TYPE,
+ self::FIELD => $field,
+ self::ENTITY_ID => (string) $block->getId(),
+ ]
+ );
+ $assets = $this->extractAssetsFromContent->execute((string) $block->getData($field));
+
+ foreach ($assets as $asset) {
+ $contentAssetLinks[] = $this->contentAssetLinkFactory->create(
+ [
+ 'assetId' => $asset->getId(),
+ 'contentIdentity' => $contentIdentity
+ ]
+ );
+ }
+ }
+ if (!empty($contentAssetLinks)) {
+ $this->deleteContentAssetLinks->execute($contentAssetLinks);
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/MediaContentCms/Observer/PageDelete.php b/app/code/Magento/MediaContentCms/Observer/PageDelete.php
new file mode 100644
index 0000000000000..96d2bf89873bd
--- /dev/null
+++ b/app/code/Magento/MediaContentCms/Observer/PageDelete.php
@@ -0,0 +1,120 @@
+extractAssetsFromContent = $extractAssetsFromContent;
+ $this->getContent = $getContent;
+ $this->deleteContentAssetLinks = $deleteContentAssetLinks;
+ $this->contentAssetLinkFactory = $contentAssetLinkFactory;
+ $this->contentIdentityFactory = $contentIdentityFactory;
+ $this->fields = $fields;
+ }
+
+ /**
+ * Retrieve the deleted category and remove relation betwen category and asset
+ *
+ * @param Observer $observer
+ * @throws \Exception
+ */
+ public function execute(Observer $observer): void
+ {
+ $page = $observer->getEvent()->getData('object');
+ $contentAssetLinks = [];
+
+ if ($page instanceof CmsPage) {
+ foreach ($this->fields as $field) {
+ $contentIdentity = $this->contentIdentityFactory->create(
+ [
+ self::TYPE => self::CONTENT_TYPE,
+ self::FIELD => $field,
+ self::ENTITY_ID => (string) $page->getId(),
+ ]
+ );
+
+ $assets = $this->extractAssetsFromContent->execute((string) $page->getData($field));
+
+ foreach ($assets as $asset) {
+ $contentAssetLinks[] = $this->contentAssetLinkFactory->create(
+ [
+ 'assetId' => $asset->getId(),
+ 'contentIdentity' => $contentIdentity
+ ]
+ );
+ }
+ }
+ if (!empty($contentAssetLinks)) {
+ $this->deleteContentAssetLinks->execute($contentAssetLinks);
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/MediaContentCms/etc/di.xml b/app/code/Magento/MediaContentCms/etc/di.xml
index f980936465faf..6f196889540af 100644
--- a/app/code/Magento/MediaContentCms/etc/di.xml
+++ b/app/code/Magento/MediaContentCms/etc/di.xml
@@ -20,4 +20,18 @@
+
+
+
+ - content
+
+
+
+
+
+
+ - content
+
+
+
diff --git a/app/code/Magento/MediaContentCms/etc/events.xml b/app/code/Magento/MediaContentCms/etc/events.xml
index 7e9abe3bf19c4..94f963f40be15 100644
--- a/app/code/Magento/MediaContentCms/etc/events.xml
+++ b/app/code/Magento/MediaContentCms/etc/events.xml
@@ -6,8 +6,14 @@
*/
-->
+
+
+
+
+
+
diff --git a/app/code/Magento/Payment/Block/Transparent/Redirect.php b/app/code/Magento/Payment/Block/Transparent/Redirect.php
index 1be6dec4cc1d8..97a09df38d120 100644
--- a/app/code/Magento/Payment/Block/Transparent/Redirect.php
+++ b/app/code/Magento/Payment/Block/Transparent/Redirect.php
@@ -53,10 +53,21 @@ public function getRedirectUrl(): string
/**
* Returns params to be redirected.
*
+ * Encodes invalid UTF-8 values to UTF-8 to prevent character escape error.
+ * Some payment methods like PayPal, send data in merchant defined language encoding
+ * which can be different from the system character encoding (UTF-8).
+ *
* @return array
*/
public function getPostParams(): array
{
- return (array)$this->_request->getPostValue();
+ $params = [];
+ foreach ($this->_request->getPostValue() as $name => $value) {
+ if (!empty($value) && mb_detect_encoding($value, 'UTF-8', true) === false) {
+ $value = utf8_encode($value);
+ }
+ $params[$name] = $value;
+ }
+ return $params;
}
}
diff --git a/app/code/Magento/Payment/Test/Unit/Block/Transparent/RedirectTest.php b/app/code/Magento/Payment/Test/Unit/Block/Transparent/RedirectTest.php
new file mode 100644
index 0000000000000..1cd1230a14634
--- /dev/null
+++ b/app/code/Magento/Payment/Test/Unit/Block/Transparent/RedirectTest.php
@@ -0,0 +1,102 @@
+context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class);
+ $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class);
+ $this->context->method('getRequest')
+ ->willReturn($this->request);
+ $this->url = $this->createMock(\Magento\Framework\UrlInterface::class);
+ $this->model = new Redirect(
+ $this->context,
+ $this->url
+ );
+ }
+
+ /**
+ * @param array $postData
+ * @param array $expected
+ * @dataProvider getPostParamsDataProvider
+ */
+ public function testGetPostParams(array $postData, array $expected): void
+ {
+ $this->request->method('getPostValue')
+ ->willReturn($postData);
+ $this->assertEquals($expected, $this->model->getPostParams());
+ }
+
+ /**
+ * @return array
+ */
+ public function getPostParamsDataProvider(): array
+ {
+ return [
+ [
+ [
+ 'BILLTOEMAIL' => 'john.doe@magento.lo',
+ 'BILLTOSTREET' => '3640 Holdrege Ave',
+ 'BILLTOZIP' => '90016',
+ 'BILLTOLASTNAME' => 'Ãtienne',
+ 'BILLTOFIRSTNAME' => 'Ãillin',
+ ],
+ [
+ 'BILLTOEMAIL' => 'john.doe@magento.lo',
+ 'BILLTOSTREET' => '3640 Holdrege Ave',
+ 'BILLTOZIP' => '90016',
+ 'BILLTOLASTNAME' => 'Ãtienne',
+ 'BILLTOFIRSTNAME' => 'Ãillin',
+ ]
+ ],
+ [
+ [
+ 'BILLTOEMAIL' => 'john.doe@magento.lo',
+ 'BILLTOSTREET' => '3640 Holdrege Ave',
+ 'BILLTOZIP' => '90016',
+ 'BILLTOLASTNAME' => mb_convert_encoding('Ãtienne', 'ISO-8859-1'),
+ 'BILLTOFIRSTNAME' => mb_convert_encoding('Ãillin', 'ISO-8859-1'),
+ ],
+ [
+ 'BILLTOEMAIL' => 'john.doe@magento.lo',
+ 'BILLTOSTREET' => '3640 Holdrege Ave',
+ 'BILLTOZIP' => '90016',
+ 'BILLTOLASTNAME' => 'Ãtienne',
+ 'BILLTOFIRSTNAME' => 'Ãillin',
+ ]
+ ]
+ ];
+ }
+}
diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote.php b/app/code/Magento/Quote/Model/ResourceModel/Quote.php
index 48945dacd1738..749e9944a6ad3 100644
--- a/app/code/Magento/Quote/Model/ResourceModel/Quote.php
+++ b/app/code/Magento/Quote/Model/ResourceModel/Quote.php
@@ -230,7 +230,8 @@ public function subtractProductFromQuotes($product)
'items_qty' => new \Zend_Db_Expr(
$connection->quoteIdentifier('q.items_qty') . ' - ' . $connection->quoteIdentifier('qi.qty')
),
- 'items_count' => new \Zend_Db_Expr($ifSql)
+ 'items_count' => new \Zend_Db_Expr($ifSql),
+ 'updated_at' => 'q.updated_at',
]
)->join(
['qi' => $this->getTable('quote_item')],
@@ -277,21 +278,27 @@ public function markQuotesRecollect($productIds)
{
$tableQuote = $this->getTable('quote');
$tableItem = $this->getTable('quote_item');
- $subSelect = $this->getConnection()->select()->from(
- $tableItem,
- ['entity_id' => 'quote_id']
- )->where(
- 'product_id IN ( ? )',
- $productIds
- )->group(
- 'quote_id'
- );
-
- $select = $this->getConnection()->select()->join(
- ['t2' => $subSelect],
- 't1.entity_id = t2.entity_id',
- ['trigger_recollect' => new \Zend_Db_Expr('1')]
- );
+ $subSelect = $this->getConnection()
+ ->select()
+ ->from(
+ $tableItem,
+ ['entity_id' => 'quote_id']
+ )->where(
+ 'product_id IN ( ? )',
+ $productIds
+ )->group(
+ 'quote_id'
+ );
+ $select = $this->getConnection()
+ ->select()
+ ->join(
+ ['t2' => $subSelect],
+ 't1.entity_id = t2.entity_id',
+ [
+ 'trigger_recollect' => new \Zend_Db_Expr('1'),
+ 'updated_at' => 't1.updated_at',
+ ]
+ );
$updateQuery = $select->crossUpdateFromSelect(['t1' => $tableQuote]);
$this->getConnection()->query($updateQuery);
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
index f73daa715c1df..e959c19a7cbe4 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
@@ -51,7 +51,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s
$shippingAddressInput = current($shippingAddressesInput) ?? [];
$customerAddressId = $shippingAddressInput['customer_address_id'] ?? null;
- if (!$customerAddressId && !isset($shippingAddressInput['address']['save_in_address_book'])) {
+ if (!$customerAddressId
+ && isset($shippingAddressInput['address'])
+ && !isset($shippingAddressInput['address']['save_in_address_book'])
+ ) {
$shippingAddressInput['address']['save_in_address_book'] = true;
}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php
index b2526bdc04e98..654a4bb558632 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php
@@ -42,12 +42,12 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s
}
$shippingMethodInput = current($shippingMethodsInput);
- if (!isset($shippingMethodInput['carrier_code']) || empty($shippingMethodInput['carrier_code'])) {
+ if (empty($shippingMethodInput['carrier_code'])) {
throw new GraphQlInputException(__('Required parameter "carrier_code" is missing.'));
}
$carrierCode = $shippingMethodInput['carrier_code'];
- if (!isset($shippingMethodInput['method_code']) || empty($shippingMethodInput['method_code'])) {
+ if (empty($shippingMethodInput['method_code'])) {
throw new GraphQlInputException(__('Required parameter "method_code" is missing.'));
}
$methodCode = $shippingMethodInput['method_code'];
diff --git a/app/code/Magento/QuoteGraphQl/Model/CartItem/DataProvider/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/CartItem/DataProvider/UpdateCartItems.php
new file mode 100644
index 0000000000000..c2e94b215956e
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/CartItem/DataProvider/UpdateCartItems.php
@@ -0,0 +1,157 @@
+cartItemRepository = $cartItemRepository;
+ $this->updateCartItem = $updateCartItem;
+ $this->itemRepository = $itemRepository;
+ $this->giftMessageHelper = $giftMessageHelper;
+ $this->giftMessageFactory = $giftMessageFactory;
+ }
+
+ /**
+ * Process cart items
+ *
+ * @param Quote $cart
+ * @param array $items
+ *
+ * @throws GraphQlInputException
+ * @throws LocalizedException
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ * @SuppressWarnings(PHPMD.NPathComplexity)
+ */
+ public function processCartItems(Quote $cart, array $items): void
+ {
+ foreach ($items as $item) {
+ if (empty($item['cart_item_id'])) {
+ throw new GraphQlInputException(__('Required parameter "cart_item_id" for "cart_items" is missing.'));
+ }
+
+ $itemId = (int)$item['cart_item_id'];
+ $customizableOptions = $item['customizable_options'] ?? [];
+ $cartItem = $cart->getItemById($itemId);
+
+ if ($cartItem && $cartItem->getParentItemId()) {
+ throw new GraphQlInputException(__('Child items may not be updated.'));
+ }
+
+ if (count($customizableOptions) === 0 && !isset($item['quantity'])) {
+ throw new GraphQlInputException(__('Required parameter "quantity" for "cart_items" is missing.'));
+ }
+
+ $quantity = (float)$item['quantity'];
+
+ if ($quantity <= 0.0) {
+ $this->cartItemRepository->deleteById((int)$cart->getId(), $itemId);
+ } else {
+ $this->updateCartItem->execute($cart, $itemId, $quantity, $customizableOptions);
+ }
+
+ if (!empty($item['gift_message'])) {
+ try {
+ if (!$this->giftMessageHelper->isMessagesAllowed('items', $cartItem)) {
+ continue;
+ }
+ if (!$this->giftMessageHelper->isMessagesAllowed('item', $cartItem)) {
+ continue;
+ }
+
+ /** @var MessageInterface $giftItemMessage */
+ $giftItemMessage = $this->itemRepository->get($cart->getEntityId(), $itemId);
+
+ if (empty($giftItemMessage)) {
+ /** @var MessageInterface $giftMessage */
+ $giftMessage = $this->giftMessageFactory->create();
+ $this->updateGiftMessageForItem($cart, $giftMessage, $item, $itemId);
+ continue;
+ }
+ } catch (LocalizedException $exception) {
+ throw new GraphQlInputException(__('Gift Message cannot be updated.'));
+ }
+
+ $this->updateGiftMessageForItem($cart, $giftItemMessage, $item, $itemId);
+ }
+ }
+ }
+
+ /**
+ * Update Gift Message for Quote item
+ *
+ * @param Quote $cart
+ * @param MessageInterface $giftItemMessage
+ * @param array $item
+ * @param int $itemId
+ *
+ * @throws GraphQlInputException
+ */
+ private function updateGiftMessageForItem(Quote $cart, MessageInterface $giftItemMessage, array $item, int $itemId)
+ {
+ try {
+ $giftItemMessage->setRecipient($item['gift_message']['to']);
+ $giftItemMessage->setSender($item['gift_message']['from']);
+ $giftItemMessage->setMessage($item['gift_message']['message']);
+ $this->itemRepository->save($cart->getEntityId(), $giftItemMessage, $itemId);
+ } catch (LocalizedException $exception) {
+ throw new GraphQlInputException(__('Gift Message cannot be updated'));
+ }
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
index dd4ce8fe7f7a6..c2e4bfa44c9bb 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php
@@ -71,14 +71,15 @@ public function __construct(
*/
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
- if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) {
+ if (empty($args['input']['cart_id'])) {
throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
}
- $maskedCartId = $args['input']['cart_id'];
- if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) {
+ if (empty($args['input']['payment_method']['code'])) {
throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.'));
}
+
+ $maskedCartId = $args['input']['cart_id'];
$paymentData = $args['input']['payment_method'];
$storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php
index fa90f08e4b553..005baaad0e1e5 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php
@@ -14,53 +14,43 @@
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\Quote\Api\CartItemRepositoryInterface;
use Magento\Quote\Api\CartRepositoryInterface;
-use Magento\Quote\Model\Quote;
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
-use Magento\QuoteGraphQl\Model\Cart\UpdateCartItem;
+use Magento\QuoteGraphQl\Model\CartItem\DataProvider\UpdateCartItems as UpdateCartItemsProvider;
/**
* @inheritdoc
*/
class UpdateCartItems implements ResolverInterface
{
- /**
- * @var UpdateCartItem
- */
- private $updateCartItem;
-
/**
* @var GetCartForUser
*/
private $getCartForUser;
/**
- * @var CartItemRepositoryInterface
+ * @var CartRepositoryInterface
*/
- private $cartItemRepository;
+ private $cartRepository;
/**
- * @var CartRepositoryInterface
+ * @var UpdateCartItemsProvider
*/
- private $cartRepository;
+ private $updateCartItems;
/**
- * @param GetCartForUser $getCartForUser
- * @param CartItemRepositoryInterface $cartItemRepository
- * @param UpdateCartItem $updateCartItem
+ * @param GetCartForUser $getCartForUser
* @param CartRepositoryInterface $cartRepository
+ * @param UpdateCartItemsProvider $updateCartItems
*/
public function __construct(
GetCartForUser $getCartForUser,
- CartItemRepositoryInterface $cartItemRepository,
- UpdateCartItem $updateCartItem,
- CartRepositoryInterface $cartRepository
+ CartRepositoryInterface $cartRepository,
+ UpdateCartItemsProvider $updateCartItems
) {
$this->getCartForUser = $getCartForUser;
- $this->cartItemRepository = $cartItemRepository;
- $this->updateCartItem = $updateCartItem;
$this->cartRepository = $cartRepository;
+ $this->updateCartItems = $updateCartItems;
}
/**
@@ -71,6 +61,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
if (empty($args['input']['cart_id'])) {
throw new GraphQlInputException(__('Required parameter "cart_id" is missing.'));
}
+
$maskedCartId = $args['input']['cart_id'];
if (empty($args['input']['cart_items'])
@@ -78,13 +69,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
) {
throw new GraphQlInputException(__('Required parameter "cart_items" is missing.'));
}
- $cartItems = $args['input']['cart_items'];
+ $cartItems = $args['input']['cart_items'];
$storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
$cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
try {
- $this->processCartItems($cart, $cartItems);
+ $this->updateCartItems->processCartItems($cart, $cartItems);
$this->cartRepository->save($cart);
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
@@ -98,39 +89,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
],
];
}
-
- /**
- * Process cart items
- *
- * @param Quote $cart
- * @param array $items
- * @throws GraphQlInputException
- * @throws LocalizedException
- */
- private function processCartItems(Quote $cart, array $items): void
- {
- foreach ($items as $item) {
- if (empty($item['cart_item_id'])) {
- throw new GraphQlInputException(__('Required parameter "cart_item_id" for "cart_items" is missing.'));
- }
- $itemId = (int)$item['cart_item_id'];
- $customizableOptions = $item['customizable_options'] ?? [];
-
- $cartItem = $cart->getItemById($itemId);
- if ($cartItem && $cartItem->getParentItemId()) {
- throw new GraphQlInputException(__('Child items may not be updated.'));
- }
-
- if (count($customizableOptions) === 0 && !isset($item['quantity'])) {
- throw new GraphQlInputException(__('Required parameter "quantity" for "cart_items" is missing.'));
- }
- $quantity = (float)$item['quantity'];
-
- if ($quantity <= 0.0) {
- $this->cartItemRepository->deleteById((int)$cart->getId(), $itemId);
- } else {
- $this->updateCartItem->execute($cart, $itemId, $quantity, $customizableOptions);
- }
- }
- }
}
diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json
index 0652d39b5f426..25f089cf75a62 100644
--- a/app/code/Magento/QuoteGraphQl/composer.json
+++ b/app/code/Magento/QuoteGraphQl/composer.json
@@ -13,7 +13,8 @@
"magento/module-customer-graph-ql": "*",
"magento/module-sales": "*",
"magento/module-directory": "*",
- "magento/module-graph-ql": "*"
+ "magento/module-graph-ql": "*",
+ "magento/module-gift-message": "*"
},
"suggest": {
"magento/module-graph-ql-cache": "*"
diff --git a/app/code/Magento/Sales/Model/Order/ItemRepository.php b/app/code/Magento/Sales/Model/Order/ItemRepository.php
index 6e029ac468370..345fffc414fbc 100644
--- a/app/code/Magento/Sales/Model/Order/ItemRepository.php
+++ b/app/code/Magento/Sales/Model/Order/ItemRepository.php
@@ -167,10 +167,7 @@ public function deleteById($id)
public function save(OrderItemInterface $entity)
{
if ($entity->getProductOption()) {
- $request = $this->getBuyRequest($entity);
- $productOptions = $entity->getProductOptions();
- $productOptions['info_buyRequest'] = $request->toArray();
- $entity->setProductOptions($productOptions);
+ $entity->setProductOptions($this->getItemProductOptions($entity));
}
$this->metadata->getMapper()->save($entity);
@@ -178,6 +175,23 @@ public function save(OrderItemInterface $entity)
return $this->registry[$entity->getEntityId()];
}
+ /**
+ * Return product options
+ *
+ * @param OrderItemInterface $entity
+ * @return array
+ */
+ private function getItemProductOptions(OrderItemInterface $entity): array
+ {
+ $request = $this->getBuyRequest($entity);
+ $productOptions = $entity->getProductOptions();
+ $productOptions['info_buyRequest'] = $productOptions && !empty($productOptions['info_buyRequest'])
+ ? array_merge($productOptions['info_buyRequest'], $request->toArray())
+ : $request->toArray();
+
+ return $productOptions;
+ }
+
/**
* Set parent item.
*
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
index 253dbd43fa580..6ddbce49829eb 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
@@ -80,11 +80,9 @@ public function draw()
$lines = [];
// draw Product name
- $lines[0] = [
- [
+ $lines[0][] = [
'text' => $this->string->split($this->prepareText((string)$item->getName()), 35, true, true),
'feed' => 35
- ]
];
// draw SKU
diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/OrderItem/DataProvider.php b/app/code/Magento/SalesGraphQl/Model/Resolver/OrderItem/DataProvider.php
index 219ea45a330ca..20cdd7313b8ad 100644
--- a/app/code/Magento/SalesGraphQl/Model/Resolver/OrderItem/DataProvider.php
+++ b/app/code/Magento/SalesGraphQl/Model/Resolver/OrderItem/DataProvider.php
@@ -225,9 +225,9 @@ private function getDiscountDetails(OrderInterface $associatedOrder, OrderItemIn
$discounts = [];
} else {
$discounts [] = [
- 'label' => $associatedOrder->getDiscountDescription() ?? _('Discount'),
+ 'label' => $associatedOrder->getDiscountDescription() ?? __('Discount'),
'amount' => [
- 'value' => $orderItem->getDiscountAmount() ?? 0,
+ 'value' => abs($orderItem->getDiscountAmount()) ?? 0,
'currency' => $associatedOrder->getOrderCurrencyCode()
]
];
diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/OrderTotal.php b/app/code/Magento/SalesGraphQl/Model/Resolver/OrderTotal.php
index 6251a7dd07c7d..6f7b943bf6ca2 100644
--- a/app/code/Magento/SalesGraphQl/Model/Resolver/OrderTotal.php
+++ b/app/code/Magento/SalesGraphQl/Model/Resolver/OrderTotal.php
@@ -11,9 +11,11 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\Sales\Api\Data\OrderExtensionInterface;
use Magento\Sales\Api\Data\OrderInterface;
+/**
+ * Resolve order totals taxes and discounts for order
+ */
class OrderTotal implements ResolverInterface
{
/**
@@ -33,21 +35,13 @@ public function resolve(
/** @var OrderInterface $order */
$order = $value['model'];
$currency = $order->getOrderCurrencyCode();
- $extensionAttributes = $order->getExtensionAttributes();
-
- $allAppliedTaxesForItemsData = $this->getAllAppliedTaxesForItems(
- $extensionAttributes->getItemAppliedTaxes() ?? []
- );
- $appliedShippingTaxesForItemsData = $this->getAppliedShippingTaxesForItems(
- $extensionAttributes->getItemAppliedTaxes() ?? []
- );
return [
'base_grand_total' => ['value' => $order->getBaseGrandTotal(), 'currency' => $currency],
'grand_total' => ['value' => $order->getGrandTotal(), 'currency' => $currency],
'subtotal' => ['value' => $order->getSubtotal(), 'currency' => $currency],
'total_tax' => ['value' => $order->getTaxAmount(), 'currency' => $currency],
- 'taxes' => $this->getAppliedTaxesDetails($order, $allAppliedTaxesForItemsData),
+ 'taxes' => $this->getAppliedTaxesDetails($order),
'discounts' => $this->getDiscountDetails($order),
'total_shipping' => ['value' => $order->getShippingAmount(), 'currency' => $currency],
'shipping_handling' => [
@@ -63,54 +57,55 @@ public function resolve(
'value' => $order->getShippingAmount(),
'currency' => $currency
],
- 'taxes' => $this->getAppliedTaxesDetails($order, $appliedShippingTaxesForItemsData),
+ 'taxes' => $this->getAppliedShippingTaxesDetails($order),
'discounts' => $this->getShippingDiscountDetails($order),
]
];
}
/**
- * Retrieve applied taxes that apply to items
+ * Retrieve applied taxes that apply to the order
*
- * @param \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface[] $itemAppliedTaxes
+ * @param OrderInterface $order
* @return array
*/
- private function getAllAppliedTaxesForItems(array $itemAppliedTaxes): array
+ private function getAllAppliedTaxesOnOrders(OrderInterface $order): array
{
- $allAppliedTaxesForItemsData = [];
- foreach ($itemAppliedTaxes as $taxItemIndex => $appliedTaxForItem) {
- foreach ($appliedTaxForItem->getAppliedTaxes() ?? [] as $taxLineItem) {
- $allAppliedTaxesForItemsData[$taxItemIndex][$taxItemIndex] = [
- 'title' => $taxLineItem->getDataByKey('title'),
- 'percent' => $taxLineItem->getDataByKey('percent'),
- 'amount' => $taxLineItem->getDataByKey('amount'),
- ];
- }
+ $extensionAttributes = $order->getExtensionAttributes();
+ $appliedTaxes = $extensionAttributes->getAppliedTaxes() ?? [];
+ $allAppliedTaxOnOrders = [];
+ foreach ($appliedTaxes as $taxIndex => $appliedTaxesData) {
+ $allAppliedTaxOnOrders[$taxIndex] = [
+ 'title' => $appliedTaxesData->getDataByKey('title'),
+ 'percent' => $appliedTaxesData->getDataByKey('percent'),
+ 'amount' => $appliedTaxesData->getDataByKey('amount'),
+ ];
}
- return $allAppliedTaxesForItemsData;
+ return $allAppliedTaxOnOrders;
}
/**
- * Retrieve applied taxes that apply to shipping
+ * Return taxes applied to the current order
*
- * @param \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface $itemAppliedTaxes
+ * @param OrderInterface $order
* @return array
*/
- private function getAppliedShippingTaxesForItems(array $itemAppliedTaxes): array
+ private function getAppliedTaxesDetails(OrderInterface $order): array
{
- $appliedShippingTaxesForItemsData = [];
- foreach ($itemAppliedTaxes as $taxItemIndex => $appliedTaxForItem) {
- foreach ($appliedTaxForItem->getAppliedTaxes() ?? [] as $taxLineItem) {
- if ($appliedTaxForItem->getType() === "shipping") {
- $appliedShippingTaxesForItemsData[$taxItemIndex][$taxItemIndex] = [
- 'title' => $taxLineItem->getDataByKey('title'),
- 'percent' => $taxLineItem->getDataByKey('percent'),
- 'amount' => $taxLineItem->getDataByKey('amount')
- ];
- }
- }
+ $allAppliedTaxOnOrders = $this->getAllAppliedTaxesOnOrders($order);
+ $taxes = [];
+ foreach ($allAppliedTaxOnOrders as $appliedTaxes) {
+ $appliedTaxesArray = [
+ 'rate' => $appliedTaxes['percent'] ?? 0,
+ 'title' => $appliedTaxes['title'] ?? null,
+ 'amount' => [
+ 'value' => $appliedTaxes['amount'] ?? 0,
+ 'currency' => $order->getOrderCurrencyCode()
+ ]
+ ];
+ $taxes[] = $appliedTaxesArray;
}
- return $appliedShippingTaxesForItemsData;
+ return $taxes;
}
/**
@@ -119,66 +114,91 @@ private function getAppliedShippingTaxesForItems(array $itemAppliedTaxes): array
* @param OrderInterface $order
* @return array
*/
- private function getShippingDiscountDetails(OrderInterface $order)
+ private function getDiscountDetails(OrderInterface $order): array
{
- $shippingDiscounts = [];
- if (!($order->getDiscountDescription() === null && $order->getShippingDiscountAmount() == 0)) {
- $shippingDiscounts[] =
- [
- 'label' => $order->getDiscountDescription() ?? __('Discount'),
- 'amount' => [
- 'value' => $order->getShippingDiscountAmount(),
- 'currency' => $order->getOrderCurrencyCode()
- ]
- ];
+ $orderDiscounts = [];
+ if (!($order->getDiscountDescription() === null && $order->getDiscountAmount() == 0)) {
+ $orderDiscounts[] = [
+ 'label' => $order->getDiscountDescription() ?? __('Discount'),
+ 'amount' => [
+ 'value' => abs($order->getDiscountAmount()),
+ 'currency' => $order->getOrderCurrencyCode()
+ ]
+ ];
}
- return $shippingDiscounts;
+ return $orderDiscounts;
}
/**
- * Return information about an applied discount
+ * Retrieve applied shipping taxes on items for the orders
*
* @param OrderInterface $order
* @return array
*/
- private function getDiscountDetails(OrderInterface $order)
+ private function getAppliedShippingTaxesForItems(OrderInterface $order): array
{
- $discounts = [];
- if (!($order->getDiscountDescription() === null && $order->getDiscountAmount() == 0)) {
- $discounts[] = [
- 'label' => $order->getDiscountDescription() ?? __('Discount'),
+ $extensionAttributes = $order->getExtensionAttributes();
+ $itemAppliedTaxes = $extensionAttributes->getItemAppliedTaxes() ?? [];
+ $appliedShippingTaxesForItems = [];
+ foreach ($itemAppliedTaxes as $appliedTaxForItem) {
+ if ($appliedTaxForItem->getType() === "shipping") {
+ foreach ($appliedTaxForItem->getAppliedTaxes() ?? [] as $taxLineItem) {
+ $taxItemIndexTitle = $taxLineItem->getDataByKey('title');
+ $appliedShippingTaxesForItems[$taxItemIndexTitle] = [
+ 'title' => $taxLineItem->getDataByKey('title'),
+ 'percent' => $taxLineItem->getDataByKey('percent'),
+ 'amount' => $taxLineItem->getDataByKey('amount')
+ ];
+ }
+ }
+ }
+ return $appliedShippingTaxesForItems;
+ }
+
+ /**
+ * Return taxes applied to the current order
+ *
+ * @param OrderInterface $order
+ * @return array
+ */
+ private function getAppliedShippingTaxesDetails(
+ OrderInterface $order
+ ): array {
+ $appliedShippingTaxesForItems = $this->getAppliedShippingTaxesForItems($order);
+ $shippingTaxes = [];
+ foreach ($appliedShippingTaxesForItems as $appliedShippingTaxes) {
+ $appliedShippingTaxesArray = [
+ 'rate' => $appliedShippingTaxes['percent'] ?? 0,
+ 'title' => $appliedShippingTaxes['title'] ?? null,
'amount' => [
- 'value' => $order->getDiscountAmount(),
+ 'value' => $appliedShippingTaxes['amount'] ?? 0,
'currency' => $order->getOrderCurrencyCode()
]
];
+ $shippingTaxes[] = $appliedShippingTaxesArray;
}
- return $discounts;
+ return $shippingTaxes;
}
/**
- * Returns taxes applied to the current order
+ * Return information about an applied discount
*
* @param OrderInterface $order
- * @param array $appliedTaxesArray
* @return array
*/
- private function getAppliedTaxesDetails(OrderInterface $order, array $appliedTaxesArray): array
+ private function getShippingDiscountDetails(OrderInterface $order): array
{
- $taxes = [];
- foreach ($appliedTaxesArray as $appliedTaxesKeyIndex => $appliedTaxes) {
- $appliedTaxesArray = [
- 'title' => $appliedTaxes[$appliedTaxesKeyIndex]['title'] ?? null,
- 'amount' => [
- 'value' => $appliedTaxes[$appliedTaxesKeyIndex]['amount'] ?? 0,
- 'currency' => $order->getOrderCurrencyCode()
- ],
- ];
- if (!empty($appliedTaxes[$appliedTaxesKeyIndex])) {
- $appliedTaxesArray['rate'] = $appliedTaxes[$appliedTaxesKeyIndex]['percent'] ?? null;
- }
- $taxes[] = $appliedTaxesArray;
+ $shippingDiscounts = [];
+ if (!($order->getDiscountDescription() === null && $order->getShippingDiscountAmount() == 0)) {
+ $shippingDiscounts[] =
+ [
+ 'label' => $order->getDiscountDescription() ?? __('Discount'),
+ 'amount' => [
+ 'value' => abs($order->getShippingDiscountAmount()),
+ 'currency' => $order->getOrderCurrencyCode()
+ ]
+ ];
}
- return $taxes;
+ return $shippingDiscounts;
}
}
diff --git a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php
index 537fec4c75df6..8f6011f1ae56f 100644
--- a/app/code/Magento/Store/Api/Data/StoreConfigInterface.php
+++ b/app/code/Magento/Store/Api/Data/StoreConfigInterface.php
@@ -6,7 +6,7 @@
namespace Magento\Store\Api\Data;
/**
- * StoreConfig interface
+ * Interface for store config
*
* @api
* @since 100.0.2
@@ -141,7 +141,7 @@ public function setWeightUnit($weightUnit);
public function getBaseUrl();
/**
- * set base URL
+ * Set base URL
*
* @param string $baseUrl
* @return $this
@@ -201,7 +201,7 @@ public function setBaseMediaUrl($baseMediaUrl);
public function getSecureBaseUrl();
/**
- * set secure base URL
+ * Set secure base URL
*
* @param string $secureBaseUrl
* @return $this
diff --git a/app/code/Magento/Store/Model/Data/StoreConfig.php b/app/code/Magento/Store/Model/Data/StoreConfig.php
index 6634e2cb05bd9..e68d98b162613 100644
--- a/app/code/Magento/Store/Model/Data/StoreConfig.php
+++ b/app/code/Magento/Store/Model/Data/StoreConfig.php
@@ -6,7 +6,7 @@
namespace Magento\Store\Model\Data;
/**
- * Class StoreConfig
+ * Allows to get and set store config values
*
* @codeCoverageIgnore
*/
@@ -188,7 +188,7 @@ public function getBaseUrl()
}
/**
- * set base URL
+ * Set base URL
*
* @param string $baseUrl
* @return $this
@@ -293,7 +293,7 @@ public function getSecureBaseUrl()
}
/**
- * set secure base URL
+ * Set secure base URL
*
* @param string $secureBaseUrl
* @return $this
@@ -367,7 +367,7 @@ public function setSecureBaseMediaUrl($secureBaseMediaUrl)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @return \Magento\Store\Api\Data\StoreConfigExtensionInterface|null
*/
@@ -377,7 +377,7 @@ public function getExtensionAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*
* @param \Magento\Store\Api\Data\StoreConfigExtensionInterface $extensionAttributes
* @return $this
diff --git a/app/code/Magento/Store/Model/Service/StoreConfigManager.php b/app/code/Magento/Store/Model/Service/StoreConfigManager.php
index b3c2208a58361..debb08438a3b4 100644
--- a/app/code/Magento/Store/Model/Service/StoreConfigManager.php
+++ b/app/code/Magento/Store/Model/Service/StoreConfigManager.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Store\Model\Service;
+/**
+ * Allows to get store config
+ */
class StoreConfigManager implements \Magento\Store\Api\StoreConfigManagerInterface
{
/**
@@ -53,6 +56,8 @@ public function __construct(
}
/**
+ * Get store configs
+ *
* @param string[] $storeCodes list of stores by store codes, will return all if storeCodes is not set
* @return \Magento\Store\Api\Data\StoreConfigInterface[]
*/
@@ -71,6 +76,8 @@ public function getStoreConfigs(array $storeCodes = null)
}
/**
+ * Get store config
+ *
* @param \Magento\Store\Model\Store $store
* @return \Magento\Store\Api\Data\StoreConfigInterface
*/
diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml
index 5bd8f6e2349fc..2da9e91e1fddd 100644
--- a/app/code/Magento/Store/etc/di.xml
+++ b/app/code/Magento/Store/etc/di.xml
@@ -65,7 +65,6 @@
-
diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
index 59f9831789a35..0baee00f468a0 100644
--- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
+++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
@@ -55,11 +55,10 @@ public function __construct(
*/
public function getStoreConfigData(StoreInterface $store): array
{
- $storeConfigData = array_merge(
+ return array_merge(
$this->getBaseConfigData($store),
$this->getExtendedConfigData((int)$store->getId())
);
- return $storeConfigData;
}
/**
@@ -72,7 +71,7 @@ private function getBaseConfigData(StoreInterface $store) : array
{
$storeConfig = current($this->storeConfigManager->getStoreConfigs([$store->getCode()]));
- $storeConfigData = [
+ return [
'id' => $storeConfig->getId(),
'code' => $storeConfig->getCode(),
'website_id' => $storeConfig->getWebsiteId(),
@@ -88,9 +87,9 @@ private function getBaseConfigData(StoreInterface $store) : array
'secure_base_url' => $storeConfig->getSecureBaseUrl(),
'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(),
'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(),
- 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl()
+ 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl(),
+ 'store_name' => $store->getName()
];
- return $storeConfigData;
}
/**
diff --git a/app/code/Magento/StoreGraphQl/etc/graphql/di.xml b/app/code/Magento/StoreGraphQl/etc/graphql/di.xml
index f3771b704c3e9..3a0143821d8b9 100644
--- a/app/code/Magento/StoreGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/StoreGraphQl/etc/graphql/di.xml
@@ -23,11 +23,4 @@
-
-
-
- - store/information/name
-
-
-
diff --git a/lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php b/app/code/Magento/Theme/Plugin/LoadDesignPlugin.php
similarity index 88%
rename from lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php
rename to app/code/Magento/Theme/Plugin/LoadDesignPlugin.php
index 2cda49c43c2ce..c4f8d3a905d0f 100644
--- a/lib/internal/Magento/Framework/App/Action/Plugin/LoadDesignPlugin.php
+++ b/app/code/Magento/Theme/Plugin/LoadDesignPlugin.php
@@ -4,7 +4,7 @@
* See COPYING.txt for license details.
*/
-namespace Magento\Framework\App\Action\Plugin;
+namespace Magento\Theme\Plugin;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Config\Dom\ValidationException;
@@ -21,12 +21,12 @@ class LoadDesignPlugin
/**
* @var DesignLoader
*/
- protected $_designLoader;
+ private $designLoader;
/**
* @var MessageManagerInterface
*/
- protected $messageManager;
+ private $messageManager;
/**
* @param DesignLoader $designLoader
@@ -36,7 +36,7 @@ public function __construct(
DesignLoader $designLoader,
MessageManagerInterface $messageManager
) {
- $this->_designLoader = $designLoader;
+ $this->designLoader = $designLoader;
$this->messageManager = $messageManager;
}
@@ -50,7 +50,7 @@ public function __construct(
public function beforeExecute(ActionInterface $subject)
{
try {
- $this->_designLoader->load();
+ $this->designLoader->load();
} catch (LocalizedException $e) {
if ($e->getPrevious() instanceof ValidationException) {
/** @var MessageInterface $message */
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php b/app/code/Magento/Theme/Test/Unit/Plugin/LoadDesignPluginTest.php
similarity index 80%
rename from lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php
rename to app/code/Magento/Theme/Test/Unit/Plugin/LoadDesignPluginTest.php
index 549d45a986cf0..4efcc584986d1 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Action/Plugin/LoadDesignPluginTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Plugin/LoadDesignPluginTest.php
@@ -3,15 +3,13 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-declare(strict_types=1);
-
-namespace Magento\Framework\App\Test\Unit\Action\Plugin;
+namespace Magento\Theme\Test\Unit\Plugin;
use Magento\Framework\App\Action\Action;
-use Magento\Framework\App\Action\Plugin\LoadDesignPlugin;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Message\ManagerInterface;
use Magento\Framework\View\DesignLoader;
+use Magento\Theme\Plugin\LoadDesignPlugin;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -26,7 +24,7 @@ public function testBeforeExecute()
$designLoaderMock = $this->createMock(DesignLoader::class);
/** @var MockObject|ManagerInterface $messageManagerMock */
- $messageManagerMock = $this->getMockForAbstractClass(ManagerInterface::class);
+ $messageManagerMock = $this->createMock(ManagerInterface::class);
$plugin = new LoadDesignPlugin($designLoaderMock, $messageManagerMock);
diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml
index c4da1f860870e..15107adb931c9 100644
--- a/app/code/Magento/Theme/etc/di.xml
+++ b/app/code/Magento/Theme/etc/di.xml
@@ -105,6 +105,9 @@
Magento\Store\Model\ScopeInterface::SCOPE_STORE
+
+
+
diff --git a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml
index 55798169cdf75..b42cabde6cd85 100644
--- a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml
+++ b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml
@@ -14,7 +14,6 @@