diff --git a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml index c68313211c2e6..06fd380cb2a44 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml +++ b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml @@ -11,7 +11,7 @@ notificationGrid - Magento\AdminNotification\Model\ResourceModel\Grid\Collection + Magento\AdminNotification\Model\ResourceModel\Grid\Collection DESC date_added 1 diff --git a/app/code/Magento/AdvancedSearch/view/adminhtml/layout/catalog_search_block.xml b/app/code/Magento/AdvancedSearch/view/adminhtml/layout/catalog_search_block.xml index b6ef596281e51..f3544863348ec 100644 --- a/app/code/Magento/AdvancedSearch/view/adminhtml/layout/catalog_search_block.xml +++ b/app/code/Magento/AdvancedSearch/view/adminhtml/layout/catalog_search_block.xml @@ -11,7 +11,7 @@ catalog_search_grid - Magento\AdvancedSearch\Model\ResourceModel\Search\Grid\Collection + Magento\AdvancedSearch\Model\ResourceModel\Search\Grid\Collection name ASC 1 diff --git a/app/code/Magento/Authorization/Model/Role.php b/app/code/Magento/Authorization/Model/Role.php index 2546df86d09dd..dcc46ee77ee12 100644 --- a/app/code/Magento/Authorization/Model/Role.php +++ b/app/code/Magento/Authorization/Model/Role.php @@ -51,19 +51,29 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritDoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $properties = parent::__sleep(); return array_diff($properties, ['_resource', '_resourceCollection']); } /** - * {@inheritdoc} + * @inheritDoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->_resource = $objectManager->get(\Magento\Authorization\Model\ResourceModel\Role::class); diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php index 593b4219d45f0..61db71c1803e2 100644 --- a/app/code/Magento/Backend/Model/Auth/Session.php +++ b/app/code/Magento/Backend/Model/Auth/Session.php @@ -5,21 +5,25 @@ */ namespace Magento\Backend\Model\Auth; +use Magento\Framework\Acl; +use Magento\Framework\AclFactory; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Backend\Spi\SessionUserHydratorInterface; +use Magento\Backend\Spi\SessionAclHydratorInterface; +use Magento\User\Model\User; +use Magento\User\Model\UserFactory; /** * Backend Auth session model * * @api - * @method \Magento\User\Model\User|null getUser() - * @method \Magento\Backend\Model\Auth\Session setUser(\Magento\User\Model\User $value) - * @method \Magento\Framework\Acl|null getAcl() - * @method \Magento\Backend\Model\Auth\Session setAcl(\Magento\Framework\Acl $value) * @method int getUpdatedAt() * @method \Magento\Backend\Model\Auth\Session setUpdatedAt(int $value) * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @todo implement solution that keeps is_first_visit flag in session during redirects * @api * @since 100.0.2 @@ -55,6 +59,36 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage */ protected $_config; + /** + * @var SessionUserHydratorInterface + */ + private $userHydrator; + + /** + * @var SessionAclHydratorInterface + */ + private $aclHydrator; + + /** + * @var UserFactory + */ + private $userFactory; + + /** + * @var AclFactory + */ + private $aclFactory; + + /** + * @var User|null + */ + private $user; + + /** + * @var Acl|null + */ + private $acl; + /** * @param \Magento\Framework\App\Request\Http $request * @param \Magento\Framework\Session\SidResolverInterface $sidResolver @@ -69,6 +103,10 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage * @param \Magento\Backend\Model\UrlInterface $backendUrl * @param \Magento\Backend\App\ConfigInterface $config * @throws \Magento\Framework\Exception\SessionException + * @param SessionUserHydratorInterface|null $userHydrator + * @param SessionAclHydratorInterface|null $aclHydrator + * @param UserFactory|null $userFactory + * @param AclFactory|null $aclFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -83,11 +121,19 @@ public function __construct( \Magento\Framework\App\State $appState, \Magento\Framework\Acl\Builder $aclBuilder, \Magento\Backend\Model\UrlInterface $backendUrl, - \Magento\Backend\App\ConfigInterface $config + \Magento\Backend\App\ConfigInterface $config, + ?SessionUserHydratorInterface $userHydrator = null, + ?SessionAclHydratorInterface $aclHydrator = null, + ?UserFactory $userFactory = null, + ?AclFactory $aclFactory = null ) { $this->_config = $config; $this->_aclBuilder = $aclBuilder; $this->_backendUrl = $backendUrl; + $this->userHydrator = $userHydrator ?? ObjectManager::getInstance()->get(SessionUserHydratorInterface::class); + $this->aclHydrator = $aclHydrator ?? ObjectManager::getInstance()->get(SessionAclHydratorInterface::class); + $this->userFactory = $userFactory ?? ObjectManager::getInstance()->get(UserFactory::class); + $this->aclFactory = $aclFactory ?? ObjectManager::getInstance()->get(AclFactory::class); parent::__construct( $request, $sidResolver, @@ -230,6 +276,16 @@ public function processLogin() return $this; } + /** + * @inheritDoc + */ + public function destroy(array $options = null) + { + $this->user = null; + $this->acl = null; + parent::destroy($options); + } + /** * Process of configuring of current auth storage when logout was performed * @@ -253,4 +309,136 @@ public function isValidForPath($path) { return true; } + + /** + * Logged-in user. + * + * @return User|null + */ + public function getUser() + { + if (!$this->user) { + $userData = $this->getUserData(); + if ($userData) { + /** @var User $user */ + $user = $this->userFactory->create(); + $this->userHydrator->hydrate($user, $userData); + $this->user = $user; + } + } + + return $this->user; + } + + /** + * Set logged-in user instance. + * + * @param User|null $user + * @return Session + */ + public function setUser($user) + { + $this->setUserData(null); + if ($user) { + $this->setUserData($this->userHydrator->extract($user)); + } + $this->user = $user; + + return $this; + } + + /** + * Is user logged in? + * + * @return bool + */ + public function hasUser() + { + return $this->user || $this->hasUserData(); + } + + /** + * Remove logged-in user. + * + * @return Session + */ + public function unsUser() + { + $this->user = null; + return $this->unsUserData(); + } + + /** + * Logged-in user's ACL data. + * + * @return Acl|null + */ + public function getAcl() + { + if (!$this->acl) { + $aclData = $this->getUserAclData(); + if ($aclData) { + /** @var Acl $acl */ + $acl = $this->aclFactory->create(); + $this->aclHydrator->hydrate($acl, $aclData); + $this->acl = $acl; + } + } + + return $this->acl; + } + + /** + * Set logged-in user's ACL data instance. + * + * @param Acl|null $acl + * @return Session + */ + public function setAcl($acl) + { + $this->setUserAclData(null); + if ($acl) { + $this->setUserAclData($this->aclHydrator->extract($acl)); + } + $this->acl = $acl; + + return $this; + } + + /** + * Whether ACL data is present. + * + * @return bool + */ + public function hasAcl() + { + return $this->acl || $this->hasUserAclData(); + } + + /** + * Remove ACL data. + * + * @return Session + */ + public function unsAcl() + { + $this->acl = null; + return $this->unsUserAclData(); + } + + /** + * @inheritDoc + */ + public function writeClose() + { + //Updating data in session in case these objects has been changed. + if ($this->user) { + $this->setUser($this->user); + } + if ($this->acl) { + $this->setAcl($this->acl); + } + + parent::writeClose(); + } } diff --git a/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php new file mode 100644 index 0000000000000..34e01be696672 --- /dev/null +++ b/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php @@ -0,0 +1,36 @@ + $acl->_rules, 'resources' => $acl->_resources, 'roles' => $acl->_roleRegistry]; + } + + /** + * @inheritDoc + */ + public function hydrate(Acl $target, array $data): void + { + $target->_rules = $data['rules']; + $target->_resources = $data['resources']; + $target->_roleRegistry = $data['roles']; + } +} diff --git a/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php new file mode 100644 index 0000000000000..6dee8b7b302c8 --- /dev/null +++ b/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php @@ -0,0 +1,54 @@ +roleFactory = $roleFactory; + } + + /** + * @inheritDoc + */ + public function extract(User $user): array + { + return ['data' => $user->getData(), 'role_data' => $user->getRole()->getData()]; + } + + /** + * @inheritDoc + */ + public function hydrate(User $target, array $data): void + { + $target->setData($data['data']); + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->setData($data['role_data']); + $target->setData('extracted_role', $role); + $target->getRole(); + } +} diff --git a/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php b/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php new file mode 100644 index 0000000000000..7227cc92fcc8e --- /dev/null +++ b/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php @@ -0,0 +1,34 @@ +cookieMetadataFactory = $this->createPartialMock( - \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory::class, - ['createPublicCookieMetadata'] - ); - - $this->config = $this->createPartialMock(\Magento\Backend\App\Config::class, ['getValue']); - $this->cookieManager = $this->createPartialMock( - \Magento\Framework\Stdlib\Cookie\PhpCookieManager::class, - ['getCookie', 'setPublicCookie'] - ); - $this->storage = $this->createPartialMock( - \Magento\Framework\Session\Storage::class, - ['getUser', 'getAcl', 'setAcl'] - ); - $this->sessionConfig = $this->createPartialMock( - \Magento\Framework\Session\Config::class, - ['getCookiePath', 'getCookieDomain', 'getCookieSecure', 'getCookieHttpOnly'] - ); - $this->aclBuilder = $this->getMockBuilder(\Magento\Framework\Acl\Builder::class) - ->disableOriginalConstructor() - ->getMock(); - $objectManager = new ObjectManager($this); - $this->session = $objectManager->getObject( - \Magento\Backend\Model\Auth\Session::class, - [ - 'config' => $this->config, - 'sessionConfig' => $this->sessionConfig, - 'cookieManager' => $this->cookieManager, - 'cookieMetadataFactory' => $this->cookieMetadataFactory, - 'storage' => $this->storage, - 'aclBuilder' => $this->aclBuilder - ] - ); - } - - protected function tearDown() - { - $this->config = null; - $this->sessionConfig = null; - $this->session = null; - } - - /** - * @dataProvider refreshAclDataProvider - * @param $isUserPassedViaParams - */ - public function testRefreshAcl($isUserPassedViaParams) - { - $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock(); - $this->aclBuilder->expects($this->any())->method('getAcl')->willReturn($aclMock); - $userMock = $this->getMockBuilder(\Magento\User\Model\User::class) - ->setMethods(['getReloadAclFlag', 'setReloadAclFlag', 'unsetData', 'save']) - ->disableOriginalConstructor() - ->getMock(); - $userMock->expects($this->any())->method('getReloadAclFlag')->willReturn(true); - $userMock->expects($this->once())->method('setReloadAclFlag')->with('0')->willReturnSelf(); - $userMock->expects($this->once())->method('save'); - $this->storage->expects($this->once())->method('setAcl')->with($aclMock); - $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock); - if ($isUserPassedViaParams) { - $this->session->refreshAcl($userMock); - } else { - $this->storage->expects($this->once())->method('getUser')->willReturn($userMock); - $this->session->refreshAcl(); - } - $this->assertSame($aclMock, $this->session->getAcl()); - } - - /** - * @return array - */ - public function refreshAclDataProvider() - { - return [ - 'User set via params' => [true], - 'User set to session object' => [false] - ]; - } - - public function testIsLoggedInPositive() - { - $user = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', '__wakeup']); - $user->expects($this->once()) - ->method('getId') - ->will($this->returnValue(1)); - - $this->storage->expects($this->any()) - ->method('getUser') - ->will($this->returnValue($user)); - - $this->assertTrue($this->session->isLoggedIn()); - } - - public function testProlong() - { - $name = session_name(); - $cookie = 'cookie'; - $lifetime = 900; - $path = '/'; - $domain = 'magento2'; - $secure = true; - $httpOnly = true; - - $this->config->expects($this->once()) - ->method('getValue') - ->with(\Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME) - ->willReturn($lifetime); - $cookieMetadata = $this->createMock(\Magento\Framework\Stdlib\Cookie\PublicCookieMetadata::class); - $cookieMetadata->expects($this->once()) - ->method('setDuration') - ->with($lifetime) - ->will($this->returnSelf()); - $cookieMetadata->expects($this->once()) - ->method('setPath') - ->with($path) - ->will($this->returnSelf()); - $cookieMetadata->expects($this->once()) - ->method('setDomain') - ->with($domain) - ->will($this->returnSelf()); - $cookieMetadata->expects($this->once()) - ->method('setSecure') - ->with($secure) - ->will($this->returnSelf()); - $cookieMetadata->expects($this->once()) - ->method('setHttpOnly') - ->with($httpOnly) - ->will($this->returnSelf()); - - $this->cookieMetadataFactory->expects($this->once()) - ->method('createPublicCookieMetadata') - ->will($this->returnValue($cookieMetadata)); - - $this->cookieManager->expects($this->once()) - ->method('getCookie') - ->with($name) - ->will($this->returnValue($cookie)); - $this->cookieManager->expects($this->once()) - ->method('setPublicCookie') - ->with($name, $cookie, $cookieMetadata); - - $this->sessionConfig->expects($this->once()) - ->method('getCookiePath') - ->will($this->returnValue($path)); - $this->sessionConfig->expects($this->once()) - ->method('getCookieDomain') - ->will($this->returnValue($domain)); - $this->sessionConfig->expects($this->once()) - ->method('getCookieSecure') - ->will($this->returnValue($secure)); - $this->sessionConfig->expects($this->once()) - ->method('getCookieHttpOnly') - ->will($this->returnValue($httpOnly)); - - $this->session->prolong(); - - $this->assertLessThanOrEqual(time(), $this->session->getUpdatedAt()); - } - - /** - * @dataProvider isAllowedDataProvider - * @param bool $isUserDefined - * @param bool $isAclDefined - * @param bool $isAllowed - * @param true $expectedResult - */ - public function testIsAllowed($isUserDefined, $isAclDefined, $isAllowed, $expectedResult) - { - $userAclRole = 'userAclRole'; - if ($isAclDefined) { - $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock(); - $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock); - } - if ($isUserDefined) { - $userMock = $this->getMockBuilder(\Magento\User\Model\User::class)->disableOriginalConstructor()->getMock(); - $this->storage->expects($this->once())->method('getUser')->willReturn($userMock); - } - if ($isAclDefined && $isUserDefined) { - $userMock->expects($this->any())->method('getAclRole')->willReturn($userAclRole); - $aclMock->expects($this->once())->method('isAllowed')->with($userAclRole)->willReturn($isAllowed); - } - - $this->assertEquals($expectedResult, $this->session->isAllowed('resource')); - } - - /** - * @return array - */ - public function isAllowedDataProvider() - { - return [ - "Negative: User not defined" => [false, true, true, false], - "Negative: Acl not defined" => [true, false, true, false], - "Negative: Permission denied" => [true, true, false, false], - "Positive: Permission granted" => [true, true, false, false], - ]; - } - - /** - * @dataProvider firstPageAfterLoginDataProvider - * @param bool $isFirstPageAfterLogin - */ - public function testFirstPageAfterLogin($isFirstPageAfterLogin) - { - $this->session->setIsFirstPageAfterLogin($isFirstPageAfterLogin); - $this->assertEquals($isFirstPageAfterLogin, $this->session->isFirstPageAfterLogin()); - } - - /** - * @return array - */ - public function firstPageAfterLoginDataProvider() - { - return [ - 'First page after login' => [true], - 'Not first page after login' => [false], - ]; - } -} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php deleted file mode 100644 index 5b3910e9445f8..0000000000000 --- a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php +++ /dev/null @@ -1,36 +0,0 @@ -_sessionMock = $this->createPartialMock( - \Magento\Backend\Model\Auth\Session::class, - ['getUser', 'getAclRole', 'hasUser'] - ); - $this->_model = new \Magento\Backend\Model\Authorization\RoleLocator($this->_sessionMock); - } - - public function testGetAclRoleIdReturnsCurrentUserAclRoleId() - { - $this->_sessionMock->expects($this->once())->method('hasUser')->will($this->returnValue(true)); - $this->_sessionMock->expects($this->once())->method('getUser')->will($this->returnSelf()); - $this->_sessionMock->expects($this->once())->method('getAclRole')->will($this->returnValue('some_role')); - $this->assertEquals('some_role', $this->_model->getAclRoleId()); - } -} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php deleted file mode 100644 index 77eb7cdb34d1f..0000000000000 --- a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php +++ /dev/null @@ -1,127 +0,0 @@ -_session = $this->createMock(\Magento\Backend\Model\Session::class); - - $this->_authSession = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['getUser']); - - $this->_backendConfig = $this->getMockForAbstractClass( - \Magento\Backend\App\ConfigInterface::class, - [], - '', - false - ); - - $userMock = new \Magento\Framework\DataObject(); - - $this->_authSession->expects($this->any())->method('getUser')->will($this->returnValue($userMock)); - - $this->_translator = $this->getMockBuilder(\Magento\Framework\TranslateInterface::class) - ->setMethods(['init', 'setLocale']) - ->getMockForAbstractClass(); - - $this->_translator->expects($this->any())->method('setLocale')->will($this->returnValue($this->_translator)); - - $this->_translator->expects($this->any())->method('init')->will($this->returnValue(false)); - - $this->_model = new \Magento\Backend\Model\Locale\Manager( - $this->_session, - $this->_authSession, - $this->_translator, - $this->_backendConfig - ); - } - - /** - * @return array - */ - public function switchBackendInterfaceLocaleDataProvider() - { - return ['case1' => ['locale' => 'de_DE'], 'case2' => ['locale' => 'en_US']]; - } - - /** - * @param string $locale - * @dataProvider switchBackendInterfaceLocaleDataProvider - * @covers \Magento\Backend\Model\Locale\Manager::switchBackendInterfaceLocale - */ - public function testSwitchBackendInterfaceLocale($locale) - { - $this->_model->switchBackendInterfaceLocale($locale); - - $userInterfaceLocale = $this->_authSession->getUser()->getInterfaceLocale(); - $this->assertEquals($userInterfaceLocale, $locale); - - $sessionLocale = $this->_session->getSessionLocale(); - $this->assertEquals($sessionLocale, null); - } - - /** - * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale - */ - public function testGetUserInterfaceLocaleDefault() - { - $locale = $this->_model->getUserInterfaceLocale(); - - $this->assertEquals($locale, Resolver::DEFAULT_LOCALE); - } - - /** - * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale - */ - public function testGetUserInterfaceLocale() - { - $this->_model->switchBackendInterfaceLocale('de_DE'); - $locale = $this->_model->getUserInterfaceLocale(); - - $this->assertEquals($locale, 'de_DE'); - } - - /** - * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale - */ - public function testGetUserInterfaceGeneralLocale() - { - $this->_backendConfig->expects($this->any()) - ->method('getValue') - ->with('general/locale/code') - ->willReturn('test_locale'); - $locale = $this->_model->getUserInterfaceLocale(); - $this->assertEquals($locale, 'test_locale'); - } -} diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index f9408768136bb..e54bd136b3494 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -22,6 +22,7 @@ "magento/module-store": "*", "magento/module-translation": "*", "magento/module-ui": "*", + "magento/module-authorization": "*", "magento/module-user": "*" }, "suggest": { diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 98b8e702b1c53..65744e56d94ac 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -182,6 +182,10 @@ Magento\Config\Model\Config\Source\Yesno Minification is not applied in developer mode. + + + Magento\Config\Model\Config\Source\Yesno + @@ -323,11 +327,11 @@ For Windows server only. - + Magento\Config\Model\Config\Source\Yesnocustom - + validate-email Magento\Config\Model\Config\Backend\Email\Address diff --git a/app/code/Magento/Backend/etc/di.xml b/app/code/Magento/Backend/etc/di.xml index c526703da9975..41db85b9323a8 100644 --- a/app/code/Magento/Backend/etc/di.xml +++ b/app/code/Magento/Backend/etc/di.xml @@ -198,4 +198,8 @@ Magento\Backend\Block\AnchorRenderer + + diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml index 50d210f71025b..6d2ecd8d36a99 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml @@ -11,7 +11,7 @@ cache_grid - Magento\Backend\Model\Cache\ResourceModel\Grid\Collection + Magento\Backend\Model\Cache\ResourceModel\Grid\Collection 0 diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml index b96614f4bd8db..41bfe6e78a2ad 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_design_grid_block.xml @@ -11,7 +11,7 @@ designGrid - Magento\Theme\Model\ResourceModel\Design\Collection + Magento\Theme\Model\ResourceModel\Design\Collection 1 1 diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml index 126de5eb4084f..d0c0d8fcbf69b 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_system_store_grid_block.xml @@ -12,7 +12,7 @@ storeGrid 1 - Magento\Store\Model\ResourceModel\Website\Grid\Collection + Magento\Store\Model\ResourceModel\Website\Grid\Collection diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index 90f6fa861b40f..aa6635b4dde4a 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -26,6 +26,7 @@ 1 + 1 Magento\Backup\Model\Config\Source\Type @@ -33,12 +34,14 @@ 1 + 1 1 + 1 Magento\Cron\Model\Config\Source\Frequency Magento\Backup\Model\Config\Backend\Cron @@ -48,6 +51,7 @@ Please put your store into maintenance mode during backup. 1 + 1 Magento\Config\Model\Config\Source\Yesno diff --git a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml index e17618b97e21f..e3e984d933f25 100644 --- a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml +++ b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml @@ -11,7 +11,7 @@ backupsGrid - Magento\Backup\Model\Fs\Collection + Magento\Backup\Model\Fs\Collection time desc diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index 2088bb5ea77cd..da3d99a8d2745 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -6,14 +6,28 @@ */ namespace Magento\Catalog\Controller\Category; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Design; use Magento\Catalog\Model\Layer\Resolver; use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; +use Magento\Catalog\Model\Session; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory; -use Magento\Framework\App\Action\Action; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; /** * View a category on storefront. Needs to be accessible by POST because of the store switching. @@ -25,41 +39,41 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; /** * Catalog session * - * @var \Magento\Catalog\Model\Session + * @var Session */ protected $_catalogSession; /** * Catalog design * - * @var \Magento\Catalog\Model\Design + * @var Design */ protected $_catalogDesign; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator + * @var CategoryUrlPathGenerator */ protected $categoryUrlPathGenerator; /** - * @var \Magento\Framework\View\Result\PageFactory + * @var PageFactory */ protected $resultPageFactory; /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ protected $resultForwardFactory; @@ -83,28 +97,28 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter /** * Constructor * - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Catalog\Model\Design $catalogDesign - * @param \Magento\Catalog\Model\Session $catalogSession - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory - * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + * @param Context $context + * @param Design $catalogDesign + * @param Session $catalogSession + * @param Registry $coreRegistry + * @param StoreManagerInterface $storeManager + * @param CategoryUrlPathGenerator $categoryUrlPathGenerator + * @param PageFactory $resultPageFactory + * @param ForwardFactory $resultForwardFactory * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository * @param ToolbarMemorizer|null $toolbarMemorizer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Catalog\Model\Design $catalogDesign, - \Magento\Catalog\Model\Session $catalogSession, - \Magento\Framework\Registry $coreRegistry, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, + Context $context, + Design $catalogDesign, + Session $catalogSession, + Registry $coreRegistry, + StoreManagerInterface $storeManager, + CategoryUrlPathGenerator $categoryUrlPathGenerator, PageFactory $resultPageFactory, - \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory, + ForwardFactory $resultForwardFactory, Resolver $layerResolver, CategoryRepositoryInterface $categoryRepository, ToolbarMemorizer $toolbarMemorizer = null @@ -125,7 +139,7 @@ public function __construct( /** * Initialize requested category object * - * @return \Magento\Catalog\Model\Category|bool + * @return Category|bool */ protected function _initCategory() { @@ -150,8 +164,8 @@ protected function _initCategory() 'catalog_controller_category_init_after', ['category' => $category, 'controller_action' => $this] ); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + } catch (LocalizedException $e) { + $this->_objectManager->get(LoggerInterface::class)->critical($e); return false; } @@ -161,13 +175,12 @@ protected function _initCategory() /** * Category view action * - * @return \Magento\Framework\Controller\ResultInterface - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) + * @return ResultInterface + * @throws NoSuchEntityException */ public function execute() { - if ($this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED)) { + if ($this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED)) { return $this->resultRedirectFactory->create()->setUrl($this->_redirect->getRedirectUrl()); } $category = $this->_initCategory(); @@ -188,29 +201,18 @@ public function execute() $page->getConfig()->setPageLayout($settings->getPageLayout()); } - $hasChildren = $category->hasChildren(); - if ($category->getIsAnchor()) { - $type = $hasChildren ? 'layered' : 'layered_without_children'; - } else { - $type = $hasChildren ? 'default' : 'default_without_children'; - } + $pageType = $this->getPageType($category); - if (!$hasChildren) { + if (!$category->hasChildren()) { // Two levels removed from parent. Need to add default page type. - $parentType = strtok($type, '_'); - $page->addPageLayoutHandles(['type' => $parentType], null, false); + $parentPageType = strtok($pageType, '_'); + $page->addPageLayoutHandles(['type' => $parentPageType], null, false); } - $page->addPageLayoutHandles(['type' => $type], null, false); + $page->addPageLayoutHandles(['type' => $pageType], null, false); $page->addPageLayoutHandles(['id' => $category->getId()]); // apply custom layout update once layout is loaded - $layoutUpdates = $settings->getLayoutUpdates(); - if ($layoutUpdates && is_array($layoutUpdates)) { - foreach ($layoutUpdates as $layoutUpdate) { - $page->addUpdate($layoutUpdate); - $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false); - } - } + $this->applyLayoutUpdates($page, $settings); $page->getConfig()->addBodyClass('page-products') ->addBodyClass('categorypath-' . $this->categoryUrlPathGenerator->getUrlPath($category)) @@ -221,4 +223,40 @@ public function execute() return $this->resultForwardFactory->create()->forward('noroute'); } } + + /** + * Get page type based on category + * + * @param Category $category + * @return string + */ + private function getPageType(Category $category) : string + { + $hasChildren = $category->hasChildren(); + if ($category->getIsAnchor()) { + return $hasChildren ? 'layered' : 'layered_without_children'; + } + + return $hasChildren ? 'default' : 'default_without_children'; + } + + /** + * Apply custom layout updates + * + * @param Page $page + * @param DataObject $settings + * @return void + */ + private function applyLayoutUpdates( + Page $page, + DataObject $settings + ) { + $layoutUpdates = $settings->getLayoutUpdates(); + if ($layoutUpdates && is_array($layoutUpdates)) { + foreach ($layoutUpdates as $layoutUpdate) { + $page->addUpdate($layoutUpdate); + $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false); + } + } + } } diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index d911bec0aaac9..a5aeb574f0a54 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -5,10 +5,13 @@ */ namespace Magento\Catalog\Model; +use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Convert\ConvertArray; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Profiler; @@ -211,6 +214,16 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements */ protected $metadataService; + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -233,6 +246,8 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param UserContextInterface|null $userContext + * @param AuthorizationInterface|null $authorization * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -256,7 +271,9 @@ public function __construct( CategoryRepositoryInterface $categoryRepository, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + ?UserContextInterface $userContext = null, + ?AuthorizationInterface $authorization = null ) { $this->metadataService = $metadataService; $this->_treeModel = $categoryTreeResource; @@ -281,6 +298,8 @@ public function __construct( $resourceCollection, $data ); + $this->userContext = $userContext ?? ObjectManager::getInstance()->get(UserContextInterface::class); + $this->authorization = $authorization ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); } /** @@ -311,6 +330,7 @@ protected function getCustomAttributesCodes() return $this->customAttributesCodes; } + // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod /** * Returns model resource * @@ -322,6 +342,7 @@ protected function _getResource() { return parent::_getResource(); } + // phpcs:enable /** * Get flat resource model flag @@ -914,6 +935,32 @@ public function beforeDelete() return parent::beforeDelete(); } + /** + * @inheritDoc + */ + public function beforeSave() + { + //Validate changing of design. + $userType = $this->userContext->getUserType(); + if (( + $userType === UserContextInterface::USER_TYPE_ADMIN + || $userType === UserContextInterface::USER_TYPE_INTEGRATION + ) + && !$this->authorization->isAllowed('Magento_Catalog::edit_category_design') + ) { + $this->getCustomAttributes(); + foreach ($this->_designAttributes as $attributeCode) { + $this->setData($attributeCode, $value = $this->getOrigData($attributeCode)); + if (array_key_exists($attributeCode, $this->_data[self::CUSTOM_ATTRIBUTES])) { + //In case custom attribute were used to update the entity. + $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]->setValue($value); + } + } + } + + return parent::beforeSave(); + } + /** * Retrieve anchors above * diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index a4127c9a97ffd..174c90ae1635c 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -25,6 +25,7 @@ use Magento\Ui\Component\Form\Field; use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; +use Magento\Framework\AuthorizationInterface; /** * Class DataProvider @@ -32,6 +33,7 @@ * @api * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) * @since 101.0.0 */ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider @@ -146,6 +148,11 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ private $fileInfo; + /** + * @var AuthorizationInterface + */ + private $auth; + /** * DataProvider constructor * @@ -162,6 +169,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param array $meta * @param array $data * @param PoolInterface|null $pool + * @param AuthorizationInterface|null $auth * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -177,7 +185,8 @@ public function __construct( CategoryFactory $categoryFactory, array $meta = [], array $data = [], - PoolInterface $pool = null + PoolInterface $pool = null, + ?AuthorizationInterface $auth = null ) { $this->eavValidationRules = $eavValidationRules; $this->collection = $categoryCollectionFactory->create(); @@ -187,6 +196,7 @@ public function __construct( $this->storeManager = $storeManager; $this->request = $request; $this->categoryFactory = $categoryFactory; + $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); } @@ -210,6 +220,8 @@ public function getMeta() } /** + * Disable fields if they are using default values. + * * @param Category $category * @param array $meta * @return array @@ -277,11 +289,20 @@ public function prepareMeta($meta) */ private function prepareFieldsMeta($fieldsMap, $fieldsMeta) { + $canEditDesign = $this->auth->isAllowed('Magento_Catalog::edit_category_design'); + $result = []; foreach ($fieldsMap as $fieldSet => $fields) { foreach ($fields as $field) { if (isset($fieldsMeta[$field])) { - $result[$fieldSet]['children'][$field]['arguments']['data']['config'] = $fieldsMeta[$field]; + $config = $fieldsMeta[$field]; + if (($fieldSet === 'design' || $fieldSet === 'schedule_design_update') && !$canEditDesign) { + $config['required'] = 1; + $config['disabled'] = 1; + $config['serviceDisabled'] = true; + } + + $result[$fieldSet]['children'][$field]['arguments']['data']['config'] = $config; } } } @@ -498,6 +519,7 @@ private function convertValues($category, $categoryData) $stat = $fileInfo->getStat($fileName); $mime = $fileInfo->getMimeType($fileName); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $categoryData[$attributeCode][0]['name'] = basename($fileName); if ($fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { @@ -533,6 +555,8 @@ public function getDefaultMetaData($result) } /** + * List of fields groups and fields. + * * @return array * @since 101.0.0 */ diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php index 7485d9f6cb247..a8636306f5e5b 100644 --- a/app/code/Magento/Catalog/Model/CategoryRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryRepository.php @@ -13,6 +13,8 @@ use Magento\Catalog\Api\Data\CategoryInterface; /** + * Repository for categories. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInterface @@ -70,7 +72,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category) { @@ -125,7 +127,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category) } /** - * {@inheritdoc} + * @inheritdoc */ public function get($categoryId, $storeId = null) { @@ -146,7 +148,7 @@ public function get($categoryId, $storeId = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(\Magento\Catalog\Api\Data\CategoryInterface $category) { @@ -167,7 +169,7 @@ public function delete(\Magento\Catalog\Api\Data\CategoryInterface $category) } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteByIdentifier($categoryId) { @@ -208,6 +210,8 @@ protected function validateCategory(Category $category) } /** + * Lazy loader for the converter. + * * @return \Magento\Framework\Api\ExtensibleDataObjectConverter * * @deprecated 101.0.0 @@ -222,6 +226,8 @@ private function getExtensibleDataObjectConverter() } /** + * Lazy loader for the metadata pool. + * * @return \Magento\Framework\EntityManager\MetadataPool */ private function getMetadataPool() diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 1e774e45df41f..d43bc51eb7273 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Model; +use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface; use Magento\Catalog\Api\Data\ProductInterface; @@ -14,6 +15,7 @@ use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\SaleableInterface; @@ -353,6 +355,16 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements */ private $filterCustomAttribute; + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -391,6 +403,8 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements * @param array $data * @param \Magento\Eav\Model\Config|null $config * @param FilterProductCustomAttribute|null $filterCustomAttribute + * @param UserContextInterface|null $userContext + * @param AuthorizationInterface|null $authorization * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -431,7 +445,9 @@ public function __construct( \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor, array $data = [], \Magento\Eav\Model\Config $config = null, - FilterProductCustomAttribute $filterCustomAttribute = null + FilterProductCustomAttribute $filterCustomAttribute = null, + ?UserContextInterface $userContext = null, + ?AuthorizationInterface $authorization = null ) { $this->metadataService = $metadataService; $this->_itemOptionFactory = $itemOptionFactory; @@ -473,6 +489,8 @@ public function __construct( $this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class); $this->filterCustomAttribute = $filterCustomAttribute ?? ObjectManager::getInstance()->get(FilterProductCustomAttribute::class); + $this->userContext = $userContext ?? ObjectManager::getInstance()->get(UserContextInterface::class); + $this->authorization = $authorization ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); } /** @@ -485,6 +503,7 @@ protected function _construct() $this->_init(\Magento\Catalog\Model\ResourceModel\Product::class); } + // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod /** * Get resource instance * @@ -496,6 +515,7 @@ protected function _getResource() { return parent::_getResource(); } + // phpcs:enable /** * Get a list of custom attribute codes that belongs to product attribute set. @@ -874,6 +894,22 @@ public function beforeSave() $this->getTypeInstance()->beforeSave($this); + //Validate changing of design. + $userType = $this->userContext->getUserType(); + if (( + $userType === UserContextInterface::USER_TYPE_ADMIN + || $userType === UserContextInterface::USER_TYPE_INTEGRATION + ) + && !$this->authorization->isAllowed('Magento_Catalog::edit_product_design') + ) { + $this->setData('custom_design', $this->getOrigData('custom_design')); + $this->setData('page_layout', $this->getOrigData('page_layout')); + $this->setData('options_container', $this->getOrigData('options_container')); + $this->setData('custom_layout_update', $this->getOrigData('custom_layout_update')); + $this->setData('custom_design_from', $this->getOrigData('custom_design_from')); + $this->setData('custom_design_to', $this->getOrigData('custom_design_to')); + } + $hasOptions = false; $hasRequiredOptions = false; @@ -1166,7 +1202,7 @@ public function getFormattedPrice() /** * Get formatted by currency product price * - * @return array|double + * @return array|double * * @deprecated * @see getFormattedPrice() @@ -1817,7 +1853,7 @@ public function formatUrlKey($str) /** * Save current attribute with code $code and assign new value * - * @param string $code Attribute code + * @param string $code Attribute code * @param mixed $value New attribute value * @param int $store Store ID * @return void diff --git a/app/code/Magento/Catalog/Model/Product/Copier.php b/app/code/Magento/Catalog/Model/Product/Copier.php index 53fa11df04b35..44ebdf0f1f283 100644 --- a/app/code/Magento/Catalog/Model/Product/Copier.php +++ b/app/code/Magento/Catalog/Model/Product/Copier.php @@ -1,7 +1,5 @@ setUpdatedAt(null); $duplicate->setId(null); $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); - $this->copyConstructor->build($product, $duplicate); - $isDuplicateSaved = false; - do { - $urlKey = $duplicate->getUrlKey(); - $urlKey = preg_match('/(.*)-(\d+)$/', $urlKey, $matches) - ? $matches[1] . '-' . ($matches[2] + 1) - : $urlKey . '-1'; - $duplicate->setUrlKey($urlKey); - $duplicate->setData('url_path', null); - try { - $duplicate->save(); - $isDuplicateSaved = true; - } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { - } - } while (!$isDuplicateSaved); + $this->setDefaultUrl($product, $duplicate); + $this->setStoresUrl($product, $duplicate); $this->getOptionRepository()->duplicate($product, $duplicate); $product->getResource()->duplicate( $product->getData($metadata->getLinkField()), @@ -98,6 +87,81 @@ public function copy(Product $product) return $duplicate; } + /** + * Set default URL. + * + * @param Product $product + * @param Product $duplicate + * @return void + */ + private function setDefaultUrl(Product $product, Product $duplicate) : void + { + $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $resource = $product->getResource(); + $attribute = $resource->getAttribute('url_key'); + $productId = $product->getId(); + $urlKey = $resource->getAttributeRawValue($productId, 'url_key', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + do { + $urlKey = $this->modifyUrl($urlKey); + $duplicate->setUrlKey($urlKey); + } while (!$attribute->getEntity()->checkAttributeUniqueValue($attribute, $duplicate)); + $duplicate->setData('url_path', null); + $duplicate->save(); + } + + /** + * Set URL for each store. + * + * @param Product $product + * @param Product $duplicate + * @return void + */ + private function setStoresUrl(Product $product, Product $duplicate) : void + { + $storeIds = $duplicate->getStoreIds(); + $productId = $product->getId(); + $productResource = $product->getResource(); + $defaultUrlKey = $productResource->getAttributeRawValue( + $productId, + 'url_key', + \Magento\Store\Model\Store::DEFAULT_STORE_ID + ); + $duplicate->setData('save_rewrites_history', false); + foreach ($storeIds as $storeId) { + $isDuplicateSaved = false; + $duplicate->setStoreId($storeId); + $urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId); + if ($urlKey === $defaultUrlKey) { + continue; + } + do { + $urlKey = $this->modifyUrl($urlKey); + $duplicate->setUrlKey($urlKey); + $duplicate->setData('url_path', null); + try { + $duplicate->save(); + $isDuplicateSaved = true; + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { + } + } while (!$isDuplicateSaved); + } + $duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + } + + /** + * Modify URL key. + * + * @param string $urlKey + * @return string + */ + private function modifyUrl(string $urlKey) : string + { + return preg_match('/(.*)-(\d+)$/', $urlKey, $matches) + ? $matches[1] . '-' . ($matches[2] + 1) + : $urlKey . '-1'; + } + /** * Returns product option repository. * diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 0e08b0af92862..c993e51c8bc09 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -71,8 +71,10 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $product->setMediaGalleryEntries($existingMediaGalleryEntries); try { $product = $this->productRepository->save($product); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (InputException $inputException) { throw $inputException; + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { throw new StateException(__("The product can't be saved.")); } @@ -105,7 +107,10 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) if ($existingEntry->getId() == $entry->getId()) { $found = true; - if ($entry->getFile()) { + + $file = $entry->getContent(); + + if ($file && $file->getBase64EncodedData() || $entry->getFile()) { $entry->setId(null); } $existingMediaGalleryEntries[$key] = $entry; diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 48f45d0ce9373..c87b6e9763205 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -1,18 +1,18 @@ productFactory = $productFactory; $this->collectionFactory = $collectionFactory; @@ -239,6 +257,8 @@ public function __construct( $this->cacheLimit = (int)$cacheLimit; $this->readExtensions = $readExtensions ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(ReadExtensions::class); + $this->linkManagement = $linkManagement ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(CategoryLinkManagementInterface::class); } /** @@ -381,6 +401,9 @@ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product /** * Process new gallery media entry. * + * @deprecated + * @see MediaGalleryProcessor::processNewMediaGalleryEntry() + * * @param ProductInterface $product * @param array $newEntry * @return $this @@ -392,40 +415,8 @@ protected function processNewMediaGalleryEntry( ProductInterface $product, array $newEntry ) { - /** @var ImageContentInterface $contentDataObject */ - $contentDataObject = $newEntry['content']; + $this->getMediaGalleryProcessor()->processNewMediaGalleryEntry($product, $newEntry); - /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */ - $mediaConfig = $product->getMediaConfig(); - $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath(); - - $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject); - $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath); - - if (!$product->hasGalleryAttribute()) { - throw new StateException( - __("The product that was requested doesn't exist. Verify the product and try again.") - ); - } - - $imageFileUri = $this->getMediaGalleryProcessor()->addImage( - $product, - $tmpFilePath, - isset($newEntry['types']) ? $newEntry['types'] : [], - true, - isset($newEntry['disabled']) ? $newEntry['disabled'] : true - ); - // Update additional fields that are still empty after addImage call - $this->getMediaGalleryProcessor()->updateImage( - $product, - $imageFileUri, - [ - 'label' => $newEntry['label'], - 'position' => $newEntry['position'], - 'disabled' => $newEntry['disabled'], - 'media_type' => $newEntry['media_type'], - ] - ); return $this; } @@ -500,68 +491,13 @@ private function processLinks(ProductInterface $product, $newLinks) * @return $this * @throws InputException * @throws StateException + * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function processMediaGallery(ProductInterface $product, $mediaGalleryEntries) { - $existingMediaGallery = $product->getMediaGallery('images'); - $newEntries = []; - $entriesById = []; - if (!empty($existingMediaGallery)) { - foreach ($mediaGalleryEntries as $entry) { - if (isset($entry['value_id'])) { - $entriesById[$entry['value_id']] = $entry; - } else { - $newEntries[] = $entry; - } - } - foreach ($existingMediaGallery as $key => &$existingEntry) { - if (isset($entriesById[$existingEntry['value_id']])) { - $updatedEntry = $entriesById[$existingEntry['value_id']]; - if ($updatedEntry['file'] === null) { - unset($updatedEntry['file']); - } - $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); - } else { - //set the removed flag - $existingEntry['removed'] = true; - } - } - $product->setData('media_gallery', ["images" => $existingMediaGallery]); - } else { - $newEntries = $mediaGalleryEntries; - } - - $images = (array)$product->getMediaGallery('images'); - $images = $this->determineImageRoles($product, $images); - - $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); - - foreach ($images as $image) { - if (!isset($image['removed']) && !empty($image['types'])) { - $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']); - } - } + $this->getMediaGalleryProcessor()->processMediaGallery($product, $mediaGalleryEntries); - foreach ($newEntries as $newEntry) { - if (!isset($newEntry['content'])) { - throw new InputException(__('The image content is invalid. Verify the content and try again.')); - } - /** @var ImageContentInterface $contentDataObject */ - $contentDataObject = $this->contentFactory->create() - ->setName($newEntry['content']['data'][ImageContentInterface::NAME]) - ->setBase64EncodedData($newEntry['content']['data'][ImageContentInterface::BASE64_ENCODED_DATA]) - ->setType($newEntry['content']['data'][ImageContentInterface::TYPE]); - $newEntry['content'] = $contentDataObject; - $this->processNewMediaGalleryEntry($product, $newEntry); - - $finalGallery = $product->getData('media_gallery'); - $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); - $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); - $entriesById[$newEntryId] = $newEntry; - $finalGallery['images'][$newEntryId] = $newEntry; - $product->setData('media_gallery', $finalGallery); - } return $this; } @@ -572,6 +508,7 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE */ public function save(ProductInterface $product, $saveOptions = false) { + $assignToCategories = false; $tierPrices = $product->getData('tier_price'); try { @@ -589,6 +526,7 @@ public function save(ProductInterface $product, $saveOptions = false) $extensionAttributes = $product->getExtensionAttributes(); if (empty($extensionAttributes->__toArray())) { $product->setExtensionAttributes($existingProduct->getExtensionAttributes()); + $assignToCategories = true; } } catch (NoSuchEntityException $e) { $existingProduct = null; @@ -626,6 +564,12 @@ public function save(ProductInterface $product, $saveOptions = false) } $this->saveProduct($product); + if ($assignToCategories === true && $product->getCategoryIds()) { + $this->linkManagement->assignProductToCategories( + $product->getSku(), + $product->getCategoryIds() + ); + } $this->removeProductFromLocalCache($product->getSku()); unset($this->instancesById[$product->getId()]); @@ -763,44 +707,19 @@ public function cleanCache() $this->instancesById = null; } - /** - * Ascertain image roles, if they are not set against the gallery entries - * - * @param ProductInterface $product - * @param array $images - * @return array - */ - private function determineImageRoles(ProductInterface $product, array $images) : array - { - $imagesWithRoles = []; - foreach ($images as $image) { - if (!isset($image['types'])) { - $image['types'] = []; - if (isset($image['file'])) { - foreach (array_keys($product->getMediaAttributes()) as $attribute) { - if ($image['file'] == $product->getData($attribute)) { - $image['types'][] = $attribute; - } - } - } - } - $imagesWithRoles[] = $image; - } - return $imagesWithRoles; - } - /** * Retrieve media gallery processor. * - * @return Product\Gallery\Processor + * @return MediaGalleryProcessor */ private function getMediaGalleryProcessor() { - if (null === $this->mediaGalleryProcessor) { - $this->mediaGalleryProcessor = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\Product\Gallery\Processor::class); + if (null === $this->mediaProcessor) { + $this->mediaProcessor = \Magento\Framework\App\ObjectManager::getInstance() + ->get(MediaGalleryProcessor::class); } - return $this->mediaGalleryProcessor; + + return $this->mediaProcessor; } /** @@ -912,6 +831,7 @@ private function saveProduct($product): void throw new CouldNotSaveException(__($e->getMessage())); } catch (LocalizedException $e) { throw $e; + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { throw new CouldNotSaveException( __('The product was unable to be saved. Please try again.'), diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php new file mode 100644 index 0000000000000..70311954f63e9 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -0,0 +1,239 @@ +processor = $processor; + $this->contentFactory = $contentFactory; + $this->imageProcessor = $imageProcessor; + } + + /** + * Process Media gallery data before save product. + * + * Compare Media Gallery Entries Data with existing Media Gallery + * * If Media entry has not value_id set it as new + * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag + * * Merge Existing and new media gallery + * + * @param ProductInterface $product contains only existing media gallery items + * @param array $mediaGalleryEntries array which contains all media gallery items + * @return void + * @throws InputException + * @throws StateException + * @throws LocalizedException + */ + public function processMediaGallery(ProductInterface $product, array $mediaGalleryEntries) :void + { + $existingMediaGallery = $product->getMediaGallery('images'); + $newEntries = []; + $entriesById = []; + if (!empty($existingMediaGallery)) { + foreach ($mediaGalleryEntries as $entry) { + if (isset($entry['value_id'])) { + $entriesById[$entry['value_id']] = $entry; + } else { + $newEntries[] = $entry; + } + } + foreach ($existingMediaGallery as $key => &$existingEntry) { + if (isset($entriesById[$existingEntry['value_id']])) { + $updatedEntry = $entriesById[$existingEntry['value_id']]; + if ($updatedEntry['file'] === null) { + unset($updatedEntry['file']); + } + $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + } else { + //set the removed flag + $existingEntry['removed'] = true; + } + } + $product->setData('media_gallery', ["images" => $existingMediaGallery]); + } else { + $newEntries = $mediaGalleryEntries; + } + + $images = (array)$product->getMediaGallery('images'); + $images = $this->determineImageRoles($product, $images); + + $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); + + $this->processMediaAttributes($product, $images); + $this->processEntries($product, $newEntries, $entriesById); + } + + /** + * Process new gallery media entry. + * + * @param ProductInterface $product + * @param array $newEntry + * @return void + * @throws InputException + * @throws StateException + * @throws LocalizedException + */ + public function processNewMediaGalleryEntry( + ProductInterface $product, + array $newEntry + ) :void { + /** @var ImageContentInterface $contentDataObject */ + $contentDataObject = $newEntry['content']; + + /** @var Config $mediaConfig */ + $mediaConfig = $product->getMediaConfig(); + $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath(); + + $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject); + $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath); + + if (!$product->hasGalleryAttribute()) { + throw new StateException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ); + } + + $imageFileUri = $this->processor->addImage( + $product, + $tmpFilePath, + isset($newEntry['types']) ? $newEntry['types'] : [], + true, + isset($newEntry['disabled']) ? $newEntry['disabled'] : true + ); + // Update additional fields that are still empty after addImage call + $this->processor->updateImage( + $product, + $imageFileUri, + [ + 'label' => $newEntry['label'], + 'position' => $newEntry['position'], + 'disabled' => $newEntry['disabled'], + 'media_type' => $newEntry['media_type'], + ] + ); + } + + /** + * Ascertain image roles, if they are not set against the gallery entries. + * + * @param ProductInterface $product + * @param array $images + * @return array + */ + private function determineImageRoles(ProductInterface $product, array $images) : array + { + $imagesWithRoles = []; + foreach ($images as $image) { + if (!isset($image['types'])) { + $image['types'] = []; + if (isset($image['file'])) { + foreach (array_keys($product->getMediaAttributes()) as $attribute) { + if ($image['file'] == $product->getData($attribute)) { + $image['types'][] = $attribute; + } + } + } + } + $imagesWithRoles[] = $image; + } + + return $imagesWithRoles; + } + + /** + * Convert entries into product media gallery data and set to product. + * + * @param ProductInterface $product + * @param array $newEntries + * @param array $entriesById + * @throws InputException + * @throws LocalizedException + * @throws StateException + */ + private function processEntries(ProductInterface $product, array $newEntries, array $entriesById): void + { + foreach ($newEntries as $newEntry) { + if (!isset($newEntry['content'])) { + throw new InputException(__('The image content is invalid. Verify the content and try again.')); + } + /** @var ImageContentInterface $contentDataObject */ + $contentDataObject = $this->contentFactory->create() + ->setName($newEntry['content']['data'][ImageContentInterface::NAME]) + ->setBase64EncodedData($newEntry['content']['data'][ImageContentInterface::BASE64_ENCODED_DATA]) + ->setType($newEntry['content']['data'][ImageContentInterface::TYPE]); + $newEntry['content'] = $contentDataObject; + $this->processNewMediaGalleryEntry($product, $newEntry); + + $finalGallery = $product->getData('media_gallery'); + $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); + $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); + $entriesById[$newEntryId] = $newEntry; + $finalGallery['images'][$newEntryId] = $newEntry; + $product->setData('media_gallery', $finalGallery); + } + } + + /** + * Set media attribute values. + * + * @param ProductInterface $product + * @param array $images + */ + private function processMediaAttributes(ProductInterface $product, array $images): void + { + foreach ($images as $image) { + if (!isset($image['removed']) && !empty($image['types'])) { + $this->processor->setMediaAttribute($product, $image['types'], $image['file']); + } + } + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 536fda7e093d3..786cec391c460 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -12,8 +12,10 @@ namespace Magento\Catalog\Model\ResourceModel; use Magento\Catalog\Model\Indexer\Category\Product\Processor; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; +use Magento\Catalog\Model\Category as CategoryEntity; /** * Resource model for category entity @@ -90,7 +92,6 @@ class Category extends AbstractResource * @var Processor */ private $indexerProcessor; - /** * Category constructor. * @param \Magento\Eav\Model\Entity\Context $context @@ -102,6 +103,7 @@ class Category extends AbstractResource * @param Processor $indexerProcessor * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Eav\Model\Entity\Context $context, @@ -125,7 +127,7 @@ public function __construct( $this->_eventManager = $eventManager; $this->connectionName = 'catalog'; $this->indexerProcessor = $indexerProcessor; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + $this->serializer = $serializer ?: ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); } @@ -1026,7 +1028,7 @@ protected function _processPositions($category, $newParent, $afterCategoryId) if ($afterCategoryId) { $select = $connection->select()->from($table, 'position')->where('entity_id = :entity_id'); $position = $connection->fetchOne($select, ['entity_id' => $afterCategoryId]); - $position += 1; + $position++; } else { $position = 1; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index b5668a12f94a5..657daca13055e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -7,7 +7,6 @@ use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; use Magento\Store\Model\ScopeInterface; /** @@ -83,8 +82,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,8 +96,7 @@ public function __construct( \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null ) { parent::__construct( $entityFactory, @@ -113,8 +109,7 @@ public function __construct( $resourceHelper, $universalFactory, $storeManager, - $connection, - $resourceModelPool + $connection ); $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ScopeConfigInterface::class); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php b/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php index 2e40d13f1ccac..3a0d47fe573fb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php @@ -5,8 +5,6 @@ */ namespace Magento\Catalog\Model\ResourceModel\Collection; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Catalog EAV collection resource abstract model * @@ -45,8 +43,6 @@ class AbstractCollection extends \Magento\Eav\Model\Entity\Collection\AbstractCo * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection - * - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -60,8 +56,7 @@ public function __construct( \Magento\Eav\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_storeManager = $storeManager; parent::__construct( @@ -74,8 +69,7 @@ public function __construct( $eavEntityFactory, $resourceHelper, $universalFactory, - $connection, - $resourceModelPool + $connection ); } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index 23f612582f42e..d56cc40ad0fc2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -845,9 +845,14 @@ public function afterDelete() /** * @inheritdoc * @since 100.0.9 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $this->unsetData('entity_type'); return array_diff( parent::__sleep(), @@ -858,9 +863,14 @@ public function __sleep() /** * @inheritdoc * @since 100.0.9 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->_indexerEavProcessor = $objectManager->get(\Magento\Catalog\Model\Indexer\Product\Flat\Processor::class); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index 24174391be829..464df2ef4c249 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -8,6 +8,7 @@ use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\Product as ProductEntity; use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; /** @@ -597,7 +598,9 @@ public function countAll() } /** - * @inheritdoc + * @inheritDoc + * + * @param ProductEntity|object $object */ public function validate($object) { @@ -667,7 +670,7 @@ protected function evaluateDelete($object, $id, $connection) /** * Save entity's attributes into the object's resource * - * @param \Magento\Framework\Model\AbstractModel $object + * @param \Magento\Framework\Model\AbstractModel $object * @return $this * @throws \Exception * @since 101.0.0 @@ -679,7 +682,7 @@ public function save(\Magento\Framework\Model\AbstractModel $object) } /** - * Retrieve entity manager object + * Retrieve entity manager. * * @return \Magento\Framework\EntityManager\EntityManager */ @@ -693,7 +696,7 @@ private function getEntityManager() } /** - * Retrieve ProductWebsiteLink object + * Retrieve ProductWebsiteLink instance. * * @deprecated 101.1.0 * @return ProductWebsiteLink @@ -704,7 +707,7 @@ private function getProductWebsiteLink() } /** - * Retrieve CategoryLink object + * Retrieve CategoryLink instance. * * @deprecated 101.1.0 * @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 136c7e800bf08..0cdf8b39f7d52 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -7,6 +7,8 @@ namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; @@ -16,12 +18,10 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\MetadataPool; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Store\Model\Indexer\WebsiteDimensionProvider; -use Magento\Store\Model\Store; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; use Magento\Framework\Indexer\DimensionFactory; use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; +use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Store\Model\Store; /** * Product collection @@ -32,6 +32,7 @@ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.NumberOfChildren) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection @@ -324,7 +325,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param TableMaintainer|null $tableMaintainer * @param PriceTableResolver|null $priceTableResolver * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -353,8 +353,7 @@ public function __construct( MetadataPool $metadataPool = null, TableMaintainer $tableMaintainer = null, PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + DimensionFactory $dimensionFactory = null ) { $this->moduleManager = $moduleManager; $this->_catalogProductFlatState = $catalogProductFlatState; @@ -382,8 +381,7 @@ public function __construct( $resourceHelper, $universalFactory, $storeManager, - $connection, - $resourceModelPool + $connection ); $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class); $this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class); @@ -445,7 +443,7 @@ protected function _preparePriceExpressionParameters($select) */ public function getPriceExpression($select) { - //@todo: Add caching of price expresion + //@todo: Add caching of price expression $this->_preparePriceExpressionParameters($select); return $this->_priceExpression; } @@ -1979,6 +1977,7 @@ protected function _productLimitationPrice($joinLeft = false) } // Set additional field filters foreach ($this->_priceDataFieldFilters as $filterData) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $select->where(call_user_func_array('sprintf', $filterData)); } } else { @@ -2284,6 +2283,7 @@ private function getBackend() public function addPriceDataFieldFilter($comparisonFormat, $fields) { if (!preg_match('/^%s( (<|>|=|<=|>=|<>) %s)*$/', $comparisonFormat)) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('Invalid comparison format.'); } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php index a45e2060d7c20..aa6fb8c1f8827 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php @@ -5,13 +5,6 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product\Compare\Item; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Catalog Product Compare Items Resource Collection * @@ -19,6 +12,7 @@ * @author Magento Core Team * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection @@ -82,12 +76,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Product\Compare\Item $catalogProductCompareItem * @param \Magento\Catalog\Helper\Product\Compare $catalogProductCompare * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection - * @param ProductLimitationFactory|null $productLimitationFactory - * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -112,13 +100,7 @@ public function __construct( \Magento\Customer\Api\GroupManagementInterface $groupManagement, \Magento\Catalog\Model\ResourceModel\Product\Compare\Item $catalogProductCompareItem, \Magento\Catalog\Helper\Product\Compare $catalogProductCompare, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_catalogProductCompareItem = $catalogProductCompareItem; $this->_catalogProductCompare = $catalogProductCompare; @@ -142,13 +124,7 @@ public function __construct( $customerSession, $dateTime, $groupManagement, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 318c9bd132ccd..494dbac02d792 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -17,11 +17,12 @@ use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; +use Magento\Catalog\Helper\Data; /** * Catalog product custom option resource model * - * @author Magento Core Team + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Value extends AbstractDb { @@ -51,6 +52,11 @@ class Value extends AbstractDb */ private $localeFormat; + /** + * @var Data + */ + private $dataHelper; + /** * Class constructor * @@ -59,17 +65,21 @@ class Value extends AbstractDb * @param StoreManagerInterface $storeManager * @param ScopeConfigInterface $config * @param string $connectionName + * @param Data $dataHelper */ public function __construct( Context $context, CurrencyFactory $currencyFactory, StoreManagerInterface $storeManager, ScopeConfigInterface $config, - $connectionName = null + $connectionName = null, + Data $dataHelper = null ) { $this->_currencyFactory = $currencyFactory; $this->_storeManager = $storeManager; $this->_config = $config; + $this->dataHelper = $dataHelper ?: ObjectManager::getInstance() + ->get(Data::class); parent::__construct($context, $connectionName); } @@ -131,7 +141,7 @@ protected function _saveValuePrices(AbstractModel $object) $optionTypeId = $this->getConnection()->fetchOne($select); if ($optionTypeId) { - if ($object->getStoreId() == '0') { + if ($object->getStoreId() == '0' || $this->dataHelper->isPriceGlobal()) { $bind = ['price' => $price, 'price_type' => $priceType]; $where = [ 'option_type_id = ?' => $optionTypeId, diff --git a/app/code/Magento/Catalog/Setup/CategorySetup.php b/app/code/Magento/Catalog/Setup/CategorySetup.php index 271387932829b..f8542454bef92 100644 --- a/app/code/Magento/Catalog/Setup/CategorySetup.php +++ b/app/code/Magento/Catalog/Setup/CategorySetup.php @@ -10,7 +10,6 @@ use Magento\Catalog\Block\Adminhtml\Category\Helper\Pricestep; use Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby\Available; use Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby\DefaultSortby; -use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\BaseImage; use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Category as CategoryFormHelper; use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight as WeightFormHelper; use Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate; @@ -54,6 +53,8 @@ use Magento\Theme\Model\Theme\Source\Theme; /** + * Setup category with default entities. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategorySetup extends EavSetup @@ -593,7 +594,6 @@ public function getDefaultEntities() 'label' => 'Base Image', 'input' => 'media_image', 'frontend' => ImageFrontendModel::class, - 'input_renderer' => BaseImage::class, 'required' => false, 'sort_order' => 0, 'global' => ScopedAttributeInterface::SCOPE_STORE, @@ -626,7 +626,6 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Media Gallery', 'input' => 'gallery', - 'backend' => Media::class, 'required' => false, 'sort_order' => 4, 'group' => 'Images', diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 3492dffd7cc7d..b8d6ec8e63e71 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -305,6 +305,15 @@ magento-again jpg + + magento-adobe + 1.00 + Upload File + Yes + adobe-base.jpg + adobe-base + jpg + 霁产品 simple diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index 88a39a9087bb3..117f094ee0607 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -43,7 +43,9 @@ - + + + diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php index d93520297e485..60c6f2f1bd821 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php @@ -124,7 +124,7 @@ protected function setUp() ->disableOriginalConstructor()->getMock(); $this->pageConfig->expects($this->any())->method('addBodyClass')->will($this->returnSelf()); - $this->page = $this->getMockBuilder(\Magento\Framework\View\Page::class) + $this->page = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) ->setMethods(['getConfig', 'initLayout', 'addPageLayoutHandles', 'getLayout', 'addUpdate']) ->disableOriginalConstructor()->getMock(); $this->page->expects($this->any())->method('getConfig')->will($this->returnValue($this->pageConfig)); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php index e9eee5c766883..80b6db2a516bd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php @@ -6,10 +6,12 @@ namespace Magento\Catalog\Test\Unit\Model\Product; use Magento\Catalog\Api\Data\ProductInterface; -use \Magento\Catalog\Model\Product\Copier; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Copier; /** + * Test for Magento\Catalog\Model\Product\Copier class. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CopierTest extends \PHPUnit\Framework\TestCase @@ -76,6 +78,9 @@ protected function setUp() ]); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testCopy() { $stockItem = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockItemInterface::class) @@ -103,8 +108,44 @@ public function testCopy() ['linkField', null, '1'], ]); - $resourceMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); - $this->productMock->expects($this->once())->method('getResource')->will($this->returnValue($resourceMock)); + $entityMock = $this->getMockForAbstractClass( + \Magento\Eav\Model\Entity\AbstractEntity::class, + [], + '', + false, + true, + true, + ['checkAttributeUniqueValue'] + ); + $entityMock->expects($this->any()) + ->method('checkAttributeUniqueValue') + ->willReturn(true); + + $attributeMock = $this->getMockForAbstractClass( + \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, + [], + '', + false, + true, + true, + ['getEntity'] + ); + $attributeMock->expects($this->any()) + ->method('getEntity') + ->willReturn($entityMock); + + $resourceMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeRawValue', 'duplicate', 'getAttribute']) + ->getMock(); + $resourceMock->expects($this->any()) + ->method('getAttributeRawValue') + ->willReturn('urk-key-1'); + $resourceMock->expects($this->any()) + ->method('getAttribute') + ->willReturn($attributeMock); + + $this->productMock->expects($this->any())->method('getResource')->will($this->returnValue($resourceMock)); $duplicateMock = $this->createPartialMock( Product::class, @@ -119,11 +160,11 @@ public function testCopy() 'setCreatedAt', 'setUpdatedAt', 'setId', - 'setStoreId', 'getEntityId', 'save', 'setUrlKey', - 'getUrlKey', + 'setStoreId', + 'getStoreIds', ] ); $this->productFactoryMock->expects($this->once())->method('create')->will($this->returnValue($duplicateMock)); @@ -138,19 +179,13 @@ public function testCopy() )->with( \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED ); + $duplicateMock->expects($this->atLeastOnce())->method('setStoreId'); $duplicateMock->expects($this->once())->method('setCreatedAt')->with(null); $duplicateMock->expects($this->once())->method('setUpdatedAt')->with(null); $duplicateMock->expects($this->once())->method('setId')->with(null); - $duplicateMock->expects( - $this->once() - )->method( - 'setStoreId' - )->with( - \Magento\Store\Model\Store::DEFAULT_STORE_ID - ); + $duplicateMock->expects($this->atLeastOnce())->method('getStoreIds')->willReturn([]); $duplicateMock->expects($this->atLeastOnce())->method('setData')->willReturn($duplicateMock); $this->copyConstructorMock->expects($this->once())->method('build')->with($this->productMock, $duplicateMock); - $duplicateMock->expects($this->once())->method('getUrlKey')->willReturn('urk-key-1'); $duplicateMock->expects($this->once())->method('setUrlKey')->with('urk-key-2')->willReturn($duplicateMock); $duplicateMock->expects($this->once())->method('save'); @@ -158,7 +193,8 @@ public function testCopy() $duplicateMock->expects($this->any())->method('getData')->willReturnMap([ ['linkField', null, '2'], - ]); $this->optionRepositoryMock->expects($this->once()) + ]); + $this->optionRepositoryMock->expects($this->once()) ->method('duplicate') ->with($this->productMock, $duplicateMock); $resourceMock->expects($this->once())->method('duplicate')->with(1, 2); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index c729a0c58e1ec..cb92cc6c2d523 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -1,6 +1,5 @@ productFactory = $this->createPartialMock( - \Magento\Catalog\Model\ProductFactory::class, + ProductFactory::class, ['create', 'setData'] ); $this->product = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, + Product::class, [ 'getId', 'getSku', @@ -200,12 +210,13 @@ protected function setUp() 'setData', 'getStoreId', 'getMediaGalleryEntries', - 'getExtensionAttributes' + 'getExtensionAttributes', + 'getCategoryIds' ] ); $this->initializedProduct = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, + Product::class, [ 'getWebsiteIds', 'setProductOptions', @@ -220,7 +231,8 @@ protected function setUp() 'validate', 'save', 'getMediaGalleryEntries', - 'getExtensionAttributes' + 'getExtensionAttributes', + 'getCategoryIds' ] ); $this->initializedProduct->expects($this->any()) @@ -232,7 +244,7 @@ protected function setUp() $this->searchCriteriaBuilder = $this->createMock(SearchCriteriaBuilder::class); $this->metadataService = $this->createMock(ProductAttributeRepositoryInterface::class); $this->searchResultsFactory = $this->createPartialMock( - \Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory::class, + ProductSearchResultsInterfaceFactory::class, ['create'] ); $this->resourceModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); @@ -269,15 +281,21 @@ protected function setUp() $this->initializedProduct ->method('getExtensionAttributes') ->willReturn($this->productExtension); + $this->product + ->method('getCategoryIds') + ->willReturn([1, 2, 3, 4]); + $this->initializedProduct + ->method('getCategoryIds') + ->willReturn([1, 2, 3, 4]); $storeMock = $this->getMockBuilder(StoreInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMockForAbstractClass(); $storeMock->expects($this->any())->method('getWebsiteId')->willReturn('1'); - $storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE); + $storeMock->expects($this->any())->method('getCode')->willReturn(Store::ADMIN_CODE); $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->mediaGalleryProcessor = $this->createMock(\Magento\Catalog\Model\Product\Gallery\Processor::class); + $this->processor = $this->createMock(Processor::class); $this->collectionProcessor = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); @@ -293,6 +311,14 @@ function ($value) { ) ); + $mediaProcessor = $this->objectManager->getObject( + ProductRepository\MediaGalleryProcessor::class, + [ + 'processor' => $this->processor, + 'contentFactory' => $this->contentFactory, + 'imageProcessor' => $this->imageProcessor, + ] + ); $this->model = $this->objectManager->getObject( ProductRepository::class, [ @@ -307,17 +333,16 @@ function ($value) { 'extensibleDataObjectConverter' => $this->extensibleDataObjectConverter, 'contentValidator' => $this->contentValidator, 'fileSystem' => $this->fileSystem, - 'contentFactory' => $this->contentFactory, 'mimeTypeExtensionMap' => $this->mimeTypeExtensionMap, 'linkTypeProvider' => $this->linkTypeProvider, - 'imageProcessor' => $this->imageProcessor, 'storeManager' => $this->storeManager, - 'mediaGalleryProcessor' => $this->mediaGalleryProcessor, + 'mediaGalleryProcessor' => $this->processor, 'collectionProcessor' => $this->collectionProcessor, 'serializer' => $this->serializerMock, 'cacheLimit' => $this->cacheLimit ] ); + $this->objectManager->setBackwardCompatibleProperty($this->model, 'mediaProcessor', $mediaProcessor); } /** @@ -500,7 +525,7 @@ private function getProductMocksForReducedCache($productsCount) $productMocks = []; for ($i = 1; $i <= $productsCount; $i++) { - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $productMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods([ 'getId', @@ -753,8 +778,8 @@ public function testDeleteById() public function testGetList() { - $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteriaInterface::class); - $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); + $searchCriteriaMock = $this->createMock(SearchCriteriaInterface::class); + $collectionMock = $this->createMock(Collection::class); $this->collectionFactory->expects($this->once())->method('create')->willReturn($collectionMock); $this->product->method('getSku')->willReturn('simple'); $collectionMock->expects($this->once())->method('addAttributeToSelect')->with('*'); @@ -769,7 +794,7 @@ public function testGetList() $collectionMock->expects($this->once())->method('addCategoryIds'); $collectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$this->product]); $collectionMock->expects($this->once())->method('getSize')->willReturn(128); - $searchResultsMock = $this->createMock(\Magento\Catalog\Api\Data\ProductSearchResultsInterface::class); + $searchResultsMock = $this->createMock(ProductSearchResultsInterface::class); $searchResultsMock->expects($this->once())->method('setSearchCriteria')->with($searchCriteriaMock); $searchResultsMock->expects($this->once())->method('setItems')->with([$this->product]); $this->searchResultsFactory->expects($this->once())->method('create')->willReturn($searchResultsMock); @@ -903,8 +928,8 @@ public function saveExistingWithOptionsDataProvider() ], ]; - /** @var \Magento\Catalog\Model\Product\Option|\PHPUnit_Framework_MockObject_MockObject $existingOption1 */ - $existingOption1 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option::class) + /** @var Option|MockObject $existingOption1 */ + $existingOption1 = $this->getMockBuilder(Option::class) ->disableOriginalConstructor() ->setMethods(null) ->getMock(); @@ -914,8 +939,8 @@ public function saveExistingWithOptionsDataProvider() "type" => "drop_down", ] ); - /** @var \Magento\Catalog\Model\Product\Option\Value $existingOptionValue1 */ - $existingOptionValue1 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option\Value::class) + /** @var Value $existingOptionValue1 */ + $existingOptionValue1 = $this->getMockBuilder(Value::class) ->disableOriginalConstructor() ->setMethods(null) ->getMock(); @@ -926,7 +951,7 @@ public function saveExistingWithOptionsDataProvider() "price" => 5, ] ); - $existingOptionValue2 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option\Value::class) + $existingOptionValue2 = $this->getMockBuilder(Value::class) ->disableOriginalConstructor() ->setMethods(null) ->getMock(); @@ -943,7 +968,7 @@ public function saveExistingWithOptionsDataProvider() "9" => $existingOptionValue2, ] ); - $existingOption2 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option::class) + $existingOption2 = $this->getMockBuilder(Option::class) ->disableOriginalConstructor() ->setMethods(null) ->getMock(); @@ -1008,8 +1033,8 @@ public function saveExistingWithOptionsDataProvider() * @param array $existingLinks * @param array $expectedData * @dataProvider saveWithLinksDataProvider - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws \Magento\Framework\Exception\InputException + * @throws CouldNotSaveException + * @throws InputException */ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $expectedData) { @@ -1037,7 +1062,7 @@ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $ ->expects($this->any())->method('getProductsIdsBySkus') ->willReturn([$newLinks['linked_product_sku'] => $newLinks['linked_product_sku']]); - $inputLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); + $inputLink = $this->objectManager->getObject(Link::class); $inputLink->setProductSku($newLinks['product_sku']); $inputLink->setLinkType($newLinks['link_type']); $inputLink->setLinkedProductSku($newLinks['linked_product_sku']); @@ -1081,7 +1106,7 @@ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $ $outputLinks = []; if (!empty($expectedData)) { foreach ($expectedData as $link) { - $outputLink = $this->objectManager->getObject(\Magento\Catalog\Model\ProductLink\Link::class); + $outputLink = $this->objectManager->getObject(Link::class); $outputLink->setProductSku($link['product_sku']); $outputLink->setLinkType($link['link_type']); $outputLink->setLinkedProductSku($link['linked_product_sku']); @@ -1235,10 +1260,10 @@ public function testSaveExistingWithNewMediaGalleryEntries() $mediaTmpPath = '/tmp'; $absolutePath = '/a/b/filename.jpg'; - $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') + $this->processor->expects($this->once())->method('clearMediaAttribute') ->with($this->initializedProduct, ['image', 'small_image']); - $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class) + $mediaConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); $mediaConfigMock->expects($this->once()) @@ -1250,7 +1275,7 @@ public function testSaveExistingWithNewMediaGalleryEntries() ->willReturn($mediaConfigMock); //verify new entries - $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class) + $contentDataObject = $this->getMockBuilder(ImageContent::class) ->disableOriginalConstructor() ->setMethods(null) ->getMock(); @@ -1263,10 +1288,10 @@ public function testSaveExistingWithNewMediaGalleryEntries() ->willReturn($absolutePath); $imageFileUri = "imageFileUri"; - $this->mediaGalleryProcessor->expects($this->once())->method('addImage') + $this->processor->expects($this->once())->method('addImage') ->with($this->initializedProduct, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false) ->willReturn($imageFileUri); - $this->mediaGalleryProcessor->expects($this->once())->method('updateImage') + $this->processor->expects($this->once())->method('updateImage') ->with( $this->initializedProduct, $imageFileUri, @@ -1386,9 +1411,9 @@ public function testSaveExistingWithMediaGalleryEntries() ->method('getMediaAttributes') ->willReturn(["image" => "filename1", "small_image" => "filename2"]); - $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') + $this->processor->expects($this->once())->method('clearMediaAttribute') ->with($this->initializedProduct, ['image', 'small_image']); - $this->mediaGalleryProcessor->expects($this->once()) + $this->processor->expects($this->once()) ->method('setMediaAttribute') ->with($this->initializedProduct, ['image', 'small_image'], 'filename1'); $this->initializedProduct->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index 5da5625189ee3..0316b2e374d2f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -5,33 +5,13 @@ */ namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; -use Magento\Catalog\Model\Indexer; -use Magento\Catalog\Model\Product as ProductModel; -use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\Framework\DB\Select; -use Magento\Eav\Model\Entity\AbstractEntity; -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Eav\Model\EntityFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Data\Collection; -use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; -use Magento\Framework\DB; -use Magento\Framework\EntityManager\EntityMetadataInterface; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Event; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\StoreManagerInterface; -use PHPUnit\Framework\TestCase; -use Psr\Log\LoggerInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CollectionTest extends TestCase +class CollectionTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager @@ -44,12 +24,12 @@ class CollectionTest extends TestCase protected $selectMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|DB\Adapter\AdapterInterface + * @var \PHPUnit_Framework_MockObject_MockObject */ protected $connectionMock; /** - * @var ProductResource\Collection + * @var \Magento\Catalog\Model\ResourceModel\Product\Collection */ protected $collection; @@ -90,50 +70,121 @@ protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->entityFactory = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class); - $this->selectMock = $this->createMock(DB\Select::class); - $this->connectionMock = $this->createMock(DB\Adapter\AdapterInterface::class); - $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); - $this->entityMock = $this->createMock(AbstractEntity::class); + $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $fetchStrategy = $this->getMockBuilder(\Magento\Framework\Data\Collection\Db\FetchStrategyInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $eventManager = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $resource = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $eavEntityFactory = $this->getMockBuilder(\Magento\Eav\Model\EntityFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $resourceHelper = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Helper::class) + ->disableOriginalConstructor() + ->getMock(); + $universalFactory = $this->getMockBuilder(\Magento\Framework\Validator\UniversalFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getStore', 'getId', 'getWebsiteId']) + ->getMockForAbstractClass(); + $moduleManager = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) + ->disableOriginalConstructor() + ->getMock(); + $catalogProductFlatState = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Flat\State::class) + ->disableOriginalConstructor() + ->getMock(); + $scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $productOptionFactory = $this->getMockBuilder(\Magento\Catalog\Model\Product\OptionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $catalogUrl = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Url::class) + ->disableOriginalConstructor() + ->getMock(); + $localeDate = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + ->disableOriginalConstructor() + ->getMock(); + $dateTime = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $groupManagement = $this->getMockBuilder(\Magento\Customer\Api\GroupManagementInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['getId']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) + ->disableOriginalConstructor() + ->getMock(); + $this->galleryResourceMock = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Gallery::class + )->disableOriginalConstructor()->getMock(); + $this->metadataPoolMock = $this->getMockBuilder( + \Magento\Framework\EntityManager\MetadataPool::class + )->disableOriginalConstructor()->getMock(); + $this->galleryReadHandlerMock = $this->getMockBuilder( + \Magento\Catalog\Model\Product\Gallery\ReadHandler::class + )->disableOriginalConstructor()->getMock(); + $this->storeManager->expects($this->any())->method('getId')->willReturn(1); + $this->storeManager->expects($this->any())->method('getStore')->willReturnSelf(); + $universalFactory->expects($this->exactly(1))->method('create')->willReturnOnConsecutiveCalls( + $this->entityMock + ); $this->entityMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); $this->entityMock->expects($this->once())->method('getDefaultAttributes')->willReturn([]); - $this->entityMock->method('getTable')->willReturnArgument(0); - $this->galleryResourceMock = $this->createMock(ProductResource\Gallery::class); - $this->metadataPoolMock = $this->createMock(MetadataPool::class); - $this->galleryReadHandlerMock = $this->createMock(ProductModel\Gallery\ReadHandler::class); + $this->entityMock->expects($this->any())->method('getTable')->willReturnArgument(0); + $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); - $storeStub = $this->createMock(StoreInterface::class); - $storeStub->method('getId')->willReturn(1); - $storeStub->method('getWebsiteId')->willReturn(1); - $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->storeManager->method('getStore')->willReturn($storeStub); - $resourceModelPool = $this->createMock(ResourceModelPoolInterface::class); - $resourceModelPool->expects($this->exactly(1))->method('get')->willReturn($this->entityMock); + $productLimitationMock = $this->createMock( + \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class + ); + $productLimitationFactoryMock = $this->getMockBuilder( + ProductLimitationFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); - $productLimitationFactoryMock = $this->createPartialMock(ProductLimitationFactory::class, ['create']); $productLimitationFactoryMock->method('create') - ->willReturn($this->createMock(ProductResource\Collection\ProductLimitation::class)); + ->willReturn($productLimitationMock); $this->collection = $this->objectManager->getObject( - ProductResource\Collection::class, + \Magento\Catalog\Model\ResourceModel\Product\Collection::class, [ 'entityFactory' => $this->entityFactory, - 'logger' => $this->createMock(LoggerInterface::class), - 'fetchStrategy' => $this->createMock(FetchStrategyInterface::class), - 'eventManager' => $this->createMock(Event\ManagerInterface::class), - 'eavConfig' => $this->createMock(\Magento\Eav\Model\Config::class), - 'resource' => $this->createMock(ResourceConnection::class), - 'eavEntityFactory' => $this->createMock(EntityFactory::class), - 'resourceHelper' => $this->createMock(\Magento\Catalog\Model\ResourceModel\Helper::class), - 'resourceModelPool' => $resourceModelPool, + 'logger' => $logger, + 'fetchStrategy' => $fetchStrategy, + 'eventManager' => $eventManager, + 'eavConfig' => $eavConfig, + 'resource' => $resource, + 'eavEntityFactory' => $eavEntityFactory, + 'resourceHelper' => $resourceHelper, + 'universalFactory' => $universalFactory, 'storeManager' => $this->storeManager, - 'moduleManager' => $this->createMock(\Magento\Framework\Module\Manager::class), - 'catalogProductFlatState' => $this->createMock(Indexer\Product\Flat\State::class), - 'scopeConfig' => $this->createMock(ScopeConfigInterface::class), - 'productOptionFactory' => $this->createMock(ProductModel\OptionFactory::class), - 'catalogUrl' => $this->createMock(\Magento\Catalog\Model\ResourceModel\Url::class), - 'localeDate' => $this->createMock(TimezoneInterface::class), - 'customerSession' => $this->createMock(\Magento\Customer\Model\Session::class), - 'dateTime' => $this->createMock(\Magento\Framework\Stdlib\DateTime::class), - 'groupManagement' => $this->createMock(\Magento\Customer\Api\GroupManagementInterface::class), + 'moduleManager' => $moduleManager, + 'catalogProductFlatState' => $catalogProductFlatState, + 'scopeConfig' => $scopeConfig, + 'productOptionFactory' => $productOptionFactory, + 'catalogUrl' => $catalogUrl, + 'localeDate' => $localeDate, + 'customerSession' => $customerSession, + 'dateTime' => $dateTime, + 'groupManagement' => $groupManagement, 'connection' => $this->connectionMock, 'productLimitationFactory' => $productLimitationFactoryMock, 'metadataPool' => $this->metadataPoolMock, @@ -158,8 +209,9 @@ public function testAddProductCategoriesFilter() $condition = ['in' => [1, 2]]; $values = [1, 2]; $conditionType = 'nin'; - $preparedSql = 'category_id IN(1,2)'; - $tableName = 'catalog_category_product'; + $preparedSql = "category_id IN(1,2)"; + $tableName = "catalog_category_product"; + $this->connectionMock->expects($this->any())->method('getId')->willReturn(1); $this->connectionMock->expects($this->exactly(2))->method('prepareSqlCondition')->withConsecutive( ['cat.category_id', $condition], ['e.entity_id', [$conditionType => $this->selectMock]] @@ -184,14 +236,19 @@ public function testAddMediaGalleryData() $rowId = 4; $linkField = 'row_id'; $mediaGalleriesMock = [[$linkField => $rowId]]; - /** @var ProductModel|\PHPUnit_Framework_MockObject_MockObject $itemMock */ - $itemMock = $this->getMockBuilder(ProductModel::class) + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() ->setMethods(['getOrigData']) ->getMock(); - $attributeMock = $this->createMock(AbstractAttribute::class); - $selectMock = $this->createMock(DB\Select::class); - $metadataMock = $this->createMock(EntityMetadataInterface::class); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $metadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->collection->addItem($itemMock); $this->galleryResourceMock->expects($this->once())->method('createBatchBaseSelect')->willReturn($selectMock); $attributeMock->expects($this->once())->method('getAttributeId')->willReturn($attributeId); @@ -221,15 +278,25 @@ public function testAddMediaGalleryData() public function testAddTierPriceDataByGroupId() { $customerGroupId = 2; - /** @var ProductModel|\PHPUnit_Framework_MockObject_MockObject $itemMock */ - $itemMock = $this->createMock(ProductModel::class); - $attributeMock = $this->getMockBuilder(AbstractAttribute::class) + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) ->disableOriginalConstructor() ->setMethods(['isScopeGlobal', 'getBackend']) ->getMock(); - $backend = $this->createMock(ProductModel\Attribute\Backend\Tierprice::class); - $resource = $this->createMock(ProductResource\Attribute\Backend\GroupPrice\AbstractGroupPrice::class); - $select = $this->createMock(DB\Select::class); + $backend = $this->getMockBuilder(\Magento\Catalog\Model\Product\Attribute\Backend\Tierprice::class) + ->disableOriginalConstructor() + ->getMock(); + $resource = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice::class + ) + ->disableOriginalConstructor() + ->getMock(); + $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); $this->connectionMock->expects($this->once())->method('getAutoIncrementField')->willReturn('entity_id'); $this->collection->addItem($itemMock); $itemMock->expects($this->atLeastOnce())->method('getData')->with('entity_id')->willReturn(1); @@ -239,6 +306,7 @@ public function testAddTierPriceDataByGroupId() ->willReturn($attributeMock); $attributeMock->expects($this->atLeastOnce())->method('getBackend')->willReturn($backend); $attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(false); + $this->storeManager->expects($this->once())->method('getWebsiteId')->willReturn(1); $backend->expects($this->once())->method('getResource')->willReturn($resource); $resource->expects($this->once())->method('getSelect')->willReturn($select); $select->expects($this->once())->method('columns')->with(['product_id' => 'entity_id'])->willReturnSelf(); @@ -265,22 +333,25 @@ public function testAddTierPriceDataByGroupId() */ public function testAddTierPriceData() { - /** @var ProductModel|\PHPUnit_Framework_MockObject_MockObject $itemMock */ - $itemMock = $this->getMockBuilder(ProductModel::class) + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() ->setMethods(['getData']) ->getMock(); - $attributeMock = $this->getMockBuilder(AbstractAttribute::class) + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) ->disableOriginalConstructor() ->setMethods(['isScopeGlobal', 'getBackend']) ->getMock(); - $backend = $this->createMock(ProductModel\Attribute\Backend\Tierprice::class); + $backend = $this->getMockBuilder(\Magento\Catalog\Model\Product\Attribute\Backend\Tierprice::class) + ->disableOriginalConstructor() + ->getMock(); $resource = $this->getMockBuilder( - ProductResource\Attribute\Backend\GroupPrice\AbstractGroupPrice::class + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice::class ) ->disableOriginalConstructor() ->getMock(); - $select = $this->createMock(DB\Select::class); + $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); $this->connectionMock->expects($this->once())->method('getAutoIncrementField')->willReturn('entity_id'); $this->collection->addItem($itemMock); $itemMock->expects($this->atLeastOnce())->method('getData')->with('entity_id')->willReturn(1); @@ -290,6 +361,7 @@ public function testAddTierPriceData() ->willReturn($attributeMock); $attributeMock->expects($this->atLeastOnce())->method('getBackend')->willReturn($backend); $attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(false); + $this->storeManager->expects($this->once())->method('getWebsiteId')->willReturn(1); $backend->expects($this->once())->method('getResource')->willReturn($resource); $resource->expects($this->once())->method('getSelect')->willReturn($select); $select->expects($this->once())->method('columns')->with(['product_id' => 'entity_id'])->willReturnSelf(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php index 80180d2033ce5..596148b627506 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php @@ -7,8 +7,6 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -28,7 +26,7 @@ class CollectionTest extends \PHPUnit\Framework\TestCase /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $loggerMock; - /** @var FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Data\Collection\Db\FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $fetchStrategyMock; /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -46,8 +44,8 @@ class CollectionTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Catalog\Model\ResourceModel\Helper|\PHPUnit_Framework_MockObject_MockObject */ protected $helperMock; - /** @var ResourceModelPoolInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $resourceModelPoolMock; + /** @var \Magento\Framework\Validator\UniversalFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $universalFactoryMock; /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $storeManagerMock; @@ -81,23 +79,29 @@ protected function setUp() $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->entityFactoryMock = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->fetchStrategyMock = $this->createMock(FetchStrategyInterface::class); + $this->fetchStrategyMock = $this->createMock( + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface::class + ); $this->managerInterfaceMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); $this->configMock = $this->createMock(\Magento\Eav\Model\Config::class); $this->resourceMock = $this->createMock(\Magento\Framework\App\ResourceConnection::class); $this->entityFactoryMock2 = $this->createMock(\Magento\Eav\Model\EntityFactory::class); $this->helperMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Helper::class); $entity = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); - $select = $this->createMock(\Magento\Framework\DB\Select::class); - $connection = $this->createMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class); + $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\Pdo\Mysql::class) + ->disableOriginalConstructor() + ->getMock(); $connection->expects($this->any()) ->method('select') ->willReturn($select); $entity->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); $entity->expects($this->any())->method('getDefaultAttributes')->will($this->returnValue([])); - $this->resourceModelPoolMock = $this->createMock(ResourceModelPoolInterface::class); - $this->resourceModelPoolMock->expects($this->any())->method('get')->will($this->returnValue($entity)); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->universalFactoryMock = $this->createMock(\Magento\Framework\Validator\UniversalFactory::class); + $this->universalFactoryMock->expects($this->any())->method('create')->will($this->returnValue($entity)); + $this->storeManagerMock = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); $this->storeManagerMock ->expects($this->any()) ->method('getStore') @@ -114,7 +118,9 @@ function ($store) { $this->timezoneInterfaceMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); $this->sessionMock = $this->createMock(\Magento\Customer\Model\Session::class); $this->dateTimeMock = $this->createMock(\Magento\Framework\Stdlib\DateTime::class); - $productLimitationFactoryMock = $this->createPartialMock(ProductLimitationFactory::class, ['create']); + $productLimitationFactoryMock = $this->getMockBuilder( + ProductLimitationFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); $productLimitationFactoryMock->method('create') ->willReturn($this->createMock(ProductLimitation::class)); @@ -130,7 +136,7 @@ function ($store) { 'resource' => $this->resourceMock, 'eavEntityFactory' => $this->entityFactoryMock2, 'resourceHelper' => $this->helperMock, - 'resourceModelPool' => $this->resourceModelPoolMock, + 'universalFactory' => $this->universalFactoryMock, 'storeManager' => $this->storeManagerMock, 'catalogData' => $this->catalogHelperMock, 'catalogProductFlatState' => $this->stateMock, diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 209fd235bcd6e..6f60a114737b0 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -21,8 +21,10 @@ use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory as GroupCollectionFactory; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Filter\Translit; use Magento\Framework\Locale\CurrencyInterface; use Magento\Framework\Stdlib\ArrayManager; @@ -213,6 +215,11 @@ class Eav extends AbstractModifier */ private $scopeConfig; + /** + * @var AuthorizationInterface + */ + private $auth; + /** * Eav constructor. * @param LocatorInterface $locator @@ -237,6 +244,7 @@ class Eav extends AbstractModifier * @param CompositeConfigProcessor|null $wysiwygConfigProcessor * @param ScopeConfigInterface|null $scopeConfig * @param AttributeCollectionFactory $attributeCollectionFactory + * @param AuthorizationInterface|null $auth * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -261,7 +269,8 @@ public function __construct( $attributesToEliminate = [], CompositeConfigProcessor $wysiwygConfigProcessor = null, ScopeConfigInterface $scopeConfig = null, - AttributeCollectionFactory $attributeCollectionFactory = null + AttributeCollectionFactory $attributeCollectionFactory = null, + ?AuthorizationInterface $auth = null ) { $this->locator = $locator; $this->catalogEavValidationRules = $catalogEavValidationRules; @@ -282,12 +291,12 @@ public function __construct( $this->dataPersistor = $dataPersistor; $this->attributesToDisable = $attributesToDisable; $this->attributesToEliminate = $attributesToEliminate; - $this->wysiwygConfigProcessor = $wysiwygConfigProcessor ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(CompositeConfigProcessor::class); - $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(ScopeConfigInterface::class); + $this->wysiwygConfigProcessor = $wysiwygConfigProcessor + ?: ObjectManager::getInstance()->get(CompositeConfigProcessor::class); + $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); $this->attributeCollectionFactory = $attributeCollectionFactory - ?: \Magento\Framework\App\ObjectManager::getInstance()->get(AttributeCollectionFactory::class); + ?: ObjectManager::getInstance()->get(AttributeCollectionFactory::class); + $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); } /** @@ -733,6 +742,23 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC break; } + //Checking access to design config. + $designDesignGroups = ['design', 'schedule-design-update']; + if (in_array($groupCode, $designDesignGroups, true)) { + if (!$this->auth->isAllowed('Magento_Catalog::edit_product_design')) { + $meta = $this->arrayManager->merge( + $configPath, + $meta, + [ + 'disabled' => true, + 'validation' => ['required' => false], + 'required' => false, + 'serviceDisabled' => true, + ] + ); + } + } + return $meta; } diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 5c3ee3da8ca81..fc22bf3438e3f 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -31,7 +31,8 @@ "magento/module-ui": "*", "magento/module-url-rewrite": "*", "magento/module-widget": "*", - "magento/module-wishlist": "*" + "magento/module-wishlist": "*", + "magento/module-authorization": "*" }, "suggest": { "magento/module-cookie": "*", diff --git a/app/code/Magento/Catalog/etc/acl.xml b/app/code/Magento/Catalog/etc/acl.xml index 358a798fc7579..fbc61141258a0 100644 --- a/app/code/Magento/Catalog/etc/acl.xml +++ b/app/code/Magento/Catalog/etc/acl.xml @@ -11,8 +11,12 @@ - - + + + + + + diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index ed27dfd646cb2..c35ffbce1a125 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -808,4 +808,6 @@ Details,Details "Start typing to find products", "Start typing to find products" "Product with ID: (%1) doesn't exist", "Product with ID: (%1) doesn't exist" "Category with ID: (%1) doesn't exist", "Category with ID: (%1) doesn't exist" -"You added product %1 to the comparison list.","You added product %1 to the comparison list." \ No newline at end of file +"You added product %1 to the comparison list.","You added product %1 to the comparison list." +"Edit Product Design","Edit Product Design" +"Edit Category Design","Edit Category Design" \ No newline at end of file diff --git a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml index 44884897461a8..4e7396608826b 100644 --- a/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml +++ b/app/code/Magento/Catalog/view/adminhtml/layout/catalog_product_set_block.xml @@ -11,7 +11,7 @@ setGrid - Magento\Eav\Model\ResourceModel\Entity\Attribute\Grid\Collection + Magento\Eav\Model\ResourceModel\Entity\Attribute\Grid\Collection set_name ASC 1 diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 90d6e0b48400e..1ce9a669f0ee6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -466,7 +466,7 @@ string - ${ $.parentName }.custom_use_parent_settings:checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled @@ -475,7 +475,7 @@ string - ${ $.parentName }.custom_use_parent_settings:checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled @@ -484,7 +484,7 @@ string - ${ $.parentName }.custom_use_parent_settings:checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled @@ -501,7 +501,7 @@ boolean - ${ $.parentName }.custom_use_parent_settings:checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled @@ -544,7 +544,7 @@ string - ns = ${ $.ns }, index = custom_use_parent_settings :checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled @@ -557,7 +557,7 @@ string - ns = ${ $.ns }, index = custom_use_parent_settings :checked + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php new file mode 100644 index 0000000000000..aba2d7b198dbd --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CategoriesIdentity.php @@ -0,0 +1,33 @@ + promo_catalog_grid - Magento\CatalogRule\Model\ResourceModel\Grid\Collection + Magento\CatalogRule\Model\ResourceModel\Grid\Collection name ASC 1 diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php index e3f61af771f8c..7791dc761ae39 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php @@ -6,8 +6,6 @@ namespace Magento\CatalogSearch\Model\ResourceModel\Advanced; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\Product; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverInterface; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; @@ -21,8 +19,6 @@ use Magento\Framework\Api\Search\SearchResultFactory; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; use Magento\Framework\Search\Request\EmptyRequestDataException; use Magento\Framework\Search\Request\NonExistingRequestNameException; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; @@ -139,10 +135,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param SearchResultFactory|null $searchResultFactory * @param ProductLimitationFactory|null $productLimitationFactory * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool * @param string $searchRequestName * @param SearchCriteriaResolverFactory|null $searchCriteriaResolverFactory * @param SearchResultApplierFactory|null $searchResultApplierFactory @@ -177,10 +169,6 @@ public function __construct( SearchResultFactory $searchResultFactory = null, ProductLimitationFactory $productLimitationFactory = null, MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null, $searchRequestName = 'advanced_search_container', SearchCriteriaResolverFactory $searchCriteriaResolverFactory = null, SearchResultApplierFactory $searchResultApplierFactory = null, @@ -225,11 +213,7 @@ public function __construct( $groupManagement, $connection, $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $metadataPool ); } diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 2c36b150fed07..59f6cd1c6e7eb 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -6,8 +6,6 @@ namespace Magento\CatalogSearch\Model\ResourceModel\Fulltext; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolverInterface; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolverFactory; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverInterface; @@ -21,8 +19,6 @@ use Magento\CatalogSearch\Model\Search\RequestGenerator; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\StateException; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; use Magento\Framework\Search\Response\QueryResponse; use Magento\Framework\Search\Request\EmptyRequestDataException; use Magento\Framework\Search\Request\NonExistingRequestNameException; @@ -41,6 +37,7 @@ * @since 100.0.2 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { @@ -167,10 +164,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param SearchResultFactory|null $searchResultFactory * @param ProductLimitationFactory|null $productLimitationFactory * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool * @param \Magento\Search\Api\SearchInterface|null $search * @param \Magento\Framework\Api\Search\SearchCriteriaBuilder|null $searchCriteriaBuilder * @param \Magento\Framework\Api\FilterBuilder|null $filterBuilder @@ -210,10 +203,6 @@ public function __construct( SearchResultFactory $searchResultFactory = null, ProductLimitationFactory $productLimitationFactory = null, MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null, \Magento\Search\Api\SearchInterface $search = null, \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder = null, \Magento\Framework\Api\FilterBuilder $filterBuilder = null, @@ -249,11 +238,7 @@ public function __construct( $groupManagement, $connection, $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $metadataPool ); $this->requestBuilder = $requestBuilder; $this->searchEngine = $searchEngine; diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php index fd948616c005b..e625ccbe51fe3 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php @@ -6,17 +6,11 @@ namespace Magento\CatalogSearch\Model\ResourceModel\Search; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Search collection * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @api * @since 100.0.2 */ @@ -67,12 +61,6 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeCollectionFactory * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection - * @param ProductLimitationFactory|null $productLimitationFactory - * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -96,13 +84,7 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Customer\Api\GroupManagementInterface $groupManagement, \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeCollectionFactory, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_attributeCollectionFactory = $attributeCollectionFactory; parent::__construct( @@ -125,13 +107,7 @@ public function __construct( $customerSession, $dateTime, $groupManagement, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); } @@ -293,6 +269,7 @@ protected function _getSearchEntityIdsSql($query, $searchOnlyInCurrentStore = tr $sql = $this->_getSearchInOptionSql($query); if ($sql) { + // phpcs:ignore Magento2.SQL.RawQuery $selects[] = "SELECT * FROM ({$sql}) AS inoptionsql"; // inherent unions may be inside } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php index fe29fa1ece011..683070c286239 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php @@ -67,7 +67,7 @@ protected function setUp() $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->eavConfig = $this->createMock(\Magento\Eav\Model\Config::class); $storeManager = $this->getStoreManager(); - $resourceModelPool = $this->getResourceModelPool(); + $universalFactory = $this->getUniversalFactory(); $this->criteriaBuilder = $this->getCriteriaBuilder(); $this->filterBuilder = $this->createMock(\Magento\Framework\Api\FilterBuilder::class); $this->temporaryStorageFactory = $this->createMock( @@ -126,7 +126,7 @@ protected function setUp() [ 'eavConfig' => $this->eavConfig, 'storeManager' => $storeManager, - 'resourceModelPool' => $resourceModelPool, + 'universalFactory' => $universalFactory, 'searchCriteriaBuilder' => $this->criteriaBuilder, 'filterBuilder' => $this->filterBuilder, 'temporaryStorageFactory' => $this->temporaryStorageFactory, diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollection.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollection.php index 5a5106593af8b..9ea103e23d2a7 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollection.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/BaseCollection.php @@ -5,8 +5,6 @@ */ namespace Magento\CatalogSearch\Test\Unit\Model\ResourceModel; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Base class for Collection tests. * @@ -44,17 +42,19 @@ protected function getStoreManager() } /** - * Get mock for ResourceModelPool so Collection can be used. + * Get mock for UniversalFactory so Collection can be used. * - * @return \PHPUnit_Framework_MockObject_MockObject|ResourceModelPoolInterface + * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getResourceModelPool() + protected function getUniversalFactory() { $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\Pdo\Mysql::class) ->disableOriginalConstructor() ->setMethods(['select']) ->getMockForAbstractClass(); - $select = $this->createMock(\Magento\Framework\DB\Select::class); + $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); $connection->expects($this->any())->method('select')->willReturn($select); $entity = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) @@ -74,14 +74,14 @@ protected function getResourceModelPool() ->method('getEntityTable') ->willReturn('table'); - $resourceModelPool = $this->getMockBuilder(ResourceModelPoolInterface::class) - ->setMethods(['get']) + $universalFactory = $this->getMockBuilder(\Magento\Framework\Validator\UniversalFactory::class) + ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $resourceModelPool->expects($this->once()) - ->method('get') + $universalFactory->expects($this->once()) + ->method('create') ->willReturn($entity); - return $resourceModelPool; + return $universalFactory; } } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php index f1c2161a052e0..9170b81dc3182 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php @@ -49,7 +49,7 @@ class CollectionTest extends BaseCollection /** * @var MockObject */ - private $resourceModelPool; + private $universalFactory; /** * @var MockObject @@ -78,7 +78,7 @@ protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->storeManager = $this->getStoreManager(); - $this->resourceModelPool = $this->getResourceModelPool(); + $this->universalFactory = $this->getUniversalFactory(); $this->scopeConfig = $this->getScopeConfig(); $this->criteriaBuilder = $this->getCriteriaBuilder(); $this->filterBuilder = $this->getFilterBuilder(); @@ -143,7 +143,7 @@ protected function setUp() \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection::class, [ 'storeManager' => $this->storeManager, - 'resourceModelPool' => $this->resourceModelPool, + 'universalFactory' => $this->universalFactory, 'scopeConfig' => $this->scopeConfig, 'temporaryStorageFactory' => $temporaryStorageFactory, 'productLimitationFactory' => $productLimitationFactoryMock, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js index c17e5e40d5c98..e8994c61b7221 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js @@ -66,9 +66,21 @@ define([ navigate: function () { var self = this; - getPaymentInformation().done(function () { - self.isVisible(true); - }); + if (!self.hasShippingMethod()) { + this.isVisible(false); + stepNavigator.setHash('shipping'); + } else { + getPaymentInformation().done(function () { + self.isVisible(true); + }); + } + }, + + /** + * @return {Boolean} + */ + hasShippingMethod: function () { + return window.checkoutConfig.selectedShippingMethod !== null; }, /** diff --git a/app/code/Magento/Cms/Block/Block.php b/app/code/Magento/Cms/Block/Block.php index d0d75ea691195..c611f4b1e9f05 100644 --- a/app/code/Magento/Cms/Block/Block.php +++ b/app/code/Magento/Cms/Block/Block.php @@ -84,4 +84,14 @@ public function getIdentities() { return [\Magento\Cms\Model\Block::CACHE_TAG . '_' . $this->getBlockId()]; } + + /** + * @inheritdoc + */ + public function getCacheKeyInfo() + { + $cacheKeyInfo = parent::getCacheKeyInfo(); + $cacheKeyInfo[] = $this->_storeManager->getStore()->getId(); + return $cacheKeyInfo; + } } diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index abbe9254a68d4..64abaffd04e66 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -6,8 +6,10 @@ namespace Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Ui\DataProvider\Modifier\PoolInterface; +use Magento\Framework\AuthorizationInterface; /** * Class DataProvider @@ -29,6 +31,11 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ protected $loadedData; + /** + * @var AuthorizationInterface + */ + private $auth; + /** * @param string $name * @param string $primaryFieldName @@ -38,6 +45,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param array $meta * @param array $data * @param PoolInterface|null $pool + * @param AuthorizationInterface|null $auth */ public function __construct( $name, @@ -47,11 +55,13 @@ public function __construct( DataPersistorInterface $dataPersistor, array $meta = [], array $data = [], - PoolInterface $pool = null + PoolInterface $pool = null, + ?AuthorizationInterface $auth = null ) { $this->collection = $pageCollectionFactory->create(); $this->dataPersistor = $dataPersistor; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); + $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); $this->meta = $this->prepareMeta($this->meta); } @@ -92,4 +102,38 @@ public function getData() return $this->loadedData; } + + /** + * @inheritDoc + */ + public function getMeta() + { + $meta = parent::getMeta(); + + if (!$this->auth->isAllowed('Magento_Cms::save_design')) { + $designMeta = [ + 'design' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'disabled' => true + ] + ] + ] + ], + 'custom_design_update' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'disabled' => true + ] + ] + ] + ] + ]; + $meta = array_merge_recursive($meta, $designMeta); + } + + return $meta; + } } diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 5578ae49f586d..4b6c933c4fef1 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -10,6 +10,7 @@ use Magento\Cms\Api\PageRepositoryInterface; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; @@ -17,6 +18,8 @@ use Magento\Cms\Model\ResourceModel\Page as ResourcePage; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Authorization\Model\UserContextInterface; /** * Class PageRepository @@ -69,6 +72,16 @@ class PageRepository implements PageRepositoryInterface */ private $collectionProcessor; + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * @param ResourcePage $resource * @param PageFactory $pageFactory @@ -79,6 +92,9 @@ class PageRepository implements PageRepositoryInterface * @param DataObjectProcessor $dataObjectProcessor * @param StoreManagerInterface $storeManager * @param CollectionProcessorInterface $collectionProcessor + * @param UserContextInterface|null $userContext + * @param AuthorizationInterface|null $authorization + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( ResourcePage $resource, @@ -89,7 +105,9 @@ public function __construct( DataObjectHelper $dataObjectHelper, DataObjectProcessor $dataObjectProcessor, StoreManagerInterface $storeManager, - CollectionProcessorInterface $collectionProcessor = null + CollectionProcessorInterface $collectionProcessor = null, + ?UserContextInterface $userContext = null, + ?AuthorizationInterface $authorization = null ) { $this->resource = $resource; $this->pageFactory = $pageFactory; @@ -100,12 +118,14 @@ public function __construct( $this->dataObjectProcessor = $dataObjectProcessor; $this->storeManager = $storeManager; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); + $this->userContext = $userContext ?? ObjectManager::getInstance()->get(UserContextInterface::class); + $this->authorization = $authorization ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); } /** * Save Page data * - * @param \Magento\Cms\Api\Data\PageInterface $page + * @param \Magento\Cms\Api\Data\PageInterface|Page $page * @return Page * @throws CouldNotSaveException */ @@ -116,6 +136,32 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $page->setStoreId($storeId); } try { + //Validate changing of design. + $userType = $this->userContext->getUserType(); + if (( + $userType === UserContextInterface::USER_TYPE_ADMIN + || $userType === UserContextInterface::USER_TYPE_INTEGRATION + ) + && !$this->authorization->isAllowed('Magento_Cms::save_design') + ) { + if (!$page->getId()) { + $page->setLayoutUpdateXml(null); + $page->setPageLayout(null); + $page->setCustomTheme(null); + $page->setCustomLayoutUpdateXml(null); + $page->setCustomThemeTo(null); + $page->setCustomThemeFrom(null); + } else { + $savedPage = $this->getById($page->getId()); + $page->setLayoutUpdateXml($savedPage->getLayoutUpdateXml()); + $page->setPageLayout($savedPage->getPageLayout()); + $page->setCustomTheme($savedPage->getCustomTheme()); + $page->setCustomLayoutUpdateXml($savedPage->getCustomLayoutUpdateXml()); + $page->setCustomThemeTo($savedPage->getCustomThemeTo()); + $page->setCustomThemeFrom($savedPage->getCustomThemeFrom()); + } + } + $this->resource->save($page); } catch (\Exception $exception) { throw new CouldNotSaveException( diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index f051271c05051..b80ab60a7ec64 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -15,7 +15,8 @@ "magento/module-theme": "*", "magento/module-ui": "*", "magento/module-variable": "*", - "magento/module-widget": "*" + "magento/module-widget": "*", + "magento/module-authorization": "*" }, "suggest": { "magento/module-cms-sample-data": "*" diff --git a/app/code/Magento/Cms/etc/acl.xml b/app/code/Magento/Cms/etc/acl.xml index b13b58b101f90..3df31923d1eb6 100644 --- a/app/code/Magento/Cms/etc/acl.xml +++ b/app/code/Magento/Cms/etc/acl.xml @@ -12,7 +12,9 @@ - + + + diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv index b34793c35d659..2947c567d75ff 100644 --- a/app/code/Magento/Cms/i18n/en_US.csv +++ b/app/code/Magento/Cms/i18n/en_US.csv @@ -155,3 +155,4 @@ Enable,Enable "Custom design to","Custom design to" "Custom Theme","Custom Theme" "Custom Layout","Custom Layout" +"Edit Page Design","Edit Page Design" diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/Block/Identity.php b/app/code/Magento/CmsGraphQl/Model/Resolver/Block/Identity.php new file mode 100644 index 0000000000000..a40d23968c3c6 --- /dev/null +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/Block/Identity.php @@ -0,0 +1,37 @@ +widgetFilter->filter($block->getContent()); $blockData = [ + BlockInterface::BLOCK_ID => $block->getId(), BlockInterface::IDENTIFIER => $block->getIdentifier(), BlockInterface::TITLE => $block->getTitle(), BlockInterface::CONTENT => $renderedContent, diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php index 22009824452be..8e1e770b01e9d 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php @@ -40,6 +40,8 @@ public function __construct( } /** + * Get the page data + * * @param int $pageId * @return array * @throws NoSuchEntityException @@ -55,6 +57,7 @@ public function getData(int $pageId): array $renderedContent = $this->widgetFilter->filter($page->getContent()); $pageData = [ + PageInterface::PAGE_ID => $page->getId(), 'url_key' => $page->getIdentifier(), PageInterface::TITLE => $page->getTitle(), PageInterface::CONTENT => $renderedContent, diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/Page/Identity.php b/app/code/Magento/CmsGraphQl/Model/Resolver/Page/Identity.php new file mode 100644 index 0000000000000..abc306451e309 --- /dev/null +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/Page/Identity.php @@ -0,0 +1,28 @@ +_eventManager = $eventManager; @@ -163,7 +163,7 @@ public function __construct( $this->scopeTypeNormalizer = $scopeTypeNormalizer ?? ObjectManager::getInstance()->get(ScopeTypeNormalizer::class); $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class); + ->get(\Magento\Framework\MessageQueue\PoisonPill\PoisonPillPutInterface::class); } /** diff --git a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php index 1a91e403a679d..ea3b1d4c74a5f 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php +++ b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php @@ -9,6 +9,8 @@ namespace Magento\Config\Model\Config\Backend; /** + * Backend model for encrypted values. + * * @api * @since 100.0.2 */ @@ -48,9 +50,14 @@ public function __construct( * Magic method called during class serialization * * @return string[] + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $properties = parent::__sleep(); return array_diff($properties, ['_encryptor']); } @@ -59,9 +66,14 @@ public function __sleep() * Magic method called during class un-serialization * * @return void + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $this->_encryptor = \Magento\Framework\App\ObjectManager::getInstance()->get( \Magento\Framework\Encryption\EncryptorInterface::class diff --git a/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php b/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php index b3474674cf76d..5beff0d043ade 100644 --- a/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php +++ b/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php @@ -4,12 +4,15 @@ * See COPYING.txt for license details. */ -/** - * Locale currency source - */ namespace Magento\Config\Model\Config\Source\Locale; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\ListsInterface; + /** + * Locale currency source. + * * @api * @since 100.0.2 */ @@ -21,27 +24,70 @@ class Currency implements \Magento\Framework\Option\ArrayInterface protected $_options; /** - * @var \Magento\Framework\Locale\ListsInterface + * @var ListsInterface */ protected $_localeLists; /** - * @param \Magento\Framework\Locale\ListsInterface $localeLists + * @var ScopeConfigInterface */ - public function __construct(\Magento\Framework\Locale\ListsInterface $localeLists) - { + private $config; + + /** + * @var array + */ + private $installedCurrencies; + + /** + * @param ListsInterface $localeLists + * @param ScopeConfigInterface $config + */ + public function __construct( + ListsInterface $localeLists, + ScopeConfigInterface $config = null + ) { $this->_localeLists = $localeLists; + $this->config = $config ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** - * @return array + * @inheritdoc */ public function toOptionArray() { if (!$this->_options) { $this->_options = $this->_localeLists->getOptionCurrencies(); } - $options = $this->_options; + + $selected = array_flip($this->getInstalledCurrencies()); + + $options = array_filter( + $this->_options, + function ($option) use ($selected) { + return isset($selected[$option['value']]); + } + ); + return $options; } + + /** + * Retrieve Installed Currencies. + * + * @return array + */ + private function getInstalledCurrencies() + { + if (!$this->installedCurrencies) { + $this->installedCurrencies = explode( + ',', + $this->config->getValue( + 'system/currency/installed', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ) + ); + } + + return $this->installedCurrencies; + } } diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 3312fb630ccda..57c067d2cae27 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -7,7 +7,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-message-queue": "*", "magento/module-backend": "*", "magento/module-cron": "*", "magento/module-deploy": "*", diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php index 7306942c3c49b..4ead9ffe0fe70 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php @@ -12,6 +12,8 @@ use Magento\Framework\EntityManager\MetadataPool; /** + * Configurable product attribute model. + * * @method Attribute setProductAttribute(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $value) * @method \Magento\Eav\Model\Entity\Attribute\AbstractAttribute getProductAttribute() */ @@ -86,7 +88,7 @@ public function getOptions() } /** - * {@inheritdoc} + * @inheritdoc */ public function getLabel() { @@ -112,10 +114,10 @@ public function afterSave() } /** - * Load configurable attribute by product and product's attribute + * Load configurable attribute by product and product's attribute. * * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute + * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute * @return void */ public function loadByProductAndAttribute($product, $attribute) @@ -144,7 +146,7 @@ public function deleteByProduct($product) } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnore */ public function getAttributeId() @@ -153,7 +155,7 @@ public function getAttributeId() } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnore */ public function getPosition() @@ -162,7 +164,7 @@ public function getPosition() } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnore */ public function getIsUseDefault() @@ -171,7 +173,7 @@ public function getIsUseDefault() } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnore */ public function getValues() @@ -182,8 +184,7 @@ public function getValues() //@codeCoverageIgnoreStart /** - * @param string $attributeId - * @return $this + * @inheritdoc */ public function setAttributeId($attributeId) { @@ -191,8 +192,7 @@ public function setAttributeId($attributeId) } /** - * @param string $label - * @return $this + * @inheritdoc */ public function setLabel($label) { @@ -200,8 +200,7 @@ public function setLabel($label) } /** - * @param int $position - * @return $this + * @inheritdoc */ public function setPosition($position) { @@ -209,8 +208,7 @@ public function setPosition($position) } /** - * @param bool $isUseDefault - * @return $this + * @inheritdoc */ public function setIsUseDefault($isUseDefault) { @@ -218,8 +216,7 @@ public function setIsUseDefault($isUseDefault) } /** - * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterface[] $values - * @return $this + * @inheritdoc */ public function setValues(array $values = null) { @@ -227,7 +224,7 @@ public function setValues(array $values = null) } /** - * {@inheritdoc} + * @inheritdoc * * @return \Magento\ConfigurableProduct\Api\Data\OptionExtensionInterface|null */ @@ -237,7 +234,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * * @param \Magento\ConfigurableProduct\Api\Data\OptionExtensionInterface $extensionAttributes * @return $this @@ -249,7 +246,7 @@ public function setExtensionAttributes( } /** - * {@inheritdoc} + * @inheritdoc */ public function getProductId() { @@ -257,7 +254,7 @@ public function getProductId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setProductId($value) { @@ -268,9 +265,14 @@ public function setProductId($value) /** * @inheritdoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + return array_diff( parent::__sleep(), ['metadataPool'] @@ -279,9 +281,14 @@ public function __sleep() /** * @inheritdoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->metadataPool = $objectManager->get(MetadataPool::class); diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php index 3c40d326be77f..81cbbd06c523c 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php @@ -18,6 +18,8 @@ use Magento\Catalog\Api\Data\ProductInterface; /** + * Collection of configurable product attributes. + * * @api * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -302,6 +304,8 @@ protected function _loadLabels() } /** + * Load related options' data. + * * @return void */ protected function loadOptions() @@ -354,9 +358,14 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr /** * @inheritdoc * @since 100.0.6 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + return array_diff( parent::__sleep(), [ @@ -373,9 +382,14 @@ public function __sleep() /** * @inheritdoc * @since 100.0.6 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = ObjectManager::getInstance(); $this->_storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class); diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index 069838231d775..5a172ca5eabdf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -131,6 +131,22 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index eccff2830d2a5..a5e74145c9fec 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -26,6 +26,10 @@ + + + + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 93df31a7d89e5..2cc71964042a4 100755 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -53,9 +53,6 @@ - - - @@ -93,10 +90,6 @@ - - - - @@ -139,9 +132,6 @@ - - - diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index e732960421541..ef40dcb9a7323 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -373,7 +373,7 @@ define([ allowedProducts, i, j, - basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount), + finalPrice = parseFloat(this.options.spConfig.prices.finalPrice.amount), optionFinalPrice, optionPriceDiff, optionPrices = this.options.spConfig.optionPrices, @@ -410,7 +410,7 @@ define([ typeof optionPrices[allowedProducts[0]] !== 'undefined') { allowedProductMinPrice = this._getAllowedProductWithMinPrice(allowedProducts); optionFinalPrice = parseFloat(optionPrices[allowedProductMinPrice].finalPrice.amount); - optionPriceDiff = optionFinalPrice - basePrice; + optionPriceDiff = optionFinalPrice - finalPrice; if (optionPriceDiff !== 0) { options[i].label = options[i].label + ' ' + priceUtils.formatPrice( diff --git a/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php b/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php index dd8dec0b94c15..c980fe1fe7769 100644 --- a/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php +++ b/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php @@ -13,6 +13,9 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +/** + * Upgrade users passwords to the new algorithm + */ class UpgradeHashAlgorithmCommand extends Command { /** @@ -65,8 +68,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $customer->load($customer->getId()); if (!$this->encryptor->validateHashVersion($customer->getPasswordHash())) { list($hash, $salt, $version) = explode(Encryptor::DELIMITER, $customer->getPasswordHash(), 3); - $version .= Encryptor::DELIMITER . Encryptor::HASH_VERSION_LATEST; - $customer->setPasswordHash($this->encryptor->getHash($hash, $salt, $version)); + $version .= Encryptor::DELIMITER . $this->encryptor->getLatestHashVersion(); + $hash = $this->encryptor->getHash($hash, $salt, $this->encryptor->getLatestHashVersion()); + list($hash, $salt) = explode(Encryptor::DELIMITER, $hash, 3); + $hash = implode(Encryptor::DELIMITER, [$hash, $salt, $version]); + $customer->setPasswordHash($hash); $customer->save(); $output->write("."); } diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 250d190f8fae7..23d667812c357 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -554,6 +554,7 @@ public function authenticate($username, $password) } try { $this->getAuthentication()->authenticate($customerId, $password); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (InvalidEmailOrPasswordException $e) { throw new InvalidEmailOrPasswordException(__('Invalid login or password.')); } @@ -894,6 +895,7 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash throw new InputMismatchException( __('A customer with the same email address already exists in an associated website.') ); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (LocalizedException $e) { throw $e; } @@ -910,6 +912,7 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash } } $this->customerRegistry->remove($customer->getId()); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (InputException $e) { $this->customerRepository->delete($customer); throw $e; @@ -1012,6 +1015,7 @@ private function changePasswordForCustomer($customer, $currentPassword, $newPass { try { $this->getAuthentication()->authenticate($customer->getId(), $currentPassword); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (InvalidEmailOrPasswordException $e) { throw new InvalidEmailOrPasswordException( __("The password doesn't match this account. Verify the password and try again.") @@ -1071,6 +1075,7 @@ public function validate(CustomerInterface $customer) $result = $this->getEavValidator()->isValid($customerModel); if ($result === false && is_array($this->getEavValidator()->getMessages())) { return $validationResults->setIsValid(false)->setMessages( + // phpcs:ignore Magento2.Functions.DiscouragedFunction call_user_func_array( 'array_merge', $this->getEavValidator()->getMessages() @@ -1532,7 +1537,7 @@ protected function getFullCustomerObject($customer) */ public function getPasswordHash($password) { - return $this->encryptor->getHash($password); + return $this->encryptor->getHash($password, true); } /** diff --git a/app/code/Magento/Customer/Model/Attribute.php b/app/code/Magento/Customer/Model/Attribute.php index 98a97872f15f4..ae714f993082e 100644 --- a/app/code/Magento/Customer/Model/Attribute.php +++ b/app/code/Magento/Customer/Model/Attribute.php @@ -202,9 +202,14 @@ public function canBeFilterableInGrid() /** * @inheritdoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $this->unsetData('entity_type'); return array_diff( parent::__sleep(), @@ -214,9 +219,14 @@ public function __sleep() /** * @inheritdoc + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->indexerRegistry = $objectManager->get(\Magento\Framework\Indexer\IndexerRegistry::class); diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 4d1bb2e6b9e99..07b8681df91ac 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -39,7 +39,6 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract private static $forbiddenCustomerFields = [ 'password_hash', 'rp_token', - 'confirmation', ]; /** diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Customer/Collection.php index 394a0d3ed556d..af8980a129d3e 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Customer/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer/Collection.php @@ -5,8 +5,6 @@ */ namespace Magento\Customer\Model\ResourceModel\Customer; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Customers collection * @@ -45,7 +43,6 @@ class Collection extends \Magento\Eav\Model\Entity\Collection\VersionControl\Abs * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param string $modelName * - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -61,8 +58,7 @@ public function __construct( \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot, \Magento\Framework\DataObject\Copy\Config $fieldsetConfig, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - $modelName = self::CUSTOMER_MODEL_NAME, - ResourceModelPoolInterface $resourceModelPool = null + $modelName = self::CUSTOMER_MODEL_NAME ) { $this->_fieldsetConfig = $fieldsetConfig; $this->_modelName = $modelName; @@ -77,8 +73,7 @@ public function __construct( $resourceHelper, $universalFactory, $entitySnapshot, - $connection, - $resourceModelPool + $connection ); } diff --git a/app/code/Magento/Customer/Setup/Patch/Data/MigrateStoresAllowedCountriesToWebsite.php b/app/code/Magento/Customer/Setup/Patch/Data/MigrateStoresAllowedCountriesToWebsite.php index 7488f3fd4a920..e4978070f53ad 100644 --- a/app/code/Magento/Customer/Setup/Patch/Data/MigrateStoresAllowedCountriesToWebsite.php +++ b/app/code/Magento/Customer/Setup/Patch/Data/MigrateStoresAllowedCountriesToWebsite.php @@ -8,12 +8,14 @@ use Magento\Directory\Model\AllowedCountries; use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Directory\Model\AllowedCountriesFactory; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; +/** + * Migrate store allowed countries to website. + */ class MigrateStoresAllowedCountriesToWebsite implements DataPatchInterface, PatchVersionInterface { /** @@ -27,7 +29,7 @@ class MigrateStoresAllowedCountriesToWebsite implements DataPatchInterface, Patc private $storeManager; /** - * @var AllowedCountriesFactory + * @var AllowedCountries */ private $allowedCountries; @@ -48,10 +50,11 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { + $this->moduleDataSetup->getConnection()->beginTransaction(); try { @@ -149,7 +152,7 @@ private function mergeAllowedCountries(array $countries, array $newCountries, $i } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -159,7 +162,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -167,7 +170,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php index 2fc3cdb927723..d5eaecb3ef35f 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php @@ -339,7 +339,6 @@ public function testGetData(): void 'default_shipping' => 2, 'password_hash' => 'password_hash', 'rp_token' => 'rp_token', - 'confirmation' => 'confirmation', ]; $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 7e6b7bbe9cd09..e87997dbdb5e9 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -74,6 +74,17 @@ false + + + + customer + + + + text + false + + diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 4e4fd1d0fa8ad..1238184075057 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") + customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") @cache(cacheable: false) isEmailAvailable ( email: String! @doc(description: "The new customer email") ): IsEmailAvailableOutput @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\IsEmailAvailable") diff --git a/app/code/Magento/DirectoryGraphQl/Controller/HttpHeaderProcessor/CurrencyProcessor.php b/app/code/Magento/DirectoryGraphQl/Controller/HttpHeaderProcessor/CurrencyProcessor.php new file mode 100644 index 0000000000000..b076b6fe9e768 --- /dev/null +++ b/app/code/Magento/DirectoryGraphQl/Controller/HttpHeaderProcessor/CurrencyProcessor.php @@ -0,0 +1,105 @@ +storeManager = $storeManager; + $this->httpContext = $httpContext; + $this->session = $session; + $this->logger = $logger; + } + + /** + * Handle the header 'Content-Currency' value. + * + * @param string $headerValue + * @return void + */ + public function processHeaderValue(string $headerValue) : void + { + try { + if (!empty($headerValue)) { + $headerCurrency = strtoupper(ltrim(rtrim($headerValue))); + /** @var \Magento\Store\Model\Store $currentStore */ + $currentStore = $this->storeManager->getStore(); + if (in_array($headerCurrency, $currentStore->getAvailableCurrencyCodes(true))) { + $currentStore->setCurrentCurrencyCode($headerCurrency); + } else { + /** @var \Magento\Store\Model\Store $store */ + $store = $this->storeManager->getStore() ?? $this->storeManager->getDefaultStoreView(); + //skip store not found exception as it will be handled in graphql validation + $this->logger->warning(__('Currency not allowed for store %1', [$store->getCode()])); + $this->httpContext->setValue( + HttpContext::CONTEXT_CURRENCY, + $headerCurrency, + $store->getDefaultCurrency()->getCode() + ); + } + } else { + if ($this->session->getCurrencyCode()) { + /** @var \Magento\Store\Model\Store $currentStore */ + $currentStore = $this->storeManager->getStore() ?? $this->storeManager->getDefaultStoreView(); + $currentStore->setCurrentCurrencyCode($this->session->getCurrencyCode()); + } else { + /** @var \Magento\Store\Model\Store $store */ + $store = $this->storeManager->getStore() ?? $this->storeManager->getDefaultStoreView(); + $this->httpContext->setValue( + HttpContext::CONTEXT_CURRENCY, + $store->getCurrentCurrency()->getCode(), + $store->getDefaultCurrency()->getCode() + ); + } + } + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //skip store not found exception as it will be handled in graphql validation + $this->logger->warning($e->getMessage()); + } + } +} diff --git a/app/code/Magento/DirectoryGraphQl/Controller/HttpRequestValidator/CurrencyValidator.php b/app/code/Magento/DirectoryGraphQl/Controller/HttpRequestValidator/CurrencyValidator.php new file mode 100644 index 0000000000000..7dab90802c205 --- /dev/null +++ b/app/code/Magento/DirectoryGraphQl/Controller/HttpRequestValidator/CurrencyValidator.php @@ -0,0 +1,62 @@ +storeManager = $storeManager; + } + + /** + * Validate the header 'Content-Currency' value. + * + * @param HttpRequestInterface $request + * @return void + * @throws GraphQlInputException + */ + public function validate(HttpRequestInterface $request): void + { + try { + $headerValue = $request->getHeader('Content-Currency'); + if (!empty($headerValue)) { + $headerCurrency = strtoupper(ltrim(rtrim($headerValue))); + /** @var \Magento\Store\Model\Store $currentStore */ + $currentStore = $this->storeManager->getStore(); + if (!in_array($headerCurrency, $currentStore->getAvailableCurrencyCodes(true))) { + throw new GraphQlInputException( + __('Please correct the target currency') + ); + } + } + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $this->storeManager->setCurrentStore(null); + throw new GraphQlInputException( + __("Requested store is not found") + ); + } + } +} diff --git a/app/code/Magento/DirectoryGraphQl/composer.json b/app/code/Magento/DirectoryGraphQl/composer.json index 0a81102a92767..d3c783e6c7bfe 100644 --- a/app/code/Magento/DirectoryGraphQl/composer.json +++ b/app/code/Magento/DirectoryGraphQl/composer.json @@ -5,11 +5,10 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/module-directory": "*", + "magento/module-store": "*", + "magento/module-graph-ql": "*", "magento/framework": "*" }, - "suggest": { - "magento/module-graph-ql": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/DirectoryGraphQl/etc/graphql/di.xml b/app/code/Magento/DirectoryGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..63f501551f535 --- /dev/null +++ b/app/code/Magento/DirectoryGraphQl/etc/graphql/di.xml @@ -0,0 +1,19 @@ + + + + + + + Magento\DirectoryGraphQl\Controller\HttpHeaderProcessor\CurrencyProcessor + + + Magento\DirectoryGraphQl\Controller\HttpRequestValidator\CurrencyValidator + + + + diff --git a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls index 8da1920f9a444..6daf13f567d4b 100644 --- a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls @@ -2,9 +2,9 @@ # See COPYING.txt for license details. type Query { - currency: Currency @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Currency") @doc(description: "The currency query returns information about store currency.") - countries: [Country] @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Countries") @doc(description: "The countries query provides information for all countries.") - country (id: String): Country @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Country") @doc(description: "The countries query provides information for a single country.") + currency: Currency @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Currency") @doc(description: "The currency query returns information about store currency.") @cache(cacheable: false) + countries: [Country] @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Countries") @doc(description: "The countries query provides information for all countries.") @cache(cacheable: false) + country (id: String): Country @resolver(class: "Magento\\DirectoryGraphQl\\Model\\Resolver\\Country") @doc(description: "The countries query provides information for a single country.") @cache(cacheable: false) } type Currency { diff --git a/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls b/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls index e2cacdf7608d6..788a5fc601ee1 100644 --- a/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - customerDownloadableProducts: CustomerDownloadableProducts @resolver(class: "Magento\\DownloadableGraphQl\\Model\\Resolver\\CustomerDownloadableProducts") @doc(description: "The query returns the contents of a customer's downloadable products") + customerDownloadableProducts: CustomerDownloadableProducts @resolver(class: "Magento\\DownloadableGraphQl\\Model\\Resolver\\CustomerDownloadableProducts") @doc(description: "The query returns the contents of a customer's downloadable products") @cache(cacheable: false) } type CustomerDownloadableProducts { diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 23054ad613c21..e23f81607a0c0 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -310,9 +310,9 @@ public function beforeSave() } /** - * Save additional data + * @inheritdoc * - * @return $this + * Save additional data. */ public function afterSave() { @@ -496,9 +496,14 @@ public function getIdentities() /** * @inheritdoc * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $this->unsetData('attribute_set_info'); return array_diff( parent::__sleep(), @@ -509,9 +514,14 @@ public function __sleep() /** * @inheritdoc * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = ObjectManager::getInstance(); $this->_localeDate = $objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index 7ed455eccf4e0..9ed4ac5293681 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -1404,9 +1404,14 @@ public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionI /** * @inheritdoc * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + return array_diff( parent::__sleep(), [ @@ -1429,9 +1434,14 @@ public function __sleep() /** * @inheritdoc * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->_eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index 52f106a0475f5..e50abbc11e54a 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -6,12 +6,10 @@ namespace Magento\Eav\Model\Entity\Collection; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection\SourceProviderInterface; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\DB\Select; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; /** * Entity/Attribute/Model - collection abstract @@ -128,15 +126,10 @@ abstract class AbstractCollection extends AbstractDb implements SourceProviderIn protected $_resourceHelper; /** - * @deprecated To instantiate resource models, use $resourceModelPool instead * * @var \Magento\Framework\Validator\UniversalFactory */ protected $_universalFactory; - /** - * @var ResourceModelPoolInterface - */ - private $resourceModelPool; /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory @@ -149,7 +142,6 @@ abstract class AbstractCollection extends AbstractDb implements SourceProviderIn * @param \Magento\Eav\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param mixed $connection - * @param ResourceModelPoolInterface|null $resourceModelPool * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -162,9 +154,8 @@ public function __construct( \Magento\Framework\App\ResourceConnection $resource, \Magento\Eav\Model\EntityFactory $eavEntityFactory, \Magento\Eav\Model\ResourceModel\Helper $resourceHelper, - \Magento\Framework\Validator\UniversalFactory $universalFactory = null, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\Validator\UniversalFactory $universalFactory, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->_eventManager = $eventManager; $this->_eavConfig = $eavConfig; @@ -172,12 +163,6 @@ public function __construct( $this->_eavEntityFactory = $eavEntityFactory; $this->_resourceHelper = $resourceHelper; $this->_universalFactory = $universalFactory; - if ($resourceModelPool === null) { - $resourceModelPool = ObjectManager::getInstance()->get( - ResourceModelPoolInterface::class - ); - } - $this->resourceModelPool = $resourceModelPool; parent::__construct($entityFactory, $logger, $fetchStrategy, $connection); $this->_construct(); $this->setConnection($this->getEntity()->getConnection()); @@ -245,7 +230,7 @@ protected function _initSelect() protected function _init($model, $entityModel) { $this->setItemObjectClass($model); - $entity = $this->resourceModelPool->get($entityModel); + $entity = $this->_universalFactory->create($entityModel); $this->setEntity($entity); return $this; diff --git a/app/code/Magento/Eav/Model/Entity/Collection/VersionControl/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/VersionControl/AbstractCollection.php index 2181c6bc1be05..631bfa3c2d2b5 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/VersionControl/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/VersionControl/AbstractCollection.php @@ -5,10 +5,10 @@ */ namespace Magento\Eav\Model\Entity\Collection\VersionControl; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Class Abstract Collection + * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -21,6 +21,7 @@ abstract class AbstractCollection extends \Magento\Eav\Model\Entity\Collection\A protected $entitySnapshot; /** + * AbstractCollection constructor. * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy @@ -30,9 +31,9 @@ abstract class AbstractCollection extends \Magento\Eav\Model\Entity\Collection\A * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory * @param \Magento\Eav\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory - * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot , - * @param mixed $connection - * @param ResourceModelPoolInterface|null $resourceModelPool + * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore */ @@ -47,8 +48,7 @@ public function __construct( \Magento\Eav\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->entitySnapshot = $entitySnapshot; @@ -62,8 +62,7 @@ public function __construct( $eavEntityFactory, $resourceHelper, $universalFactory, - $connection, - $resourceModelPool + $connection ); } diff --git a/app/code/Magento/Eav/Model/Entity/Type.php b/app/code/Magento/Eav/Model/Entity/Type.php index b24f86c73e8df..444d58bf546d4 100644 --- a/app/code/Magento/Eav/Model/Entity/Type.php +++ b/app/code/Magento/Eav/Model/Entity/Type.php @@ -5,9 +5,6 @@ */ namespace Magento\Eav\Model\Entity; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Entity type model * @@ -78,16 +75,10 @@ class Type extends \Magento\Framework\Model\AbstractModel protected $_storeFactory; /** - * @deprecated To instantiate resource models, use $resourceModelPool instead * @var \Magento\Framework\Validator\UniversalFactory */ protected $_universalFactory; - /** - * @var ResourceModelPoolInterface - */ - private $resourceModelPool; - /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -98,9 +89,7 @@ class Type extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @param ResourceModelPoolInterface|null $resourceModelPool * @codeCoverageIgnore - * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -111,20 +100,13 @@ public function __construct( \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [], - ResourceModelPoolInterface $resourceModelPool = null + array $data = [] ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->_attributeFactory = $attributeFactory; $this->_attSetFactory = $attSetFactory; $this->_storeFactory = $storeFactory; $this->_universalFactory = $universalFactory; - if ($resourceModelPool === null) { - $resourceModelPool = ObjectManager::getInstance()->get( - ResourceModelPoolInterface::class - ); - } - $this->resourceModelPool = $resourceModelPool; } /** @@ -381,7 +363,7 @@ public function getAttributeModel() */ public function getEntity() { - return $this->resourceModelPool->get($this->_data['entity_model']); + return $this->_universalFactory->create($this->_data['entity_model']); } /** diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php index 0e7a46125d872..5e7226e7a36dd 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php @@ -725,9 +725,14 @@ public function getValidAttributeIds($attributeIds) * * @return array * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __sleep() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + $properties = parent::__sleep(); $properties = array_diff($properties, ['_storeManager']); return $properties; @@ -738,9 +743,14 @@ public function __sleep() * * @return void * @since 100.0.7 + * + * @SuppressWarnings(PHPMD.SerializationAware) + * @deprecated Do not use PHP serialization. */ public function __wakeup() { + trigger_error('Using PHP serialization is deprecated', E_USER_DEPRECATED); + parent::__wakeup(); $this->_storeManager = \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Store\Model\StoreManagerInterface::class); diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php index c7af666604b39..ab5b40c56685c 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php @@ -63,7 +63,7 @@ class AbstractCollectionTest extends \PHPUnit\Framework\TestCase /** * @var ResourceModelPoolInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $resourceModelPoolMock; + protected $validatorFactoryMock; /** * @var \Magento\Framework\DB\Statement\Pdo\Mysql|\PHPUnit_Framework_MockObject_MockObject @@ -74,11 +74,17 @@ protected function setUp() { $this->coreEntityFactoryMock = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->fetchStrategyMock = $this->createMock(FetchStrategyInterface::class); + $this->fetchStrategyMock = $this->createMock( + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface::class + ); $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); $this->configMock = $this->createMock(\Magento\Eav\Model\Config::class); + $this->coreResourceMock = $this->createMock(\Magento\Framework\App\ResourceConnection::class); $this->resourceHelperMock = $this->createMock(\Magento\Eav\Model\ResourceModel\Helper::class); + $this->validatorFactoryMock = $this->createMock(\Magento\Framework\Validator\UniversalFactory::class); $this->entityFactoryMock = $this->createMock(\Magento\Eav\Model\EntityFactory::class); + /** @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ + $connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class); $this->statementMock = $this->createPartialMock(\Magento\Framework\DB\Statement\Pdo\Mysql::class, ['fetch']); /** @var $selectMock \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject */ $selectMock = $this->createMock(\Magento\Framework\DB\Select::class); @@ -89,12 +95,9 @@ protected function setUp() )->will( $this->returnCallback([$this, 'getMagentoObject']) ); - /** @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ - $connectionMock = $this->createMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class); $connectionMock->expects($this->any())->method('select')->will($this->returnValue($selectMock)); $connectionMock->expects($this->any())->method('query')->willReturn($this->statementMock); - $this->coreResourceMock = $this->createMock(\Magento\Framework\App\ResourceConnection::class); $this->coreResourceMock->expects( $this->any() )->method( @@ -106,11 +109,10 @@ protected function setUp() $entityMock->expects($this->any())->method('getConnection')->will($this->returnValue($connectionMock)); $entityMock->expects($this->any())->method('getDefaultAttributes')->will($this->returnValue([])); - $this->resourceModelPoolMock = $this->createMock(ResourceModelPoolInterface::class); - $this->resourceModelPoolMock->expects( + $this->validatorFactoryMock->expects( $this->any() )->method( - 'get' + 'create' )->with( 'test_entity_model' // see \Magento\Eav\Test\Unit\Model\Entity\Collection\AbstractCollectionStub )->will( @@ -126,9 +128,8 @@ protected function setUp() $this->coreResourceMock, $this->entityFactoryMock, $this->resourceHelperMock, - null, - null, - $this->resourceModelPoolMock + $this->validatorFactoryMock, + null ); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/VersionControl/AbstractCollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/VersionControl/AbstractCollectionTest.php index 5b41b9b71f4b5..cce7b43786a76 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/VersionControl/AbstractCollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/VersionControl/AbstractCollectionTest.php @@ -39,7 +39,7 @@ protected function setUp() \Magento\Eav\Test\Unit\Model\Entity\Collection\VersionControl\AbstractCollectionStub::class, [ 'entityFactory' => $this->coreEntityFactoryMock, - 'resourceModelPool' => $this->resourceModelPoolMock, + 'universalFactory' => $this->validatorFactoryMock, 'entitySnapshot' => $this->entitySnapshot ] ); diff --git a/app/code/Magento/EavGraphQl/etc/schema.graphqls b/app/code/Magento/EavGraphQl/etc/schema.graphqls index adada3030f501..0299067bd0523 100644 --- a/app/code/Magento/EavGraphQl/etc/schema.graphqls +++ b/app/code/Magento/EavGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - customAttributeMetadata(attributes: [AttributeInput!]!): CustomAttributeMetadata @resolver(class: "Magento\\EavGraphQl\\Model\\Resolver\\CustomAttributeMetadata") @doc(description: "The customAttributeMetadata query returns the attribute type, given an attribute code and entity type") + customAttributeMetadata(attributes: [AttributeInput!]!): CustomAttributeMetadata @resolver(class: "Magento\\EavGraphQl\\Model\\Resolver\\CustomAttributeMetadata") @doc(description: "The customAttributeMetadata query returns the attribute type, given an attribute code and entity type") @cache(cacheable: false) } type CustomAttributeMetadata @doc(description: "CustomAttributeMetadata defines an array of attribute_codes and entity_types") { diff --git a/app/code/Magento/Email/Model/Template.php b/app/code/Magento/Email/Model/Template.php index 7672d900615ce..ef2c4341dafa4 100644 --- a/app/code/Magento/Email/Model/Template.php +++ b/app/code/Magento/Email/Model/Template.php @@ -418,7 +418,7 @@ public function setOptions(array $options) } /** - * Get filter factory + * Return filter factory. * * @return \Magento\Email\Model\Template\FilterFactory */ diff --git a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php index 5429530fd1f61..9599cd1fef448 100644 --- a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php @@ -5,21 +5,37 @@ */ namespace Magento\Email\Test\Unit\Model; +use Magento\Email\Model\Template; +use Magento\Email\Model\Template\Config; +use Magento\Email\Model\Template\FilterFactory; +use Magento\Email\Model\TemplateFactory; use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\TemplateTypesInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Url; +use Magento\Framework\View\Asset\Repository; use Magento\Setup\Module\I18n\Locale; +use Magento\Store\Model\App\Emulation; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManager; +use Magento\Theme\Model\View\Design; +use PHPUnit\Framework\TestCase; /** * Covers \Magento\Email\Model\Template * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class TemplateTest extends \PHPUnit\Framework\TestCase +class TemplateTest extends TestCase { /** - * @var \Magento\Framework\Model\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|\PHPUnit_Framework_MockObject_MockObject */ private $context; @@ -29,13 +45,13 @@ class TemplateTest extends \PHPUnit\Framework\TestCase private $design; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|\PHPUnit_Framework_MockObject_MockObject * @deprecated since 2.3.0 in favor of stateful global objects elimination. */ private $registry; /** - * @var \Magento\Store\Model\App\Emulation|\PHPUnit_Framework_MockObject_MockObject + * @var Emulation|\PHPUnit_Framework_MockObject_MockObject */ private $appEmulation; @@ -45,53 +61,53 @@ class TemplateTest extends \PHPUnit\Framework\TestCase private $storeManager; /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject */ private $filesystem; /** - * @var \Magento\Framework\View\Asset\Repository|\PHPUnit_Framework_MockObject_MockObject + * @var Repository|\PHPUnit_Framework_MockObject_MockObject */ private $assetRepo; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ private $scopeConfig; /** - * @var \Magento\Email\Model\Template\FilterFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FilterFactory|\PHPUnit_Framework_MockObject_MockObject */ private $filterFactory; /** - * @var \Magento\Framework\Filter\FilterManager|\PHPUnit_Framework_MockObject_MockObject + * @var FilterManager|\PHPUnit_Framework_MockObject_MockObject */ private $filterManager; /** - * @var \Magento\Framework\Url|\PHPUnit_Framework_MockObject_MockObject + * @var Url|\PHPUnit_Framework_MockObject_MockObject */ private $urlModel; /** - * @var \Magento\Email\Model\Template\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|\PHPUnit_Framework_MockObject_MockObject */ private $emailConfig; /** - * @var \Magento\Email\Model\TemplateFactory|\PHPUnit_Framework_MockObject_MockObject + * @var TemplateFactory|\PHPUnit_Framework_MockObject_MockObject */ private $templateFactory; /** - * @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject + * @var Json|\PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; protected function setUp() { - $this->context = $this->getMockBuilder(\Magento\Framework\Model\Context::class) + $this->context = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); @@ -99,11 +115,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->registry = $this->getMockBuilder(\Magento\Framework\Registry::class) + $this->registry = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); - $this->appEmulation = $this->getMockBuilder(\Magento\Store\Model\App\Emulation::class) + $this->appEmulation = $this->getMockBuilder(Emulation::class) ->disableOriginalConstructor() ->getMock(); @@ -111,51 +127,51 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->assetRepo = $this->getMockBuilder(\Magento\Framework\View\Asset\Repository::class) + $this->assetRepo = $this->getMockBuilder(Repository::class) ->disableOriginalConstructor() ->getMock(); - $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class) + $this->filesystem = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() ->getMock(); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->emailConfig = $this->getMockBuilder(\Magento\Email\Model\Template\Config::class) + $this->emailConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->templateFactory = $this->getMockBuilder(\Magento\Email\Model\TemplateFactory::class) + $this->templateFactory = $this->getMockBuilder(TemplateFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->filterManager = $this->getMockBuilder(\Magento\Framework\Filter\FilterManager::class) + $this->filterManager = $this->getMockBuilder(FilterManager::class) ->disableOriginalConstructor() ->getMock(); - $this->urlModel = $this->getMockBuilder(\Magento\Framework\Url::class) + $this->urlModel = $this->getMockBuilder(Url::class) ->disableOriginalConstructor() ->getMock(); - $this->filterFactory = $this->getMockBuilder(\Magento\Email\Model\Template\FilterFactory::class) + $this->filterFactory = $this->getMockBuilder(FilterFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->serializerMock = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class)->getMock(); + $this->serializerMock = $this->getMockBuilder(Json::class)->getMock(); } /** * Return the model under test with additional methods mocked. * * @param array $mockedMethods - * @return \Magento\Email\Model\Template|\PHPUnit_Framework_MockObject_MockObject + * @return Template|\PHPUnit_Framework_MockObject_MockObject */ protected function getModelMock(array $mockedMethods = []) { - return $this->getMockBuilder(\Magento\Email\Model\Template::class) + return $this->getMockBuilder(Template::class) ->setMethods(array_merge($mockedMethods, ['__wakeup', '__sleep', '_init'])) ->setConstructorArgs([ $this->context, @@ -210,7 +226,7 @@ public function testGetTemplateFilterWithEmptyValue() ->method('setStoreId') ->will($this->returnSelf()); $this->filterFactory->method('create') - ->will($this->returnValue($filterTemplate)); + ->willReturn($filterTemplate); $designConfig = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->setMethods(['getStore']) ->disableOriginalConstructor() @@ -219,7 +235,7 @@ public function testGetTemplateFilterWithEmptyValue() $model = $this->getModelMock(['getUseAbsoluteLinks', 'getDesignConfig']); $model->expects($this->once()) ->method('getDesignConfig') - ->will($this->returnValue($designConfig)); + ->willReturn($designConfig); $this->assertSame($filterTemplate, $model->getTemplateFilter()); } @@ -253,7 +269,7 @@ public function testLoadDefault( $model->expects($this->once()) ->method('getDesignParams') - ->will($this->returnValue($designParams)); + ->willReturn($designParams); $templateId = 'templateId'; @@ -261,11 +277,11 @@ public function testLoadDefault( $this->emailConfig->expects($this->once()) ->method('getTemplateFilename') ->with($templateId) - ->will($this->returnValue($templateFile)); + ->willReturn($templateFile); $this->emailConfig->expects($this->once()) ->method('getTemplateType') ->with($templateId) - ->will($this->returnValue($templateType)); + ->willReturn($templateType); $modulesDir = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) ->setMethods(['readFile', 'getRelativePath']) @@ -275,15 +291,15 @@ public function testLoadDefault( $modulesDir->expects($this->once()) ->method('getRelativePath') ->with($templateFile) - ->will($this->returnValue($relativePath)); + ->willReturn($relativePath); $modulesDir->expects($this->once()) ->method('readFile') - ->will($this->returnValue($templateText)); + ->willReturn($templateText); $this->filesystem->expects($this->once()) ->method('getDirectoryRead') ->with(DirectoryList::ROOT) - ->will($this->returnValue($modulesDir)); + ->willReturn($modulesDir); $model->loadDefault($templateId); @@ -382,10 +398,10 @@ public function testLoadByConfigPath($loadFromDatabase) $storeId = 'storeId'; $designConfig->expects($this->once()) ->method('getStore') - ->will($this->returnValue($storeId)); + ->willReturn($storeId); $model->expects($this->once()) ->method('getDesignConfig') - ->will($this->returnValue($designConfig)); + ->willReturn($designConfig); if ($loadFromDatabase) { $templateId = '1'; @@ -404,7 +420,7 @@ public function testLoadByConfigPath($loadFromDatabase) $this->scopeConfig->expects($this->once()) ->method('getValue') ->with($configPath, ScopeInterface::SCOPE_STORE, $storeId) - ->will($this->returnValue($templateId)); + ->willReturn($templateId); $model->loadByConfigPath($configPath); } @@ -447,13 +463,13 @@ public function testIsValidForSend($senderName, $senderEmail, $templateSubject, $model = $this->getModelMock(['getSenderName', 'getSenderEmail', 'getTemplateSubject']); $model->expects($this->any()) ->method('getSenderName') - ->will($this->returnValue($senderName)); + ->willReturn($senderName); $model->expects($this->any()) ->method('getSenderEmail') - ->will($this->returnValue($senderEmail)); + ->willReturn($senderEmail); $model->expects($this->any()) ->method('getTemplateSubject') - ->will($this->returnValue($templateSubject)); + ->willReturn($templateSubject); $this->assertEquals($expectedValue, $model->isValidForSend()); } @@ -503,7 +519,7 @@ public function testGetProcessedTemplateSubject() ->getMock(); $model->expects($this->once()) ->method('getTemplateFilter') - ->will($this->returnValue($filterTemplate)); + ->willReturn($filterTemplate); $model->expects($this->once()) ->method('applyDesignConfig'); @@ -515,10 +531,10 @@ public function testGetProcessedTemplateSubject() $storeId = 'storeId'; $designConfig->expects($this->once()) ->method('getStore') - ->will($this->returnValue($storeId)); + ->willReturn($storeId); $model->expects($this->once()) ->method('getDesignConfig') - ->will($this->returnValue($designConfig)); + ->willReturn($designConfig); $filterTemplate->expects($this->once()) ->method('setStoreId') @@ -528,7 +544,7 @@ public function testGetProcessedTemplateSubject() $filterTemplate->expects($this->once()) ->method('filter') ->with($templateSubject) - ->will($this->returnValue($expectedResult)); + ->willReturn($expectedResult); $variables = [ 'key' => 'value' ]; $filterTemplate->expects($this->once()) @@ -644,17 +660,17 @@ public function testProcessTemplate($templateId, $expectedResult) $model->expects($this->once()) ->method('applyDesignConfig') - ->will($this->returnValue(true)); + ->willReturn(true); $model->expects($this->once()) ->method('cancelDesignConfig') - ->will($this->returnValue(true)); + ->willReturn(true); $vars = [ 'key' => 'value' ]; $model->setVars($vars); $model->expects($this->once()) ->method('getProcessedTemplate') ->with($vars) - ->will($this->returnValue($expectedResult)); + ->willReturn($expectedResult); $this->assertEquals($expectedResult, $model->processTemplate()); $this->assertTrue($model->getUseAbsoluteLinks()); @@ -688,11 +704,11 @@ public function testProcessTemplateThrowsExceptionNonExistentTemplate() ]); $model->expects($this->once()) ->method('loadDefault') - ->will($this->returnValue(true)); + ->willReturn(true); $model->expects($this->once()) ->method('applyDesignConfig') - ->will($this->returnValue(true)); + ->willReturn(true); $model->processTemplate(); } @@ -706,7 +722,7 @@ public function testGetSubject() $model->expects($this->once()) ->method('getProcessedTemplateSubject') ->with($variables) - ->will($this->returnValue($expectedResult)); + ->willReturn($expectedResult); $this->assertEquals($expectedResult, $model->getSubject()); } @@ -727,30 +743,32 @@ public function testSetOptions() */ public function testGetType($templateType, $expectedResult) { - $emailConfig = $this->getMockBuilder(\Magento\Email\Model\Template\Config::class) + $emailConfig = $this->getMockBuilder(Config::class) ->setMethods(['getTemplateType']) ->disableOriginalConstructor() ->getMock(); $emailConfig->expects($this->once())->method('getTemplateType')->will($this->returnValue($templateType)); - /** @var \Magento\Email\Model\Template $model */ - $model = $this->getMockBuilder(\Magento\Email\Model\Template::class) + /** @var Template $model */ + $model = $this->getMockBuilder(Template::class) ->setMethods(['_init']) ->setConstructorArgs([ - $this->createMock(\Magento\Framework\Model\Context::class), - $this->createMock(\Magento\Theme\Model\View\Design::class), - $this->createMock(\Magento\Framework\Registry::class), - $this->createMock(\Magento\Store\Model\App\Emulation::class), - $this->createMock(\Magento\Store\Model\StoreManager::class), - $this->createMock(\Magento\Framework\View\Asset\Repository::class), - $this->createMock(\Magento\Framework\Filesystem::class), - $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class), + $this->createMock(Context::class), + $this->createMock(Design::class), + $this->createMock(Registry::class), + $this->createMock(Emulation::class), + $this->createMock(StoreManager::class), + $this->createMock(Repository::class), + $this->createMock(Filesystem::class), + $this->createMock(ScopeConfigInterface::class), $emailConfig, - $this->createMock(\Magento\Email\Model\TemplateFactory::class), - $this->createMock(\Magento\Framework\Filter\FilterManager::class), - $this->createMock(\Magento\Framework\Url::class), - $this->createMock(\Magento\Email\Model\Template\FilterFactory::class), + $this->createMock(TemplateFactory::class), + $this->createMock(FilterManager::class), + $this->createMock(Url::class), + $this->createMock(FilterFactory::class), + [], + $this->createMock(Json::class) ]) ->getMock(); diff --git a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_grid_block.xml b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_grid_block.xml index fa15560817dd9..87da146d8ce56 100644 --- a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_grid_block.xml +++ b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_grid_block.xml @@ -11,7 +11,7 @@ systemEmailTemplateGrid - Magento\Email\Model\ResourceModel\Template\Collection + Magento\Email\Model\ResourceModel\Template\Collection 1 1 diff --git a/app/code/Magento/GraphQl/Controller/GraphQl.php b/app/code/Magento/GraphQl/Controller/GraphQl.php index 9e27ca5d608f0..75b3ad277c603 100644 --- a/app/code/Magento/GraphQl/Controller/GraphQl.php +++ b/app/code/Magento/GraphQl/Controller/GraphQl.php @@ -12,23 +12,27 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\GraphQl\Exception\ExceptionFormatter; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\QueryProcessor; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\SchemaGeneratorInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\Response; +use Magento\Framework\App\Response\Http as HttpResponse; use Magento\Framework\GraphQl\Query\Fields as QueryFields; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\App\ObjectManager; /** * Front controller for web API GraphQL area. * * @api + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GraphQl implements FrontControllerInterface { /** - * @var Response + * @var \Magento\Framework\Webapi\Response + * @deprecated */ private $response; @@ -67,6 +71,16 @@ class GraphQl implements FrontControllerInterface */ private $queryFields; + /** + * @var JsonFactory + */ + private $jsonFactory; + + /** + * @var HttpResponse + */ + private $httpResponse; + /** * @param Response $response * @param SchemaGeneratorInterface $schemaGenerator @@ -76,6 +90,9 @@ class GraphQl implements FrontControllerInterface * @param ContextInterface $resolverContext * @param HttpRequestProcessor $requestProcessor * @param QueryFields $queryFields + * @param JsonFactory|null $jsonFactory + * @param HttpResponse|null $httpResponse + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Response $response, @@ -85,7 +102,9 @@ public function __construct( ExceptionFormatter $graphQlError, ContextInterface $resolverContext, HttpRequestProcessor $requestProcessor, - QueryFields $queryFields + QueryFields $queryFields, + JsonFactory $jsonFactory = null, + HttpResponse $httpResponse = null ) { $this->response = $response; $this->schemaGenerator = $schemaGenerator; @@ -95,6 +114,8 @@ public function __construct( $this->resolverContext = $resolverContext; $this->requestProcessor = $requestProcessor; $this->queryFields = $queryFields; + $this->jsonFactory = $jsonFactory ?: ObjectManager::getInstance()->get(JsonFactory::class); + $this->httpResponse = $httpResponse ?: ObjectManager::getInstance()->get(HttpResponse::class); } /** @@ -106,10 +127,10 @@ public function __construct( public function dispatch(RequestInterface $request) : ResponseInterface { $statusCode = 200; + $jsonResult = $this->jsonFactory->create(); try { /** @var Http $request */ $this->requestProcessor->validateRequest($request); - $this->requestProcessor->processHeaders($request); $data = $this->getDataFromRequest($request); $query = $data['query'] ?? ''; @@ -131,11 +152,11 @@ public function dispatch(RequestInterface $request) : ResponseInterface $result['errors'][] = $this->graphQlError->create($error); $statusCode = ExceptionFormatter::HTTP_GRAPH_QL_SCHEMA_ERROR_STATUS; } - $this->response->setBody($this->jsonSerializer->serialize($result))->setHeader( - 'Content-Type', - 'application/json' - )->setHttpResponseCode($statusCode); - return $this->response; + + $jsonResult->setHttpResponseCode($statusCode); + $jsonResult->setData($result); + $jsonResult->renderResult($this->httpResponse); + return $this->httpResponse; } /** @@ -153,6 +174,8 @@ private function getDataFromRequest(RequestInterface $request) : array $data = $request->getParams(); $data['variables'] = isset($data['variables']) ? $this->jsonSerializer->unserialize($data['variables']) : null; + $data['variables'] = is_array($data['variables']) ? + $data['variables'] : null; } else { return []; } diff --git a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php b/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php deleted file mode 100644 index 246ad15379f85..0000000000000 --- a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php +++ /dev/null @@ -1,56 +0,0 @@ -storeManager = $storeManager; - } - - /** - * Handle the value of the store and set the scope - * - * @param string $headerValue - * @return void - * @throws GraphQlInputException - */ - public function processHeaderValue(string $headerValue) : void - { - if ($headerValue) { - $storeCode = ltrim(rtrim($headerValue)); - $stores = $this->storeManager->getStores(false, true); - if (isset($stores[$storeCode])) { - $this->storeManager->setCurrentStore($storeCode); - } elseif (strtolower($storeCode) !== 'default') { - throw new GraphQlInputException( - new \Magento\Framework\Phrase('Store code %1 does not exist', [$storeCode]) - ); - } - } - } -} diff --git a/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php b/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php index c0873b0caff89..2d9d50569e344 100644 --- a/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php +++ b/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Controller; use Magento\Framework\App\HttpRequestInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; /** * Use this interface to implement a validator for a Graphql HTTP requests @@ -19,6 +20,7 @@ interface HttpRequestValidatorInterface * * @param HttpRequestInterface $request * @return void + * @throws GraphQlInputException */ public function validate(HttpRequestInterface $request) : void; } diff --git a/app/code/Magento/GraphQl/composer.json b/app/code/Magento/GraphQl/composer.json index 3e821b0909444..3a1e8d1bfd9f4 100644 --- a/app/code/Magento/GraphQl/composer.json +++ b/app/code/Magento/GraphQl/composer.json @@ -5,12 +5,12 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/module-authorization": "*", - "magento/module-store": "*", "magento/module-eav": "*", "magento/framework": "*" }, "suggest": { - "magento/module-webapi": "*" + "magento/module-webapi": "*", + "magento/module-graph-ql-cache": "*" }, "license": [ "OSL-3.0", diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index b4f0113f58776..03bae5c80e12c 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -27,9 +27,6 @@ - - Magento\GraphQl\Controller\HttpHeaderProcessor\StoreProcessor - Magento\GraphQl\Controller\HttpRequestValidator\ContentTypeValidator Magento\GraphQl\Controller\HttpRequestValidator\HttpVerbValidator diff --git a/app/code/Magento/GraphQl/etc/module.xml b/app/code/Magento/GraphQl/etc/module.xml index 4d8b2090a8514..af0f5d06d3bae 100644 --- a/app/code/Magento/GraphQl/etc/module.xml +++ b/app/code/Magento/GraphQl/etc/module.xml @@ -9,7 +9,6 @@ - diff --git a/app/code/Magento/GraphQlCache/Controller/Plugin/GraphQl.php b/app/code/Magento/GraphQlCache/Controller/Plugin/GraphQl.php new file mode 100644 index 0000000000000..7c026e7d4136e --- /dev/null +++ b/app/code/Magento/GraphQlCache/Controller/Plugin/GraphQl.php @@ -0,0 +1,108 @@ +cacheableQuery = $cacheableQuery; + $this->config = $config; + $this->response = $response; + $this->requestProcessor = $requestProcessor; + } + + /** + * Process graphql headers + * + * @param FrontControllerInterface $subject + * @param RequestInterface $request + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeDispatch( + FrontControllerInterface $subject, + RequestInterface $request + ) { + /** @var \Magento\Framework\App\Request\Http $request */ + $this->requestProcessor->processHeaders($request); + } + + /** + * Plugin for GraphQL after render from dispatch to set tag and cache headers + * + * @param ResultInterface $subject + * @param ResultInterface $result + * @param ResponseHttp $response + * @return ResultInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterRenderResult(ResultInterface $subject, ResultInterface $result, ResponseHttp $response) + { + $sendNoCacheHeaders = false; + if ($this->config->isEnabled()) { + if ($this->cacheableQuery->shouldPopulateCacheHeadersWithTags()) { + $this->response->setPublicHeaders($this->config->getTtl()); + $this->response->setHeader('X-Magento-Tags', implode(',', $this->cacheableQuery->getCacheTags()), true); + } else { + $sendNoCacheHeaders = true; + } + } else { + $sendNoCacheHeaders = true; + } + + if ($sendNoCacheHeaders) { + $this->response->setNoCacheHeaders(); + } + + return $result; + } +} diff --git a/app/code/Magento/GraphQlCache/Model/CacheableQuery.php b/app/code/Magento/GraphQlCache/Model/CacheableQuery.php new file mode 100644 index 0000000000000..451e1039eec57 --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/CacheableQuery.php @@ -0,0 +1,77 @@ +cacheTags; + } + + /** + * Add Cache Tags + * + * @param array $cacheTags + * @return void + */ + public function addCacheTags(array $cacheTags): void + { + $this->cacheTags = array_merge($this->cacheTags, $cacheTags); + } + + /** + * Return if its valid to cache the response + * + * @return bool + */ + public function isCacheable(): bool + { + return $this->cacheable; + } + + /** + * Set cache validity + * + * @param bool $cacheable + */ + public function setCacheValidity(bool $cacheable): void + { + $this->cacheable = $cacheable; + } + + /** + * Check if query is cacheable and we have a list of tags to populate + * + * @return bool + */ + public function shouldPopulateCacheHeadersWithTags() : bool + { + $cacheTags = $this->getCacheTags(); + $isQueryCaheable = $this->isCacheable(); + return !empty($cacheTags) && $isQueryCaheable; + } +} diff --git a/app/code/Magento/GraphQlCache/Model/CacheableQueryHandler.php b/app/code/Magento/GraphQlCache/Model/CacheableQueryHandler.php new file mode 100644 index 0000000000000..7e624845f5682 --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/CacheableQueryHandler.php @@ -0,0 +1,94 @@ +cacheableQuery = $cacheableQuery; + $this->request = $request; + $this->identityPool = $identityPool; + } + + /** + * Set cache validity to the cacheableQuery after resolving any resolver or evaluating a promise in a query + * + * @param array $resolvedValue + * @param Field $field + * @return void + */ + public function handleCacheFromResolverResponse(array $resolvedValue, Field $field) : void + { + $cache = $field->getCache(); + $cacheIdentityClass = $cache['cacheIdentity'] ?? ''; + $cacheable = $cache['cacheable'] ?? true; + $cacheTag = $cache['cacheTag'] ?? null; + + $cacheTags = []; + if ($cacheTag && $this->request->isGet()) { + if (!empty($cacheIdentityClass)) { + $cacheIdentity = $this->identityPool->get($cacheIdentityClass); + $cacheTagIds = $cacheIdentity->getIdentities($resolvedValue); + if (!empty($cacheTagIds)) { + $cacheTags[] = $cacheTag; + foreach ($cacheTagIds as $cacheTagId) { + $cacheTags[] = $cacheTag . '_' . $cacheTagId; + } + } + } + $this->cacheableQuery->addCacheTags($cacheTags); + } + $this->setCacheValidity($cacheable); + } + + /** + * Set cache validity for the graphql request + * + * @param bool $isValid + * @return void + */ + private function setCacheValidity(bool $isValid): void + { + $cacheValidity = $this->cacheableQuery->isCacheable() && $isValid; + $this->cacheableQuery->setCacheValidity($cacheValidity); + } +} diff --git a/app/code/Magento/GraphQlCache/Model/Plugin/App/PageCache/Identifier.php b/app/code/Magento/GraphQlCache/Model/Plugin/App/PageCache/Identifier.php new file mode 100644 index 0000000000000..e02a51d2c1ca1 --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/Plugin/App/PageCache/Identifier.php @@ -0,0 +1,73 @@ +request = $request; + $this->context = $context; + $this->serializer = $serializer; + $this->config = $config; + } + + /** + * Add/Override a unique key identifier for graphql specific query and variables that skips X-Magento-Vary cookie + * + * @param \Magento\Framework\App\PageCache\Identifier $identifier + * @param string $result + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetValue(\Magento\Framework\App\PageCache\Identifier $identifier, string $result) : string + { + if ($this->config->isEnabled()) { + $data = [ + $this->request->isSecure(), + $this->request->getUriString(), + $this->context->getVaryString() + ]; + $result = sha1($this->serializer->serialize($data)); + } + return $result; + } +} diff --git a/app/code/Magento/GraphQlCache/Model/Plugin/Query/Resolver.php b/app/code/Magento/GraphQlCache/Model/Plugin/Query/Resolver.php new file mode 100644 index 0000000000000..54cb5559923af --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/Plugin/Query/Resolver.php @@ -0,0 +1,73 @@ +cacheableQueryHandler = $cacheableQueryHandler; + } + + /** + * Set cache validity to the cacheableQuery after resolving any resolver in a query + * + * @param ResolverInterface $subject + * @param mixed|Value $resolvedValue + * @param Field $field + * @param Context $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return mixed + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterResolve( + ResolverInterface $subject, + $resolvedValue, + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** Only if array @see \Magento\Framework\GraphQl\Query\Resolver\Value */ + if (is_array($resolvedValue) && !empty($field->getCache())) { + $this->cacheableQueryHandler->handleCacheFromResolverResponse($resolvedValue, $field); + } elseif ($resolvedValue instanceof \Magento\Framework\GraphQl\Query\Resolver\Value) { + $resolvedValue->then(function () use ($resolvedValue, $field) { + if (is_array($resolvedValue->promise->result) && $field) { + $this->cacheableQueryHandler->handleCacheFromResolverResponse( + $resolvedValue->promise->result, + $field + ); + } + }); + } + return $resolvedValue; + } +} diff --git a/app/code/Magento/GraphQlCache/Model/Resolver/IdentityPool.php b/app/code/Magento/GraphQlCache/Model/Resolver/IdentityPool.php new file mode 100644 index 0000000000000..00ef8762c28ef --- /dev/null +++ b/app/code/Magento/GraphQlCache/Model/Resolver/IdentityPool.php @@ -0,0 +1,49 @@ +objectManager = $objectManager; + } + + /** + * Get an identity resolver by class name + * + * @param string $identityClass + * @return IdentityInterface + */ + public function get(string $identityClass): IdentityInterface + { + if (!isset($this->identityInstances[$identityClass])) { + $this->identityInstances[$identityClass] = $this->objectManager->create($identityClass); + } + return $this->identityInstances[$identityClass]; + } +} diff --git a/app/code/Magento/GraphQlCache/README.md b/app/code/Magento/GraphQlCache/README.md new file mode 100644 index 0000000000000..fd2f19f957c5e --- /dev/null +++ b/app/code/Magento/GraphQlCache/README.md @@ -0,0 +1,4 @@ +# GraphQl Cache + +**GraphQL Cache** provides the ability to cache GraphQL queries. +This module allows Magento's built-in cache or Varnish as the application for serving the Full Page Cache to the front end. diff --git a/app/code/Magento/GraphQlCache/Test/Unit/Model/CacheableQueryHandlerTest.php b/app/code/Magento/GraphQlCache/Test/Unit/Model/CacheableQueryHandlerTest.php new file mode 100644 index 0000000000000..9c1be89928215 --- /dev/null +++ b/app/code/Magento/GraphQlCache/Test/Unit/Model/CacheableQueryHandlerTest.php @@ -0,0 +1,99 @@ +cacheableQueryMock = $this->createMock(CacheableQuery::class); + $this->requestMock = $this->createMock(Http::class); + $this->identityPoolMock = $this->createMock(IdentityPool::class); + $this->cacheableQueryHandler = $objectManager->getObject( + CacheableQueryHandler::class, + [ + 'cacheableQuery' => $this->cacheableQueryMock, + 'request' => $this->requestMock, + 'identityPool' => $this->identityPoolMock + ] + ); + } + + /** + * @param array $resolvedData + * @param array $identities + * @dataProvider resolvedDataProvider + */ + public function testhandleCacheFromResolverResponse( + array $resolvedData, + array $identities, + array $expectedCacheTags + ): void { + $cacheData = [ + 'cacheIdentity' => IdentityInterface::class, + 'cacheTag' => 'cat_p' + ]; + $fieldMock = $this->createMock(Field::class); + $mockIdentity = $this->getMockBuilder($cacheData['cacheIdentity']) + ->setMethods(['getIdentities']) + ->getMockForAbstractClass(); + + $this->requestMock->expects($this->once())->method('isGet')->willReturn(true); + $this->identityPoolMock->expects($this->once())->method('get')->willReturn($mockIdentity); + $fieldMock->expects($this->once())->method('getCache')->willReturn($cacheData); + $mockIdentity->expects($this->once()) + ->method('getIdentities') + ->with($resolvedData) + ->willReturn($identities); + $this->cacheableQueryMock->expects($this->once())->method('addCacheTags')->with($expectedCacheTags); + $this->cacheableQueryMock->expects($this->once())->method('isCacheable')->willReturn(true); + $this->cacheableQueryMock->expects($this->once())->method('setCacheValidity')->with(true); + + $this->cacheableQueryHandler->handleCacheFromResolverResponse($resolvedData, $fieldMock); + } + + /** + * @return array + */ + public function resolvedDataProvider(): array + { + return [ + [ + "resolvedData" => [ + "id" => 10, + "name" => "TesName", + "sku" => "TestSku" + ], + "identities" => [10], + "expectedCacheTags" => ["cat_p", "cat_p_10"] + ] + ]; + } +} diff --git a/app/code/Magento/GraphQlCache/composer.json b/app/code/Magento/GraphQlCache/composer.json new file mode 100644 index 0000000000000..436ae95da40f5 --- /dev/null +++ b/app/code/Magento/GraphQlCache/composer.json @@ -0,0 +1,23 @@ +{ + "name": "magento/module-graph-ql-cache", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-page-cache": "*", + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\GraphQlCache\\": "" + } + } +} diff --git a/app/code/Magento/GraphQlCache/etc/graphql/di.xml b/app/code/Magento/GraphQlCache/etc/graphql/di.xml new file mode 100644 index 0000000000000..5dd8c816ce929 --- /dev/null +++ b/app/code/Magento/GraphQlCache/etc/graphql/di.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/GraphQlCache/etc/module.xml b/app/code/Magento/GraphQlCache/etc/module.xml new file mode 100644 index 0000000000000..3cbd4d8f0cb48 --- /dev/null +++ b/app/code/Magento/GraphQlCache/etc/module.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/app/code/Magento/GraphQlCache/registration.php b/app/code/Magento/GraphQlCache/registration.php new file mode 100644 index 0000000000000..2dfe717003a03 --- /dev/null +++ b/app/code/Magento/GraphQlCache/registration.php @@ -0,0 +1,9 @@ +_coreRegistry = $coreRegistry; $this->_config = $config; @@ -121,13 +103,7 @@ public function __construct( $customerSession, $dateTime, $groupManagement, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); } diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/layout/groupedproduct_popup_grid.xml b/app/code/Magento/GroupedProduct/view/adminhtml/layout/groupedproduct_popup_grid.xml index 503404c6cb3cf..fab7851df5bc4 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/layout/groupedproduct_popup_grid.xml +++ b/app/code/Magento/GroupedProduct/view/adminhtml/layout/groupedproduct_popup_grid.xml @@ -11,7 +11,7 @@ grouped_grid_popup - Magento\GroupedProduct\Model\ResourceModel\Product\Type\Grouped\AssociatedProductsCollection + Magento\GroupedProduct\Model\ResourceModel\Product\Type\Grouped\AssociatedProductsCollection 1 id ASC diff --git a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml index 02fc198cb0ada..51e5827c8ab98 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml +++ b/app/code/Magento/ImportExport/view/adminhtml/layout/adminhtml_history_grid_block.xml @@ -11,7 +11,7 @@ importHistoryGrid - Magento\ImportExport\Model\ResourceModel\History\Collection + Magento\ImportExport\Model\ResourceModel\History\Collection history_id desc diff --git a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml index bf6b2351f75f1..f1099c4133bca 100644 --- a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml +++ b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml @@ -13,7 +13,7 @@ 0 0 gridIndexer - Magento\Indexer\Ui\DataProvider\Indexer\DataCollection + Magento\Indexer\Ui\DataProvider\Indexer\DataCollection diff --git a/app/code/Magento/Integration/etc/db_schema.xml b/app/code/Magento/Integration/etc/db_schema.xml index f1824fadb97fd..cbf43d79b2cf6 100644 --- a/app/code/Magento/Integration/etc/db_schema.xml +++ b/app/code/Magento/Integration/etc/db_schema.xml @@ -107,7 +107,7 @@ comment="Oauth consumer"/> - diff --git a/app/code/Magento/Integration/view/adminhtml/layout/adminhtml_integration_grid_block.xml b/app/code/Magento/Integration/view/adminhtml/layout/adminhtml_integration_grid_block.xml index 506f836f99514..43b67d6904f1a 100644 --- a/app/code/Magento/Integration/view/adminhtml/layout/adminhtml_integration_grid_block.xml +++ b/app/code/Magento/Integration/view/adminhtml/layout/adminhtml_integration_grid_block.xml @@ -13,7 +13,7 @@ integrationGrid - Magento\Integration\Model\ResourceModel\Integration\Collection + Magento\Integration\Model\ResourceModel\Integration\Collection 1 integration_id asc diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php deleted file mode 100644 index f6305363fc1a6..0000000000000 --- a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php +++ /dev/null @@ -1,66 +0,0 @@ -poisonPillRead = $poisonPillRead; - $this->poisonPillCompare = $poisonPillCompare; - } - - /** - * @inheritdoc - */ - public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) - { - $this->poisonPillVersion = $this->poisonPillRead->getLatestVersion(); - for ($i = $maxNumberOfMessages; $i > 0; $i--) { - do { - $message = $queue->dequeue(); - // phpcs:ignore Magento2.Functions.DiscouragedFunction - } while ($message === null && (sleep(1) === 0)); - if (false === $this->poisonPillCompare->isLatestVersion($this->poisonPillVersion)) { - $queue->reject($message); - // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage - exit(0); - } - $callback($message); - } - } -} diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php index a8e40ea495002..ffa478aecf36c 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php @@ -7,8 +7,8 @@ namespace Magento\MessageQueue\Model; -use Magento\MessageQueue\Api\PoisonPillCompareInterface; -use Magento\MessageQueue\Api\PoisonPillReadInterface; +use Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompareInterface; +use Magento\Framework\MessageQueue\PoisonPill\PoisonPillReadInterface; /** * Poison pill compare @@ -33,7 +33,7 @@ public function __construct( /** * @inheritdoc */ - public function isLatestVersion(int $poisonPillVersion): bool + public function isLatestVersion(string $poisonPillVersion): bool { return $poisonPillVersion === $this->poisonPillRead->getLatestVersion(); } diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index 283fff8ace7c7..e59abec8724fb 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -7,13 +7,12 @@ namespace Magento\MessageQueue\Model\ResourceModel; -use Magento\MessageQueue\Api\PoisonPillReadInterface; -use Magento\MessageQueue\Api\PoisonPillPutInterface; -use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\MessageQueue\PoisonPill\PoisonPillPutInterface; +use Magento\Framework\MessageQueue\PoisonPill\PoisonPillReadInterface; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** - * PoisonPill. + * PoisonPill class that enclose read and put interface. */ class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPillReadInterface { @@ -22,19 +21,6 @@ class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPil */ const QUEUE_POISON_PILL_TABLE = 'queue_poison_pill'; - /** - * PoisonPill constructor. - * - * @param Context $context - * @param string|null $connectionName - */ - public function __construct( - Context $context, - string $connectionName = null - ) { - parent::__construct($context, $connectionName); - } - /** * @inheritdoc */ @@ -46,30 +32,43 @@ protected function _construct() /** * @inheritdoc */ - public function put(): int + public function put(): string { $connection = $this->getConnection(); $table = $this->getMainTable(); - $connection->insert($table, []); - return (int)$connection->lastInsertId($table); + $uuid = uniqid('version-'); + $version = $this->getVersionFromDb(); + if ($version !== '') { + $connection->update($table, ['version' => $uuid]); + } else { + $connection->insert($table, ['version' => $uuid]); + } + + return $uuid; } /** * @inheritdoc */ - public function getLatestVersion() : int + public function getLatestVersion(): string + { + return $this->getVersionFromDb(); + } + + /** + * Returns version form DB or null. + * + * @return string + */ + private function getVersionFromDb(): string { $select = $this->getConnection()->select()->from( $this->getTable(self::QUEUE_POISON_PILL_TABLE), 'version' - )->order( - 'version ' . \Magento\Framework\DB\Select::SQL_DESC - )->limit( - 1 ); - $version = (int)$this->getConnection()->fetchOne($select); + $result = $this->getConnection()->fetchOne($select); - return $version; + return (string)$result; } } diff --git a/app/code/Magento/MessageQueue/etc/db_schema.xml b/app/code/Magento/MessageQueue/etc/db_schema.xml index 9cdf414dd06e1..4403384e9311a 100644 --- a/app/code/Magento/MessageQueue/etc/db_schema.xml +++ b/app/code/Magento/MessageQueue/etc/db_schema.xml @@ -21,12 +21,7 @@ - - - - - +
+
diff --git a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json index d9d623a994b37..37a342b21e64c 100644 --- a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json +++ b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json @@ -13,9 +13,6 @@ "queue_poison_pill": { "column": { "version": true - }, - "constraint": { - "PRIMARY": true } } } diff --git a/app/code/Magento/MessageQueue/etc/di.xml b/app/code/Magento/MessageQueue/etc/di.xml index 22cfea976a722..f60eb5fbc20df 100644 --- a/app/code/Magento/MessageQueue/etc/di.xml +++ b/app/code/Magento/MessageQueue/etc/di.xml @@ -13,10 +13,9 @@ - - - - + + + diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index 9882a1ce9b0b8..bce42b4e90074 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -21,7 +21,7 @@ class NewRelicWrapper */ public function addCustomParameter($param, $value) { - if (extension_loaded('newrelic')) { + if ($this->isExtensionInstalled()) { newrelic_add_custom_parameter($param, $value); return true; } @@ -36,7 +36,7 @@ public function addCustomParameter($param, $value) */ public function reportError($exception) { - if (extension_loaded('newrelic')) { + if ($this->isExtensionInstalled()) { newrelic_notice_error($exception->getMessage(), $exception); } } @@ -49,11 +49,24 @@ public function reportError($exception) */ public function setAppName(string $appName) { - if (extension_loaded('newrelic')) { + if ($this->isExtensionInstalled()) { newrelic_set_appname($appName); } } + /** + * Wrapper for 'newrelic_name_transaction' + * + * @param string $transactionName + * @return void + */ + public function setTransactionName(string $transactionName): void + { + if ($this->isExtensionInstalled()) { + newrelic_name_transaction($transactionName); + } + } + /** * Checks whether newrelic-php5 agent is installed * diff --git a/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php new file mode 100644 index 0000000000000..04ad3d0504d34 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php @@ -0,0 +1,55 @@ +config = $config; + $this->newRelicWrapper = $newRelicWrapper; + } + + /** + * Set NewRelic Transaction name before running command. + * + * @param Command $command + * @param array $args + * @return array + */ + public function beforeRun(Command $command, ...$args) + { + $this->newRelicWrapper->setTransactionName( + sprintf('CLI %s', $command->getName()) + ); + + return $args; + } +} diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml index bab7d6611f14b..15516f6df89be 100644 --- a/app/code/Magento/NewRelicReporting/etc/di.xml +++ b/app/code/Magento/NewRelicReporting/etc/di.xml @@ -40,4 +40,7 @@ + + + diff --git a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_problem_block.xml b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_problem_block.xml index 5cc268333de71..838a9dbb41b4b 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_problem_block.xml +++ b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_problem_block.xml @@ -11,7 +11,7 @@ problemGrid - Magento\Newsletter\Model\ResourceModel\Grid\Collection + Magento\Newsletter\Model\ResourceModel\Grid\Collection true true 1 diff --git a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_queue_grid_block.xml b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_queue_grid_block.xml index 3bfb52157bb99..8a2c891c68f81 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_queue_grid_block.xml +++ b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_queue_grid_block.xml @@ -11,7 +11,7 @@ queueGrid - Magento\Newsletter\Model\ResourceModel\Queue\Grid\Collection + Magento\Newsletter\Model\ResourceModel\Queue\Grid\Collection start_at DESC 1 diff --git a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_subscriber_block.xml b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_subscriber_block.xml index 9de1807af18ec..e8600c2d49d7b 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_subscriber_block.xml +++ b/app/code/Magento/Newsletter/view/adminhtml/layout/newsletter_subscriber_block.xml @@ -11,7 +11,7 @@ subscriberGrid - Magento\Newsletter\Model\ResourceModel\Subscriber\Grid\Collection + Magento\Newsletter\Model\ResourceModel\Subscriber\Grid\Collection subscriber_id desc 1 diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 801e6cb475d8a..6de6b4e917044 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -123,6 +123,10 @@ sub vcl_hash { hash_data(server.ip); } + if (req.url ~ "/graphql") { + call process_graphql_headers; + } + # To make sure http users don't see ssl warning if (req.http./* {{ ssl_offloaded_header }} */) { hash_data(req.http./* {{ ssl_offloaded_header }} */); @@ -130,6 +134,15 @@ sub vcl_hash { /* {{ design_exceptions_code }} */ } +sub process_graphql_headers { + if (req.http.Store) { + hash_data(req.http.Store); + } + if (req.http.Content-Currency) { + hash_data(req.http.Content-Currency); + } +} + sub vcl_backend_response { set beresp.grace = 3d; diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 76c5ffee5f14f..4505e74629714 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -129,6 +129,19 @@ sub vcl_hash { hash_data(req.http./* {{ ssl_offloaded_header }} */); } /* {{ design_exceptions_code }} */ + + if (req.url ~ "/graphql") { + call process_graphql_headers; + } +} + +sub process_graphql_headers { + if (req.http.Store) { + hash_data(req.http.Store); + } + if (req.http.Content-Currency) { + hash_data(req.http.Content-Currency); + } } sub vcl_backend_response { diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php index da5599984b701..8a2825a16d33a 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php @@ -64,6 +64,7 @@ public function requestToken(Quote $quote) $request->setTrxtype(Payflowpro::TRXTYPE_AUTH_ONLY); $request->setVerbosity('HIGH'); $request->setAmt(0); + $request->setCurrency($quote->getBaseCurrencyCode()); $request->setCreatesecuretoken('Y'); $request->setSecuretokenid($this->mathRandom->getUniqueHash()); $request->setReturnurl($this->url->getUrl('paypal/transparent/response')); diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php deleted file mode 100644 index cfdfe17b1e004..0000000000000 --- a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php +++ /dev/null @@ -1,107 +0,0 @@ -_group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); - $this->_element = $this->getMockForAbstractClass( - \Magento\Framework\Data\Form\Element\AbstractElement::class, - [], - '', - false, - true, - true, - ['getHtmlId', 'getElementHtml', 'getName', 'getElements', 'getId'] - ); - $this->_element->expects($this->any()) - ->method('getHtmlId') - ->will($this->returnValue('html id')); - $this->_element->expects($this->any()) - ->method('getElementHtml') - ->will($this->returnValue('element html')); - $this->_element->expects($this->any()) - ->method('getName') - ->will($this->returnValue('name')); - $this->_element->expects($this->any()) - ->method('getElements') - ->will($this->returnValue([])); - $this->_element->expects($this->any()) - ->method('getId') - ->will($this->returnValue('id')); - $this->_user = $this->createMock(\Magento\User\Model\User::class); - $this->_authSession = $this->createMock(\Magento\Backend\Model\Auth\Session::class); - $this->_authSession->expects($this->any()) - ->method('__call') - ->with('getUser') - ->will($this->returnValue($this->_user)); - $this->_model = $helper->getObject( - \Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Group::class, - ['authSession' => $this->_authSession] - ); - $this->_model->setGroup($this->_group); - } - - /** - * @param mixed $expanded - * @param int $expected - * @dataProvider isCollapseStateDataProvider - */ - public function testIsCollapseState($expanded, $expected) - { - $this->_user->setExtra(['configState' => []]); - $this->_element->setGroup(isset($expanded) ? ['expanded' => $expanded] : []); - $html = $this->_model->render($this->_element); - $this->assertContains( - '', - $html - ); - } - - /** - * @return array - */ - public function isCollapseStateDataProvider() - { - return [ - [null, 0], - [false, 0], - ['', 0], - [1, 1], - ['1', 1], - ]; - } -} diff --git a/app/code/Magento/Paypal/view/adminhtml/layout/adminhtml_paypal_reports_block.xml b/app/code/Magento/Paypal/view/adminhtml/layout/adminhtml_paypal_reports_block.xml index 12dcc46e3ecee..596381a4b1143 100644 --- a/app/code/Magento/Paypal/view/adminhtml/layout/adminhtml_paypal_reports_block.xml +++ b/app/code/Magento/Paypal/view/adminhtml/layout/adminhtml_paypal_reports_block.xml @@ -11,7 +11,7 @@ settlementGrid - Magento\Paypal\Model\ResourceModel\Report\Settlement\Row\Collection + Magento\Paypal\Model\ResourceModel\Report\Settlement\Row\Collection row_id DESC 1 diff --git a/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_ordersgrid.xml b/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_ordersgrid.xml index 76a105f5abcdf..0510011a6a559 100644 --- a/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_ordersgrid.xml +++ b/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_ordersgrid.xml @@ -9,7 +9,7 @@ - + Magento\Paypal\Model\Billing\Agreement\OrdersUpdater diff --git a/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_view.xml b/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_view.xml index c76539f5cb206..d9c376701db33 100644 --- a/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_view.xml +++ b/app/code/Magento/Paypal/view/adminhtml/layout/paypal_billing_agreement_view.xml @@ -12,7 +12,7 @@ - + Magento\Paypal\Model\Billing\Agreement\OrdersUpdater diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 2fcfd2dfadabb..0ad99ffe759f6 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -146,6 +146,16 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ private $addressesToSync = []; + /** + * @var \Magento\Framework\App\RequestInterface + */ + private $request; + + /** + * @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress + */ + private $remoteAddress; + /** * @param EventManager $eventManager * @param QuoteValidator $quoteValidator @@ -169,6 +179,8 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface * @param QuoteFactory $quoteFactory * @param \Magento\Quote\Model\QuoteIdMaskFactory|null $quoteIdMaskFactory * @param \Magento\Customer\Api\AddressRepositoryInterface|null $addressRepository + * @param \Magento\Framework\App\RequestInterface|null $request + * @param \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -193,7 +205,9 @@ public function __construct( \Magento\Customer\Api\AccountManagementInterface $accountManagement, \Magento\Quote\Model\QuoteFactory $quoteFactory, \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory = null, - \Magento\Customer\Api\AddressRepositoryInterface $addressRepository = null + \Magento\Customer\Api\AddressRepositoryInterface $addressRepository = null, + \Magento\Framework\App\RequestInterface $request = null, + \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress = null ) { $this->eventManager = $eventManager; $this->quoteValidator = $quoteValidator; @@ -219,6 +233,10 @@ public function __construct( ->get(\Magento\Quote\Model\QuoteIdMaskFactory::class); $this->addressRepository = $addressRepository ?: ObjectManager::getInstance() ->get(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->request = $request ?: ObjectManager::getInstance() + ->get(\Magento\Framework\App\RequestInterface::class); + $this->remoteAddress = $remoteAddress ?: ObjectManager::getInstance() + ->get(\Magento\Framework\HTTP\PhpEnvironment\RemoteAddress::class); } /** @@ -281,6 +299,7 @@ public function assignCustomer($cartId, $customerId, $storeId) throw new StateException( __("The customer can't be assigned to the cart because the customer already has an active cart.") ); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } @@ -368,6 +387,14 @@ public function placeOrder($cartId, PaymentInterface $paymentMethod = null) $quote->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID); } + $remoteAddress = $this->remoteAddress->getRemoteAddress(); + if ($remoteAddress !== false) { + $quote->setRemoteIp($remoteAddress); + $quote->setXForwardedFor( + $this->request->getServer('HTTP_X_FORWARDED_FOR') + ); + } + $this->eventManager->dispatch('checkout_submit_before', ['quote' => $quote]); $order = $this->submit($quote); @@ -627,12 +654,14 @@ private function rollbackAddresses( 'exception' => $e, ] ); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $consecutiveException) { $message = sprintf( "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", $consecutiveException->getMessage() ); + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($message, 0, $e); } } diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index 01c21bbbe50a7..30931821ddc7d 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -3,27 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Quote\Model; +use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Framework\Api\Search\FilterGroup; +use Magento\Framework\Api\SearchCriteria\CollectionProcessor; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Api\SortOrder; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\Api\Search\FilterGroup; -use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; -use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory; use Magento\Quote\Model\QuoteRepository\SaveHandler; use Magento\Quote\Model\QuoteRepository\LoadHandler; +use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; +use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; +use Magento\Store\Model\StoreManagerInterface; /** + * Quote repository. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface +class QuoteRepository implements CartRepositoryInterface { /** * @var Quote[] @@ -37,6 +43,7 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface /** * @var QuoteFactory + * @deprecated */ protected $quoteFactory; @@ -46,13 +53,13 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface protected $storeManager; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Collection + * @var QuoteCollection * @deprecated 100.2.0 */ protected $quoteCollection; /** - * @var \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory + * @var CartSearchResultsInterfaceFactory */ protected $searchResultsDataFactory; @@ -77,43 +84,51 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface private $collectionProcessor; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory + * @var QuoteCollectionFactory */ private $quoteCollectionFactory; + /** + * @var CartInterfaceFactory + */ + private $cartFactory; + /** * Constructor * * @param QuoteFactory $quoteFactory * @param StoreManagerInterface $storeManager - * @param \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection - * @param \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory + * @param QuoteCollection $quoteCollection + * @param CartSearchResultsInterfaceFactory $searchResultsDataFactory * @param JoinProcessorInterface $extensionAttributesJoinProcessor * @param CollectionProcessorInterface|null $collectionProcessor - * @param \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory|null $quoteCollectionFactory + * @param QuoteCollectionFactory|null $quoteCollectionFactory + * @param CartInterfaceFactory|null $cartFactory * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( QuoteFactory $quoteFactory, StoreManagerInterface $storeManager, - \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection, - \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory, + QuoteCollection $quoteCollection, + CartSearchResultsInterfaceFactory $searchResultsDataFactory, JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor = null, - \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $quoteCollectionFactory = null + QuoteCollectionFactory $quoteCollectionFactory = null, + CartInterfaceFactory $cartFactory = null ) { $this->quoteFactory = $quoteFactory; $this->storeManager = $storeManager; $this->searchResultsDataFactory = $searchResultsDataFactory; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; - $this->collectionProcessor = $collectionProcessor ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Api\SearchCriteria\CollectionProcessor::class); - $this->quoteCollectionFactory = $quoteCollectionFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Quote\Model\ResourceModel\Quote\CollectionFactory::class); + $this->collectionProcessor = $collectionProcessor ?: ObjectManager::getInstance() + ->get(CollectionProcessor::class); + $this->quoteCollectionFactory = $quoteCollectionFactory ?: ObjectManager::getInstance() + ->get(QuoteCollectionFactory::class); + $this->cartFactory = $cartFactory ?: ObjectManager::getInstance()->get(CartInterfaceFactory::class); } /** - * {@inheritdoc} + * @inheritdoc */ public function get($cartId, array $sharedStoreIds = []) { @@ -126,7 +141,7 @@ public function get($cartId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getForCustomer($customerId, array $sharedStoreIds = []) { @@ -140,7 +155,7 @@ public function getForCustomer($customerId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getActive($cartId, array $sharedStoreIds = []) { @@ -152,7 +167,7 @@ public function getActive($cartId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getActiveForCustomer($customerId, array $sharedStoreIds = []) { @@ -164,9 +179,9 @@ public function getActiveForCustomer($customerId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ - public function save(\Magento\Quote\Api\Data\CartInterface $quote) + public function save(CartInterface $quote) { if ($quote->getId()) { $currentQuote = $this->get($quote->getId(), [$quote->getStoreId()]); @@ -184,9 +199,9 @@ public function save(\Magento\Quote\Api\Data\CartInterface $quote) } /** - * {@inheritdoc} + * @inheritdoc */ - public function delete(\Magento\Quote\Api\Data\CartInterface $quote) + public function delete(CartInterface $quote) { $quoteId = $quote->getId(); $customerId = $quote->getCustomerId(); @@ -203,13 +218,13 @@ public function delete(\Magento\Quote\Api\Data\CartInterface $quote) * @param int $identifier * @param int[] $sharedStoreIds * @throws NoSuchEntityException - * @return Quote + * @return CartInterface */ protected function loadQuote($loadMethod, $loadField, $identifier, array $sharedStoreIds = []) { - /** @var Quote $quote */ - $quote = $this->quoteFactory->create(); - if ($sharedStoreIds) { + /** @var CartInterface $quote */ + $quote = $this->cartFactory->create(); + if ($sharedStoreIds && method_exists($quote, 'setSharedStoreIds')) { $quote->setSharedStoreIds($sharedStoreIds); } $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier); @@ -220,9 +235,9 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared } /** - * {@inheritdoc} + * @inheritdoc */ - public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria) + public function getList(SearchCriteriaInterface $searchCriteria) { $this->quoteCollection = $this->quoteCollectionFactory->create(); /** @var \Magento\Quote\Api\Data\CartSearchResultsInterface $searchData */ @@ -265,6 +280,7 @@ protected function addFilterGroupToCollection(FilterGroup $filterGroup, QuoteCol /** * Get new SaveHandler dependency for application code. + * * @return SaveHandler * @deprecated 100.1.0 */ @@ -277,6 +293,8 @@ private function getSaveHandler() } /** + * Get load handler instance. + * * @return LoadHandler * @deprecated 100.1.0 */ diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index b61f95b4eee6c..8d8200cd6ef62 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -6,9 +6,12 @@ namespace Magento\Quote\Test\Unit\Model; +use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress; use Magento\Quote\Model\CustomerManagement; +use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Sales\Api\Data\OrderAddressInterface; /** @@ -137,6 +140,21 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase */ private $quoteFactoryMock; + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress|\PHPUnit_Framework_MockObject_MockObject + */ + private $remoteAddressMock; + + /** + * @var \Magento\Quote\Model\QuoteIdMaskFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteIdMaskFactoryMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -178,18 +196,20 @@ protected function setUp() ); $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ - 'getId', - 'getCheckoutMethod', - 'setCheckoutMethod', - 'setCustomerId', - 'setCustomerEmail', - 'getBillingAddress', - 'setCustomerIsGuest', - 'setCustomerGroupId', - 'assignCustomer', - 'getPayment', - 'collectTotals' - ]); + 'assignCustomer', + 'collectTotals', + 'getBillingAddress', + 'getCheckoutMethod', + 'getPayment', + 'setCheckoutMethod', + 'setCustomerEmail', + 'setCustomerGroupId', + 'setCustomerId', + 'setCustomerIsGuest', + 'setRemoteIp', + 'setXForwardedFor', + 'getId', + ]); $this->quoteAddressFactory = $this->createPartialMock( \Magento\Quote\Model\Quote\AddressFactory::class, @@ -237,8 +257,11 @@ protected function setUp() // Set the new dependency $this->quoteIdMock = $this->createMock(\Magento\Quote\Model\QuoteIdMask::class); - $quoteIdFactoryMock = $this->createPartialMock(\Magento\Quote\Model\QuoteIdMaskFactory::class, ['create']); - $this->setPropertyValue($this->model, 'quoteIdMaskFactory', $quoteIdFactoryMock); + $this->quoteIdMaskFactoryMock = $this->createPartialMock(QuoteIdMaskFactory::class, ['create']); + $this->setPropertyValue($this->model, 'quoteIdMaskFactory', $this->quoteIdMaskFactoryMock); + + $this->requestMock = $this->createPartialMockForAbstractClass(RequestInterface::class, ['getServer']); + $this->remoteAddressMock = $this->createMock(RemoteAddress::class); } public function testCreateEmptyCartAnonymous() @@ -676,7 +699,11 @@ public function testPlaceOrderIfCustomerIsGuest() 'checkoutSession' => $this->checkoutSessionMock, 'customerSession' => $this->customerSessionMock, 'accountManagement' => $this->accountManagementMock, - 'quoteFactory' => $this->quoteFactoryMock + 'quoteFactory' => $this->quoteFactoryMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock, + 'addressRepository' => $this->addressRepositoryMock, + 'request' => $this->requestMock, + 'remoteAddress' => $this->remoteAddressMock, ] ) ->getMock(); @@ -709,13 +736,15 @@ public function testPlaceOrder() $orderId = 332; $orderIncrementId = 100003332; $orderStatus = 'status1'; + $remoteAddress = '192.168.1.10'; + $forwardedForIp = '192.168.1.11'; /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\QuoteManagement $service */ $service = $this->getMockBuilder(\Magento\Quote\Model\QuoteManagement::class) ->setMethods(['submit']) ->setConstructorArgs( [ - 'eventManager' => $this->eventManager, + 'eventManager' => $this->eventManager, 'quoteValidator' => $this->quoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, @@ -734,7 +763,11 @@ public function testPlaceOrder() 'checkoutSession' => $this->checkoutSessionMock, 'customerSession' => $this->customerSessionMock, 'accountManagement' => $this->accountManagementMock, - 'quoteFactory' => $this->quoteFactoryMock + 'quoteFactory' => $this->quoteFactoryMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock, + 'addressRepository' => $this->addressRepositoryMock, + 'request' => $this->requestMock, + 'remoteAddress' => $this->remoteAddressMock, ] ) ->getMock(); @@ -762,6 +795,17 @@ public function testPlaceOrder() ->method('setCustomerIsGuest') ->with(true); + $this->remoteAddressMock + ->method('getRemoteAddress') + ->willReturn($remoteAddress); + + $this->requestMock + ->method('getServer') + ->willReturn($forwardedForIp); + + $this->quoteMock->expects($this->once())->method('setRemoteIp')->with($remoteAddress); + $this->quoteMock->expects($this->once())->method('setXForwardedFor')->with($forwardedForIp); + $service->expects($this->once())->method('submit')->willReturn($orderMock); $this->quoteMock->expects($this->atLeastOnce())->method('getId')->willReturn($cartId); diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php index 3101c7d0677a9..095e1760df86f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php @@ -5,17 +5,31 @@ */ namespace Magento\Quote\Test\Unit\Model; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\Quote\Api\Data\CartSearchResultsInterface; +use Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteRepository; use Magento\Quote\Model\QuoteRepository\LoadHandler; use Magento\Quote\Model\QuoteRepository\SaveHandler; +use Magento\Quote\Model\ResourceModel\Quote\Collection; use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyMethods) */ -class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase +class QuoteRepositoryTest extends TestCase { /** * @var \Magento\Quote\Api\CartRepositoryInterface @@ -23,32 +37,32 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Quote\Model\QuoteFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CartInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ - private $quoteFactoryMock; + private $cartFactoryMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ private $storeManagerMock; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var Store|\PHPUnit_Framework_MockObject_MockObject */ private $storeMock; /** - * @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject + * @var Quote|\PHPUnit_Framework_MockObject_MockObject */ private $quoteMock; /** - * @var \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CartSearchResultsInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ private $searchResultsDataFactory; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|\PHPUnit_Framework_MockObject_MockObject */ private $quoteCollectionMock; @@ -78,21 +92,21 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase private $objectManagerMock; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ private $quoteCollectionFactoryMock; protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); \Magento\Framework\App\ObjectManager::setInstance($this->objectManagerMock); - $this->quoteFactoryMock = $this->createPartialMock(\Magento\Quote\Model\QuoteFactory::class, ['create']); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->cartFactoryMock = $this->createPartialMock(CartInterfaceFactory::class, ['create']); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'load', 'loadByIdWithoutStore', @@ -108,35 +122,35 @@ protected function setUp() 'getData' ] ); - $this->storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->storeMock = $this->createMock(Store::class); $this->searchResultsDataFactory = $this->createPartialMock( - \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory::class, + CartSearchResultsInterfaceFactory::class, ['create'] ); $this->quoteCollectionMock = - $this->createMock(\Magento\Quote\Model\ResourceModel\Quote\Collection::class); + $this->createMock(Collection::class); $this->extensionAttributesJoinProcessorMock = $this->createMock( - \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::class + JoinProcessorInterface::class ); $this->collectionProcessor = $this->createMock( - \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface::class + CollectionProcessorInterface::class ); $this->quoteCollectionFactoryMock = $this->createPartialMock( - \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory::class, + CollectionFactory::class, ['create'] ); $this->model = $objectManager->getObject( - \Magento\Quote\Model\QuoteRepository::class, + QuoteRepository::class, [ - 'quoteFactory' => $this->quoteFactoryMock, 'storeManager' => $this->storeManagerMock, 'searchResultsDataFactory' => $this->searchResultsDataFactory, 'quoteCollection' => $this->quoteCollectionMock, 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock, 'collectionProcessor' => $this->collectionProcessor, - 'quoteCollectionFactory' => $this->quoteCollectionFactoryMock + 'quoteCollectionFactory' => $this->quoteCollectionFactoryMock, + 'cartFactory' => $this->cartFactoryMock ] ); @@ -161,7 +175,7 @@ public function testGetWithExceptionById() { $cartId = 14; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -178,7 +192,7 @@ public function testGet() { $cartId = 15; - $this->quoteFactoryMock->expects(static::once()) + $this->cartFactoryMock->expects(static::once()) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::once()) @@ -211,7 +225,7 @@ public function testGetForCustomerAfterGet() $cartId = 15; $customerId = 23; - $this->quoteFactoryMock->expects(static::exactly(2)) + $this->cartFactoryMock->expects(static::exactly(2)) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::exactly(2)) @@ -249,7 +263,7 @@ public function testGetWithSharedStoreIds() $cartId = 16; $sharedStoreIds = [1, 2]; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once()) @@ -275,7 +289,7 @@ public function testGetForCustomer() $cartId = 17; $customerId = 23; - $this->quoteFactoryMock->expects(static::once()) + $this->cartFactoryMock->expects(static::once()) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::once()) @@ -310,7 +324,7 @@ public function testGetActiveWithExceptionById() { $cartId = 14; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -332,7 +346,7 @@ public function testGetActiveWithExceptionByIsActive() { $cartId = 15; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -355,7 +369,7 @@ public function testGetActive() { $cartId = 15; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -379,7 +393,7 @@ public function testGetActiveWithSharedStoreIds() $cartId = 16; $sharedStoreIds = [1, 2]; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once()) @@ -406,7 +420,7 @@ public function testGetActiveForCustomer() $cartId = 17; $customerId = 23; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -430,14 +444,14 @@ public function testSave() { $cartId = 100; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getId', 'getCustomerId', 'getStoreId', 'hasData', 'setData'] ); $quoteMock->expects($this->exactly(3))->method('getId')->willReturn($cartId); $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(2); $quoteMock->expects($this->once())->method('getStoreId')->willReturn(5); - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once())->method('getId')->willReturn($cartId); @@ -481,8 +495,8 @@ public function testGetList() ->method('load') ->with($cartMock); - $searchResult = $this->createMock(\Magento\Quote\Api\Data\CartSearchResultsInterface::class); - $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); + $searchResult = $this->createMock(CartSearchResultsInterface::class); + $searchCriteriaMock = $this->createMock(SearchCriteria::class); $this->searchResultsDataFactory ->expects($this->once()) ->method('create') @@ -495,7 +509,7 @@ public function testGetList() $this->extensionAttributesJoinProcessorMock->expects($this->once()) ->method('process') ->with( - $this->isInstanceOf(\Magento\Quote\Model\ResourceModel\Quote\Collection::class) + $this->isInstanceOf(Collection::class) ); $this->quoteCollectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$cartMock]); $searchResult->expects($this->once())->method('setTotalCount')->with($pageSize); diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index 48954f1af90fc..b4c75fc1d21d0 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -108,7 +108,7 @@ default="0" comment="Quote Id"/> - @@ -220,7 +220,7 @@ default="0" comment="Quote Id"/> - @@ -324,7 +324,7 @@ default="0" comment="Quote Item Id"/> - @@ -436,7 +436,7 @@ default="0" comment="Quote Id"/> - @@ -472,7 +472,7 @@ default="0" comment="Address Id"/> - diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php index 840dedb4f274e..4d832f603cd91 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php @@ -50,7 +50,6 @@ public function execute(QuoteAddress $address): array } $addressData = array_merge($addressData, [ - 'address_id' => $address->getId(), 'address_type' => $addressType, 'country' => [ 'code' => $address->getCountryId(), diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetQuoteAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetQuoteAddress.php deleted file mode 100644 index 89124c594dd87..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetQuoteAddress.php +++ /dev/null @@ -1,83 +0,0 @@ -quoteAddressFactory = $quoteAddressFactory; - $this->quoteAddressResource = $quoteAddressResource; - } - - /** - * Get quote address - * - * @param CartInterface $cart - * @param int $quoteAddressId - * @param int|null $customerId - * @return AddressInterface - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException - */ - public function execute(CartInterface $cart, int $quoteAddressId, ?int $customerId): AddressInterface - { - $quoteAddress = $this->quoteAddressFactory->create(); - - $this->quoteAddressResource->load($quoteAddress, $quoteAddressId); - if (null === $quoteAddress->getId()) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart address with ID "%cart_address_id"', ['cart_address_id' => $quoteAddressId]) - ); - } - - // TODO: GetQuoteAddress::execute should depend only on AddressInterface contract - // https://github.com/magento/graphql-ce/issues/550 - if ($quoteAddress->getQuoteId() !== $cart->getId()) { - throw new GraphQlNoSuchEntityException( - __('Cart does not contain address with ID "%cart_address_id"', ['cart_address_id' => $quoteAddressId]) - ); - } - - if ((int)$quoteAddress->getCustomerId() !== (int)$customerId) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot use cart address with ID "%cart_address_id"', - ['cart_address_id' => $quoteAddressId] - ) - ); - } - return $quoteAddress; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php index 730cf1b0ffee3..b2526bdc04e98 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodsOnCart.php @@ -16,25 +16,17 @@ */ class SetShippingMethodsOnCart implements SetShippingMethodsOnCartInterface { - /** - * @var GetQuoteAddress - */ - private $getQuoteAddress; - /** * @var AssignShippingMethodToCart */ private $assignShippingMethodToCart; /** - * @param GetQuoteAddress $getQuoteAddress * @param AssignShippingMethodToCart $assignShippingMethodToCart */ public function __construct( - GetQuoteAddress $getQuoteAddress, AssignShippingMethodToCart $assignShippingMethodToCart ) { - $this->getQuoteAddress = $getQuoteAddress; $this->assignShippingMethodToCart = $assignShippingMethodToCart; } @@ -50,11 +42,6 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s } $shippingMethodInput = current($shippingMethodsInput); - if (!isset($shippingMethodInput['cart_address_id']) || empty($shippingMethodInput['cart_address_id'])) { - throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing.')); - } - $cartAddressId = $shippingMethodInput['cart_address_id']; - if (!isset($shippingMethodInput['carrier_code']) || empty($shippingMethodInput['carrier_code'])) { throw new GraphQlInputException(__('Required parameter "carrier_code" is missing.')); } @@ -65,7 +52,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s } $methodCode = $shippingMethodInput['method_code']; - $quoteAddress = $this->getQuoteAddress->execute($cart, $cartAddressId, $context->getUserId()); - $this->assignShippingMethodToCart->execute($cart, $quoteAddress, $carrierCode, $methodCode); + $shippingAddress = $cart->getShippingAddress(); + $this->assignShippingMethodToCart->execute($cart, $shippingAddress, $carrierCode, $methodCode); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index d1dcb4a48a76b..7b81964f111c6 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -69,17 +69,15 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $paymentMethodCode = $args['input']['payment_method']['code']; - $poNumber = isset($args['input']['payment_method']['purchase_order_number']) - && empty($args['input']['payment_method']['purchase_order_number']) - ? $args['input']['payment_method']['purchase_order_number'] - : null; + $poNumber = $args['input']['payment_method']['purchase_order_number'] ?? null; + $additionalData = $args['input']['payment_method']['additional_data'] ?? []; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $payment = $this->paymentFactory->create([ 'data' => [ PaymentInterface::KEY_METHOD => $paymentMethodCode, PaymentInterface::KEY_PO_NUMBER => $poNumber, - PaymentInterface::KEY_ADDITIONAL_DATA => [], + PaymentInterface::KEY_ADDITIONAL_DATA => $additionalData, ] ]); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php index 78a07506556c0..25a79ae126ef8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php @@ -16,6 +16,7 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartItemRepositoryInterface; use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Item; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** @@ -111,8 +112,35 @@ private function processCartItems(Quote $cart, array $items): void $this->cartItemRepository->deleteById((int)$cart->getId(), $itemId); } else { $cartItem->setQty($qty); + $this->validateCartItem($cartItem); $this->cartItemRepository->save($cartItem); } } } + + /** + * Validate cart item + * + * @param Item $cartItem + * @return void + * @throws GraphQlInputException + */ + private function validateCartItem(Item $cartItem): void + { + if ($cartItem->getHasError()) { + $errors = []; + foreach ($cartItem->getMessage(false) as $message) { + $errors[] = $message; + } + + if (!empty($errors)) { + throw new GraphQlInputException( + __( + 'Could not update the product with SKU %sku: %message', + ['sku' => $cartItem->getSku(), 'message' => __(implode("\n", $errors))] + ) + ); + } + } + } } diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index 22ca9cfdfae9a..a3c07f7df2cee 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -14,7 +14,8 @@ "magento/module-sales": "*" }, "suggest": { - "magento/module-graph-ql": "*" + "magento/module-graph-ql": "*", + "magento/module-graph-ql-cache": "*" }, "license": [ "OSL-3.0", diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index a9784e97c8952..6d94685ac4d0f 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - cart(cart_id: String!): Cart @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart") @doc(description:"Returns information about shopping cart") + cart(cart_id: String!): Cart @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart") @doc(description:"Returns information about shopping cart") @cache(cacheable: false) } type Mutation { @@ -115,7 +115,6 @@ input SetShippingMethodsOnCartInput { } input ShippingMethodInput { - cart_address_id: Int! carrier_code: String! method_code: String! } @@ -131,9 +130,13 @@ input SetPaymentMethodOnCartInput { input PaymentMethodInput { code: String! @doc(description:"Payment method code") + additional_data: PaymentMethodAdditionalDataInput @doc(description:"Additional payment data") purchase_order_number: String @doc(description:"Purchase order number") } +input PaymentMethodAdditionalDataInput { +} + input SetGuestEmailOnCartInput { cart_id: String! email: String! @@ -188,7 +191,6 @@ type Cart { } type CartAddress { - address_id: Int firstname: String lastname: String company: String @@ -231,13 +233,14 @@ type SelectedShippingMethod { type AvailableShippingMethod { carrier_code: String! carrier_title: String! - method_code: String! - method_title: String! + method_code: String @doc(description: "Could be null if method is not available") + method_title: String @doc(description: "Could be null if method is not available") error_message: String amount: Float! - base_amount: Float! + base_amount: Float @doc(description: "Could be null if method is not available") price_excl_tax: Float! price_incl_tax: Float! + available: Boolean! } type AvailablePaymentMethod { @@ -247,9 +250,13 @@ type AvailablePaymentMethod { type SelectedPaymentMethod { code: String @doc(description: "The payment method code") + additional_data: SelectedPaymentMethodAdditionalData @doc(description: "Additional payment data") purchase_order_number: String @doc(description: "The purchase order number.") } +type SelectedPaymentMethodAdditionalData { +} + enum AdressTypeEnum { SHIPPING BILLING diff --git a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php index 55f448730a506..b86f8dff2b3b1 100644 --- a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php @@ -12,6 +12,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\CacheInterface; +use Magento\User\Model\User; class CanViewNotificationTest extends \PHPUnit\Framework\TestCase { @@ -33,6 +34,11 @@ class CanViewNotificationTest extends \PHPUnit\Framework\TestCase /** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */ private $cacheStorageMock; + /** + * @var User|\PHPUnit_Framework_MockObject_MockObject + */ + private $userMock; + public function setUp() { $this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class) @@ -41,7 +47,6 @@ public function setUp() ->getMock(); $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() - ->setMethods(['getUser', 'getId']) ->getMock(); $this->viewerLoggerMock = $this->getMockBuilder(Logger::class) ->disableOriginalConstructor() @@ -49,6 +54,7 @@ public function setUp() $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) ->disableOriginalConstructor() ->getMock(); + $this->userMock = $this->createMock(User::class); $objectManager = new ObjectManager($this); $this->canViewNotification = $objectManager->getObject( CanViewNotification::class, @@ -65,8 +71,8 @@ public function testIsVisibleLoadDataFromCache() { $this->sessionMock->expects($this->once()) ->method('getUser') - ->willReturn($this->sessionMock); - $this->sessionMock->expects($this->once()) + ->willReturn($this->userMock); + $this->userMock->expects($this->once()) ->method('getId') ->willReturn(1); $this->cacheStorageMock->expects($this->once()) @@ -90,8 +96,8 @@ public function testIsVisible($expected, $version, $lastViewVersion) ->willReturn(false); $this->sessionMock->expects($this->once()) ->method('getUser') - ->willReturn($this->sessionMock); - $this->sessionMock->expects($this->once()) + ->willReturn($this->userMock); + $this->userMock->expects($this->once()) ->method('getId') ->willReturn(1); $this->productMetadataMock->expects($this->once()) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Customer/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Customer/Collection.php index aa01e33caf3d2..b6e55af96f4c1 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Customer/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Customer/Collection.php @@ -6,8 +6,6 @@ namespace Magento\Reports\Model\ResourceModel\Customer; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Customers Report collection. * @@ -75,6 +73,7 @@ class Collection extends \Magento\Customer\Model\ResourceModel\Customer\Collecti protected $orderResource; /** + * Collection constructor. * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy @@ -89,10 +88,9 @@ class Collection extends \Magento\Customer\Model\ResourceModel\Customer\Collecti * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository * @param \Magento\Quote\Model\ResourceModel\Quote\Item\CollectionFactory $quoteItemFactory * @param \Magento\Sales\Model\ResourceModel\Order\Collection $orderResource - * @param mixed $connection + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @param string $modelName * - * @param ResourceModelPoolInterface|null $resourceModelPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -111,8 +109,7 @@ public function __construct( \Magento\Quote\Model\ResourceModel\Quote\Item\CollectionFactory $quoteItemFactory, \Magento\Sales\Model\ResourceModel\Order\Collection $orderResource, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - $modelName = self::CUSTOMER_MODEL_NAME, - ResourceModelPoolInterface $resourceModelPool = null + $modelName = self::CUSTOMER_MODEL_NAME ) { parent::__construct( $entityFactory, @@ -127,8 +124,7 @@ public function __construct( $entitySnapshot, $fieldsetConfig, $connection, - $modelName, - $resourceModelPool + $modelName ); $this->orderResource = $orderResource; $this->quoteRepository = $quoteRepository; diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php index 451007960a1ce..966ee14c2cb64 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php @@ -9,17 +9,11 @@ */ namespace Magento\Reports\Model\ResourceModel\Product; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Products Report collection. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @api * @since 100.0.2 */ @@ -95,14 +89,8 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Reports\Model\Event\TypeFactory $eventTypeFactory * @param \Magento\Catalog\Model\Product\Type $productType * @param \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteResource - * @param mixed $connection - * @param ProductLimitationFactory|null $productLimitationFactory - * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface $resourceModelPool - * @throws \Magento\Framework\Exception\LocalizedException + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -129,13 +117,7 @@ public function __construct( \Magento\Reports\Model\Event\TypeFactory $eventTypeFactory, \Magento\Catalog\Model\Product\Type $productType, \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteResource, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { $this->setProductEntityId($product->getEntityIdField()); $this->setProductEntityTableName($product->getEntityTable()); @@ -160,13 +142,7 @@ public function __construct( $customerSession, $dateTime, $groupManagement, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); $this->_eventTypeFactory = $eventTypeFactory; $this->_productType = $productType; diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php index bec8faaee0ca7..5b4cf39d65def 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php @@ -9,17 +9,12 @@ */ namespace Magento\Reports\Model\ResourceModel\Product\Index\Collection; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * Reports Product Index Abstract Product Resource Collection. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ @@ -59,13 +54,8 @@ abstract class AbstractCollection extends \Magento\Catalog\Model\ResourceModel\P * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement * @param \Magento\Customer\Model\Visitor $customerVisitor - * @param mixed $connection - * @param ProductLimitationFactory|null $productLimitationFactory - * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -89,13 +79,7 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Customer\Api\GroupManagementInterface $groupManagement, \Magento\Customer\Model\Visitor $customerVisitor, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { parent::__construct( $entityFactory, @@ -117,13 +101,7 @@ public function __construct( $customerSession, $dateTime, $groupManagement, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); $this->_customerVisitor = $customerVisitor; } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php index 8bf50f4c1b8e7..39d673911111f 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php @@ -5,22 +5,19 @@ */ /** + * Product Low Stock Report Collection + * * @author Magento Core Team */ namespace Magento\Reports\Model\ResourceModel\Product\Lowstock; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; /** * Product Low Stock Report Collection. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @api * @since 100.0.2 */ @@ -56,7 +53,6 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Product\Collection protected $_itemResource; /** - * Collection constructor. * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy @@ -84,13 +80,7 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Product\Collection * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration * @param \Magento\CatalogInventory\Model\ResourceModel\Stock\Item $itemResource * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection - * @param ProductLimitationFactory|null $productLimitationFactory - * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool - * @throws LocalizedException + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -120,13 +110,7 @@ public function __construct( \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, \Magento\CatalogInventory\Model\ResourceModel\Stock\Item $itemResource, - \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, - ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null ) { parent::__construct( $entityFactory, @@ -152,13 +136,7 @@ public function __construct( $eventTypeFactory, $productType, $quoteResource, - $connection, - $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $connection ); $this->stockRegistry = $stockRegistry; $this->stockConfiguration = $stockConfiguration; diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index cb4d51e0c540d..038d37a990442 100644 --- a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -12,7 +12,6 @@ use Magento\Catalog\Model\Product\Type as ProductType; use Magento\Catalog\Model\ResourceModel\Helper; use Magento\Catalog\Model\ResourceModel\Product as ResourceProduct; -use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\Catalog\Model\ResourceModel\Url; use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Model\Session; @@ -26,9 +25,7 @@ use Magento\Framework\Data\Collection\EntityFactory; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Event\ManagerInterface; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; use Magento\Framework\Module\Manager; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; @@ -37,7 +34,6 @@ use Magento\Quote\Model\ResourceModel\Quote\Collection; use Magento\Reports\Model\Event\TypeFactory; use Magento\Reports\Model\ResourceModel\Product\Collection as ProductCollection; -use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; @@ -82,6 +78,46 @@ class CollectionTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = new ObjectManager($this); + $context = $this->createPartialMock(Context::class, ['getResource', 'getEavConfig']); + $entityFactoryMock = $this->createMock(EntityFactory::class); + $loggerMock = $this->createMock(LoggerInterface::class); + $fetchStrategyMock = $this->createMock(FetchStrategyInterface::class); + $eventManagerMock = $this->createMock(ManagerInterface::class); + $eavConfigMock = $this->createMock(Config::class); + $this->resourceMock = $this->createPartialMock(ResourceConnection::class, ['getTableName', 'getConnection']); + $eavEntityFactoryMock = $this->createMock(EavEntityFactory::class); + $resourceHelperMock = $this->createMock(Helper::class); + $universalFactoryMock = $this->createMock(UniversalFactory::class); + $storeManagerMock = $this->createPartialMockForAbstractClass( + StoreManagerInterface::class, + ['getStore', 'getId'] + ); + $moduleManagerMock = $this->createMock(Manager::class); + $productFlatStateMock = $this->createMock(State::class); + $scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $optionFactoryMock = $this->createMock(OptionFactory::class); + $catalogUrlMock = $this->createMock(Url::class); + $localeDateMock = $this->createMock(TimezoneInterface::class); + $customerSessionMock = $this->createMock(Session::class); + $dateTimeMock = $this->createMock(DateTime::class); + $groupManagementMock = $this->createMock(GroupManagementInterface::class); + $eavConfig = $this->createPartialMock(Config::class, ['getEntityType']); + $entityType = $this->createMock(Type::class); + + $eavConfig->expects($this->atLeastOnce())->method('getEntityType')->willReturn($entityType); + $context->expects($this->atLeastOnce())->method('getResource')->willReturn($this->resourceMock); + $context->expects($this->atLeastOnce())->method('getEavConfig')->willReturn($eavConfig); + + $defaultAttributes = $this->createPartialMock(DefaultAttributes::class, ['_getDefaultAttributes']); + $productMock = $this->objectManager->getObject( + ResourceProduct::class, + ['context' => $context, 'defaultAttributes' => $defaultAttributes] + ); + + $this->eventTypeFactoryMock = $this->createMock(TypeFactory::class); + $productTypeMock = $this->createMock(ProductType::class); + $quoteResourceMock = $this->createMock(Collection::class); + $this->connectionMock = $this->createPartialMockForAbstractClass(AdapterInterface::class, ['select']); $this->selectMock = $this->createPartialMock( Select::class, [ @@ -94,65 +130,39 @@ protected function setUp() 'having', ] ); - $this->connectionMock = $this->createMock(AdapterInterface::class); - $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); - $this->resourceMock = $this->createPartialMock(ResourceConnection::class, ['getTableName', 'getConnection']); + + $storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeManagerMock); + $storeManagerMock->expects($this->atLeastOnce())->method('getId')->willReturn(1); + $universalFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($productMock); $this->resourceMock->expects($this->atLeastOnce())->method('getTableName')->willReturn('test_table'); $this->resourceMock->expects($this->atLeastOnce())->method('getConnection')->willReturn($this->connectionMock); - $eavConfig = $this->createPartialMock(Config::class, ['getEntityType']); - $eavConfig->expects($this->atLeastOnce())->method('getEntityType')->willReturn($this->createMock(Type::class)); - $context = $this->createPartialMock(Context::class, ['getResource', 'getEavConfig']); - $context->expects($this->atLeastOnce())->method('getResource')->willReturn($this->resourceMock); - $context->expects($this->atLeastOnce())->method('getEavConfig')->willReturn($eavConfig); - $storeMock = $this->createMock(StoreInterface::class); - $storeMock->expects($this->atLeastOnce())->method('getId')->willReturn(1); - $storeManagerMock = $this->createMock(StoreManagerInterface::class); - $storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); - $productMock = $this->objectManager->getObject( - ResourceProduct::class, - [ - 'context' => $context, - 'defaultAttributes' => $this->createPartialMock( - DefaultAttributes::class, - ['_getDefaultAttributes'] - ) - ] - ); - $resourceModelPoolMock = $this->createMock(ResourceModelPoolInterface::class); - $resourceModelPoolMock->expects($this->atLeastOnce())->method('get')->willReturn($productMock); - $this->eventTypeFactoryMock = $this->createMock(TypeFactory::class); + $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); $this->collection = new ProductCollection( - $this->createMock(EntityFactory::class), - $this->createMock(LoggerInterface::class), - $this->createMock(FetchStrategyInterface::class), - $this->createMock(ManagerInterface::class), - $this->createMock(Config::class), + $entityFactoryMock, + $loggerMock, + $fetchStrategyMock, + $eventManagerMock, + $eavConfigMock, $this->resourceMock, - $this->createMock(EavEntityFactory::class), - $this->createMock(Helper::class), - $this->createMock(UniversalFactory::class), + $eavEntityFactoryMock, + $resourceHelperMock, + $universalFactoryMock, $storeManagerMock, - $this->createMock(Manager::class), - $this->createMock(State::class), - $this->createMock(ScopeConfigInterface::class), - $this->createMock(OptionFactory::class), - $this->createMock(Url::class), - $this->createMock(TimezoneInterface::class), - $this->createMock(Session::class), - $this->createMock(DateTime::class), - $this->createMock(GroupManagementInterface::class), + $moduleManagerMock, + $productFlatStateMock, + $scopeConfigMock, + $optionFactoryMock, + $catalogUrlMock, + $localeDateMock, + $customerSessionMock, + $dateTimeMock, + $groupManagementMock, $productMock, $this->eventTypeFactoryMock, - $this->createMock(ProductType::class), - $this->createMock(Collection::class), - $this->connectionMock, - $this->createMock(ProductLimitationFactory::class), - $this->createMock(MetadataPool::class), - $this->createMock(\Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer::class), - $this->createMock(\Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver::class), - $this->createMock(\Magento\Framework\Indexer\DimensionFactory::class), - $resourceModelPoolMock + $productTypeMock, + $quoteResourceMock, + $this->connectionMock ); } @@ -252,4 +262,25 @@ public function testAddViewsCount() $this->collection->addViewsCount(); } + + /** + * Get mock for abstract class with methods. + * + * @param string $className + * @param array $methods + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createPartialMockForAbstractClass($className, $methods) + { + return $this->getMockForAbstractClass( + $className, + [], + '', + true, + true, + true, + $methods + ); + } } diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_accounts_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_accounts_grid.xml index 900dc08d571da..55ca286ad3d47 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_accounts_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_accounts_grid.xml @@ -11,7 +11,7 @@ gridAccounts - Magento\Reports\Model\ResourceModel\Accounts\Collection\Initial + Magento\Reports\Model\ResourceModel\Accounts\Collection\Initial diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_orders_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_orders_grid.xml index d886e5724cb0b..f97bec3c15253 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_orders_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_orders_grid.xml @@ -11,7 +11,7 @@ gridOrdersCustomer - Magento\Reports\Model\ResourceModel\Customer\Orders\Collection\Initial + Magento\Reports\Model\ResourceModel\Customer\Orders\Collection\Initial diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_totals_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_totals_grid.xml index 4914829cf6ebc..e1df04237c2a6 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_totals_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_customer_totals_grid.xml @@ -11,7 +11,7 @@ gridTotalsCustomer - Magento\Reports\Model\ResourceModel\Customer\Totals\Collection\Initial + Magento\Reports\Model\ResourceModel\Customer\Totals\Collection\Initial diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_grid.xml index 82aa475807a25..0f6fbabb6a55c 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_grid.xml @@ -24,7 +24,7 @@ 0 0 gridReport - Magento\Reports\Model\ResourceModel\Report\Collection + Magento\Reports\Model\ResourceModel\Report\Collection diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_lowstock_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_lowstock_grid.xml index 070c39259aabd..62916fe1c1d78 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_lowstock_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_lowstock_grid.xml @@ -12,7 +12,7 @@ gridLowstock 0 - Magento\Reports\Model\ResourceModel\Product\Lowstock\Collection + Magento\Reports\Model\ResourceModel\Product\Lowstock\Collection diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_sold_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_sold_grid.xml index a1b01aeeb526f..22c66352b32e4 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_sold_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_product_sold_grid.xml @@ -11,7 +11,7 @@ gridProductsSold - Magento\Reports\Model\ResourceModel\Product\Sold\Collection\Initial + Magento\Reports\Model\ResourceModel\Product\Sold\Collection\Initial diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_customer_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_customer_grid.xml index f941ca52eef59..a728f471e4def 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_customer_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_customer_grid.xml @@ -13,7 +13,7 @@ customers_grid review_cnt desc - Magento\Reports\Model\ResourceModel\Review\Customer\Collection + Magento\Reports\Model\ResourceModel\Review\Customer\Collection diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml index 1275e761ade3c..26d0e8b13659d 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml @@ -13,7 +13,7 @@ gridProducts review_cnt desc - Magento\Reports\Model\ResourceModel\Review\Product\Collection + Magento\Reports\Model\ResourceModel\Review\Product\Collection diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml index 4ec984ef9fc11..649dc7ceeb065 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml @@ -15,7 +15,7 @@ 0 0 0 - Magento\Reports\Model\ResourceModel\Refresh\Collection + Magento\Reports\Model\ResourceModel\Refresh\Collection diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php index d4e50a9e43d68..ab264ef1b6179 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php @@ -5,20 +5,17 @@ */ namespace Magento\Review\Model\ResourceModel\Review\Product; -use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; /** * Review Product Collection * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection @@ -92,10 +89,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @param ProductLimitationFactory|null $productLimitationFactory * @param MetadataPool|null $metadataPool - * @param TableMaintainer|null $tableMaintainer - * @param PriceTableResolver|null $priceTableResolver - * @param DimensionFactory|null $dimensionFactory - * @param ResourceModelPoolInterface|null $resourceModelPool + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -122,11 +116,7 @@ public function __construct( \Magento\Review\Model\Rating\Option\VoteFactory $voteFactory, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, ProductLimitationFactory $productLimitationFactory = null, - MetadataPool $metadataPool = null, - TableMaintainer $tableMaintainer = null, - PriceTableResolver $priceTableResolver = null, - DimensionFactory $dimensionFactory = null, - ResourceModelPoolInterface $resourceModelPool = null + MetadataPool $metadataPool = null ) { $this->_ratingFactory = $ratingFactory; $this->_voteFactory = $voteFactory; @@ -152,11 +142,7 @@ public function __construct( $groupManagement, $connection, $productLimitationFactory, - $metadataPool, - $tableMaintainer, - $priceTableResolver, - $dimensionFactory, - $resourceModelPool + $metadataPool ); } diff --git a/app/code/Magento/Review/view/adminhtml/layout/rating_block.xml b/app/code/Magento/Review/view/adminhtml/layout/rating_block.xml index b439bcb1c2710..414cec14c3bec 100644 --- a/app/code/Magento/Review/view/adminhtml/layout/rating_block.xml +++ b/app/code/Magento/Review/view/adminhtml/layout/rating_block.xml @@ -11,7 +11,7 @@ ratingsGrid - Magento\Review\Model\ResourceModel\Rating\Grid\Collection + Magento\Review\Model\ResourceModel\Rating\Grid\Collection rating_code ASC 1 diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index 1a12a68a6874a..1c3cf9cc2b350 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -9,7 +9,7 @@
- + diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 2dc467d6ca247..e437918b683b2 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -27,18 +27,23 @@ + required-number validate-number + required-number validate-number + required-number validate-number + required-number validate-number + required-number validate-number diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml index 0f5a3559f3008..fe2cb7e01b727 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml @@ -13,7 +13,7 @@ sales_order_create_customer_grid 1 entity_id - Magento\Sales\Model\ResourceModel\Order\Customer\Collection + Magento\Sales\Model\ResourceModel\Order\Customer\Collection 1 customer_grid diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_grid_block.xml index 7f14ff3728a47..318416a777f3f 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_grid_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_grid_block.xml @@ -11,7 +11,7 @@ order_creditmemos - Magento\Sales\Model\ResourceModel\Order\Creditmemo\Order\Grid\Collection + Magento\Sales\Model\ResourceModel\Order\Creditmemo\Order\Grid\Collection true created_at DESC diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_grid_block.xml index 941696f0ce898..d69ed42677109 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_grid_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_grid_block.xml @@ -11,7 +11,7 @@ order_invoices - Magento\Sales\Model\ResourceModel\Order\Invoice\Orders\Grid\Collection + Magento\Sales\Model\ResourceModel\Order\Invoice\Orders\Grid\Collection true created_at DESC diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_shipment_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_shipment_grid_block.xml index 0180efd29d2fc..21b5d3b06c167 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_shipment_grid_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_shipment_grid_block.xml @@ -11,7 +11,7 @@ order_shipments - Magento\Sales\Model\ResourceModel\Order\Shipment\Order\Grid\Collection + Magento\Sales\Model\ResourceModel\Order\Shipment\Order\Grid\Collection true created_at DESC diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_status_index.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_status_index.xml index 87d7644a4b00f..6854d3bb7bc38 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_status_index.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_status_index.xml @@ -12,7 +12,7 @@ sales_order_status_grid - Magento\Sales\Model\ResourceModel\Status\Collection + Magento\Sales\Model\ResourceModel\Status\Collection state desc 1 diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_transactions_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_transactions_grid_block.xml index c2f5532857202..adb722f6d0f54 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_transactions_grid_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_transactions_grid_block.xml @@ -13,7 +13,7 @@ order_transactions - + Magento\Sales\Model\Grid\CollectionUpdater true diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_transaction_child_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_transaction_child_block.xml index aa8672a68bc6c..5bdad85ebc160 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_transaction_child_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_transaction_child_block.xml @@ -11,7 +11,7 @@ transactionChildGrid - + Magento\Sales\Model\Grid\Child\CollectionUpdater false diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_transactions_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_transactions_grid_block.xml index 9f3b7f23ba20f..37b319b94352d 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_transactions_grid_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_transactions_grid_block.xml @@ -11,7 +11,7 @@ sales_transactions_grid - Magento\Sales\Model\ResourceModel\Transaction\Grid\Collection + Magento\Sales\Model\ResourceModel\Transaction\Grid\Collection true created_at DESC diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml index 17d43266ba524..4f8123c72e9e7 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml @@ -47,15 +47,17 @@
-
-
- getChildHtml('shipping_method') ?> +
+
+
-
- -
-
- getChildHtml('billing_method') ?> +
+
+ getChildHtml('billing_method') ?> +
+
+ getChildHtml('shipping_method') ?> +
diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index 44f106532858f..06146f805c644 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - customerOrders: CustomerOrders @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Orders") @doc(description: "List of customer orders") + customerOrders: CustomerOrders @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Orders") @doc(description: "List of customer orders") @cache(cacheable: false) } type CustomerOrder @doc(description: "Order mapping fields") { diff --git a/app/code/Magento/SalesRule/view/adminhtml/layout/sales_rule_promo_quote_index.xml b/app/code/Magento/SalesRule/view/adminhtml/layout/sales_rule_promo_quote_index.xml index 21ce8bfc3eaac..7796f280efcbc 100644 --- a/app/code/Magento/SalesRule/view/adminhtml/layout/sales_rule_promo_quote_index.xml +++ b/app/code/Magento/SalesRule/view/adminhtml/layout/sales_rule_promo_quote_index.xml @@ -12,7 +12,7 @@ promo_quote_grid - Magento\SalesRule\Model\ResourceModel\Rule\Quote\Collection + Magento\SalesRule\Model\ResourceModel\Rule\Quote\Collection sort_order ASC 1 diff --git a/app/code/Magento/Search/view/adminhtml/layout/search_term_block.xml b/app/code/Magento/Search/view/adminhtml/layout/search_term_block.xml index cc2e1509e4412..9dc60bde11315 100644 --- a/app/code/Magento/Search/view/adminhtml/layout/search_term_block.xml +++ b/app/code/Magento/Search/view/adminhtml/layout/search_term_block.xml @@ -11,7 +11,7 @@ searchReportGrid - Magento\Search\Model\ResourceModel\Query\Collection + Magento\Search\Model\ResourceModel\Query\Collection query_id DESC diff --git a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml index 7330ac1f92712..38c6fa52455b9 100644 --- a/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml +++ b/app/code/Magento/Search/view/adminhtml/layout/search_term_grid_block.xml @@ -11,7 +11,7 @@ search_term_grid - Magento\Search\Model\ResourceModel\Query\Collection + Magento\Search\Model\ResourceModel\Query\Collection name ASC 1 diff --git a/app/code/Magento/Search/view/adminhtml/layout/search_term_report_block.xml b/app/code/Magento/Search/view/adminhtml/layout/search_term_report_block.xml index b6b1455cf27ca..f4c5d1ab85a0a 100644 --- a/app/code/Magento/Search/view/adminhtml/layout/search_term_report_block.xml +++ b/app/code/Magento/Search/view/adminhtml/layout/search_term_report_block.xml @@ -12,7 +12,7 @@ searchReportGrid - Magento\Search\Model\ResourceModel\Query\Collection + Magento\Search\Model\ResourceModel\Query\Collection query_id DESC diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index a98a70a90cede..44c8db3f1a663 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -16,31 +16,26 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class);