diff --git a/.php_cs.dist b/.php_cs.dist index 0f254c63283..84a5f88bf43 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -5,9 +5,9 @@ */ /** - * Pre-commit hook installation: - * vendor/bin/static-review.php hook:install dev/tools/Magento/Tools/StaticReview/pre-commit .git/hooks/pre-commit + * PHP Coding Standards fixer configuration */ + $finder = PhpCsFixer\Finder::create() ->name('*.phtml') ->exclude('dev/tests/functional/generated') diff --git a/README.md b/README.md index c72357db26d..0fe25bb6029 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/magento/magento2.svg?branch=develop)](https://travis-ci.org/magento/magento2) +[![Build Status](https://travis-ci.org/magento/magento2.svg?branch=2.3-develop)](https://travis-ci.org/magento/magento2) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magento/magento2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/magento-2/localized.png)](https://crowdin.com/project/magento-2)

Welcome

diff --git a/app/code/Magento/AdminNotification/etc/db_schema.xml b/app/code/Magento/AdminNotification/etc/db_schema.xml index 6d969b3f009..35e6045b607 100644 --- a/app/code/Magento/AdminNotification/etc/db_schema.xml +++ b/app/code/Magento/AdminNotification/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> diff --git a/app/code/Magento/AdminNotification/etc/module.xml b/app/code/Magento/AdminNotification/etc/module.xml index 8a792ee8453..607ecbde10a 100644 --- a/app/code/Magento/AdminNotification/etc/module.xml +++ b/app/code/Magento/AdminNotification/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml index ac4b8dafd01..230fb17ae55 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/module.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/module.xml @@ -6,6 +6,6 @@ */ --> - + diff --git a/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php b/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php index c4118792255..34f2b7d53d9 100644 --- a/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php +++ b/app/code/Magento/Analytics/Block/Adminhtml/System/Config/CollectionTimeLabel.php @@ -5,13 +5,35 @@ */ namespace Magento\Analytics\Block\Adminhtml\System\Config; +use Magento\Framework\App\ObjectManager; + /** * Provides label with default Time Zone */ class CollectionTimeLabel extends \Magento\Config\Block\System\Config\Form\Field { /** - * Add default time zone to comment + * @var \Magento\Framework\Locale\ResolverInterface + */ + private $localeResolver; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param array $data + * @param \Magento\Framework\Locale\ResolverInterface|null $localeResolver + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + array $data = [], + \Magento\Framework\Locale\ResolverInterface $localeResolver = null + ) { + $this->localeResolver = $localeResolver ?: + ObjectManager::getInstance()->get(\Magento\Framework\Locale\ResolverInterface::class); + parent::__construct($context, $data); + } + + /** + * Add current time zone to comment, properly translated according to locale * * @param \Magento\Framework\Data\Form\Element\AbstractElement $element * @return string @@ -19,7 +41,9 @@ class CollectionTimeLabel extends \Magento\Config\Block\System\Config\Form\Field public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) { $timeZoneCode = $this->_localeDate->getConfigTimezone(); - $getLongTimeZoneName = \IntlTimeZone::createTimeZone($timeZoneCode)->getDisplayName(); + $locale = $this->localeResolver->getLocale(); + $getLongTimeZoneName = \IntlTimeZone::createTimeZone($timeZoneCode) + ->getDisplayName(false, \IntlTimeZone::DISPLAY_LONG, $locale); $element->setData( 'comment', sprintf("%s (%s)", $getLongTimeZoneName, $timeZoneCode) diff --git a/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php b/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php index e26ad01fc74..524062eec35 100644 --- a/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php +++ b/app/code/Magento/Analytics/Model/Config/Backend/CollectionTime.php @@ -66,7 +66,9 @@ public function afterSave() $result = preg_match('#(?\d{2}),(?\d{2}),(?\d{2})#', $this->getValue(), $time); if (!$result) { - throw new LocalizedException(__('Time value has an unsupported format')); + throw new LocalizedException( + __('The time value is using an unsupported format. Enter a supported format and try again.') + ); } $cronExprArray = [ diff --git a/app/code/Magento/Analytics/Model/Cryptographer.php b/app/code/Magento/Analytics/Model/Cryptographer.php index 6905eee372a..665d564814b 100644 --- a/app/code/Magento/Analytics/Model/Cryptographer.php +++ b/app/code/Magento/Analytics/Model/Cryptographer.php @@ -56,13 +56,18 @@ public function encode($source) try { $source = (string)$source; } catch (\Exception $e) { - throw new LocalizedException(__('Input data must be string or convertible into string.')); + throw new LocalizedException( + __( + 'The data is invalid. ' + . 'Enter the data as a string or data that can be converted into a string and try again.' + ) + ); } } elseif (!$source) { - throw new LocalizedException(__('Input data must be non-empty string.')); + throw new LocalizedException(__('The data is invalid. Enter the data as a string and try again.')); } if (!$this->validateCipherMethod($this->cipherMethod)) { - throw new LocalizedException(__('Not valid cipher method.')); + throw new LocalizedException(__('The data is invalid. Use a valid cipher method and try again.')); } $initializationVector = $this->getInitializationVector(); @@ -90,7 +95,7 @@ private function getKey() { $token = $this->analyticsToken->getToken(); if (!$token) { - throw new LocalizedException(__('Encryption key can\'t be empty.')); + throw new LocalizedException(__('Enter the encryption key and try again.')); } return hash('sha256', $token); } diff --git a/app/code/Magento/Analytics/Model/ExportDataHandler.php b/app/code/Magento/Analytics/Model/ExportDataHandler.php index b9d3b634018..dc17a548763 100644 --- a/app/code/Magento/Analytics/Model/ExportDataHandler.php +++ b/app/code/Magento/Analytics/Model/ExportDataHandler.php @@ -195,7 +195,7 @@ private function pack($source, $destination) private function validateSource(WriteInterface $directory, $path) { if (!$directory->isExist($path)) { - throw new LocalizedException(__('Source "%1" is not exist', $directory->getAbsolutePath($path))); + throw new LocalizedException(__('The "%1" source doesn\'t exist.', $directory->getAbsolutePath($path))); } return $directory->getAbsolutePath($path); diff --git a/app/code/Magento/Analytics/Setup/InstallData.php b/app/code/Magento/Analytics/Setup/InstallData.php deleted file mode 100644 index 9832849bacc..00000000000 --- a/app/code/Magento/Analytics/Setup/InstallData.php +++ /dev/null @@ -1,52 +0,0 @@ -getConnection()->insertMultiple( - $setup->getTable('core_config_data'), - [ - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => 'analytics/subscription/enabled', - 'value' => 1 - ], - [ - 'scope' => 'default', - 'scope_id' => 0, - 'path' => SubscriptionHandler::CRON_STRING_PATH, - 'value' => join(' ', SubscriptionHandler::CRON_EXPR_ARRAY) - ] - ] - ); - - $setup->getConnection()->insert( - $setup->getTable('flag'), - [ - 'flag_code' => SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, - 'state' => 0, - 'flag_data' => 24, - ] - ); - } -} diff --git a/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php new file mode 100644 index 00000000000..a352854a8b7 --- /dev/null +++ b/app/code/Magento/Analytics/Setup/Patch/Data/PrepareInitialConfig.php @@ -0,0 +1,92 @@ +moduleDataSetup = $moduleDataSetup; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $this->moduleDataSetup->getConnection()->insertMultiple( + $this->moduleDataSetup->getTable('core_config_data'), + [ + [ + 'scope' => 'default', + 'scope_id' => 0, + 'path' => 'analytics/subscription/enabled', + 'value' => 1 + ], + [ + 'scope' => 'default', + 'scope_id' => 0, + 'path' => SubscriptionHandler::CRON_STRING_PATH, + 'value' => join(' ', SubscriptionHandler::CRON_EXPR_ARRAY) + ] + ] + ); + + $this->moduleDataSetup->getConnection()->insert( + $this->moduleDataSetup->getTable('flag'), + [ + 'flag_code' => SubscriptionHandler::ATTEMPTS_REVERSE_COUNTER_FLAG_CODE, + 'state' => 0, + 'flag_data' => 24, + ] + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php index a652cf6b3d5..462b3c909a7 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php @@ -9,6 +9,7 @@ use Magento\Backend\Block\Template\Context; use Magento\Framework\Data\Form; use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -34,6 +35,11 @@ class CollectionTimeLabelTest extends \PHPUnit\Framework\TestCase */ private $abstractElementMock; + /** + * @var ResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $localeResolver; + protected function setUp() { $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) @@ -53,12 +59,17 @@ protected function setUp() $this->contextMock->expects($this->any()) ->method('getLocaleDate') ->willReturn($this->timeZoneMock); + $this->localeResolver = $this->getMockBuilder(ResolverInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getLocale']) + ->getMockForAbstractClass(); $objectManager = new ObjectManager($this); $this->collectionTimeLabel = $objectManager->getObject( CollectionTimeLabel::class, [ - 'context' => $this->contextMock + 'context' => $this->contextMock, + 'localeResolver' => $this->localeResolver ] ); } @@ -73,6 +84,9 @@ public function testRender() $this->abstractElementMock->expects($this->any()) ->method('getComment') ->willReturn('Eastern Standard Time (America/New_York)'); + $this->localeResolver->expects($this->once()) + ->method('getLocale') + ->willReturn('en_US'); $this->assertRegexp( "/Eastern Standard Time \(America\/New_York\)/", $this->collectionTimeLabel->render($this->abstractElementMock) diff --git a/app/code/Magento/Analytics/etc/module.xml b/app/code/Magento/Analytics/etc/module.xml index 32ee5d23a4d..24c2fbc8144 100644 --- a/app/code/Magento/Analytics/etc/module.xml +++ b/app/code/Magento/Analytics/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Authorization/Model/Acl/AclRetriever.php b/app/code/Magento/Authorization/Model/Acl/AclRetriever.php index f22cbaf4633..904c8d0ea77 100644 --- a/app/code/Magento/Authorization/Model/Acl/AclRetriever.php +++ b/app/code/Magento/Authorization/Model/Acl/AclRetriever.php @@ -84,7 +84,7 @@ public function getAllowedResourcesByUser($userType, $userId) $role = $this->_getUserRole($userType, $userId); if (!$role) { throw new AuthorizationException( - __('We can\'t find the role for the user you wanted.') + __("The role wasn't found for the user. Verify the role and try again.") ); } $allowedResources = $this->getAllowedResourcesByRole($role->getId()); diff --git a/app/code/Magento/Authorization/Setup/InstallData.php b/app/code/Magento/Authorization/Setup/Patch/Data/InitializeAuthRoles.php similarity index 59% rename from app/code/Magento/Authorization/Setup/InstallData.php rename to app/code/Magento/Authorization/Setup/Patch/Data/InitializeAuthRoles.php index b8b18706722..84992badf65 100644 --- a/app/code/Magento/Authorization/Setup/InstallData.php +++ b/app/code/Magento/Authorization/Setup/Patch/Data/InitializeAuthRoles.php @@ -4,40 +4,48 @@ * See COPYING.txt for license details. */ -namespace Magento\Authorization\Setup; +namespace Magento\Authorization\Setup\Patch\Data; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; use Magento\Authorization\Model\Acl\Role\Group as RoleGroup; use Magento\Authorization\Model\UserContextInterface; /** - * @codeCoverageIgnore + * Class InitializeAuthRoles + * @package Magento\Authorization\Setup\Patch */ -class InstallData implements InstallDataInterface +class InitializeAuthRoles implements DataPatchInterface, PatchVersionInterface { /** - * Authorization factory - * - * @var AuthorizationFactory + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var \Magento\Authorization\Setup\AuthorizationFactory */ private $authFactory; /** - * Init - * - * @param AuthorizationFactory $authFactory + * InitializeAuthRoles constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Authorization\Setup\AuthorizationFactory $authorizationFactory */ - public function __construct(AuthorizationFactory $authFactory) - { - $this->authFactory = $authFactory; + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Authorization\Setup\AuthorizationFactory $authorizationFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->authFactory = $authorizationFactory; } /** * {@inheritdoc} */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { $roleCollection = $this->authFactory->createRoleCollection() ->addFieldToFilter('parent_id', 0) @@ -60,6 +68,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] )->save(); } else { + /** @var \Magento\Authorization\Model\ResourceModel\Role $item */ foreach ($roleCollection as $item) { $admGroupRole = $item; break; @@ -89,13 +98,36 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface /** * Delete rows by condition from authorization_rule */ - $setup->startSetup(); - - $tableName = $setup->getTable('authorization_rule'); + $tableName = $this->moduleDataSetup->getTable('authorization_rule'); if ($tableName) { - $setup->getConnection()->delete($tableName, ['resource_id = ?' => 'admin/system/tools/compiler']); + $this->moduleDataSetup->getConnection()->delete( + $tableName, + ['resource_id = ?' => 'admin/system/tools/compiler'] + ); } + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } - $setup->endSetup(); + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; } } diff --git a/app/code/Magento/Authorization/Test/Unit/Model/Acl/AclRetrieverTest.php b/app/code/Magento/Authorization/Test/Unit/Model/Acl/AclRetrieverTest.php index bd1a3616a74..c214cfc8325 100644 --- a/app/code/Magento/Authorization/Test/Unit/Model/Acl/AclRetrieverTest.php +++ b/app/code/Magento/Authorization/Test/Unit/Model/Acl/AclRetrieverTest.php @@ -60,7 +60,7 @@ public function testGetAllowedResourcesByUserTypeCustomer() /** * @expectedException \Magento\Framework\Exception\AuthorizationException - * @expectedExceptionMessage We can't find the role for the user you wanted. + * @expectedExceptionMessage The role wasn't found for the user. Verify the role and try again. */ public function testGetAllowedResourcesByUserRoleNotFound() { diff --git a/app/code/Magento/Authorization/etc/db_schema.xml b/app/code/Magento/Authorization/etc/db_schema.xml index ef615b4508a..45c02128bfc 100644 --- a/app/code/Magento/Authorization/etc/db_schema.xml +++ b/app/code/Magento/Authorization/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/Authorization/etc/module.xml b/app/code/Magento/Authorization/etc/module.xml index 357e36d937e..145b1ba10d0 100644 --- a/app/code/Magento/Authorization/etc/module.xml +++ b/app/code/Magento/Authorization/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php index ea73b4b7345..92957481b92 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Authorizenet\Controller\Directpost\Payment; use Magento\Authorizenet\Controller\Directpost\Payment; @@ -147,7 +148,7 @@ protected function placeCheckoutOrder() $result->setData('error', true); $result->setData( 'error_messages', - __('An error occurred on the server. Please try to place the order again.') + __('A server error stopped your order from being placed. Please try to place your order again.') ); } if ($response instanceof Http) { diff --git a/app/code/Magento/Authorizenet/Model/TransactionService.php b/app/code/Magento/Authorizenet/Model/TransactionService.php index fef22d6c913..693a5b890fa 100644 --- a/app/code/Magento/Authorizenet/Model/TransactionService.php +++ b/app/code/Magento/Authorizenet/Model/TransactionService.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Authorizenet\Model; use Magento\Framework\Exception\LocalizedException; @@ -124,7 +125,7 @@ protected function loadTransactionDetails(Authorizenet $context, $transactionId) $responseXmlDocument = new Element($responseBody); libxml_use_internal_errors(false); } catch (\Exception $e) { - throw new LocalizedException(__('Unable to get transaction details. Try again later.')); + throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); } finally { $context->debugData($debugData); } @@ -132,7 +133,7 @@ protected function loadTransactionDetails(Authorizenet $context, $transactionId) if (!isset($responseXmlDocument->messages->resultCode) || $responseXmlDocument->messages->resultCode != static::PAYMENT_UPDATE_STATUS_CODE_SUCCESS ) { - throw new LocalizedException(__('Unable to get transaction details. Try again later.')); + throw new LocalizedException(__('The transaction details are unavailable. Please try again later.')); } $this->transactionDetails[$transactionId] = $responseXmlDocument; diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php index 95ceed1ee11..c0a50e66759 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Authorizenet\Test\Unit\Controller\Directpost\Payment; use Magento\Authorizenet\Controller\Directpost\Payment\Place; @@ -297,7 +298,9 @@ public function textExecuteFailedPlaceOrderDataProvider() $objectFailed1 = new \Magento\Framework\DataObject( [ 'error' => true, - 'error_messages' => __('An error occurred on the server. Please try to place the order again.') + 'error_messages' => __( + 'A server error stopped your order from being placed. Please try to place your order again.' + ) ] ); $generalException = new \Exception('Exception logging will save the world!'); diff --git a/app/code/Magento/Authorizenet/etc/module.xml b/app/code/Magento/Authorizenet/etc/module.xml index 6d05f14d213..a30fd349277 100644 --- a/app/code/Magento/Authorizenet/etc/module.xml +++ b/app/code/Magento/Authorizenet/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php index 8252ed1a1e2..dfddef6a11f 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Block\Widget\Grid\Massaction; use Magento\Backend\Block\Widget\Grid\Massaction\VisibilityCheckerInterface as VisibilityChecker; @@ -57,7 +58,7 @@ protected function _construct() { parent::_construct(); - $this->setErrorText($this->escapeHtml(__('Please select items.'))); + $this->setErrorText($this->escapeHtml(__('An item needs to be selected. Select and try again.'))); if (null !== $this->getOptions()) { foreach ($this->getOptions() as $optionId => $option) { diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php index 42f5e61bf5f..e90d49847ee 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/Extended.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Block\Widget\Grid\Massaction; /** @@ -69,7 +70,7 @@ public function __construct( public function _construct() { parent::_construct(); - $this->setErrorText($this->escapeHtml(__('Please select items.'))); + $this->setErrorText($this->escapeHtml(__('An item needs to be selected. Select and try again.'))); } /** diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache.php index daaa2a9aaec..4fcd5993fb5 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Controller\Adminhtml; use Magento\Backend\App\Action; @@ -73,7 +74,7 @@ protected function _validateTypes(array $types) $allTypes = array_keys($this->_cacheTypeList->getTypes()); $invalidTypes = array_diff($types, $allTypes); if (count($invalidTypes) > 0) { - throw new LocalizedException(__('Specified cache type(s) don\'t exist: %1', join(', ', $invalidTypes))); + throw new LocalizedException(__('These cache type(s) don\'t exist: %1', join(', ', $invalidTypes))); } } } diff --git a/app/code/Magento/Backend/Model/Auth.php b/app/code/Magento/Backend/Model/Auth.php index 08921d1615f..02bf64fef07 100644 --- a/app/code/Magento/Backend/Model/Auth.php +++ b/app/code/Magento/Backend/Model/Auth.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Model; use Magento\Framework\Exception\AuthenticationException; @@ -148,7 +149,12 @@ public function getCredentialStorage() public function login($username, $password) { if (empty($username) || empty($password)) { - self::throwException(__('You did not sign in correctly or your account is temporarily disabled.')); + self::throwException( + __( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) + ); } try { @@ -165,7 +171,12 @@ public function login($username, $password) } if (!$this->getAuthStorage()->getUser()) { - self::throwException(__('You did not sign in correctly or your account is temporarily disabled.')); + self::throwException( + __( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) + ); } } catch (PluginAuthenticationException $e) { $this->_eventManager->dispatch( @@ -179,7 +190,10 @@ public function login($username, $password) ['user_name' => $username, 'exception' => $e] ); self::throwException( - __($e->getMessage()? : 'You did not sign in correctly or your account is temporarily disabled.') + __( + $e->getMessage()? : 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) ); } } @@ -215,7 +229,7 @@ public function isLoggedIn() public static function throwException(Phrase $msg = null) { if ($msg === null) { - $msg = __('Authentication error occurred.'); + $msg = __('An authentication error occurred. Verify and try again.'); } throw new AuthenticationException($msg); } diff --git a/app/code/Magento/Backend/Model/Auth/StorageInterface.php b/app/code/Magento/Backend/Model/Auth/StorageInterface.php index 52b2b089c71..e643165a933 100644 --- a/app/code/Magento/Backend/Model/Auth/StorageInterface.php +++ b/app/code/Magento/Backend/Model/Auth/StorageInterface.php @@ -23,7 +23,7 @@ interface StorageInterface public function processLogin(); /** - * Perform login specific actions + * Perform logout specific actions * * @return $this * @abstract diff --git a/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php index 09f33abd0d4..c106afb90a0 100644 --- a/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php +++ b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Model\Config\SessionLifetime; use Magento\Framework\App\Config\Value; @@ -26,14 +27,17 @@ class BackendModel extends Value */ public function beforeSave() { - $value = (int) $this->getValue(); + $value = (int)$this->getValue(); if ($value > self::MAX_LIFETIME) { throw new LocalizedException( - __('Admin session lifetime must be less than or equal to 31536000 seconds (one year)') + __( + 'The Admin session lifetime is invalid. ' + . 'Set the lifetime to 31536000 seconds (one year) or shorter and try again.' + ) ); } elseif ($value < self::MIN_LIFETIME) { throw new LocalizedException( - __('Admin session lifetime must be greater than or equal to 60 seconds') + __('The Admin session lifetime is invalid. Set the lifetime to 60 seconds or longer and try again.') ); } return parent::beforeSave(); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php index 556db311748..197b46acc61 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Test\Unit\Controller\Adminhtml\Cache; use PHPUnit_Framework_MockObject_MockObject as MockObject; @@ -156,7 +157,7 @@ public function testExecuteInvalidTypeCache() $this->messageManagerMock->expects($this->once()) ->method('addError') - ->with('Specified cache type(s) don\'t exist: someCache') + ->with('These cache type(s) don\'t exist: someCache') ->willReturnSelf(); $this->assertSame($this->redirectMock, $this->controller->execute()); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php index ad622ca6975..9b364019315 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Test\Unit\Controller\Adminhtml\Cache; use PHPUnit_Framework_MockObject_MockObject as MockObject; @@ -156,7 +157,7 @@ public function testExecuteInvalidTypeCache() $this->messageManagerMock->expects($this->once()) ->method('addError') - ->with('Specified cache type(s) don\'t exist: someCache') + ->with('These cache type(s) don\'t exist: someCache') ->willReturnSelf(); $this->assertSame($this->redirectMock, $this->controller->execute()); diff --git a/app/code/Magento/Backend/Test/Unit/Model/AuthTest.php b/app/code/Magento/Backend/Test/Unit/Model/AuthTest.php index 4b79d504dad..4af060b157e 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/AuthTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/AuthTest.php @@ -54,7 +54,6 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\AuthenticationException - * @expectedExceptionMessage You did not sign in correctly or your account is temporarily disabled. */ public function testLoginFailed() { @@ -64,7 +63,10 @@ public function testLoginFailed() ->with(\Magento\Backend\Model\Auth\Credential\StorageInterface::class) ->will($this->returnValue($this->_credentialStorage)); $exceptionMock = new \Magento\Framework\Exception\LocalizedException( - __('You did not sign in correctly or your account is temporarily disabled.') + __( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ) ); $this->_credentialStorage ->expects($this->once()) @@ -74,5 +76,10 @@ public function testLoginFailed() $this->_credentialStorage->expects($this->never())->method('getId'); $this->_eventManagerMock->expects($this->once())->method('dispatch')->with('backend_auth_user_login_failed'); $this->_model->login('username', 'password'); + + $this->expectExceptionMessage( + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ); } } diff --git a/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php index 31a13191750..92c549c3edf 100755 --- a/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Config/SessionLifetime/BackendModelTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Test\Unit\Model\Config\SessionLifetime; use Magento\Backend\Model\Config\SessionLifetime\BackendModel; @@ -32,11 +33,12 @@ public function adminSessionLifetimeDataProvider() return [ [ BackendModel::MIN_LIFETIME - 1, - 'Admin session lifetime must be greater than or equal to 60 seconds' + 'The Admin session lifetime is invalid. Set the lifetime to 60 seconds or longer and try again.' ], [ BackendModel::MAX_LIFETIME + 1, - 'Admin session lifetime must be less than or equal to 31536000 seconds (one year)' + 'The Admin session lifetime is invalid. ' + . 'Set the lifetime to 31536000 seconds (one year) or shorter and try again.' ], [ 900 diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 04bfe76bce9..6d950ae2c91 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -425,7 +425,7 @@ general Magento_Config::web - + @@ -435,7 +435,7 @@ Warning! When using Store Code in URLs, in some cases system may not work properly if URLs without Store Codes are specified in the third party services (e.g. PayPal etc.).]]> - + Magento\Config\Model\Config\Source\Web\Redirect I.e. redirect from http://example.com/store/ to http://www.example.com/store/ diff --git a/app/code/Magento/Backend/etc/module.xml b/app/code/Magento/Backend/etc/module.xml index 57e00489391..6d1691a0e56 100644 --- a/app/code/Magento/Backend/etc/module.xml +++ b/app/code/Magento/Backend/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Backup/etc/module.xml b/app/code/Magento/Backup/etc/module.xml index 667ec9d8a74..3e9906a5ecd 100644 --- a/app/code/Magento/Backup/etc/module.xml +++ b/app/code/Magento/Backup/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Braintree/Controller/Paypal/AbstractAction.php b/app/code/Magento/Braintree/Controller/Paypal/AbstractAction.php index d6a81eefc6f..e0ed9960195 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/AbstractAction.php +++ b/app/code/Magento/Braintree/Controller/Paypal/AbstractAction.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Controller\Paypal; use Magento\Checkout\Model\Session; @@ -73,7 +74,7 @@ public function dispatch(RequestInterface $request) protected function validateQuote($quote) { if (!$quote || !$quote->getItemsCount()) { - throw new \InvalidArgumentException(__('We can\'t initialize checkout.')); + throw new \InvalidArgumentException(__('Checkout failed to initialize. Verify and try again.')); } } } diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 4576e3b033d..ca252aabe54 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Controller\Paypal; use Magento\Checkout\Model\Session; @@ -66,7 +67,7 @@ public function execute() $quote ); } elseif (!$quote->getPayment()->getAdditionalInformation(self::$paymentMethodNonce)) { - throw new LocalizedException(__('We can\'t initialize checkout.')); + throw new LocalizedException(__('Checkout failed to initialize. Verify and try again.')); } /** @var \Magento\Framework\View\Result\Page $resultPage */ diff --git a/app/code/Magento/Braintree/Gateway/Command/GetPaymentNonceCommand.php b/app/code/Magento/Braintree/Gateway/Command/GetPaymentNonceCommand.php index 91c9f6c14bb..13a1762d5cb 100644 --- a/app/code/Magento/Braintree/Gateway/Command/GetPaymentNonceCommand.php +++ b/app/code/Magento/Braintree/Gateway/Command/GetPaymentNonceCommand.php @@ -77,7 +77,7 @@ public function execute(array $commandSubject) $customerId = $this->subjectReader->readCustomerId($commandSubject); $paymentToken = $this->tokenManagement->getByPublicHash($publicHash, $customerId); if (!$paymentToken) { - throw new Exception('No available payment tokens'); + throw new Exception('No payment tokens are available.'); } $data = $this->adapter->createNonce($paymentToken->getGatewayToken()); diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/OrderPlace.php b/app/code/Magento/Braintree/Model/Paypal/Helper/OrderPlace.php index b833798eabf..6c4332ef22a 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/OrderPlace.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/OrderPlace.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Model\Paypal\Helper; use Magento\Quote\Model\Quote; @@ -71,7 +72,9 @@ public function __construct( public function execute(Quote $quote, array $agreement) { if (!$this->agreementsValidator->isValid($agreement)) { - throw new LocalizedException(__('Please agree to all the terms and conditions before placing the order.')); + throw new LocalizedException(__( + "The order wasn't placed. First, agree to the terms and conditions, then try placing your order again." + )); } if ($this->getCheckoutMethod($quote) === Onepage::METHOD_GUEST) { diff --git a/app/code/Magento/Braintree/Setup/UpgradeData.php b/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php similarity index 57% rename from app/code/Magento/Braintree/Setup/UpgradeData.php rename to app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php index a7b39f12273..a0704002842 100644 --- a/app/code/Magento/Braintree/Setup/UpgradeData.php +++ b/app/code/Magento/Braintree/Setup/Patch/Data/ConvertSerializedDataToJson.php @@ -4,14 +4,23 @@ * See COPYING.txt for license details. */ -namespace Magento\Braintree\Setup; +namespace Magento\Braintree\Setup\Patch\Data; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Setup\UpgradeDataInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; -class UpgradeData implements UpgradeDataInterface +/** + * Convert data fro php native serialized data to JSON. + */ +class ConvertSerializedDataToJson implements DataPatchInterface, PatchVersionInterface { + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + /** * @var \Magento\Framework\DB\FieldDataConverterFactory */ @@ -23,41 +32,36 @@ class UpgradeData implements UpgradeDataInterface private $queryModifierFactory; /** - * UpgradeData constructor. - * + * ConvertSerializedDataToJson constructor. + * @param ModuleDataSetupInterface $moduleDataSetup * @param \Magento\Framework\DB\FieldDataConverterFactory $fieldDataConverterFactory * @param \Magento\Framework\DB\Select\QueryModifierFactory $queryModifierFactory */ public function __construct( + ModuleDataSetupInterface $moduleDataSetup, \Magento\Framework\DB\FieldDataConverterFactory $fieldDataConverterFactory, \Magento\Framework\DB\Select\QueryModifierFactory $queryModifierFactory ) { + $this->moduleDataSetup = $moduleDataSetup; $this->fieldDataConverterFactory = $fieldDataConverterFactory; $this->queryModifierFactory = $queryModifierFactory; } /** - * Upgrades data for Braintree module - * - * @param ModuleDataSetupInterface $setup - * @param ModuleContextInterface $context - * @return void + * {@inheritdoc} */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { - if (version_compare($context->getVersion(), '2.0.1', '<')) { - $this->convertSerializedDataToJson($setup); - } + $this->convertSerializedDataToJson(); } /** * Upgrade data to version 2.0.1, converts row data in the core_config_data table that uses the path * payment/braintree/countrycreditcard from serialized to JSON * - * @param ModuleDataSetupInterface $setup * @return void */ - private function convertSerializedDataToJson(ModuleDataSetupInterface $setup) + private function convertSerializedDataToJson() { $fieldDataConverter = $this->fieldDataConverterFactory->create( \Magento\Framework\DB\DataConverter\SerializedToJson::class @@ -73,11 +77,35 @@ private function convertSerializedDataToJson(ModuleDataSetupInterface $setup) ); $fieldDataConverter->convert( - $setup->getConnection(), - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getConnection(), + $this->moduleDataSetup->getTable('core_config_data'), 'config_id', 'value', $queryModifier ); } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.1'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/PlaceOrderTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/PlaceOrderTest.php index 5a10b4abb3f..4bea03153b9 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/PlaceOrderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/PlaceOrderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Test\Unit\Controller\Paypal; use Magento\Braintree\Controller\Paypal\PlaceOrder; @@ -186,7 +187,7 @@ public function testExecuteException() ->method('addExceptionMessage') ->with( self::isInstanceOf('\InvalidArgumentException'), - 'We can\'t initialize checkout.' + 'Checkout failed to initialize. Verify and try again.' ); self::assertEquals($this->placeOrder->execute(), $resultMock); diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php index cb911a8396b..609b7f21dbf 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Test\Unit\Controller\Paypal; use Magento\Quote\Model\Quote; @@ -188,7 +189,7 @@ public function testExecuteException() ->method('addExceptionMessage') ->with( self::isInstanceOf('\InvalidArgumentException'), - 'We can\'t initialize checkout.' + 'Checkout failed to initialize. Verify and try again.' ); $this->resultFactoryMock->expects(self::once()) @@ -235,7 +236,7 @@ public function testExecuteExceptionPaymentWithoutNonce() ->method('addExceptionMessage') ->with( self::isInstanceOf(\Magento\Framework\Exception\LocalizedException::class), - 'We can\'t initialize checkout.' + 'Checkout failed to initialize. Verify and try again.' ); $this->resultFactoryMock->expects(self::once()) diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/SaveShippingMethodTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/SaveShippingMethodTest.php index 5be5df0e33c..32ed698189f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/SaveShippingMethodTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/SaveShippingMethodTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Test\Unit\Controller\Paypal; use Magento\Quote\Model\Quote; @@ -225,7 +226,10 @@ public function testExecuteAjaxException() $this->messageManagerMock->expects(self::once()) ->method('addExceptionMessage') - ->with(self::isInstanceOf('\InvalidArgumentException'), 'We can\'t initialize checkout.'); + ->with( + self::isInstanceOf('\InvalidArgumentException'), + 'Checkout failed to initialize. Verify and try again.' + ); $this->urlMock->expects(self::once()) ->method('getUrl') @@ -265,7 +269,10 @@ public function testExecuteException() $this->messageManagerMock->expects(self::once()) ->method('addExceptionMessage') - ->with(self::isInstanceOf('\InvalidArgumentException'), 'We can\'t initialize checkout.'); + ->with( + self::isInstanceOf('\InvalidArgumentException'), + 'Checkout failed to initialize. Verify and try again.' + ); $this->urlMock->expects(self::once()) ->method('getUrl') diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php index 333f29eb291..6debedf0a0e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Test\Unit\Gateway\Command; use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; @@ -154,7 +155,7 @@ public function testExecuteWithExceptionForCustomerId() /** * @covers \Magento\Braintree\Gateway\Command\GetPaymentNonceCommand::execute * @expectedException \Exception - * @expectedExceptionMessage No available payment tokens + * @expectedExceptionMessage No payment tokens are available. */ public function testExecuteWithExceptionForTokenManagement() { @@ -169,7 +170,7 @@ public function testExecuteWithExceptionForTokenManagement() ->method('readCustomerId') ->willReturn($customerId); - $exception = new \Exception('No available payment tokens'); + $exception = new \Exception('No payment tokens are available.'); $this->tokenManagement->expects(static::once()) ->method('getByPublicHash') ->willThrowException($exception); diff --git a/app/code/Magento/Braintree/etc/module.xml b/app/code/Magento/Braintree/etc/module.xml index e3415c4935f..8be79268e7b 100644 --- a/app/code/Magento/Braintree/etc/module.xml +++ b/app/code/Magento/Braintree/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Bundle/Model/LinkManagement.php b/app/code/Magento/Bundle/Model/LinkManagement.php index 1a533f5d249..8c85c06c734 100644 --- a/app/code/Magento/Bundle/Model/LinkManagement.php +++ b/app/code/Magento/Bundle/Model/LinkManagement.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Bundle\Model; use Magento\Catalog\Api\Data\ProductInterface; @@ -87,7 +88,7 @@ public function getChildren($productSku, $optionId = null) { $product = $this->productRepository->get($productSku, true); if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { - throw new InputException(__('Only implemented for bundle product')); + throw new InputException(__('This is implemented for bundle products only.')); } $childrenList = []; @@ -125,25 +126,30 @@ public function saveChild( $product = $this->productRepository->get($sku, true); if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { throw new InputException( - __('Product with specified sku: "%1" is not a bundle product', [$product->getSku()]) + __('The product with the "%1" SKU isn\'t a bundle product.', [$product->getSku()]) ); } /** @var \Magento\Catalog\Model\Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { - throw new InputException(__('Bundle product could not contain another composite product')); + throw new InputException(__('The bundle product can\'t contain another composite product.')); } if (!$linkedProduct->getId()) { - throw new InputException(__('Id field of product link is required')); + throw new InputException(__('The product link needs an ID field entered. Enter and try again.')); } /** @var \Magento\Bundle\Model\Selection $selectionModel */ $selectionModel = $this->bundleSelection->create(); $selectionModel->load($linkedProduct->getId()); if (!$selectionModel->getId()) { - throw new InputException(__('Can not find product link with id "%1"', [$linkedProduct->getId()])); + throw new InputException( + __( + 'The product link with the "%1" ID field wasn\'t found. Verify the ID and try again.', + [$linkedProduct->getId()] + ) + ); } $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); $selectionModel = $this->mapProductLinkToSelectionModel( @@ -218,7 +224,7 @@ public function addChild( ) { if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { throw new InputException( - __('Product with specified sku: "%1" is not a bundle product', $product->getSku()) + __('The product with the "%1" SKU isn\'t a bundle product.', $product->getSku()) ); } @@ -246,7 +252,7 @@ public function addChild( /** @var \Magento\Catalog\Model\Product $linkProductModel */ $linkProductModel = $this->productRepository->get($linkedProduct->getSku()); if ($linkProductModel->isComposite()) { - throw new InputException(__('Bundle product could not contain another composite product')); + throw new InputException(__('The bundle product can\'t contain another composite product.')); } if ($selections) { @@ -295,7 +301,7 @@ public function removeChild($sku, $optionId, $childSku) $product = $this->productRepository->get($sku, true); if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { - throw new InputException(__('Product with specified sku: %1 is not a bundle product', $sku)); + throw new InputException(__('The product with the "%1" SKU isn\'t a bundle product.', $sku)); } $excludeSelectionIds = []; @@ -314,7 +320,7 @@ public function removeChild($sku, $optionId, $childSku) } if (empty($removeSelectionIds)) { throw new \Magento\Framework\Exception\NoSuchEntityException( - __('Requested bundle option product doesn\'t exist') + __("The bundle product doesn't exist. Review your request and try again.") ); } $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); diff --git a/app/code/Magento/Bundle/Model/Option/Validator.php b/app/code/Magento/Bundle/Model/Option/Validator.php index 1a82e271149..3eb574c1751 100644 --- a/app/code/Magento/Bundle/Model/Option/Validator.php +++ b/app/code/Magento/Bundle/Model/Option/Validator.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Bundle\Model\Option; use Magento\Framework\Validator\NotEmpty; @@ -51,7 +52,8 @@ protected function validateRequiredFields($value) ]; foreach ($requiredFields as $requiredField => $requiredValue) { if (!$this->notEmpty->isValid(trim($requiredValue))) { - $messages[$requiredField] = __('%fieldName is a required field.', ['fieldName' => $requiredField]); + $messages[$requiredField] = + __('"%fieldName" is required. Enter and try again.', ['fieldName' => $requiredField]); } } $this->_addMessages($messages); diff --git a/app/code/Magento/Bundle/Model/OptionManagement.php b/app/code/Magento/Bundle/Model/OptionManagement.php index dd20487b7f7..3e8efcc7bbb 100644 --- a/app/code/Magento/Bundle/Model/OptionManagement.php +++ b/app/code/Magento/Bundle/Model/OptionManagement.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Bundle\Model; use Magento\Framework\Exception\InputException; @@ -39,7 +40,7 @@ public function save(\Magento\Bundle\Api\Data\OptionInterface $option) { $product = $this->productRepository->get($option->getSku(), true); if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { - throw new InputException(__('Only implemented for bundle product')); + throw new InputException(__('This is implemented for bundle products only.')); } return $this->optionRepository->save($product, $option); } diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php index 9940344b5b6..b4b3c84b88e 100644 --- a/app/code/Magento/Bundle/Model/OptionRepository.php +++ b/app/code/Magento/Bundle/Model/OptionRepository.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Bundle\Model; use Magento\Catalog\Api\Data\ProductInterface; @@ -112,7 +113,9 @@ public function get($sku, $optionId) /** @var \Magento\Bundle\Model\Option $option */ $option = $this->type->getOptionsCollection($product)->getItemById($optionId); if (!$option || !$option->getId()) { - throw new NoSuchEntityException(__('Requested option doesn\'t exist')); + throw new NoSuchEntityException( + __("The option that was requested doesn't exist. Verify the entity and try again.") + ); } $productLinks = $this->linkList->getItems($product, $optionId); @@ -159,7 +162,7 @@ public function delete(\Magento\Bundle\Api\Data\OptionInterface $option) $this->optionResource->delete($option); } catch (\Exception $exception) { throw new \Magento\Framework\Exception\StateException( - __('Cannot delete option with id %1', $option->getOptionId()), + __('The option with "%1" ID can\'t be deleted.', $option->getOptionId()), $exception ); } @@ -208,7 +211,9 @@ public function save( } } else { if (!$existingOption->getOptionId()) { - throw new NoSuchEntityException(__('Requested option doesn\'t exist')); + throw new NoSuchEntityException( + __("The option that was requested doesn't exist. Verify the entity and try again.") + ); } $option->setData(array_merge($existingOption->getData(), $option->getData())); @@ -218,7 +223,7 @@ public function save( try { $this->optionResource->save($option); } catch (\Exception $e) { - throw new CouldNotSaveException(__('Could not save option'), $e); + throw new CouldNotSaveException(__("The option couldn't be saved."), $e); } /** @var \Magento\Bundle\Api\Data\LinkInterface $linkedProduct */ @@ -282,7 +287,7 @@ private function getProduct($sku) { $product = $this->productRepository->get($sku, true); if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) { - throw new InputException(__('Only implemented for bundle product')); + throw new InputException(__('This is implemented for bundle products only.')); } return $product; } diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option.php b/app/code/Magento/Bundle/Model/ResourceModel/Option.php index 2ad7e57f522..46fd8b910f6 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option.php @@ -81,31 +81,39 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) { parent::_afterSave($object); - $condition = [ + $conditions = [ 'option_id = ?' => $object->getId(), 'store_id = ? OR store_id = 0' => $object->getStoreId(), 'parent_product_id = ?' => $object->getParentId() ]; $connection = $this->getConnection(); - $connection->delete($this->getTable('catalog_product_bundle_option_value'), $condition); - $data = new \Magento\Framework\DataObject(); - $data->setOptionId($object->getId()) - ->setStoreId($object->getStoreId()) - ->setParentProductId($object->getParentId()) - ->setTitle($object->getTitle()); - - $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); - - /** - * also saving default value if this store view scope - */ + if ($this->isOptionPresent($conditions)) { + $connection->update( + $this->getTable('catalog_product_bundle_option_value'), + [ + 'title' => $object->getTitle() + ], + $conditions + ); + } else { + $data = new \Magento\Framework\DataObject(); + $data->setOptionId($object->getId()) + ->setStoreId($object->getStoreId()) + ->setParentProductId($object->getParentId()) + ->setTitle($object->getTitle()); - if ($object->getStoreId()) { - $data->setStoreId(0); - $data->setTitle($object->getDefaultTitle()); $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); + + /** + * also saving default value if this store view scope + */ + if ($object->getStoreId()) { + $data->setStoreId(0); + $data->setTitle($object->getDefaultTitle()); + $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); + } } return $this; @@ -210,4 +218,26 @@ public function save(\Magento\Framework\Model\AbstractModel $object) return $this; } + + /** + * Is Bundle option present in the database + * + * @param array $conditions + * + * @return bool + */ + private function isOptionPresent($conditions) + { + $connection = $this->getConnection(); + + $select = $connection->select()->from($this->getTable('catalog_product_bundle_option_value')); + foreach ($conditions as $condition => $conditionValue) { + $select->where($condition, $conditionValue); + } + $select->limit(1); + + $rowSelect = $connection->fetchRow($select); + + return (is_array($rowSelect) && !empty($rowSelect)); + } } diff --git a/app/code/Magento/Bundle/Setup/InstallData.php b/app/code/Magento/Bundle/Setup/Patch/Data/ApplyAttributesUpdate.php similarity index 85% rename from app/code/Magento/Bundle/Setup/InstallData.php rename to app/code/Magento/Bundle/Setup/Patch/Data/ApplyAttributesUpdate.php index 6a3ff08c4d7..d8ad1757ab2 100644 --- a/app/code/Magento/Bundle/Setup/InstallData.php +++ b/app/code/Magento/Bundle/Setup/Patch/Data/ApplyAttributesUpdate.php @@ -4,33 +4,42 @@ * See COPYING.txt for license details. */ -namespace Magento\Bundle\Setup; +namespace Magento\Bundle\Setup\Patch\Data; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; /** - * @codeCoverageIgnore + * Class ApplyAttributesUpdate + * @package Magento\Bundle\Setup\Patch */ -class InstallData implements InstallDataInterface +class ApplyAttributesUpdate implements DataPatchInterface, PatchVersionInterface { /** - * EAV setup factory - * + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** * @var EavSetupFactory */ private $eavSetupFactory; /** - * Init + * ApplyAttributesUpdate constructor. * + * @param ModuleDataSetupInterface $moduleDataSetup * @param EavSetupFactory $eavSetupFactory */ - public function __construct(EavSetupFactory $eavSetupFactory) - { + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Eav\Setup\EavSetupFactory $eavSetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; $this->eavSetupFactory = $eavSetupFactory; } @@ -38,10 +47,10 @@ public function __construct(EavSetupFactory $eavSetupFactory) * {@inheritdoc} * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { /** @var EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); $fieldList = [ 'price', 'special_price', @@ -205,4 +214,28 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/Bundle/Setup/Patch/Data/UpdateBundleRelatedEntityTytpes.php b/app/code/Magento/Bundle/Setup/Patch/Data/UpdateBundleRelatedEntityTytpes.php new file mode 100644 index 00000000000..44647ea76a1 --- /dev/null +++ b/app/code/Magento/Bundle/Setup/Patch/Data/UpdateBundleRelatedEntityTytpes.php @@ -0,0 +1,204 @@ +moduleDataSetup = $moduleDataSetup; + $this->eavSetupFactory = $eavSetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var \Magento\Eav\Setup\EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); + + $attributeSetId = $eavSetup->getDefaultAttributeSetId(ProductAttributeInterface::ENTITY_TYPE_CODE); + $eavSetup->addAttributeGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $attributeSetId, + 'Bundle Items', + 16 + ); + $this->upgradePriceType($eavSetup); + $this->upgradeSkuType($eavSetup); + $this->upgradeWeightType($eavSetup); + $this->upgradeShipmentType($eavSetup); + } + + /** + * Upgrade Dynamic Price attribute + * + * @param EavSetup $eavSetup + * @return void + */ + private function upgradePriceType(EavSetup $eavSetup) + { + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'price_type', + 'frontend_input', + 'boolean', + 31 + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'price_type', + 'frontend_label', + 'Dynamic Price' + ); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'price_type', 'default_value', 0); + } + + /** + * Upgrade Dynamic Sku attribute + * + * @param EavSetup $eavSetup + * @return void + */ + private function upgradeSkuType(EavSetup $eavSetup) + { + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'sku_type', + 'frontend_input', + 'boolean', + 21 + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'sku_type', + 'frontend_label', + 'Dynamic SKU' + ); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'sku_type', 'default_value', 0); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'sku_type', 'is_visible', 1); + } + + /** + * Upgrade Dynamic Weight attribute + * + * @param EavSetup $eavSetup + * @return void + */ + private function upgradeWeightType(EavSetup $eavSetup) + { + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'weight_type', + 'frontend_input', + 'boolean', + 71 + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'weight_type', + 'frontend_label', + 'Dynamic Weight' + ); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'weight_type', 'default_value', 0); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'weight_type', 'is_visible', 1); + } + + /** + * Upgrade Ship Bundle Items attribute + * + * @param EavSetup $eavSetup + * @return void + */ + private function upgradeShipmentType(EavSetup $eavSetup) + { + $attributeSetId = $eavSetup->getDefaultAttributeSetId(ProductAttributeInterface::ENTITY_TYPE_CODE); + $eavSetup->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $attributeSetId, + 'Bundle Items', + 'shipment_type', + 1 + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'shipment_type', + 'frontend_input', + 'select' + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'shipment_type', + 'frontend_label', + 'Ship Bundle Items' + ); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'shipment_type', + 'source_model', + \Magento\Bundle\Model\Product\Attribute\Source\Shipment\Type::class + ); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'shipment_type', 'default_value', 0); + $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'shipment_type', 'is_visible', 1); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + ApplyAttributesUpdate::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.2'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Bundle/Setup/Patch/Schema/UpdateBundleRelatedSchema.php b/app/code/Magento/Bundle/Setup/Patch/Schema/UpdateBundleRelatedSchema.php new file mode 100644 index 00000000000..94bf1d11fb4 --- /dev/null +++ b/app/code/Magento/Bundle/Setup/Patch/Schema/UpdateBundleRelatedSchema.php @@ -0,0 +1,159 @@ +schemaSetup = $schemaSetup; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $this->schemaSetup->startSetup(); + // Updating data of the 'catalog_product_bundle_option_value' table. + $tableName = $this->schemaSetup->getTable('catalog_product_bundle_option_value'); + + $select = $this->schemaSetup->getConnection()->select() + ->from( + ['values' => $tableName], + ['value_id'] + )->joinLeft( + [ + 'options' => $this->schemaSetup->getTable( + 'catalog_product_bundle_option' + ) + ], + 'values.option_id = options.option_id', + ['parent_product_id' => 'parent_id'] + ); + + $this->schemaSetup->getConnection()->query( + $this->schemaSetup->getConnection()->insertFromSelect( + $select, + $tableName, + ['value_id', 'parent_product_id'], + \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + + // Updating data of the 'catalog_product_bundle_selection_price' table. + $tableName = $this->schemaSetup->getTable( + 'catalog_product_bundle_selection_price' + ); + $tmpTableName = $this->schemaSetup->getTable( + 'catalog_product_bundle_selection_price_tmp' + ); + + $existingForeignKeys = $this->schemaSetup->getConnection()->getForeignKeys($tableName); + + foreach ($existingForeignKeys as $key) { + $this->schemaSetup->getConnection()->dropForeignKey($key['TABLE_NAME'], $key['FK_NAME']); + } + + $this->schemaSetup->getConnection()->createTable( + $this->schemaSetup->getConnection()->createTableByDdl($tableName, $tmpTableName) + ); + + foreach ($existingForeignKeys as $key) { + $this->schemaSetup->getConnection()->addForeignKey( + $key['FK_NAME'], + $key['TABLE_NAME'], + $key['COLUMN_NAME'], + $key['REF_TABLE_NAME'], + $key['REF_COLUMN_NAME'], + $key['ON_DELETE'] + ); + } + + $this->schemaSetup->getConnection()->query( + $this->schemaSetup->getConnection()->insertFromSelect( + $this->schemaSetup->getConnection()->select()->from($tableName), + $tmpTableName + ) + ); + + $this->schemaSetup->getConnection()->truncateTable($tableName); + + $columnsToSelect = []; + + foreach ($this->schemaSetup->getConnection()->describeTable($tmpTableName) as $column) { + $alias = $column['COLUMN_NAME'] == 'parent_product_id' ? 'selections.' : 'prices.'; + + $columnsToSelect[] = $alias . $column['COLUMN_NAME']; + } + + $select = $this->schemaSetup->getConnection()->select() + ->from( + ['prices' => $tmpTableName], + [] + )->joinLeft( + [ + 'selections' => $this->schemaSetup->getTable( + 'catalog_product_bundle_selection' + ) + ], + 'prices.selection_id = selections.selection_id', + [] + )->columns($columnsToSelect); + + $this->schemaSetup->getConnection()->query( + $this->schemaSetup->getConnection()->insertFromSelect($select, $tableName) + ); + + $this->schemaSetup->getConnection()->dropTable($tmpTableName); + + $this->schemaSetup->endSetup(); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.4'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Bundle/Setup/UpgradeData.php b/app/code/Magento/Bundle/Setup/UpgradeData.php deleted file mode 100644 index 750bc79d848..00000000000 --- a/app/code/Magento/Bundle/Setup/UpgradeData.php +++ /dev/null @@ -1,255 +0,0 @@ -eavSetupFactory = $eavSetupFactory; - } - - /** - * {@inheritdoc} - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $setup->startSetup(); - - if (version_compare($context->getVersion(), '2.0.2', '<')) { - /** @var \Magento\Eav\Setup\EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); - - $attributeSetId = $eavSetup->getDefaultAttributeSetId(ProductAttributeInterface::ENTITY_TYPE_CODE); - $eavSetup->addAttributeGroup( - ProductAttributeInterface::ENTITY_TYPE_CODE, - $attributeSetId, - 'Bundle Items', - 16 - ); - - $this->upgradePriceType($eavSetup); - $this->upgradeSkuType($eavSetup); - $this->upgradeWeightType($eavSetup); - $this->upgradeShipmentType($eavSetup); - } - - if (version_compare($context->getVersion(), '2.0.4', '<')) { - // Updating data of the 'catalog_product_bundle_option_value' table. - $tableName = $setup->getTable('catalog_product_bundle_option_value'); - - $select = $setup->getConnection()->select() - ->from( - ['values' => $tableName], - ['value_id'] - )->joinLeft( - ['options' => $setup->getTable('catalog_product_bundle_option')], - 'values.option_id = options.option_id', - ['parent_product_id' => 'parent_id'] - ); - - $setup->getConnection()->query( - $setup->getConnection()->insertFromSelect( - $select, - $tableName, - ['value_id', 'parent_product_id'], - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE - ) - ); - - // Updating data of the 'catalog_product_bundle_selection_price' table. - $tableName = $setup->getTable('catalog_product_bundle_selection_price'); - $tmpTableName = $setup->getTable('catalog_product_bundle_selection_price_tmp'); - - $existingForeignKeys = $setup->getConnection()->getForeignKeys($tableName); - - foreach ($existingForeignKeys as $key) { - $setup->getConnection()->dropForeignKey($key['TABLE_NAME'], $key['FK_NAME']); - } - - $setup->getConnection()->createTable( - $setup->getConnection()->createTableByDdl($tableName, $tmpTableName) - ); - - foreach ($existingForeignKeys as $key) { - $setup->getConnection()->addForeignKey( - $key['FK_NAME'], - $key['TABLE_NAME'], - $key['COLUMN_NAME'], - $key['REF_TABLE_NAME'], - $key['REF_COLUMN_NAME'], - $key['ON_DELETE'] - ); - } - - $setup->getConnection()->query( - $setup->getConnection()->insertFromSelect( - $setup->getConnection()->select()->from($tableName), - $tmpTableName - ) - ); - - $setup->getConnection()->truncateTable($tableName); - - $columnsToSelect = []; - - foreach ($setup->getConnection()->describeTable($tmpTableName) as $column) { - $alias = $column['COLUMN_NAME'] == 'parent_product_id' ? 'selections.' : 'prices.'; - - $columnsToSelect[] = $alias . $column['COLUMN_NAME']; - } - - $select = $setup->getConnection()->select() - ->from( - ['prices' => $tmpTableName], - [] - )->joinLeft( - ['selections' => $setup->getTable('catalog_product_bundle_selection')], - 'prices.selection_id = selections.selection_id', - [] - )->columns($columnsToSelect); - - $setup->getConnection()->query( - $setup->getConnection()->insertFromSelect($select, $tableName) - ); - - $setup->getConnection()->dropTable($tmpTableName); - } - - $setup->endSetup(); - } - - /** - * Upgrade Dynamic Price attribute - * - * @param EavSetup $eavSetup - * @return void - */ - private function upgradePriceType(EavSetup $eavSetup) - { - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'price_type', - 'frontend_input', - 'boolean', - 31 - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'price_type', - 'frontend_label', - 'Dynamic Price' - ); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'price_type', 'default_value', 0); - } - - /** - * Upgrade Dynamic Sku attribute - * - * @param EavSetup $eavSetup - * @return void - */ - private function upgradeSkuType(EavSetup $eavSetup) - { - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'sku_type', - 'frontend_input', - 'boolean', - 21 - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'sku_type', - 'frontend_label', - 'Dynamic SKU' - ); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'sku_type', 'default_value', 0); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'sku_type', 'is_visible', 1); - } - - /** - * Upgrade Dynamic Weight attribute - * - * @param EavSetup $eavSetup - * @return void - */ - private function upgradeWeightType(EavSetup $eavSetup) - { - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'weight_type', - 'frontend_input', - 'boolean', - 71 - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'weight_type', - 'frontend_label', - 'Dynamic Weight' - ); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'weight_type', 'default_value', 0); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'weight_type', 'is_visible', 1); - } - - /** - * Upgrade Ship Bundle Items attribute - * - * @param EavSetup $eavSetup - * @return void - */ - private function upgradeShipmentType(EavSetup $eavSetup) - { - $attributeSetId = $eavSetup->getDefaultAttributeSetId(ProductAttributeInterface::ENTITY_TYPE_CODE); - $eavSetup->addAttributeToGroup( - ProductAttributeInterface::ENTITY_TYPE_CODE, - $attributeSetId, - 'Bundle Items', - 'shipment_type', - 1 - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'shipment_type', - 'frontend_input', - 'select' - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'shipment_type', - 'frontend_label', - 'Ship Bundle Items' - ); - $eavSetup->updateAttribute( - ProductAttributeInterface::ENTITY_TYPE_CODE, - 'shipment_type', - 'source_model', - \Magento\Bundle\Model\Product\Attribute\Source\Shipment\Type::class - ); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'shipment_type', 'default_value', 0); - $eavSetup->updateAttribute(ProductAttributeInterface::ENTITY_TYPE_CODE, 'shipment_type', 'is_visible', 1); - } -} diff --git a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php index d1292146f66..ccc8c52d502 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/LinkManagementTest.php @@ -339,7 +339,7 @@ public function testAddChildNonExistingOption() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Bundle product could not contain another composite product + * @expectedExceptionMessage The bundle product can't contain another composite product. */ public function testAddChildLinkedProductIsComposite() { @@ -797,7 +797,7 @@ public function testSaveChildWithoutId() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Can not find product link with id "12345" + * @expectedExceptionMessage The product link with the "12345" ID field wasn't found. Verify the ID and try again. */ public function testSaveChildWithInvalidId() { diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Option/ValidatorTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Option/ValidatorTest.php index f47c978190a..791126a9c17 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Option/ValidatorTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Option/ValidatorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Bundle\Test\Unit\Model\Option; use Magento\Framework\Validator\NotEmpty; @@ -71,9 +72,17 @@ public function providerIsValid() { return [ ['title', 'select', true, []], - ['title', null, false, ['type' => 'type is a required field.']], - [null, 'select', false, ['title' => 'title is a required field.']], - [null, null, false, ['type' => 'type is a required field.', 'title' => 'title is a required field.']] + ['title', null, false, ['type' => '"type" is required. Enter and try again.']], + [null, 'select', false, ['title' => '"title" is required. Enter and try again.']], + [ + null, + null, + false, + [ + 'type' => '"type" is required. Enter and try again.', + 'title' => '"title" is required. Enter and try again.' + ] + ] ]; } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/OptionManagementTest.php b/app/code/Magento/Bundle/Test/Unit/Model/OptionManagementTest.php index f0a69bd4fb2..8e309e48872 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/OptionManagementTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/OptionManagementTest.php @@ -67,7 +67,7 @@ public function testSave() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Only implemented for bundle product + * @expectedExceptionMessage This is implemented for bundle products only. */ public function testSaveWithException() { diff --git a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php index d438dc2e9b2..7549d402a57 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php @@ -116,7 +116,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Only implemented for bundle product + * @expectedExceptionMessage This is implemented for bundle products only. */ public function testGetThrowsExceptionIfProductIsSimple() { @@ -134,7 +134,7 @@ public function testGetThrowsExceptionIfProductIsSimple() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Requested option doesn't exist + * @expectedExceptionMessage The option that was requested doesn't exist. Verify the entity and try again. */ public function testGetThrowsExceptionIfOptionDoesNotExist() { @@ -226,7 +226,7 @@ public function testDelete() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Cannot delete option with id 1 + * @expectedExceptionMessage The option with "1" ID can't be deleted. */ public function testDeleteThrowsExceptionIfCannotDelete() { @@ -382,7 +382,7 @@ public function testSaveNewOption() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Could not save option + * @expectedExceptionMessage The option couldn't be saved. */ public function testSaveCanNotSave() { @@ -456,7 +456,7 @@ public function testGetList() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Only implemented for bundle product + * @expectedExceptionMessage This is implemented for bundle products only. */ public function testGetListException() { diff --git a/app/code/Magento/Bundle/etc/db_schema.xml b/app/code/Magento/Bundle/etc/db_schema.xml index 5f9e1902697..ef46a8f934c 100644 --- a/app/code/Magento/Bundle/etc/db_schema.xml +++ b/app/code/Magento/Bundle/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
- + diff --git a/app/code/Magento/BundleGraphQl/etc/module.xml b/app/code/Magento/BundleGraphQl/etc/module.xml index 34e721bf02e..d6c45dd617a 100644 --- a/app/code/Magento/BundleGraphQl/etc/module.xml +++ b/app/code/Magento/BundleGraphQl/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/BundleImportExport/etc/module.xml b/app/code/Magento/BundleImportExport/etc/module.xml index e324145cabc..44b09e4c5fb 100644 --- a/app/code/Magento/BundleImportExport/etc/module.xml +++ b/app/code/Magento/BundleImportExport/etc/module.xml @@ -6,6 +6,6 @@ */ --> - + diff --git a/app/code/Magento/CacheInvalidate/etc/module.xml b/app/code/Magento/CacheInvalidate/etc/module.xml index dda90ba8b38..789b74480b4 100644 --- a/app/code/Magento/CacheInvalidate/etc/module.xml +++ b/app/code/Magento/CacheInvalidate/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Captcha/etc/db_schema.xml b/app/code/Magento/Captcha/etc/db_schema.xml index 25aef55c606..fa9a14abb89 100644 --- a/app/code/Magento/Captcha/etc/db_schema.xml +++ b/app/code/Magento/Captcha/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/Captcha/etc/module.xml b/app/code/Magento/Captcha/etc/module.xml index 03ab6dbee39..36a44a65430 100644 --- a/app/code/Magento/Captcha/etc/module.xml +++ b/app/code/Magento/Captcha/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Catalog/Api/Data/CategoryInterface.php b/app/code/Magento/Catalog/Api/Data/CategoryInterface.php index b65cdafbe26..b9a23e9d08e 100644 --- a/app/code/Magento/Catalog/Api/Data/CategoryInterface.php +++ b/app/code/Magento/Catalog/Api/Data/CategoryInterface.php @@ -14,6 +14,37 @@ */ interface CategoryInterface extends \Magento\Framework\Api\CustomAttributesDataInterface { + /**#@+ + * Constants defined for keys of data array + */ + const KEY_PARENT_ID = 'parent_id'; + const KEY_NAME = 'name'; + const KEY_IS_ACTIVE = 'is_active'; + const KEY_POSITION = 'position'; + const KEY_LEVEL = 'level'; + const KEY_UPDATED_AT = 'updated_at'; + const KEY_CREATED_AT = 'created_at'; + const KEY_PATH = 'path'; + const KEY_AVAILABLE_SORT_BY = 'available_sort_by'; + const KEY_INCLUDE_IN_MENU = 'include_in_menu'; + const KEY_PRODUCT_COUNT = 'product_count'; + const KEY_CHILDREN_DATA = 'children_data'; + + const ATTRIBUTES = [ + 'id', + self::KEY_PARENT_ID, + self::KEY_NAME, + self::KEY_IS_ACTIVE, + self::KEY_POSITION, + self::KEY_LEVEL, + self::KEY_UPDATED_AT, + self::KEY_CREATED_AT, + self::KEY_AVAILABLE_SORT_BY, + self::KEY_INCLUDE_IN_MENU, + self::KEY_CHILDREN_DATA, + ]; + /**#@-*/ + /** * @return int|null */ diff --git a/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php b/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php index 12adf5b0893..590c23a0aa0 100644 --- a/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php +++ b/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php @@ -32,4 +32,9 @@ interface ProductAttributeInterface extends \Magento\Catalog\Api\Data\EavAttribu const CODE_TIER_PRICE_FIELD_VALUE_TYPE = 'value_type'; const CODE_SEO_FIELD_META_DESCRIPTION = 'meta_description'; const CODE_WEIGHT = 'weight'; + + /** + * @return \Magento\Eav\Api\Data\AttributeExtensionInterface|null + */ + public function getExtensionAttributes(); } diff --git a/app/code/Magento/Catalog/Api/Data/ProductInterface.php b/app/code/Magento/Catalog/Api/Data/ProductInterface.php index a79c76fd8e2..4968f49fd20 100644 --- a/app/code/Magento/Catalog/Api/Data/ProductInterface.php +++ b/app/code/Magento/Catalog/Api/Data/ProductInterface.php @@ -36,6 +36,24 @@ interface ProductInterface extends \Magento\Framework\Api\CustomAttributesDataIn const UPDATED_AT = 'updated_at'; + const MEDIA_GALLERY = 'media_gallery'; + + const TIER_PRICE = 'tier_price'; + + const ATTRIBUTES = [ + self::SKU, + self::NAME, + self::PRICE, + self::WEIGHT, + self::STATUS, + self::VISIBILITY, + self::ATTRIBUTE_SET_ID, + self::TYPE_ID, + self::CREATED_AT, + self::UPDATED_AT, + self::MEDIA_GALLERY, + self::TIER_PRICE, + ]; /**#@-*/ /** diff --git a/app/code/Magento/Catalog/Block/Product/ImageBuilder.php b/app/code/Magento/Catalog/Block/Product/ImageBuilder.php index 04d30270cbb..b752000f5a1 100644 --- a/app/code/Magento/Catalog/Block/Product/ImageBuilder.php +++ b/app/code/Magento/Catalog/Block/Product/ImageBuilder.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Block\Product; use Magento\Catalog\Helper\ImageFactory as HelperFactory; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException; class ImageBuilder @@ -21,7 +22,7 @@ class ImageBuilder protected $helperFactory; /** - * @var \Magento\Catalog\Model\Product + * @var Product */ protected $product; @@ -50,10 +51,10 @@ public function __construct( /** * Set product * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return $this */ - public function setProduct(\Magento\Catalog\Model\Product $product) + public function setProduct(Product $product) { $this->product = $product; return $this; @@ -79,9 +80,7 @@ public function setImageId($imageId) */ public function setAttributes(array $attributes) { - if ($attributes) { - $this->attributes = $attributes; - } + $this->attributes = $attributes; return $this; } diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index 4a7d28c383d..ee63d3400ad 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -9,11 +9,20 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Block\Product\ProductList\Toolbar; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Config; +use Magento\Catalog\Model\Layer; +use Magento\Catalog\Model\Layer\Resolver; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Pricing\Price\FinalPrice; use Magento\Eav\Model\Entity\Collection\AbstractCollection; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Config\Element; +use Magento\Framework\Data\Helper\PostHelper; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Pricing\Render; +use Magento\Framework\Url\Helper\Data; /** * Product list @@ -40,17 +49,17 @@ class ListProduct extends AbstractProduct implements IdentityInterface /** * Catalog layer * - * @var \Magento\Catalog\Model\Layer + * @var Layer */ protected $_catalogLayer; /** - * @var \Magento\Framework\Data\Helper\PostHelper + * @var PostHelper */ protected $_postDataHelper; /** - * @var \Magento\Framework\Url\Helper\Data + * @var Data */ protected $urlHelper; @@ -61,18 +70,18 @@ class ListProduct extends AbstractProduct implements IdentityInterface /** * @param Context $context - * @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper - * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver + * @param PostHelper $postDataHelper + * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository - * @param \Magento\Framework\Url\Helper\Data $urlHelper + * @param Data $urlHelper * @param array $data */ public function __construct( - \Magento\Catalog\Block\Product\Context $context, - \Magento\Framework\Data\Helper\PostHelper $postDataHelper, - \Magento\Catalog\Model\Layer\Resolver $layerResolver, + Context $context, + PostHelper $postDataHelper, + Resolver $layerResolver, CategoryRepositoryInterface $categoryRepository, - \Magento\Framework\Url\Helper\Data $urlHelper, + Data $urlHelper, array $data = [] ) { $this->_catalogLayer = $layerResolver->get(); @@ -113,7 +122,7 @@ protected function _getProductCollection() /** * Get catalog layer model * - * @return \Magento\Catalog\Model\Layer + * @return Layer */ public function getLayer() { @@ -137,7 +146,35 @@ public function getLoadedProductCollection() */ public function getMode() { - return $this->getChildBlock('toolbar')->getCurrentMode(); + if ($this->getChildBlock('toolbar')) { + return $this->getChildBlock('toolbar')->getCurrentMode(); + } + + return $this->getDefaultListingMode(); + } + + /** + * Get listing mode for products if toolbar is removed from layout. + * Use the general configuration for product list mode from config path catalog/frontend/list_mode as default value + * or mode data from block declaration from layout. + * + * @return string + */ + private function getDefaultListingMode() + { + // default Toolbar when the toolbar layout is not used + $defaultToolbar = $this->getToolbarBlock(); + $availableModes = $defaultToolbar->getModes(); + + // layout config mode + $mode = $this->getData('mode'); + + if (!$mode || !isset($availableModes[$mode])) { + // default config mode + $mode = $defaultToolbar->getCurrentMode(); + } + + return $mode; } /** @@ -148,28 +185,60 @@ public function getMode() protected function _beforeToHtml() { $collection = $this->_getProductCollection(); - $this->configureToolbar($this->getToolbarBlock(), $collection); + + $this->addToolbarBlock($collection); + $collection->load(); return parent::_beforeToHtml(); } /** - * Retrieve Toolbar block + * Add toolbar block from product listing layout + * + * @param Collection $collection + */ + private function addToolbarBlock(Collection $collection) + { + $toolbarLayout = $this->getToolbarFromLayout(); + + if ($toolbarLayout) { + $this->configureToolbar($toolbarLayout, $collection); + } + } + + /** + * Retrieve Toolbar block from layout or a default Toolbar * * @return Toolbar */ public function getToolbarBlock() + { + $block = $this->getToolbarFromLayout(); + + if (!$block) { + $block = $this->getLayout()->createBlock($this->_defaultToolbarBlock, uniqid(microtime())); + } + + return $block; + } + + /** + * Get toolbar block from layout + * + * @return bool|Toolbar + */ + private function getToolbarFromLayout() { $blockName = $this->getToolbarBlockName(); + + $toolbarLayout = false; + if ($blockName) { - $block = $this->getLayout()->getBlock($blockName); - if ($block) { - return $block; - } + $toolbarLayout = $this->getLayout()->getBlock($blockName); } - $block = $this->getLayout()->createBlock($this->_defaultToolbarBlock, uniqid(microtime())); - return $block; + + return $toolbarLayout; } /** @@ -203,7 +272,7 @@ public function setCollection($collection) } /** - * @param array|string|integer|\Magento\Framework\App\Config\Element $code + * @param array|string|integer| Element $code * @return $this */ public function addAttribute($code) @@ -223,7 +292,7 @@ public function getPriceBlockTemplate() /** * Retrieve Catalog Config object * - * @return \Magento\Catalog\Model\Config + * @return Config */ protected function _getConfig() { @@ -233,8 +302,8 @@ protected function _getConfig() /** * Prepare Sort By fields from Category Data * - * @param \Magento\Catalog\Model\Category $category - * @return \Magento\Catalog\Block\Product\ListProduct + * @param Category $category + * @return $this */ public function prepareSortableFieldsByCategory($category) { @@ -286,38 +355,38 @@ public function getIdentities() /** * Get post parameters * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - public function getAddToCartPostParams(\Magento\Catalog\Model\Product $product) + public function getAddToCartPostParams(Product $product) { $url = $this->getAddToCartUrl($product); return [ 'action' => $url, 'data' => [ 'product' => $product->getEntityId(), - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlHelper->getEncodedUrl($url), + ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlHelper->getEncodedUrl($url), ] ]; } /** - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - public function getProductPrice(\Magento\Catalog\Model\Product $product) + public function getProductPrice(Product $product) { $priceRender = $this->getPriceRender(); $price = ''; if ($priceRender) { $price = $priceRender->render( - \Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE, + FinalPrice::PRICE_CODE, $product, [ 'include_container' => true, 'display_minimal_price' => true, - 'zone' => \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST, + 'zone' => Render::ZONE_ITEM_LIST, 'list_category_page' => true ] ); @@ -330,7 +399,7 @@ public function getProductPrice(\Magento\Catalog\Model\Product $product) * Specifies that price rendering should be done for the list of products * i.e. rendering happens in the scope of product list, but not single product * - * @return \Magento\Framework\Pricing\Render + * @return Render */ protected function getPriceRender() { @@ -356,7 +425,7 @@ protected function getPriceRender() private function initializeProductCollection() { $layer = $this->getLayer(); - /* @var $layer \Magento\Catalog\Model\Layer */ + /* @var $layer Layer */ if ($this->getShowRootCategory()) { $this->setCategoryId($this->_storeManager->getStore()->getRootCategoryId()); } @@ -395,8 +464,7 @@ private function initializeProductCollection() $layer->setCurrentCategory($origCategory); } - $toolbar = $this->getToolbarBlock(); - $this->configureToolbar($toolbar, $collection); + $this->addToolbarBlock($collection); $this->_eventManager->dispatch( 'catalog_block_product_list_collection', diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index b3b5dea9602..8055d17a64a 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -120,51 +120,6 @@ public function getWishlistOptions() return ['productType' => $this->getProduct()->getTypeId()]; } - /** - * Add meta information from product to head block - * - * @return \Magento\Catalog\Block\Product\View - */ - protected function _prepareLayout() - { - $this->getLayout()->createBlock(\Magento\Catalog\Block\Breadcrumbs::class); - $product = $this->getProduct(); - if (!$product) { - return parent::_prepareLayout(); - } - - $title = $product->getMetaTitle(); - if ($title) { - $this->pageConfig->getTitle()->set($title); - } - $keyword = $product->getMetaKeyword(); - $currentCategory = $this->_coreRegistry->registry('current_category'); - if ($keyword) { - $this->pageConfig->setKeywords($keyword); - } elseif ($currentCategory) { - $this->pageConfig->setKeywords($product->getName()); - } - $description = $product->getMetaDescription(); - if ($description) { - $this->pageConfig->setDescription($description); - } else { - $this->pageConfig->setDescription($this->string->substr($product->getDescription(), 0, 255)); - } - if ($this->_productHelper->canUseCanonicalTag()) { - $this->pageConfig->addRemotePageAsset( - $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]), - 'canonical', - ['attributes' => ['rel' => 'canonical']] - ); - } - - $pageMainTitle = $this->getLayout()->getBlock('page.main.title'); - if ($pageMainTitle) { - $pageMainTitle->setPageTitle($product->getName()); - } - return parent::_prepareLayout(); - } - /** * Retrieve current product model * diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index d1ec3be1a88..bcc7d468fd0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Controller\Adminhtml\Category; use Magento\Catalog\Api\Data\CategoryAttributeInterface; @@ -126,8 +127,7 @@ public function execute() return $resultRedirect->setPath('catalog/*/', ['_current' => true, 'id' => null]); } - $data['general'] = $this->getRequest()->getPostValue(); - $categoryPostData = $data['general']; + $categoryPostData = $this->getRequest()->getPostValue(); $isNewCategory = !isset($categoryPostData['entity_id']); $categoryPostData = $this->stringToBoolConverting($categoryPostData); @@ -202,7 +202,7 @@ public function execute() if ($error === true) { $attribute = $categoryResource->getAttribute($code)->getFrontend()->getLabel(); throw new \Magento\Framework\Exception\LocalizedException( - __('Attribute "%1" is required.', $attribute) + __('The "%1" attribute is required. Enter and try again.', $attribute) ); } else { throw new \Exception($error); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php index 174f06c2ea0..bbef1de28e5 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Catalog\Api\AttributeSetRepositoryInterface; @@ -182,7 +183,7 @@ private function getBasicAttributeSearchCriteriaBuilder() $attributeIds = (array)$this->getRequest()->getParam('attributeIds', []); if (empty($attributeIds['selected'])) { - throw new LocalizedException(__('Please, specify attributes')); + throw new LocalizedException(__('Attributes were missing and must be specified.')); } return $this->getSearchCriteriaBuilder() diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index 5335837a4d3..f0ba9b518fa 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -139,7 +139,9 @@ public function execute() ->setName($name) ->getAttributeSet(); } catch (AlreadyExistsException $alreadyExists) { - $this->messageManager->addErrorMessage(__('An attribute set named \'%1\' already exists.', $name)); + $this->messageManager->addErrorMessage( + __('A "%1" attribute set name already exists. Create a new name and try again.', $name) + ); $this->_session->setAttributeData($data); return $this->returnResult('catalog/*/edit', ['_current' => true], ['error' => true]); } catch (LocalizedException $e) { diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index 4dd406a5d14..8e11a57f96d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute; use Magento\Framework\DataObject; @@ -91,7 +92,9 @@ public function execute() $attributeSet->setEntityTypeId($this->_entityTypeId)->load($setName, 'attribute_set_name'); if ($attributeSet->getId()) { $setName = $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($setName); - $this->messageManager->addError(__('An attribute set named \'%1\' already exists.', $setName)); + $this->messageManager->addError( + __('A "%1" attribute set name already exists. Create a new name and try again.', $setName) + ); $layout = $this->layoutFactory->create(); $layout->initMessages(); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index d34f4bedd80..1481687205d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Backend\App\Action; @@ -103,7 +104,9 @@ public function execute() $this->productTypeManager->processProduct($product); if (isset($data['product'][$product->getIdFieldName()])) { - throw new \Magento\Framework\Exception\LocalizedException(__('Unable to save product')); + throw new \Magento\Framework\Exception\LocalizedException( + __('The product was unable to be saved. Please try again.') + ); } $originalSku = $product->getSku(); diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 2558fa2d85a..11c61df4af6 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -59,6 +59,11 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper */ protected $categoryUrlPathGenerator; + /** + * @var \Magento\Framework\Stdlib\StringUtils + */ + private $string; + /** * Constructor * @@ -70,6 +75,7 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator * @param array $messageGroups + * @param \Magento\Framework\Stdlib\StringUtils|null $string */ public function __construct( \Magento\Framework\App\Helper\Context $context, @@ -79,7 +85,8 @@ public function __construct( \Magento\Framework\Registry $coreRegistry, \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, - array $messageGroups = [] + array $messageGroups = [], + \Magento\Framework\Stdlib\StringUtils $string = null ) { $this->_catalogSession = $catalogSession; $this->_catalogDesign = $catalogDesign; @@ -88,9 +95,61 @@ public function __construct( $this->messageGroups = $messageGroups; $this->messageManager = $messageManager; $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; + $this->string = $string ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Stdlib\StringUtils::class); parent::__construct($context); } + /** + * Add meta information from product to layout + * + * @param \Magento\Framework\View\Result\Page $resultPage + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\Framework\View\Result\Page + */ + private function preparePageMetadata(ResultPage $resultPage, $product) + { + $pageLayout = $resultPage->getLayout(); + $pageLayout->createBlock(\Magento\Catalog\Block\Breadcrumbs::class); + + $pageConfig = $resultPage->getConfig(); + + $title = $product->getMetaTitle(); + if ($title) { + $pageConfig->getTitle()->set($title); + } + + $keyword = $product->getMetaKeyword(); + $currentCategory = $this->_coreRegistry->registry('current_category'); + if ($keyword) { + $pageConfig->setKeywords($keyword); + } elseif ($currentCategory) { + $pageConfig->setKeywords($product->getName()); + } + + $description = $product->getMetaDescription(); + if ($description) { + $pageConfig->setDescription($description); + } else { + $pageConfig->setDescription($this->string->substr($product->getDescription(), 0, 255)); + } + + if ($this->_catalogProduct->canUseCanonicalTag()) { + $pageConfig->addRemotePageAsset( + $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]), + 'canonical', + ['attributes' => ['rel' => 'canonical']] + ); + } + + $pageMainTitle = $pageLayout->getBlock('page.main.title'); + if ($pageMainTitle) { + $pageMainTitle->setPageTitle($product->getName()); + } + + return $this; + } + /** * Init layout for viewing product page * @@ -225,6 +284,7 @@ public function prepareAndRender(ResultPage $resultPage, $productId, $controller } $this->initProductLayout($resultPage, $product, $params); + $this->preparePageMetadata($resultPage, $product); return $this; } } diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index ff840635e4c..69340665b2c 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -6,8 +6,12 @@ namespace Magento\Catalog\Model; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Entity\GetCategoryCustomAttributeCodes; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\Eav\Model\Entity\GetCustomAttributeCodesInterface; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Convert\ConvertArray; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Profiler; @@ -69,23 +73,6 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements const CACHE_TAG = 'cat_c'; - /**#@+ - * Constants - */ - const KEY_PARENT_ID = 'parent_id'; - const KEY_NAME = 'name'; - const KEY_IS_ACTIVE = 'is_active'; - const KEY_POSITION = 'position'; - const KEY_LEVEL = 'level'; - const KEY_UPDATED_AT = 'updated_at'; - const KEY_CREATED_AT = 'created_at'; - const KEY_PATH = 'path'; - const KEY_AVAILABLE_SORT_BY = 'available_sort_by'; - const KEY_INCLUDE_IN_MENU = 'include_in_menu'; - const KEY_PRODUCT_COUNT = 'product_count'; - const KEY_CHILDREN_DATA = 'children_data'; - /**#@-*/ - /**#@-*/ protected $_eventPrefix = 'catalog_category'; @@ -142,21 +129,11 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements /** * Attributes are that part of interface * + * @deprecated + * @see CategoryInterface::ATTRIBUTES * @var array */ - protected $interfaceAttributes = [ - 'id', - self::KEY_PARENT_ID, - self::KEY_NAME, - self::KEY_IS_ACTIVE, - self::KEY_POSITION, - self::KEY_LEVEL, - self::KEY_UPDATED_AT, - self::KEY_CREATED_AT, - self::KEY_AVAILABLE_SORT_BY, - self::KEY_INCLUDE_IN_MENU, - self::KEY_CHILDREN_DATA, - ]; + protected $interfaceAttributes = CategoryInterface::ATTRIBUTES; /** * Category tree model @@ -230,6 +207,11 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements */ protected $metadataService; + /** + * @var GetCustomAttributeCodesInterface + */ + private $getCustomAttributeCodes; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -252,6 +234,7 @@ 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 GetCustomAttributeCodesInterface|null $getCustomAttributeCodes * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -275,7 +258,8 @@ public function __construct( CategoryRepositoryInterface $categoryRepository, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + GetCustomAttributeCodesInterface $getCustomAttributeCodes = null ) { $this->metadataService = $metadataService; $this->_treeModel = $categoryTreeResource; @@ -290,6 +274,9 @@ public function __construct( $this->urlFinder = $urlFinder; $this->indexerRegistry = $indexerRegistry; $this->categoryRepository = $categoryRepository; + $this->getCustomAttributeCodes = $getCustomAttributeCodes ?? ObjectManager::getInstance()->get( + GetCategoryCustomAttributeCodes::class + ); parent::__construct( $context, $registry, @@ -323,11 +310,7 @@ protected function _construct() */ protected function getCustomAttributesCodes() { - if ($this->customAttributesCodes === null) { - $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService); - $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes); - } - return $this->customAttributesCodes; + return $this->getCustomAttributeCodes->execute($this->metadataService); } /** @@ -667,9 +650,22 @@ public function getImageUrl($attributeCode = 'image') $image = $this->getData($attributeCode); if ($image) { if (is_string($image)) { - $url = $this->_storeManager->getStore()->getBaseUrl( + $store = $this->_storeManager->getStore(); + + $isRelativeUrl = substr($image, 0, 1) === '/'; + + $mediaBaseUrl = $store->getBaseUrl( \Magento\Framework\UrlInterface::URL_TYPE_MEDIA - ) . 'catalog/category/' . $image; + ); + + if ($isRelativeUrl) { + $url = $image; + } else { + $url = $mediaBaseUrl + . ltrim(\Magento\Catalog\Model\Category\FileInfo::ENTITY_MEDIA_PATH, '/') + . '/' + . $image; + } } else { throw new \Magento\Framework\Exception\LocalizedException( __('Something went wrong while getting the image url.') diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 19587ce56f5..a2dff83173b 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Category\Attribute\Backend; +use Magento\Framework\App\Filesystem\DirectoryList; + /** * Catalog category image attribute backend model * @@ -95,6 +97,11 @@ public function beforeSave($object) $attributeName = $this->getAttribute()->getName(); $value = $object->getData($attributeName); + if ($this->fileResidesOutsideCategoryDir($value)) { + // use relative path for image attribute so we know it's outside of category dir when we fetch it + $value[0]['name'] = $value[0]['url']; + } + if ($imageName = $this->getUploadedImageName($value)) { $object->setData($this->additionalData . $attributeName, $value); $object->setData($attributeName, $imageName); @@ -131,6 +138,26 @@ private function isTmpFileAvailable($value) return is_array($value) && isset($value[0]['tmp_name']); } + /** + * Check for file path resides outside of category media dir. The URL will be a path including pub/media if true + * + * @param array|null $value + * @return bool + */ + private function fileResidesOutsideCategoryDir($value) + { + if (!is_array($value) || !isset($value[0]['url'])) { + return false; + } + + $fileUrl = ltrim($value[0]['url'], '/'); + $baseMediaDir = $this->_filesystem->getUri(DirectoryList::MEDIA); + + $usingPathRelativeToBase = strpos($fileUrl, $baseMediaDir) === 0; + + return $usingPathRelativeToBase; + } + /** * Save uploaded file and set its name to category * @@ -148,6 +175,7 @@ public function afterSave($object) $this->_logger->critical($e); } } + return $this; } } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php index b6653b73aee..057933c55e6 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Sortby.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Category\Attribute\Backend; /** @@ -58,7 +59,7 @@ public function validate($object) if (!$this->getAttribute()->getEntity()->checkAttributeUniqueValue($this->getAttribute(), $object)) { $label = $this->getAttribute()->getFrontend()->getLabel(); throw new \Magento\Framework\Exception\LocalizedException( - __('The value of attribute "%1" must be unique.', $label) + __('The value of the "%1" attribute isn\'t unique. Set a unique value and try again.', $label) ); } } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 803c89a2504..a4127c9a97f 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -492,12 +492,20 @@ private function convertValues($category, $categoryData) unset($categoryData[$attributeCode]); $fileName = $category->getData($attributeCode); - if ($this->getFileInfo()->isExist($fileName)) { - $stat = $this->getFileInfo()->getStat($fileName); - $mime = $this->getFileInfo()->getMimeType($fileName); + $fileInfo = $this->getFileInfo(); + + if ($fileInfo->isExist($fileName)) { + $stat = $fileInfo->getStat($fileName); + $mime = $fileInfo->getMimeType($fileName); + + $categoryData[$attributeCode][0]['name'] = basename($fileName); + + if ($fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { + $categoryData[$attributeCode][0]['url'] = $fileName; + } else { + $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode); + } - $categoryData[$attributeCode][0]['name'] = $fileName; - $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode); $categoryData[$attributeCode][0]['size'] = isset($stat) ? $stat['size'] : 0; $categoryData[$attributeCode][0]['type'] = $mime; } diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index b26036ff983..9715bb2b161 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -9,6 +9,7 @@ use Magento\Framework\File\Mime; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\Directory\ReadInterface; /** * Class FileInfo @@ -37,6 +38,11 @@ class FileInfo */ private $mediaDirectory; + /** + * @var ReadInterface + */ + private $baseDirectory; + /** * @param Filesystem $filesystem * @param Mime $mime @@ -62,6 +68,20 @@ private function getMediaDirectory() return $this->mediaDirectory; } + /** + * Get Base Directory read instance + * + * @return ReadInterface + */ + private function getBaseDirectory() + { + if (!isset($this->baseDirectory)) { + $this->baseDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); + } + + return $this->baseDirectory; + } + /** * Retrieve MIME type of requested file * @@ -70,7 +90,7 @@ private function getMediaDirectory() */ public function getMimeType($fileName) { - $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + $filePath = $this->getFilePath($fileName); $absoluteFilePath = $this->getMediaDirectory()->getAbsolutePath($filePath); $result = $this->mime->getMimeType($absoluteFilePath); @@ -85,7 +105,7 @@ public function getMimeType($fileName) */ public function getStat($fileName) { - $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + $filePath = $this->getFilePath($fileName); $result = $this->getMediaDirectory()->stat($filePath); return $result; @@ -99,9 +119,65 @@ public function getStat($fileName) */ public function isExist($fileName) { - $filePath = self::ENTITY_MEDIA_PATH . '/' . ltrim($fileName, '/'); + $filePath = $this->getFilePath($fileName); $result = $this->getMediaDirectory()->isExist($filePath); return $result; } + + /** + * Construct and return file subpath based on filename relative to media directory + * + * @param string $fileName + * @return string + */ + private function getFilePath($fileName) + { + $filePath = ltrim($fileName, '/'); + + $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath(); + $isFileNameBeginsWithMediaDirectoryPath = $this->isBeginsWithMediaDirectoryPath($fileName); + + // if the file is not using a relative path, it resides in the catalog/category media directory + $fileIsInCategoryMediaDir = !$isFileNameBeginsWithMediaDirectoryPath; + + if ($fileIsInCategoryMediaDir) { + $filePath = self::ENTITY_MEDIA_PATH . '/' . $filePath; + } else { + $filePath = substr($filePath, strlen($mediaDirectoryRelativeSubpath)); + } + + return $filePath; + } + + /** + * Checks for whether $fileName string begins with media directory path + * + * @param string $fileName + * @return bool + */ + public function isBeginsWithMediaDirectoryPath($fileName) + { + $filePath = ltrim($fileName, '/'); + + $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath(); + $isFileNameBeginsWithMediaDirectoryPath = strpos($filePath, $mediaDirectoryRelativeSubpath) === 0; + + return $isFileNameBeginsWithMediaDirectoryPath; + } + + /** + * Get media directory subpath relative to base directory path + * + * @return string + */ + private function getMediaDirectoryPathRelativeToBaseDirectoryPath() + { + $baseDirectoryPath = $this->getBaseDirectory()->getAbsolutePath(); + $mediaDirectoryPath = $this->getMediaDirectory()->getAbsolutePath(); + + $mediaDirectoryRelativeSubpath = substr($mediaDirectoryPath, strlen($baseDirectoryPath)); + + return $mediaDirectoryRelativeSubpath; + } } diff --git a/app/code/Magento/Catalog/Model/CategoryLinkRepository.php b/app/code/Magento/Catalog/Model/CategoryLinkRepository.php index 6c3d84d5f5e..fb0ea680569 100644 --- a/app/code/Magento/Catalog/Model/CategoryLinkRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryLinkRepository.php @@ -78,7 +78,7 @@ public function deleteByIds($categoryId, $sku) $productID = $product->getId(); if (!isset($productPositions[$productID])) { - throw new InputException(__('Category does not contain specified product')); + throw new InputException(__("The category doesn't contain the specified product.")); } $backupPosition = $productPositions[$productID]; unset($productPositions[$productID]); diff --git a/app/code/Magento/Catalog/Model/CategoryRepository.php b/app/code/Magento/Catalog/Model/CategoryRepository.php index 505c729ac10..8cb11c4306d 100644 --- a/app/code/Magento/Catalog/Model/CategoryRepository.php +++ b/app/code/Magento/Catalog/Model/CategoryRepository.php @@ -197,7 +197,7 @@ protected function validateCategory(Category $category) if ($error === true) { $attribute = $this->categoryResource->getAttribute($code)->getFrontend()->getLabel(); throw new \Magento\Framework\Exception\LocalizedException( - __('Attribute "%1" is required.', $attribute) + __('The "%1" attribute is required. Enter and try again.', $attribute) ); } else { throw new \Magento\Framework\Exception\LocalizedException(__($error)); diff --git a/app/code/Magento/Catalog/Model/Entity/GetCategoryCustomAttributeCodes.php b/app/code/Magento/Catalog/Model/Entity/GetCategoryCustomAttributeCodes.php new file mode 100644 index 00000000000..b2b9199cc56 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Entity/GetCategoryCustomAttributeCodes.php @@ -0,0 +1,37 @@ +baseCustomAttributeCodes = $baseCustomAttributeCodes; + } + + /** + * @inheritdoc + */ + public function execute(MetadataServiceInterface $metadataService): array + { + $customAttributesCodes = $this->baseCustomAttributeCodes->execute($metadataService); + return array_diff($customAttributesCodes, CategoryInterface::ATTRIBUTES); + } +} diff --git a/app/code/Magento/Catalog/Model/Entity/GetProductCustomAttributeCodes.php b/app/code/Magento/Catalog/Model/Entity/GetProductCustomAttributeCodes.php new file mode 100644 index 00000000000..23678ffcf48 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Entity/GetProductCustomAttributeCodes.php @@ -0,0 +1,37 @@ +baseCustomAttributeCodes = $baseCustomAttributeCodes; + } + + /** + * @inheritdoc + */ + public function execute(MetadataServiceInterface $metadataService): array + { + $customAttributesCodes = $this->baseCustomAttributeCodes->execute($metadataService); + return array_diff($customAttributesCodes, ProductInterface::ATTRIBUTES); + } +} diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Dynamic/AlgorithmFactory.php b/app/code/Magento/Catalog/Model/Layer/Filter/Dynamic/AlgorithmFactory.php index 6a9c37e06a7..b0467c98907 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/Dynamic/AlgorithmFactory.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/Dynamic/AlgorithmFactory.php @@ -71,7 +71,7 @@ public function create(array $data = []) ); if (!isset($this->algorithms[$calculationType])) { - throw new LocalizedException(__('%1 was not found in algorithms', $calculationType)); + throw new LocalizedException(__("The %1 value wasn't found in the algorithms.", $calculationType)); } $className = $this->algorithms[$calculationType]; diff --git a/app/code/Magento/Catalog/Model/Layer/State.php b/app/code/Magento/Catalog/Model/Layer/State.php index 25f0caddd98..c404e5539d2 100644 --- a/app/code/Magento/Catalog/Model/Layer/State.php +++ b/app/code/Magento/Catalog/Model/Layer/State.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Layer; use Magento\Catalog\Model\Layer\Filter\Item; @@ -42,7 +43,7 @@ public function addFilter($filter) public function setFilters($filters) { if (!is_array($filters)) { - throw new LocalizedException(__('The filters must be an array.')); + throw new LocalizedException(__('The filters are invalid. Set them in an array and try again.')); } $this->setData('filters', $filters); return $this; diff --git a/app/code/Magento/Catalog/Model/Locator/RegistryLocator.php b/app/code/Magento/Catalog/Model/Locator/RegistryLocator.php index 9770c8b3939..700fd4b0b89 100644 --- a/app/code/Magento/Catalog/Model/Locator/RegistryLocator.php +++ b/app/code/Magento/Catalog/Model/Locator/RegistryLocator.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Locator; use Magento\Catalog\Api\Data\ProductInterface; @@ -52,7 +53,7 @@ public function getProduct() return $this->product = $product; } - throw new NotFoundException(__('Product was not registered')); + throw new NotFoundException(__("The product wasn't registered.")); } /** @@ -69,7 +70,7 @@ public function getStore() return $this->store = $store; } - throw new NotFoundException(__('Store was not registered')); + throw new NotFoundException(__("The store wasn't registered. Verify the store and try again.")); } /** diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index cb5669a4bb4..db16c34f123 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -9,9 +9,12 @@ use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductLinkRepositoryInterface; +use Magento\Catalog\Model\Entity\GetProductCustomAttributeCodes; use Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool; +use Magento\Eav\Model\Entity\GetCustomAttributeCodesInterface; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\SaleableInterface; @@ -305,22 +308,12 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements /** * List of attributes in ProductInterface + * + * @deprecated + * @see ProductInterface::ATTRIBUTES * @var array */ - protected $interfaceAttributes = [ - ProductInterface::SKU, - ProductInterface::NAME, - ProductInterface::PRICE, - ProductInterface::WEIGHT, - ProductInterface::STATUS, - ProductInterface::VISIBILITY, - ProductInterface::ATTRIBUTE_SET_ID, - ProductInterface::TYPE_ID, - ProductInterface::CREATED_AT, - ProductInterface::UPDATED_AT, - 'media_gallery', - 'tier_price', - ]; + protected $interfaceAttributes = ProductInterface::ATTRIBUTES; /** * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface @@ -346,6 +339,11 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements */ protected $linkTypeProvider; + /** + * @var GetCustomAttributeCodesInterface + */ + private $getCustomAttributeCodes; + /** * Product constructor. * @param \Magento\Framework\Model\Context $context @@ -383,7 +381,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor * @param array $data - * + * @param GetCustomAttributeCodesInterface|null $getCustomAttributeCodes * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -422,7 +420,8 @@ public function __construct( EntryConverterPool $mediaGalleryEntryConverterPool, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor, - array $data = [] + array $data = [], + GetCustomAttributeCodesInterface $getCustomAttributeCodes = null ) { $this->metadataService = $metadataService; $this->_itemOptionFactory = $itemOptionFactory; @@ -451,6 +450,9 @@ public function __construct( $this->mediaGalleryEntryConverterPool = $mediaGalleryEntryConverterPool; $this->dataObjectHelper = $dataObjectHelper; $this->joinProcessor = $joinProcessor; + $this->getCustomAttributeCodes = $getCustomAttributeCodes ?? ObjectManager::getInstance()->get( + GetProductCustomAttributeCodes::class + ); parent::__construct( $context, $registry, @@ -478,11 +480,7 @@ protected function _construct() */ protected function getCustomAttributesCodes() { - if ($this->customAttributesCodes === null) { - $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService); - $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes); - } - return $this->customAttributesCodes; + return $this->getCustomAttributeCodes->execute($this->metadataService); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index a312f00b26b..a652d0ef902 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -51,7 +51,7 @@ public function validate($object) $value = $object->getData($attrCode); if ($this->getAttribute()->getIsRequired() && strlen($value) === 0) { throw new \Magento\Framework\Exception\LocalizedException( - __('The value of attribute "%1" must be set', $attrCode) + __('The "%1" attribute value is empty. Set the attribute and try again.', $attrCode) ); } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Image.php b/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Image.php index 6173a76eca4..cdd6da7019d 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Image.php @@ -9,23 +9,28 @@ * * @author Magento Core Team */ + namespace Magento\Catalog\Model\Product\Attribute\Frontend; -class Image extends \Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend +use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; +use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; + +class Image extends AbstractFrontend { /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** * Construct * - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager */ - public function __construct(\Magento\Store\Model\StoreManagerInterface $storeManager) + public function __construct(StoreManagerInterface $storeManager) { $this->_storeManager = $storeManager; } @@ -42,9 +47,9 @@ public function getUrl($product) $image = $product->getData($this->getAttribute()->getAttributeCode()); $url = false; if (!empty($image)) { - $url = $this->_storeManager->getStore($product->getStore()) - ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) - . 'catalog/product/' . $image; + $url = $this->_storeManager + ->getStore($product->getStore()) + ->getBaseUrl(UrlInterface::URL_TYPE_MEDIA) . 'catalog/product/' . ltrim($image, '/'); } return $url; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php b/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php index fb52e83a51c..19cb71e4231 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/SetManagement.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Attribute; use Magento\Framework\Exception\StateException; @@ -65,12 +66,12 @@ protected function validateSkeletonSet($skeletonId) $productEntityId = $this->eavConfig->getEntityType(\Magento\Catalog\Model\Product::ENTITY)->getId(); if ($skeletonSet->getEntityTypeId() != $productEntityId) { throw new StateException( - __('Can not create attribute set based on non product attribute set.') + __("The attribute set couldn't be created because it's based on a non-product attribute set.") ); } } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { throw new StateException( - __('Can not create attribute set based on not existing attribute set') + __("The attribute set couldn't be created because it's based on a non-existing attribute set.") ); } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php index adbd6579e68..07a8c4cc4d1 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Inputtype.php @@ -28,13 +28,16 @@ class Inputtype extends \Magento\Eav\Model\Adminhtml\System\Config\Source\Inputt /** * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\Registry $coreRegistry + * @param array $optionsArray */ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Framework\Registry $coreRegistry + \Magento\Framework\Registry $coreRegistry, + array $optionsArray = [] ) { $this->_eventManager = $eventManager; $this->_coreRegistry = $coreRegistry; + parent::__construct($optionsArray); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index 03d418f3ba0..bd28b65bb79 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -245,12 +245,13 @@ protected function processNewAndExistingImages($product, array &$images) if (empty($image['removed'])) { $data = $this->processNewImage($product, $image); - $this->resourceModel->deleteGalleryValueInStore( - $image['value_id'], - $product->getData($this->metadata->getLinkField()), - $product->getStoreId() - ); - + if (!$product->isObjectNew()) { + $this->resourceModel->deleteGalleryValueInStore( + $image['value_id'], + $product->getData($this->metadata->getLinkField()), + $product->getStoreId() + ); + } // Add per store labels, position, disabled $data['value_id'] = $image['value_id']; $data['label'] = isset($image['label']) ? $image['label'] : ''; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 1b5f96baeaf..4d274a071d0 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Gallery; use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface; @@ -51,7 +52,7 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $entryContent = $entry->getContent(); if (!$this->contentValidator->isValid($entryContent)) { - throw new InputException(__('The image content is not valid.')); + throw new InputException(__('The image content is invalid. Verify the content and try again.')); } $product = $this->productRepository->get($sku); @@ -71,7 +72,7 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) } catch (InputException $inputException) { throw $inputException; } catch (\Exception $e) { - throw new StateException(__('Cannot save product.')); + throw new StateException(__("The product can't be saved.")); } foreach ($product->getMediaGalleryEntries() as $entry) { @@ -79,7 +80,7 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) return $entry->getId(); } } - throw new StateException(__('Failed to save new media gallery entry.')); + throw new StateException(__('The new media gallery entry failed to save.')); } /** @@ -90,7 +91,9 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) $product = $this->productRepository->get($sku); $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); if ($existingMediaGalleryEntries == null) { - throw new NoSuchEntityException(__('There is no image with provided ID.')); + throw new NoSuchEntityException( + __('No image with the provided ID was found. Verify the ID and try again.') + ); } $found = false; foreach ($existingMediaGalleryEntries as $key => $existingEntry) { @@ -107,14 +110,16 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) } } if (!$found) { - throw new NoSuchEntityException(__('There is no image with provided ID.')); + throw new NoSuchEntityException( + __('No image with the provided ID was found. Verify the ID and try again.') + ); } $product->setMediaGalleryEntries($existingMediaGalleryEntries); try { $this->productRepository->save($product); } catch (\Exception $exception) { - throw new StateException(__('Cannot save product.')); + throw new StateException(__("The product can't be saved.")); } return true; } @@ -127,7 +132,9 @@ public function remove($sku, $entryId) $product = $this->productRepository->get($sku); $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); if ($existingMediaGalleryEntries == null) { - throw new NoSuchEntityException(__('There is no image with provided ID.')); + throw new NoSuchEntityException( + __('No image with the provided ID was found. Verify the ID and try again.') + ); } $found = false; foreach ($existingMediaGalleryEntries as $key => $entry) { @@ -138,7 +145,9 @@ public function remove($sku, $entryId) } } if (!$found) { - throw new NoSuchEntityException(__('There is no image with provided ID.')); + throw new NoSuchEntityException( + __('No image with the provided ID was found. Verify the ID and try again.') + ); } $product->setMediaGalleryEntries($existingMediaGalleryEntries); $this->productRepository->save($product); @@ -153,7 +162,7 @@ public function get($sku, $entryId) try { $product = $this->productRepository->get($sku); } catch (\Exception $exception) { - throw new NoSuchEntityException(__('Such product doesn\'t exist')); + throw new NoSuchEntityException(__("The product doesn't exist. Verify and try again.")); } $mediaGalleryEntries = $product->getMediaGalleryEntries(); @@ -163,7 +172,7 @@ public function get($sku, $entryId) } } - throw new NoSuchEntityException(__('Such image doesn\'t exist')); + throw new NoSuchEntityException(__("The image doesn't exist. Verify and try again.")); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index 73d0f1fa679..c6c7fbda7e9 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Gallery; use Magento\Framework\App\Filesystem\DirectoryList; @@ -108,7 +109,9 @@ public function validate($object) if ($this->getAttribute()->getIsUnique()) { if (!$this->getAttribute()->getEntity()->checkAttributeUniqueValue($this->getAttribute(), $object)) { $label = $this->getAttribute()->getFrontend()->getLabel(); - throw new LocalizedException(__('The value of attribute "%1" must be unique.', $label)); + throw new LocalizedException( + __('The value of the "%1" attribute isn\'t unique. Set a unique value and try again.', $label) + ); } } @@ -139,13 +142,15 @@ public function addImage( ) { $file = $this->mediaDirectory->getRelativePath($file); if (!$this->mediaDirectory->isFile($file)) { - throw new LocalizedException(__('The image does not exist.')); + throw new LocalizedException(__("The image doesn't exist.")); } $pathinfo = pathinfo($file); $imgExtensions = ['jpg', 'jpeg', 'gif', 'png']; if (!isset($pathinfo['extension']) || !in_array(strtolower($pathinfo['extension']), $imgExtensions)) { - throw new LocalizedException(__('Please correct the image file type.')); + throw new LocalizedException( + __('The image type for the file is invalid. Enter the correct image type and try again.') + ); } $fileName = \Magento\MediaStorage\Model\File\Uploader::getCorrectFileName($pathinfo['basename']); @@ -170,7 +175,7 @@ public function addImage( $storageHelper->saveFile($this->mediaConfig->getTmpMediaShortUrl($fileName)); } } catch (\Exception $e) { - throw new LocalizedException(__('We couldn\'t move this file: %1.', $e->getMessage())); + throw new LocalizedException(__('The "%1" file couldn\'t be moved.', $e->getMessage())); } $fileName = str_replace('\\', '/', $fileName); diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 91f589f9b5b..971f34e02f9 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -9,6 +9,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; use Magento\Framework\Image as MagentoImage; +use Magento\Framework\Serialize\SerializerInterface; /** * @method string getFile() @@ -172,6 +173,16 @@ class Image extends \Magento\Framework\Model\AbstractModel */ private $imageAsset; + /** + * @var string + */ + private $cachePrefix = 'IMG_INFO'; + + /** + * @var SerializerInterface + */ + private $serializer; + /** * Constructor * @@ -190,6 +201,7 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param array $data * @param \Magento\Catalog\Model\View\Asset\ImageFactory|null $viewAssetImageFactory * @param \Magento\Catalog\Model\View\Asset\PlaceholderFactory|null $viewAssetPlaceholderFactory + * @param SerializerInterface|null $serializer * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ @@ -208,7 +220,8 @@ public function __construct( \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], \Magento\Catalog\Model\View\Asset\ImageFactory $viewAssetImageFactory = null, - \Magento\Catalog\Model\View\Asset\PlaceholderFactory $viewAssetPlaceholderFactory = null + \Magento\Catalog\Model\View\Asset\PlaceholderFactory $viewAssetPlaceholderFactory = null, + SerializerInterface $serializer = null ) { $this->_storeManager = $storeManager; $this->_catalogProductMediaConfig = $catalogProductMediaConfig; @@ -223,6 +236,7 @@ public function __construct( ->get(\Magento\Catalog\Model\View\Asset\ImageFactory::class); $this->viewAssetPlaceholderFactory = $viewAssetPlaceholderFactory ?: ObjectManager::getInstance() ->get(\Magento\Catalog\Model\View\Asset\PlaceholderFactory::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** @@ -356,86 +370,6 @@ public function setSize($size) return $this; } - /** - * @param string|null $file - * @return bool - */ - protected function _checkMemory($file = null) - { - return $this->_getMemoryLimit() > $this->_getMemoryUsage() + $this->_getNeedMemoryForFile( - $file - ) - || $this->_getMemoryLimit() == -1; - } - - /** - * @return string - */ - protected function _getMemoryLimit() - { - $memoryLimit = trim(strtoupper(ini_get('memory_limit'))); - - if (!isset($memoryLimit[0])) { - $memoryLimit = "128M"; - } - - if (substr($memoryLimit, -1) == 'K') { - return substr($memoryLimit, 0, -1) * 1024; - } - if (substr($memoryLimit, -1) == 'M') { - return substr($memoryLimit, 0, -1) * 1024 * 1024; - } - if (substr($memoryLimit, -1) == 'G') { - return substr($memoryLimit, 0, -1) * 1024 * 1024 * 1024; - } - return $memoryLimit; - } - - /** - * @return int - */ - protected function _getMemoryUsage() - { - if (function_exists('memory_get_usage')) { - return memory_get_usage(); - } - return 0; - } - - /** - * @param string|null $file - * @return float|int - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function _getNeedMemoryForFile($file = null) - { - $file = $file === null ? $this->getBaseFile() : $file; - if (!$file) { - return 0; - } - - if (!$this->_mediaDirectory->isExist($file)) { - return 0; - } - - $imageInfo = getimagesize($this->_mediaDirectory->getAbsolutePath($file)); - - if (!isset($imageInfo[0]) || !isset($imageInfo[1])) { - return 0; - } - if (!isset($imageInfo['channels'])) { - // if there is no info about this parameter lets set it for maximum - $imageInfo['channels'] = 4; - } - if (!isset($imageInfo['bits'])) { - // if there is no info about this parameter lets set it for maximum - $imageInfo['bits'] = 8; - } - return round( - ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $imageInfo['channels'] / 8 + Pow(2, 16)) * 1.65 - ); - } - /** * Convert array of 3 items (decimal r, g, b) to string of their hex values * @@ -472,9 +406,7 @@ public function setBaseFile($file) 'filePath' => $file, ] ); - if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile()) - || !$this->_checkMemory($this->imageAsset->getSourceFile()) - ) { + if ($file == 'no_selection' || !$this->_fileExists($this->imageAsset->getSourceFile())) { $this->_isBaseFilePlaceholder = true; $this->imageAsset = $this->viewAssetPlaceholderFactory->create( [ @@ -682,11 +614,14 @@ public function getDestinationSubdir() } /** - * @return bool|void + * @return bool */ public function isCached() { - return file_exists($this->imageAsset->getPath()); + return ( + is_array($this->loadImageInfoFromCache($this->imageAsset->getPath())) || + file_exists($this->imageAsset->getPath()) + ); } /** @@ -856,6 +791,7 @@ public function clearCache() $this->_mediaDirectory->delete($directory); $this->_coreFileStorageDatabase->deleteFolder($this->_mediaDirectory->getAbsolutePath($directory)); + $this->clearImageInfoFromCache(); } /** @@ -890,7 +826,7 @@ public function getResizedImageInfo() $image = $this->imageAsset->getPath(); } - $imageProperties = getimagesize($image); + $imageProperties = $this->getimagesize($image); return $imageProperties; } finally { @@ -932,4 +868,66 @@ private function getMiscParams() return $miscParams; } + + /** + * Get image size + * + * @param string $imagePath + * @return array + */ + private function getImageSize($imagePath) + { + $imageInfo = $this->loadImageInfoFromCache($imagePath); + if (!isset($imageInfo['size'])) { + $imageSize = getimagesize($imagePath); + $this->saveImageInfoToCache(['size' => $imageSize], $imagePath); + return $imageSize; + } else { + return $imageInfo['size']; + } + } + + /** + * Save image data to cache + * + * @param array $imageInfo + * @param string $imagePath + * @return void + */ + private function saveImageInfoToCache(array $imageInfo, string $imagePath) + { + $imagePath = $this->cachePrefix . $imagePath; + $this->_cacheManager->save( + $this->serializer->serialize($imageInfo), + $imagePath, + [$this->cachePrefix] + ); + } + + /** + * Load image data from cache + * + * @param string $imagePath + * @return array|false + */ + private function loadImageInfoFromCache(string $imagePath) + { + $imagePath = $this->cachePrefix . $imagePath; + $cacheData = $this->_cacheManager->load($imagePath); + if (!$cacheData) { + return false; + } else { + return $this->serializer->unserialize($cacheData); + } + } + + /** + * Clear image data from cache + * + * @return void + */ + private function clearImageInfoFromCache() + { + $this->_cacheManager->clean([$this->cachePrefix]); + } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Repository.php b/app/code/Magento/Catalog/Model/Product/Option/Repository.php index c2d7781b37a..9dc9695daff 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Repository.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Repository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option; use Magento\Catalog\Api\Data\ProductInterface; @@ -147,7 +148,7 @@ public function save(\Magento\Catalog\Api\Data\ProductCustomOptionInterface $opt { $productSku = $option->getProductSku(); if (!$productSku) { - throw new CouldNotSaveException(__('ProductSku should be specified')); + throw new CouldNotSaveException(__('The ProductSku is empty. Set the ProductSku and try again.')); } /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($productSku); @@ -200,7 +201,7 @@ public function deleteByIdentifier($sku, $optionId) $this->productRepository->save($product); } } catch (\Exception $e) { - throw new CouldNotSaveException(__('Could not remove custom option')); + throw new CouldNotSaveException(__("The custom option couldn't be removed.")); } return true; } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php index cb6e76aebaa..7517459da65 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Date.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option\Type; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; @@ -122,7 +123,10 @@ public function validateUserValue($values) ); } else { throw new \Magento\Framework\Exception\LocalizedException( - __('Please specify product\'s required option(s).') + __( + "The product's required option(s) weren't entered. " + . "Make sure the options are entered and try again." + ) ); } } else { diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php index 6b1b42b305b..51480e849d9 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -212,7 +212,9 @@ public function validateUserValue($values) $option = $this->getOption(); if (!isset($values[$option->getId()]) && $option->getIsRequire() && !$this->getSkipCheckRequiredOption()) { - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __("The product's required option(s) weren't entered. Make sure the options are entered and try again.") + ); } elseif (isset($values[$option->getId()])) { $this->setUserValue($values[$option->getId()]); $this->setIsValid(true); diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php index a7304c9b67b..aecb8525915 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option\Type; use Magento\Framework\App\Filesystem\DirectoryList; @@ -255,7 +256,12 @@ public function validateUserValue($values) } catch (ProductException $e) { switch ($this->getProcessMode()) { case \Magento\Catalog\Model\Product\Type\AbstractType::PROCESS_MODE_FULL: - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __( + "The product's required option(s) weren't entered. " + . "Make sure the options are entered and try again." + ) + ); break; default: $this->setUserValue(null); @@ -367,7 +373,7 @@ protected function _getOptionHtml($optionValue) $sizes ); } catch (\Exception $e) { - throw new LocalizedException(__('The file options format is not valid.')); + throw new LocalizedException(__('The file options format is invalid. Use a correct format and try again.')); } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/Validator.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/Validator.php index 2c4b76d637d..860010c3db0 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/Validator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/Validator.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option\Type\File; use Magento\Framework\App\Filesystem\DirectoryList; @@ -102,7 +103,7 @@ protected function getValidatorErrors($errors, $fileInfo, $option) break; case \Zend_Validate_File_ImageSize::NOT_DETECTED: $result[] = __( - "The file '%1' is empty. Please choose another one", + 'The file "%1" is empty. Select another file and try again.', $fileInfo['title'] ); break; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php index b54c66d75a0..d6a5cb1cbc2 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorFile.php @@ -115,7 +115,10 @@ public function validate($processingParams, $option) $runValidation = $option->getIsRequire() || $upload->isUploaded($file); if (!$runValidation) { throw new \Magento\Framework\Validator\Exception( - __('Validation failed. Required options were not filled or the file was not uploaded.') + __( + 'The validation failed. ' + . 'Make sure the required options are entered and the file is uploaded, then try again.' + ) ); } @@ -128,10 +131,14 @@ public function validate($processingParams, $option) if ($this->validateContentLength()) { $value = $this->fileSize->getMaxFileSizeInMb(); throw new LocalizedException( - __('The file you uploaded is larger than %1 Megabytes allowed by server', $value) + __( + "The file was too big and couldn't be uploaded. " + . "Use a file smaller than %1 MBs and try to upload again.", + $value + ) ); } else { - throw new ProductException(__('Option required.')); + throw new ProductException(__("The required option wasn't entered. Enter the option and try again.")); } } @@ -182,7 +189,7 @@ public function validate($processingParams, $option) $imageSize = getimagesize($fileInfo['tmp_name']); } } else { - throw new LocalizedException(__('The file is empty. Please choose another one')); + throw new LocalizedException(__('The file is empty. Select another file and try again.')); } if (!empty($imageSize)) { @@ -209,7 +216,9 @@ public function validate($processingParams, $option) throw new LocalizedException(__(implode("\n", $errors))); } } else { - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __("The product's required option(s) weren't entered. Make sure the options are entered and try again.") + ); } return $userValue; } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php index 30c3de932c3..37e4c7b310a 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php @@ -102,7 +102,7 @@ public function validate($optionValue, $option) } } else { throw new \Magento\Framework\Exception\LocalizedException( - __('Please specify product\'s required option(s).') + __("The product's required option(s) weren't entered. Make sure the options are entered and try again.") ); } return $result; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php index 78cce7bd761..08e1a3e327d 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option\Type; use Magento\Framework\Exception\LocalizedException; @@ -64,13 +65,20 @@ public function validateUserValue($values) if (empty($value) && $option->getIsRequire() && !$this->getSkipCheckRequiredOption()) { $this->setIsValid(false); - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __("The product's required option(s) weren't entered. Make sure the options are entered and try again.") + ); } if (!$this->_isSingleSelection()) { $valuesCollection = $option->getOptionValuesByOptionId($value, $this->getProduct()->getStoreId())->load(); if ($valuesCollection->count() != count($value)) { $this->setIsValid(false); - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __( + "The product's required option(s) weren't entered. " + . "Make sure the options are entered and try again." + ) + ); } } return $this; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php index 79ee37c5167..fd0eae188fe 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Option\Type; use Magento\Framework\Exception\LocalizedException; @@ -60,14 +61,16 @@ public function validateUserValue($values) // Check requires option to have some value if (strlen($value) == 0 && $option->getIsRequire() && !$this->getSkipCheckRequiredOption()) { $this->setIsValid(false); - throw new LocalizedException(__('Please specify product\'s required option(s).')); + throw new LocalizedException( + __("The product's required option(s) weren't entered. Make sure the options are entered and try again.") + ); } // Check maximal length limit $maxCharacters = $option->getMaxCharacters(); if ($maxCharacters > 0 && $this->string->strlen($value) > $maxCharacters) { $this->setIsValid(false); - throw new LocalizedException(__('The text is too long.')); + throw new LocalizedException(__('The text is too long. Shorten the text and try again.')); } $this->setUserValue($value); diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php index 8f1e64e1b7a..83a2d134079 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php @@ -143,7 +143,7 @@ private function retrieveValidPrices(array $prices) $this->validationResult->addFailedItem( $key, __( - 'Requested product doesn\'t exist. ' + 'The product that was requested doesn\'t exist. Verify the product and try again. ' . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', [ 'SKU' => $price->getSku(), diff --git a/app/code/Magento/Catalog/Model/Product/PriceModifier.php b/app/code/Magento/Catalog/Model/Product/PriceModifier.php index 4d81000501c..48d53b46145 100644 --- a/app/code/Magento/Catalog/Model/Product/PriceModifier.php +++ b/app/code/Magento/Catalog/Model/Product/PriceModifier.php @@ -40,7 +40,7 @@ public function removeTierPrice(\Magento\Catalog\Model\Product $product, $custom $prices = $product->getData('tier_price'); // verify if price exist if ($prices === null) { - throw new NoSuchEntityException(__('This product doesn\'t have tier price')); + throw new NoSuchEntityException(__('Tier price is unavailable for this product.')); } $tierPricesQty = count($prices); @@ -69,7 +69,7 @@ public function removeTierPrice(\Magento\Catalog\Model\Product $product, $custom try { $this->productRepository->save($product); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Invalid data provided for tier_price')); + throw new CouldNotSaveException(__('The tier_price data is invalid. Verify the data and try again.')); } } } diff --git a/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php b/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php index 9e2a79c05fe..ca5b8038b32 100644 --- a/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php +++ b/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product; use Magento\Catalog\Api\Data\ProductTierPriceInterface; @@ -69,7 +70,7 @@ public function add($sku, ProductTierPriceInterface $tierPrice) try { $this->productRepository->save($product); } catch (\Exception $e) { - throw new \Magento\Framework\Exception\CouldNotSaveException(__('Could not save group price')); + throw new \Magento\Framework\Exception\CouldNotSaveException(__("The group price couldn't be saved.")); } return true; } @@ -126,7 +127,9 @@ private function validate(ProductTierPriceInterface $tierPrice) $data = ['qty' => $tierPrice->getQty(), 'price' => $tierPrice->getValue()]; foreach ($data as $value) { if (!is_float($value) || $value <= 0) { - throw new \Magento\Framework\Exception\InputException(__('Please provide valid data')); + throw new \Magento\Framework\Exception\InputException( + __('The data was invalid. Verify the data and try again.') + ); } } } diff --git a/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php b/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php index 7cecd2f37bb..822959bfc85 100644 --- a/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php +++ b/app/code/Magento/Catalog/Model/Product/TierPriceManagement.php @@ -89,7 +89,7 @@ public function __construct( public function add($sku, $customerGroupId, $price, $qty) { if (!\Zend_Validate::is($price, 'Float') || $price <= 0 || !\Zend_Validate::is($qty, 'Float') || $qty <= 0) { - throw new InputException(__('Please provide valid data')); + throw new InputException(__('The data was invalid. Verify the data and try again.')); } $product = $this->productRepository->get($sku, ['edit_mode' => true]); $tierPrices = $product->getData('tier_price'); @@ -132,7 +132,7 @@ public function add($sku, $customerGroupId, $price, $qty) if (is_array($errors) && count($errors)) { $errorAttributeCodes = implode(', ', array_keys($errors)); throw new InputException( - __('Values of following attributes are invalid: %1', $errorAttributeCodes) + __('Values in the %1 attributes are invalid. Verify the values and try again.', $errorAttributeCodes) ); } try { @@ -142,7 +142,7 @@ public function add($sku, $customerGroupId, $price, $qty) // temporary state exception must be already localized throw $e; } - throw new CouldNotSaveException(__('Could not save group price')); + throw new CouldNotSaveException(__("The group price couldn't be saved.")); } return true; } diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 33ff3ecccd4..0ab1fbab471 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\Product\Type; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -506,7 +507,7 @@ public function processFileQueue() $rootDir->create($rootDir->getRelativePath($path)); } catch (\Magento\Framework\Exception\FileSystemException $e) { throw new \Magento\Framework\Exception\LocalizedException( - __('We can\'t create writeable directory "%1".', $path) + __('We can\'t create the "%1" writeable directory.', $path) ); } @@ -519,7 +520,9 @@ public function processFileQueue() if (isset($queueOptions['option'])) { $queueOptions['option']->setIsValid(false); } - throw new \Magento\Framework\Exception\LocalizedException(__('The file upload failed.')); + throw new \Magento\Framework\Exception\LocalizedException( + __('The file upload failed. Try to upload again.') + ); } $this->_fileStorageDb->saveFile($dst); break; @@ -535,7 +538,7 @@ public function processFileQueue() /** * Add file to File Queue - * @param array $queueOptions Array of File Queue + * @param array $queueOptions Array of File Queue * (eg. ['operation'=>'move', * 'src_name'=>'filename', * 'dst_name'=>'filename2']) @@ -564,7 +567,7 @@ protected function _isStrictProcessMode($processMode) */ public function getSpecifyOptionMessage() { - return __('Please specify product\'s required option(s).'); + return __("The product's required option(s) weren't entered. Make sure the options are entered and try again."); } /** @@ -635,7 +638,7 @@ public function checkProductBuyState($product) if (!$customOption || strlen($customOption->getValue()) == 0) { $product->setSkipCheckRequiredOption(true); throw new \Magento\Framework\Exception\LocalizedException( - __('The product has required options.') + __('The product has required options. Enter the options and try again.') ); } } @@ -974,7 +977,7 @@ public function setConfig($config) } if (isset($config['can_use_qty_decimals'])) { - $this->_canUseQtyDecimals = (bool) $config['can_use_qty_decimals']; + $this->_canUseQtyDecimals = (bool)$config['can_use_qty_decimals']; } return $this; diff --git a/app/code/Magento/Catalog/Model/ProductAttributeGroupRepository.php b/app/code/Magento/Catalog/Model/ProductAttributeGroupRepository.php index ad1ea248916..f43ff45b93e 100644 --- a/app/code/Magento/Catalog/Model/ProductAttributeGroupRepository.php +++ b/app/code/Magento/Catalog/Model/ProductAttributeGroupRepository.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model; use Magento\Framework\Exception\NoSuchEntityException; @@ -66,7 +67,9 @@ public function get($groupId) $group = $this->groupFactory->create(); $this->groupResource->load($group, $groupId); if (!$group->getId()) { - throw new NoSuchEntityException(__('Group with id "%1" does not exist.', $groupId)); + throw new NoSuchEntityException( + __('The group with the "%1" ID doesn\'t exist. Verify the ID and try again.', $groupId) + ); } return $group; } @@ -90,7 +93,7 @@ public function delete(\Magento\Eav\Api\Data\AttributeGroupInterface $group) /** @var \Magento\Catalog\Model\Product\Attribute\Group $group */ if ($group->hasSystemAttributes()) { throw new StateException( - __('Attribute group that contains system attributes can not be deleted') + __("The attribute group can't be deleted because it contains system attributes.") ); } return $this->groupRepository->delete($group); diff --git a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php index b635d854c9e..bc212adae2c 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php +++ b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php @@ -42,7 +42,7 @@ public function __construct(ConverterPool $converterPool, array $providers = []) public function getCollection(\Magento\Catalog\Model\Product $product, $type) { if (!isset($this->providers[$type])) { - throw new NoSuchEntityException(__('Collection provider is not registered')); + throw new NoSuchEntityException(__("The collection provider isn't registered.")); } $products = $this->providers[$type]->getLinkedProducts($product); diff --git a/app/code/Magento/Catalog/Model/ProductLink/Management.php b/app/code/Magento/Catalog/Model/ProductLink/Management.php index 18950b74eca..066549274b0 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Management.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Management.php @@ -45,7 +45,9 @@ public function getLinkedItemsByType($sku, $type) $linkTypes = $this->linkTypeProvider->getLinkTypes(); if (!isset($linkTypes[$type])) { - throw new NoSuchEntityException(__('Unknown link type: %1', (string)$type)); + throw new NoSuchEntityException( + __('The "%1" link type is unknown. Verify the type and try again.', (string)$type) + ); } $product = $this->productRepository->get($sku); $links = $product->getProductLinks(); @@ -76,7 +78,7 @@ public function setProductLinks($sku, array $items) } if (!isset($linkTypes[$type])) { throw new NoSuchEntityException( - __('Provided link type "%1" does not exist', $type) + __('The "%1" link type wasn\'t found. Verify the type and try again.', $type) ); } } @@ -101,7 +103,7 @@ public function setProductLinks($sku, array $items) try { $this->productRepository->save($product); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Invalid data provided for linked products')); + throw new CouldNotSaveException(__('The linked products data is invalid. Verify the data and try again.')); } return true; diff --git a/app/code/Magento/Catalog/Model/ProductLink/Repository.php b/app/code/Magento/Catalog/Model/ProductLink/Repository.php index f8dee9216dd..5bac99dbebb 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Repository.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Repository.php @@ -137,7 +137,7 @@ public function save(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) $linkTypesToId[$entity->getLinkType()] ); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Invalid data provided for linked products')); + throw new CouldNotSaveException(__('The linked products data is invalid. Verify the data and try again.')); } return true; } @@ -212,7 +212,7 @@ public function delete(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) try { $this->getLinkResource()->deleteProductLink($linkId); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Invalid data provided for linked products')); + throw new CouldNotSaveException(__('The linked products data is invalid. Verify the data and try again.')); } return true; } diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index e814dc03cf3..e3329832e13 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model; use Magento\Catalog\Api\Data\ProductInterface; @@ -237,7 +238,9 @@ public function get($sku, $editMode = false, $storeId = null, $forceReload = fal $productId = $this->resourceModel->getIdBySku($sku); if (!$productId) { - throw new NoSuchEntityException(__('Requested product doesn\'t exist')); + throw new NoSuchEntityException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ); } if ($editMode) { $product->setData('_edit_mode', true); @@ -270,7 +273,9 @@ public function getById($productId, $editMode = false, $storeId = null, $forceRe } $product->load($productId); if (!$product->getId()) { - throw new NoSuchEntityException(__('Requested product doesn\'t exist')); + throw new NoSuchEntityException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ); } $this->cacheProduct($cacheKey, $product); } @@ -393,7 +398,9 @@ protected function processNewMediaGalleryEntry( $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath); if (!$product->hasGalleryAttribute()) { - throw new StateException(__('Requested product does not support images.')); + throw new StateException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ); } $imageFileUri = $this->getMediaGalleryProcessor()->addImage( @@ -460,7 +467,7 @@ private function processLinks(\Magento\Catalog\Api\Data\ProductInterface $produc $linkedSku = $link->getLinkedProductSku(); if (!isset($linkedProductIds[$linkedSku])) { throw new NoSuchEntityException( - __('Product with SKU "%1" does not exist', $linkedSku) + __('The Product with the "%1" SKU doesn\'t exist.', $linkedSku) ); } $linkDataArray['product_id'] = $linkedProductIds[$linkedSku]; @@ -532,7 +539,7 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE foreach ($newEntries as $newEntry) { if (!isset($newEntry['content'])) { - throw new InputException(__('The image content is not valid.')); + throw new InputException(__('The image content is invalid. Verify the content and try again.')); } /** @var ImageContentInterface $contentDataObject */ $contentDataObject = $this->contentFactory->create() @@ -638,7 +645,10 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO } catch (LocalizedException $e) { throw $e; } catch (\Exception $e) { - throw new \Magento\Framework\Exception\CouldNotSaveException(__('Unable to save product'), $e); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('The product was unable to be saved. Please try again.'), + $e + ); } unset($this->instances[$product->getSku()]); unset($this->instancesById[$product->getId()]); @@ -660,7 +670,7 @@ public function delete(\Magento\Catalog\Api\Data\ProductInterface $product) throw new CouldNotSaveException(__($e->getMessage())); } catch (\Exception $e) { throw new \Magento\Framework\Exception\StateException( - __('Unable to remove product %1', $sku) + __('The "%1" product couldn\'t be removed.', $sku) ); } unset($this->instances[$sku]); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index 3cf342cbbd9..2e5b4ca538f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -180,7 +180,10 @@ protected function _prepareLoadSelect(array $selects) protected function _saveAttributeValue($object, $attribute, $value) { $connection = $this->getConnection(); - $storeId = (int) $this->_storeManager->getStore($object->getStoreId())->getId(); + $hasSingleStore = $this->_storeManager->hasSingleStore(); + $storeId = $hasSingleStore + ? $this->getDefaultStoreId() + : (int) $this->_storeManager->getStore($object->getStoreId())->getId(); $table = $attribute->getBackend()->getTable(); /** @@ -189,15 +192,18 @@ protected function _saveAttributeValue($object, $attribute, $value) * In this case we clear all not default values */ $entityIdField = $this->getLinkField(); - if ($this->_storeManager->hasSingleStore()) { - $storeId = $this->getDefaultStoreId(); + $conditions = [ + 'attribute_id = ?' => $attribute->getAttributeId(), + "{$entityIdField} = ?" => $object->getData($entityIdField), + 'store_id <> ?' => $storeId + ]; + if ($hasSingleStore + && !$object->isObjectNew() + && $this->isAttributePresentForNonDefaultStore($attribute, $conditions) + ) { $connection->delete( $table, - [ - 'attribute_id = ?' => $attribute->getAttributeId(), - "{$entityIdField} = ?" => $object->getData($entityIdField), - 'store_id <> ?' => $storeId - ] + $conditions ); } @@ -236,6 +242,27 @@ protected function _saveAttributeValue($object, $attribute, $value) return $this; } + /** + * Check if attribute present for non default Store View. + * Prevent "delete" query locking in a case when nothing to delete + * + * @param AbstractAttribute $attribute + * @param array $conditions + * + * @return boolean + */ + private function isAttributePresentForNonDefaultStore($attribute, $conditions) + { + $connection = $this->getConnection(); + $select = $connection->select()->from($attribute->getBackend()->getTable()); + foreach ($conditions as $condition => $conditionValue) { + $select->where($condition, $conditionValue); + } + $select->limit(1); + + return !empty($connection->fetchRow($select)); + } + /** * Insert entity attribute value * diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index 7879c629970..4f232ba4ac8 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -21,7 +21,6 @@ * @method int setSearchWeight(int $value) * @method bool getIsUsedForPriceRules() * @method int setIsUsedForPriceRules(int $value) - * @method \Magento\Eav\Api\Data\AttributeExtensionInterface getExtensionAttributes() * * @author Magento Core Team * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -80,6 +79,11 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements */ protected $_indexerEavProcessor; + /** + * @var \Magento\Eav\Api\Data\AttributeExtensionFactory + */ + private $eavAttributeFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -101,9 +105,10 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements * @param \Magento\Catalog\Model\Indexer\Product\Eav\Processor $indexerEavProcessor * @param \Magento\Catalog\Helper\Product\Flat\Indexer $productFlatIndexerHelper * @param LockValidatorInterface $lockValidator - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data + * @param \Magento\Eav\Api\Data\AttributeExtensionFactory|null $eavAttributeFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -129,12 +134,15 @@ public function __construct( LockValidatorInterface $lockValidator, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Eav\Api\Data\AttributeExtensionFactory $eavAttributeFactory = null ) { $this->_indexerEavProcessor = $indexerEavProcessor; $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->_productFlatIndexerHelper = $productFlatIndexerHelper; $this->attrLockValidator = $lockValidator; + $this->eavAttributeFactory = $eavAttributeFactory ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Eav\Api\Data\AttributeExtensionFactory::class); parent::__construct( $context, $registry, @@ -887,4 +895,18 @@ public function setIsFilterableInGrid($isFilterableInGrid) $this->setData(self::IS_FILTERABLE_IN_GRID, $isFilterableInGrid); return $this; } + + /** + * @return \Magento\Eav\Api\Data\AttributeExtensionInterface + */ + public function getExtensionAttributes() + { + $extensionAttributes = $this->_getExtensionAttributes(); + if (null === $extensionAttributes) { + /** @var \Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes */ + $extensionAttributes = $this->eavAttributeFactory->create(); + $this->setExtensionAttributes($extensionAttributes); + } + return $extensionAttributes; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php index 6e2642d0991..b54c19a1115 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php @@ -83,11 +83,12 @@ public function saveCategoryLinks(ProductInterface $product, array $categoryLink $insertUpdate = $this->processCategoryLinks($categoryLinks, $oldCategoryLinks); $deleteUpdate = $this->processCategoryLinks($oldCategoryLinks, $categoryLinks); - list($delete, $insert) = $this->analyseUpdatedLinks($deleteUpdate, $insertUpdate); + list($delete, $insert, $update) = $this->analyseUpdatedLinks($deleteUpdate, $insertUpdate); return array_merge( - $this->updateCategoryLinks($product, $insert), - $this->deleteCategoryLinks($product, $delete) + $this->deleteCategoryLinks($product, $delete), + $this->updateCategoryLinks($product, $insert, true), + $this->updateCategoryLinks($product, $update) ); } @@ -133,16 +134,15 @@ private function processCategoryLinks($newCategoryPositions, &$oldCategoryPositi /** * @param ProductInterface $product * @param array $insertLinks + * @param bool $insert * @return array */ - private function updateCategoryLinks(ProductInterface $product, array $insertLinks) + private function updateCategoryLinks(ProductInterface $product, array $insertLinks, $insert = false) { if (empty($insertLinks)) { return []; } - $connection = $this->resourceConnection->getConnection(); - $data = []; foreach ($insertLinks as $categoryLink) { $data[] = [ @@ -153,11 +153,22 @@ private function updateCategoryLinks(ProductInterface $product, array $insertLin } if ($data) { - $connection->insertOnDuplicate( - $this->getCategoryLinkMetadata()->getEntityTable(), - $data, - ['position'] - ); + $connection = $this->resourceConnection->getConnection(); + if ($insert) { + $connection->insertArray( + $this->getCategoryLinkMetadata()->getEntityTable(), + array_keys($data[0]), + $data, + \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE + ); + } else { + // for mass update category links with constraint by unique key use insert on duplicate statement + $connection->insertOnDuplicate( + $this->getCategoryLinkMetadata()->getEntityTable(), + $data, + ['position'] + ); + } } return array_column($insertLinks, 'category_id'); @@ -215,7 +226,7 @@ private function verifyCategoryLinks(array $links) } /** - * Analyse category links for update or/and delete + * Analyse category links for update or/and delete. Return array of links for delete, insert and update * * @param array $deleteUpdate * @param array $insertUpdate @@ -226,8 +237,7 @@ private function analyseUpdatedLinks($deleteUpdate, $insertUpdate) $delete = $deleteUpdate['changed'] ?: []; $insert = $insertUpdate['changed'] ?: []; $insert = array_merge_recursive($insert, $deleteUpdate['updated']); - $insert = array_merge_recursive($insert, $insertUpdate['updated']); - return [$delete, $insert]; + return [$delete, $insert, $insertUpdate['updated']]; } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php index 4b420329eef..ef3f83e5ffa 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\DB\Adapter\AdapterInterface; + /** * Catalog product link resource model * @@ -136,7 +138,6 @@ public function saveProductLinks($parentId, $data, $typeId) $data = []; } - $attributes = $this->getAttributesByType($typeId); $connection = $this->getConnection(); $bind = [':product_id' => (int)$parentId, ':link_type_id' => (int)$typeId]; @@ -151,42 +152,38 @@ public function saveProductLinks($parentId, $data, $typeId) $links = $connection->fetchPairs($select, $bind); - foreach ($data as $linkedProductId => $linkInfo) { - $linkId = null; - if (isset($links[$linkedProductId])) { - $linkId = $links[$linkedProductId]; - unset($links[$linkedProductId]); - } else { - $bind = [ - 'product_id' => $parentId, - 'linked_product_id' => $linkedProductId, - 'link_type_id' => $typeId, - ]; - $connection->insert($this->getMainTable(), $bind); - $linkId = $connection->lastInsertId($this->getMainTable()); - } + list($insertData, $updateData, $deleteConditions) = $this->prepareProductLinksData( + $parentId, + $data, + $typeId, + $links + ); - foreach ($attributes as $attributeInfo) { - $attributeTable = $this->getAttributeTypeTable($attributeInfo['type']); - if ($attributeTable) { - if (isset($linkInfo[$attributeInfo['code']])) { - $value = $this->_prepareAttributeValue( - $attributeInfo['type'], - $linkInfo[$attributeInfo['code']] - ); - $bind = [ - 'product_link_attribute_id' => $attributeInfo['id'], - 'link_id' => $linkId, - 'value' => $value, - ]; - $connection->insertOnDuplicate($attributeTable, $bind, ['value']); - } else { - $connection->delete( - $attributeTable, - ['link_id = ?' => $linkId, 'product_link_attribute_id = ?' => $attributeInfo['id']] - ); - } - } + if ($insertData) { + $insertColumns = [ + 'product_link_attribute_id', + 'link_id', + 'value', + ]; + foreach ($insertData as $table => $values) { + $connection->insertArray($table, $insertColumns, $values, AdapterInterface::INSERT_IGNORE); + } + } + if ($updateData) { + // for mass update product links with constraint by unique key use insert on duplicate statement + foreach ($updateData as $table => $values) { + $connection->insertOnDuplicate($table, $values, ['value']); + } + } + if ($deleteConditions) { + foreach ($deleteConditions as $table => $deleteCondition) { + $connection->delete( + $table, + [ + 'link_id = ?' => $deleteCondition['link_id'], + 'product_link_attribute_id = ?' => $deleteCondition['product_link_attribute_id'] + ] + ); } } @@ -302,4 +299,69 @@ public function getParentIdsByChild($childId, $typeId) return $parentIds; } + + /** + * Prepare data for insert, update or delete product link attributes + * + * @param int $parentId + * @param array $data + * @param int $typeId + * @param array $links + * @return array + */ + private function prepareProductLinksData($parentId, $data, $typeId, $links) + { + $connection = $this->getConnection(); + $attributes = $this->getAttributesByType($typeId); + + $insertData = []; + $updateData = []; + $deleteConditions = []; + + foreach ($data as $linkedProductId => $linkInfo) { + $linkId = null; + if (isset($links[$linkedProductId])) { + $linkId = $links[$linkedProductId]; + } else { + $bind = [ + 'product_id' => $parentId, + 'linked_product_id' => $linkedProductId, + 'link_type_id' => $typeId, + ]; + $connection->insert($this->getMainTable(), $bind); + $linkId = $connection->lastInsertId($this->getMainTable()); + } + + foreach ($attributes as $attributeInfo) { + $attributeTable = $this->getAttributeTypeTable($attributeInfo['type']); + if (!$attributeTable) { + continue; + } + if (isset($linkInfo[$attributeInfo['code']])) { + $value = $this->_prepareAttributeValue( + $attributeInfo['type'], + $linkInfo[$attributeInfo['code']] + ); + if (isset($links[$linkedProductId])) { + $updateData[$attributeTable][] = [ + 'product_link_attribute_id' => $attributeInfo['id'], + 'link_id' => $linkId, + 'value' => $value, + ]; + } else { + $insertData[$attributeTable][] = [ + 'product_link_attribute_id' => $attributeInfo['id'], + 'link_id' => $linkId, + 'value' => $value, + ]; + } + } else { + $deleteConditions[$attributeTable]['link_id'][] = $linkId; + $deleteConditions[$attributeTable]['product_link_attribute_id'][] = $attributeInfo['id']; + } + } + } + + return [$insertData, $updateData, $deleteConditions]; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/DeleteHandler.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/DeleteHandler.php index 1204c66b956..024c87c9fc8 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/DeleteHandler.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/DeleteHandler.php @@ -93,7 +93,9 @@ public function execute($entityType, $entity) try { $this->linkResource->deleteProductLink($linkId); } catch (\Exception $exception) { - throw new CouldNotDeleteException(__('Invalid data provided for linked products')); + throw new CouldNotDeleteException( + __('The linked products data is invalid. Verify the data and try again.') + ); } } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php index d1372feb777..c40872ed64b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/SaveHandler.php @@ -105,7 +105,7 @@ public function execute($entityType, $entity) $linkTypesToId[$entity->getLinkType()] ); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Invalid data provided for linked products')); + throw new CouldNotSaveException(__('The linked products data is invalid. Verify the data and try again.')); } return $entity; } diff --git a/app/code/Magento/Catalog/Observer/UnsetSpecialPrice.php b/app/code/Magento/Catalog/Observer/UnsetSpecialPrice.php deleted file mode 100644 index 0ba1251edc7..00000000000 --- a/app/code/Magento/Catalog/Observer/UnsetSpecialPrice.php +++ /dev/null @@ -1,31 +0,0 @@ -getEvent()->getProduct(); - if ($product->getSpecialPrice() === null) { - $product->setData('special_price', ''); - } - - return $this; - } -} diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php new file mode 100644 index 00000000000..4dae4ec68ef --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php @@ -0,0 +1,95 @@ +metadataPool = $metadataPool; + $this->config = $config; + } + + /** + * @param ReadSnapshot $subject + * @param array $entityData + * @param string $entityType + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute(ReadSnapshot $subject, array $entityData, $entityType) + { + if (!in_array($entityType, [ProductInterface::class, CategoryInterface::class], true)) { + return $entityData; + } + + $metadata = $this->metadataPool->getMetadata($entityType); + $connection = $metadata->getEntityConnection(); + $globalAttributes = []; + $attributesMap = []; + $eavEntityType = $metadata->getEavEntityType(); + $attributes = (null === $eavEntityType) ? [] : $this->config->getEntityAttributes($eavEntityType); + + /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ + foreach ($attributes as $attribute) { + if (!$attribute->isStatic() && $attribute->isScopeGlobal()) { + $globalAttributes[$attribute->getBackend()->getTable()][] = $attribute->getAttributeId(); + $attributesMap[$attribute->getAttributeId()] = $attribute->getAttributeCode(); + } + } + + if ($globalAttributes) { + $selects = []; + foreach ($globalAttributes as $table => $attributeIds) { + $select = $connection->select() + ->from( + ['t' => $table], + ['value' => 't.value', 'attribute_id' => 't.attribute_id'] + ) + ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]) + ->where('attribute_id' . ' in (?)', $attributeIds) + ->where('store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + $selects[] = $select; + } + $unionSelect = new \Magento\Framework\DB\Sql\UnionExpression( + $selects, + \Magento\Framework\DB\Select::SQL_UNION_ALL + ); + foreach ($connection->fetchAll($unionSelect) as $attributeValue) { + $entityData[$attributesMap[$attributeValue['attribute_id']]] = $attributeValue['value']; + } + } + + return $entityData; + } +} diff --git a/app/code/Magento/Catalog/Setup/CategorySetup.php b/app/code/Magento/Catalog/Setup/CategorySetup.php index f85407a9a9d..27138793282 100644 --- a/app/code/Magento/Catalog/Setup/CategorySetup.php +++ b/app/code/Magento/Catalog/Setup/CategorySetup.php @@ -7,13 +7,51 @@ */ namespace Magento\Catalog\Setup; +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; +use Magento\Catalog\Model\Attribute\Backend\Startdate; +use Magento\Catalog\Model\Category\Attribute\Backend\Image; +use Magento\Catalog\Model\Category\Attribute\Backend\Sortby as SortbyBackendModel; +use Magento\Catalog\Model\Category\Attribute\Source\Layout; +use Magento\Catalog\Model\Category\Attribute\Source\Mode; +use Magento\Catalog\Model\Category\Attribute\Source\Page; +use Magento\Catalog\Model\Category\Attribute\Source\Sortby; use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Entity\Product\Attribute\Design\Options\Container; +use Magento\Catalog\Model\Product\Attribute\Backend\Category as CategoryBackendAttribute; +use Magento\Catalog\Model\Product\Attribute\Backend\Price; +use Magento\Catalog\Model\Product\Attribute\Backend\Sku; +use Magento\Catalog\Model\Product\Attribute\Backend\Stock; +use Magento\Catalog\Model\Product\Attribute\Backend\Tierprice; +use Magento\Catalog\Model\Product\Attribute\Backend\Weight; +use Magento\Catalog\Model\Product\Attribute\Frontend\Image as ImageFrontendModel; +use Magento\Catalog\Model\Product\Attribute\Source\Countryofmanufacture; +use Magento\Catalog\Model\Product\Attribute\Source\Layout as LayoutModel; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Category; +use Magento\Catalog\Model\ResourceModel\Category\Attribute\Collection; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\CatalogInventory\Block\Adminhtml\Form\Field\Stock as StockField; +use Magento\CatalogInventory\Model\Source\Stock as StockSourceModel; +use Magento\CatalogInventory\Model\Stock as StockModel; +use Magento\Eav\Model\Entity\Attribute\Backend\Datetime; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Eav\Model\Entity\Attribute\Source\Boolean; use Magento\Eav\Model\Entity\Setup\Context; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; use Magento\Eav\Setup\EavSetup; use Magento\Framework\App\CacheInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Catalog\Model\Product\Type; +use Magento\Theme\Model\Theme\Source\Theme; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -80,28 +118,28 @@ public function getDefaultEntities() return [ 'catalog_category' => [ 'entity_type_id' => self::CATEGORY_ENTITY_TYPE_ID, - 'entity_model' => \Magento\Catalog\Model\ResourceModel\Category::class, - 'attribute_model' => \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, + 'entity_model' => Category::class, + 'attribute_model' => Attribute::class, 'table' => 'catalog_category_entity', 'additional_attribute_table' => 'catalog_eav_attribute', 'entity_attribute_collection' => - \Magento\Catalog\Model\ResourceModel\Category\Attribute\Collection::class, + Collection::class, 'attributes' => [ 'name' => [ 'type' => 'varchar', 'label' => 'Name', 'input' => 'text', 'sort_order' => 1, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'is_active' => [ 'type' => 'int', 'label' => 'Is Active', 'input' => 'select', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + 'source' => Boolean::class, 'sort_order' => 2, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'description' => [ @@ -110,7 +148,7 @@ public function getDefaultEntities() 'input' => 'textarea', 'required' => false, 'sort_order' => 4, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'wysiwyg_enabled' => true, 'is_html_allowed_on_front' => true, 'group' => 'General Information', @@ -119,10 +157,10 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Image', 'input' => 'image', - 'backend' => \Magento\Catalog\Model\Category\Attribute\Backend\Image::class, + 'backend' => Image::class, 'required' => false, 'sort_order' => 5, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'meta_title' => [ @@ -131,7 +169,7 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 6, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'meta_keywords' => [ @@ -140,7 +178,7 @@ public function getDefaultEntities() 'input' => 'textarea', 'required' => false, 'sort_order' => 7, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'meta_description' => [ @@ -149,34 +187,34 @@ public function getDefaultEntities() 'input' => 'textarea', 'required' => false, 'sort_order' => 8, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'display_mode' => [ 'type' => 'varchar', 'label' => 'Display Mode', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Mode::class, + 'source' => Mode::class, 'required' => false, 'sort_order' => 10, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Display Settings', ], 'landing_page' => [ 'type' => 'int', 'label' => 'CMS Block', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Page::class, + 'source' => Page::class, 'required' => false, 'sort_order' => 20, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Display Settings', ], 'is_anchor' => [ 'type' => 'int', 'label' => 'Is Anchor', 'input' => 'select', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + 'source' => Boolean::class, 'required' => false, 'sort_order' => 30, 'group' => 'Display Settings', @@ -222,50 +260,50 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Custom Design', 'input' => 'select', - 'source' => \Magento\Theme\Model\Theme\Source\Theme::class, + 'source' => Theme::class, 'required' => false, 'sort_order' => 10, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'custom_design_from' => [ 'type' => 'datetime', 'label' => 'Active From', 'input' => 'date', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Startdate::class, + 'backend' => Startdate::class, 'required' => false, 'sort_order' => 30, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'custom_design_to' => [ 'type' => 'datetime', 'label' => 'Active To', 'input' => 'date', - 'backend' => \Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class, + 'backend' => Datetime::class, 'required' => false, 'sort_order' => 40, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'page_layout' => [ 'type' => 'varchar', 'label' => 'Page Layout', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Layout::class, + 'source' => Layout::class, 'required' => false, 'sort_order' => 50, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'custom_layout_update' => [ 'type' => 'text', 'label' => 'Custom Layout Update', 'input' => 'textarea', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate::class, + 'backend' => Customlayoutupdate::class, 'required' => false, 'sort_order' => 60, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'level' => [ @@ -288,53 +326,53 @@ public function getDefaultEntities() 'type' => 'text', 'label' => 'Available Product Listing Sort By', 'input' => 'multiselect', - 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Sortby::class, - 'backend' => \Magento\Catalog\Model\Category\Attribute\Backend\Sortby::class, + 'source' => Sortby::class, + 'backend' => SortbyBackendModel::class, 'sort_order' => 40, - 'input_renderer' => \Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby\Available::class, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'input_renderer' => Available::class, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Display Settings', ], 'default_sort_by' => [ 'type' => 'varchar', 'label' => 'Default Product Listing Sort By', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Sortby::class, - 'backend' => \Magento\Catalog\Model\Category\Attribute\Backend\Sortby::class, + 'source' => Sortby::class, + 'backend' => SortbyBackendModel::class, 'sort_order' => 50, 'input_renderer' => - \Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby\DefaultSortby::class, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + DefaultSortby::class, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Display Settings', ], 'include_in_menu' => [ 'type' => 'int', 'label' => 'Include in Navigation Menu', 'input' => 'select', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + 'source' => Boolean::class, 'default' => '1', 'sort_order' => 10, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General Information', ], 'custom_use_parent_settings' => [ 'type' => 'int', 'label' => 'Use Parent Category Settings', 'input' => 'select', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + 'source' => Boolean::class, 'required' => false, 'sort_order' => 5, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'custom_apply_to_products' => [ 'type' => 'int', 'label' => 'Apply To Products', 'input' => 'select', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + 'source' => Boolean::class, 'required' => false, 'sort_order' => 6, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', ], 'filter_price_range' => [ @@ -343,20 +381,20 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 51, - 'input_renderer' => \Magento\Catalog\Block\Adminhtml\Category\Helper\Pricestep::class, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'input_renderer' => Pricestep::class, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Display Settings', ], ], ], 'catalog_product' => [ 'entity_type_id' => self::CATALOG_PRODUCT_ENTITY_TYPE_ID, - 'entity_model' => \Magento\Catalog\Model\ResourceModel\Product::class, - 'attribute_model' => \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, + 'entity_model' => Product::class, + 'attribute_model' => Attribute::class, 'table' => 'catalog_product_entity', 'additional_attribute_table' => 'catalog_eav_attribute', 'entity_attribute_collection' => - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class, + Product\Attribute\Collection::class, 'attributes' => [ 'name' => [ 'type' => 'varchar', @@ -364,7 +402,7 @@ public function getDefaultEntities() 'input' => 'text', 'frontend_class' => 'validate-length maximum-length-255', 'sort_order' => 1, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'searchable' => true, 'visible_in_advanced_search' => true, 'used_in_product_listing' => true, @@ -375,7 +413,7 @@ public function getDefaultEntities() 'label' => 'SKU', 'input' => 'text', 'frontend_class' => 'validate-length maximum-length-64', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Sku::class, + 'backend' => Sku::class, 'unique' => true, 'sort_order' => 2, 'searchable' => true, @@ -387,7 +425,7 @@ public function getDefaultEntities() 'label' => 'Description', 'input' => 'textarea', 'sort_order' => 3, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'searchable' => true, 'comparable' => true, 'wysiwyg_enabled' => true, @@ -399,7 +437,7 @@ public function getDefaultEntities() 'label' => 'Short Description', 'input' => 'textarea', 'sort_order' => 4, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'searchable' => true, 'comparable' => true, 'wysiwyg_enabled' => true, @@ -414,9 +452,9 @@ public function getDefaultEntities() 'type' => 'decimal', 'label' => 'Price', 'input' => 'price', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Price::class, + 'backend' => Price::class, 'sort_order' => 1, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'searchable' => true, 'filterable' => true, 'visible_in_advanced_search' => true, @@ -429,10 +467,10 @@ public function getDefaultEntities() 'type' => 'decimal', 'label' => 'Special Price', 'input' => 'price', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Price::class, + 'backend' => Price::class, 'required' => false, 'sort_order' => 3, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'used_in_product_listing' => true, 'apply_to' => 'simple,virtual', 'group' => 'Prices', @@ -444,10 +482,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Special Price From Date', 'input' => 'date', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Startdate::class, + 'backend' => Startdate::class, 'required' => false, 'sort_order' => 4, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'used_in_product_listing' => true, 'apply_to' => 'simple,virtual', 'group' => 'Prices', @@ -459,10 +497,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Special Price To Date', 'input' => 'date', - 'backend' => \Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class, + 'backend' => Datetime::class, 'required' => false, 'sort_order' => 5, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'used_in_product_listing' => true, 'apply_to' => 'simple,virtual', 'group' => 'Prices', @@ -474,11 +512,11 @@ public function getDefaultEntities() 'type' => 'decimal', 'label' => 'Cost', 'input' => 'price', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Price::class, + 'backend' => Price::class, 'required' => false, 'user_defined' => true, 'sort_order' => 6, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'apply_to' => 'simple,virtual', 'group' => 'Prices', 'is_used_in_grid' => true, @@ -489,8 +527,8 @@ public function getDefaultEntities() 'type' => 'decimal', 'label' => 'Weight', 'input' => 'weight', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Weight::class, - 'input_renderer' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Weight::class, + 'backend' => Weight::class, + 'input_renderer' => WeightFormHelper::class, 'sort_order' => 5, 'apply_to' => 'simple,virtual', 'is_used_in_grid' => true, @@ -518,7 +556,7 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 20, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Meta Information', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -530,7 +568,7 @@ public function getDefaultEntities() 'input' => 'textarea', 'required' => false, 'sort_order' => 30, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Meta Information', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -544,7 +582,7 @@ public function getDefaultEntities() 'note' => 'Maximum 255 chars', 'class' => 'validate-length maximum-length-255', 'sort_order' => 40, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Meta Information', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -554,11 +592,11 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Base Image', 'input' => 'media_image', - 'frontend' => \Magento\Catalog\Model\Product\Attribute\Frontend\Image::class, - 'input_renderer' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\BaseImage::class, + 'frontend' => ImageFrontendModel::class, + 'input_renderer' => BaseImage::class, 'required' => false, 'sort_order' => 0, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'used_in_product_listing' => true, 'group' => 'General', ], @@ -566,10 +604,10 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Small Image', 'input' => 'media_image', - 'frontend' => \Magento\Catalog\Model\Product\Attribute\Frontend\Image::class, + 'frontend' => ImageFrontendModel::class, 'required' => false, 'sort_order' => 2, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'used_in_product_listing' => true, 'group' => 'Images', ], @@ -577,10 +615,10 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Thumbnail', 'input' => 'media_image', - 'frontend' => \Magento\Catalog\Model\Product\Attribute\Frontend\Image::class, + 'frontend' => ImageFrontendModel::class, 'required' => false, 'sort_order' => 3, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'used_in_product_listing' => true, 'group' => 'Images', ], @@ -588,7 +626,7 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Media Gallery', 'input' => 'gallery', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Media::class, + 'backend' => Media::class, 'required' => false, 'sort_order' => 4, 'group' => 'Images', @@ -598,10 +636,10 @@ public function getDefaultEntities() 'type' => 'decimal', 'label' => 'Tier Price', 'input' => 'text', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice::class, + 'backend' => Tierprice::class, 'required' => false, 'sort_order' => 7, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'apply_to' => 'simple,virtual', 'group' => 'Prices', ], @@ -624,10 +662,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Set Product as New from Date', 'input' => 'date', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Startdate::class, + 'backend' => Startdate::class, 'required' => false, 'sort_order' => 7, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'used_in_product_listing' => true, 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -637,10 +675,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Set Product as New to Date', 'input' => 'date', - 'backend' => \Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class, + 'backend' => Datetime::class, 'required' => false, 'sort_order' => 8, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'used_in_product_listing' => true, 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -658,9 +696,9 @@ public function getDefaultEntities() 'type' => 'int', 'label' => 'Status', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Product\Attribute\Source\Status::class, + 'source' => Status::class, 'sort_order' => 9, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'searchable' => true, 'used_in_product_listing' => true, ], @@ -670,7 +708,7 @@ public function getDefaultEntities() 'input' => 'price', 'required' => false, 'sort_order' => 8, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'visible' => false, 'apply_to' => 'simple,virtual', 'group' => 'Prices', @@ -679,19 +717,19 @@ public function getDefaultEntities() 'type' => 'int', 'label' => 'Visibility', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Product\Visibility::class, + 'source' => Visibility::class, 'default' => '4', 'sort_order' => 12, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, ], 'custom_design' => [ 'type' => 'varchar', 'label' => 'Custom Design', 'input' => 'select', - 'source' => \Magento\Theme\Model\Theme\Source\Theme::class, + 'source' => Theme::class, 'required' => false, 'sort_order' => 1, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -701,10 +739,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Active From', 'input' => 'date', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Startdate::class, + 'backend' => Startdate::class, 'required' => false, 'sort_order' => 2, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -714,10 +752,10 @@ public function getDefaultEntities() 'type' => 'datetime', 'label' => 'Active To', 'input' => 'date', - 'backend' => \Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class, + 'backend' => Datetime::class, 'required' => false, 'sort_order' => 3, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -727,20 +765,20 @@ public function getDefaultEntities() 'type' => 'text', 'label' => 'Custom Layout Update', 'input' => 'textarea', - 'backend' => \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate::class, + 'backend' => Customlayoutupdate::class, 'required' => false, 'sort_order' => 4, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', ], 'page_layout' => [ 'type' => 'varchar', 'label' => 'Page Layout', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Product\Attribute\Source\Layout::class, + 'source' => LayoutModel::class, 'required' => false, 'sort_order' => 5, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', 'is_used_in_grid' => true, 'is_visible_in_grid' => false, @@ -749,9 +787,9 @@ public function getDefaultEntities() 'category_ids' => [ 'type' => 'static', 'label' => 'Categories', - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Category::class, - 'input_renderer' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Category::class, + 'global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'backend' => CategoryBackendAttribute::class, + 'input_renderer' => CategoryFormHelper::class, 'required' => false, 'sort_order' => 9, 'visible' => true, @@ -764,11 +802,11 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Display Product Options In', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Entity\Product\Attribute\Design\Options\Container::class, + 'source' => Container::class, 'required' => false, 'default' => 'container2', 'sort_order' => 6, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', ], 'required_options' => [ @@ -792,7 +830,7 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 16, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'visible' => false, 'used_in_product_listing' => true, ], @@ -802,7 +840,7 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 17, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'visible' => false, 'used_in_product_listing' => true, ], @@ -812,7 +850,7 @@ public function getDefaultEntities() 'input' => 'text', 'required' => false, 'sort_order' => 18, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'global' => ScopedAttributeInterface::SCOPE_STORE, 'visible' => false, 'used_in_product_listing' => true, ], @@ -832,9 +870,9 @@ public function getDefaultEntities() 'type' => 'varchar', 'label' => 'Country of Manufacture', 'input' => 'select', - 'source' => \Magento\Catalog\Model\Product\Attribute\Source\Countryofmanufacture::class, + 'source' => Countryofmanufacture::class, 'required' => false, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE, + 'global' => ScopedAttributeInterface::SCOPE_WEBSITE, 'visible' => true, 'user_defined' => false, 'searchable' => false, @@ -851,13 +889,13 @@ public function getDefaultEntities() 'quantity_and_stock_status' => [ 'group' => 'General', 'type' => 'int', - 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\Stock::class, + 'backend' => Stock::class, 'label' => 'Quantity', 'input' => 'select', - 'input_renderer' => \Magento\CatalogInventory\Block\Adminhtml\Form\Field\Stock::class, - 'source' => \Magento\CatalogInventory\Model\Source\Stock::class, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, - 'default' => \Magento\CatalogInventory\Model\Stock::STOCK_IN_STOCK, + 'input_renderer' => StockField::class, + 'source' => StockSourceModel::class, + 'global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'default' => StockModel::STOCK_IN_STOCK, 'user_defined' => false, 'visible' => true, 'required' => false, diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/ChangePriceAttributeDefaultScope.php b/app/code/Magento/Catalog/Setup/Patch/Data/ChangePriceAttributeDefaultScope.php new file mode 100644 index 00000000000..9698e2e049f --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/ChangePriceAttributeDefaultScope.php @@ -0,0 +1,100 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $this->changePriceAttributeDefaultScope($categorySetup); + } + + /** + * @param CategorySetup $categorySetup + * @return void + */ + private function changePriceAttributeDefaultScope($categorySetup) + { + $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); + foreach (['price', 'cost', 'special_price'] as $attributeCode) { + $attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode); + if (isset($attribute['attribute_id'])) { + $categorySetup->updateAttribute( + $entityTypeId, + $attribute['attribute_id'], + 'is_global', + \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL + ); + } + } + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateProductMetaDescription::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.1.3'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php b/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php new file mode 100644 index 00000000000..ea8f6bbf39b --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/DisallowUsingHtmlForProductName.php @@ -0,0 +1,86 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); + $attribute = $categorySetup->getAttribute($entityTypeId, 'name'); + + $this->moduleDataSetup->getConnection()->update( + $this->moduleDataSetup->getTable('catalog_eav_attribute'), + ['is_html_allowed_on_front' => 0], + $this->moduleDataSetup->getConnection()->quoteInto('attribute_id = ?', $attribute['attribute_id']) + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + ChangePriceAttributeDefaultScope::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.1.5'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/InstallData.php b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultCategories.php similarity index 82% rename from app/code/Magento/Catalog/Setup/InstallData.php rename to app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultCategories.php index 5b1a10b098e..f1d836a5862 100644 --- a/app/code/Magento/Catalog/Setup/InstallData.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/InstallDefaultCategories.php @@ -4,66 +4,68 @@ * See COPYING.txt for license details. */ -namespace Magento\Catalog\Setup; +namespace Magento\Catalog\Setup\Patch\Data; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Catalog\Helper\DefaultCategory; +use Magento\Catalog\Helper\DefaultCategoryFactory; +use Magento\Catalog\Setup\CategorySetupFactory; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * @codeCoverageIgnore + * Class InstallDefaultCategories data patch. + * + * @package Magento\Catalog\Setup\Patch * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class InstallData implements InstallDataInterface +class InstallDefaultCategories implements DataPatchInterface, PatchVersionInterface { /** - * Category setup factory - * - * @var CategorySetupFactory + * @var ModuleDataSetupInterface */ - private $categorySetupFactory; + private $moduleDataSetup; /** - * @var DefaultCategory + * @var CategorySetupFactory */ - private $defaultCategory; + private $categorySetupFactory; /** - * @deprecated 101.0.0 - * @return DefaultCategory + * @var DefaultCategoryFactory */ - private function getDefaultCategory() - { - if ($this->defaultCategory === null) { - $this->defaultCategory = \Magento\Framework\App\ObjectManager::getInstance() - ->get(DefaultCategory::class); - } - return $this->defaultCategory; - } + private $defaultCategoryFactory; /** - * Init - * + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup * @param CategorySetupFactory $categorySetupFactory + * @param DefaultCategoryFactory $defaultCategoryFactory */ - public function __construct(CategorySetupFactory $categorySetupFactory) - { + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + CategorySetupFactory $categorySetupFactory, + \Magento\Catalog\Helper\DefaultCategoryFactory $defaultCategoryFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; $this->categorySetupFactory = $categorySetupFactory; + $this->defaultCategoryFactory = $defaultCategoryFactory; } /** * {@inheritdoc} - * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); $rootCategoryId = \Magento\Catalog\Model\Category::TREE_ROOT_ID; - $defaultCategoryId = $this->getDefaultCategory()->getId(); + $defaultCategory = $this->defaultCategoryFactory->create(); + $defaultCategoryId = $defaultCategory->getId(); $categorySetup->installEntities(); // Create Root Catalog Node @@ -99,8 +101,11 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'path' => \Magento\Catalog\Helper\Category::XML_PATH_CATEGORY_ROOT_ID, 'value' => $category->getId(), ]; - $setup->getConnection() - ->insertOnDuplicate($setup->getTable('core_config_data'), $data, ['value']); + $this->moduleDataSetup->getConnection()->insertOnDuplicate( + $this->moduleDataSetup->getTable('core_config_data'), + $data, + ['value'] + ); $categorySetup->addAttributeGroup(\Magento\Catalog\Model\Product::ENTITY, 'Default', 'Design', 6); @@ -156,8 +161,12 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ]; foreach ($data as $bind) { - $setup->getConnection() - ->insertForce($setup->getTable('catalog_product_link_type'), $bind); + $this->moduleDataSetup->getConnection()->insertForce( + $this->moduleDataSetup->getTable( + 'catalog_product_link_type' + ), + $bind + ); } /** @@ -181,21 +190,25 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ], ]; - $setup->getConnection() - ->insertMultiple($setup->getTable('catalog_product_link_attribute'), $data); + $this->moduleDataSetup->getConnection()->insertMultiple( + $this->moduleDataSetup->getTable('catalog_product_link_attribute'), + $data + ); /** * Remove Catalog specified attribute options (columns) from eav/attribute table * */ - $describe = $setup->getConnection() - ->describeTable($setup->getTable('catalog_eav_attribute')); + $describe = $this->moduleDataSetup->getConnection() + ->describeTable($this->moduleDataSetup->getTable('catalog_eav_attribute')); foreach ($describe as $columnData) { if ($columnData['COLUMN_NAME'] == 'attribute_id') { continue; } - $setup->getConnection() - ->dropColumn($setup->getTable('eav_attribute'), $columnData['COLUMN_NAME']); + $this->moduleDataSetup->getConnection()->dropColumn( + $this->moduleDataSetup->getTable('eav_attribute'), + $columnData['COLUMN_NAME'] + ); } $newGeneralTabName = 'Product Details'; @@ -337,4 +350,28 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface \Magento\Eav\Model\Entity\Attribute\Frontend\Datetime::class ); } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/SetNewResourceModelsPaths.php b/app/code/Magento/Catalog/Setup/Patch/Data/SetNewResourceModelsPaths.php new file mode 100644 index 00000000000..d59347f501d --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/SetNewResourceModelsPaths.php @@ -0,0 +1,116 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + // set new resource model paths + /** @var CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Category::ENTITY, + 'entity_model', + \Magento\Catalog\Model\ResourceModel\Category::class + ); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Category::ENTITY, + 'attribute_model', + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + ); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Category::ENTITY, + 'entity_attribute_collection', + \Magento\Catalog\Model\ResourceModel\Category\Attribute\Collection::class + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Category::ENTITY, + 'custom_design_from', + 'attribute_model', + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + ); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Product::ENTITY, + 'entity_model', + \Magento\Catalog\Model\ResourceModel\Product::class + ); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Product::ENTITY, + 'attribute_model', + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + ); + $categorySetup->updateEntityType( + \Magento\Catalog\Model\Product::ENTITY, + 'entity_attribute_collection', + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + InstallDefaultCategories::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.2'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php new file mode 100644 index 00000000000..1d58de1994a --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php @@ -0,0 +1,80 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $categorySetup->updateAttribute(3, 54, 'default_value', 1); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + SetNewResourceModelsPaths::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.3'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateMediaAttributesBackendTypes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateMediaAttributesBackendTypes.php new file mode 100644 index 00000000000..43665c569c0 --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateMediaAttributesBackendTypes.php @@ -0,0 +1,93 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $mediaBackendType = 'static'; + $mediaBackendModel = null; + /** @var CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $categorySetup->updateAttribute( + 'catalog_product', + 'media_gallery', + 'backend_type', + $mediaBackendType + ); + $categorySetup->updateAttribute( + 'catalog_product', + 'media_gallery', + 'backend_model', + $mediaBackendModel + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateDefaultAttributeValue::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.4'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductAttributes.php new file mode 100644 index 00000000000..d02753d44ad --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductAttributes.php @@ -0,0 +1,266 @@ +moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function apply() + { + /** @var CategorySetup $categorySetup */ + $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + + //Product Details tab + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'status', + 'frontend_label', + 'Enable Product', + 5 + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'name', + 'frontend_label', + 'Product Name' + ); + $attributeSetId = $categorySetup->getDefaultAttributeSetId(\Magento\Catalog\Model\Product::ENTITY); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Product Details', + 'visibility', + 80 + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Product Details', + 'news_from_date', + 90 + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Product Details', + 'news_to_date', + 100 + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Product Details', + 'country_of_manufacture', + 110 + ); + + //Content tab + $categorySetup->addAttributeGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Content', + 15 + ); + $categorySetup->updateAttributeGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Content', + 'tab_group_code', + 'basic' + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Content', + 'description' + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Content', + 'short_description', + 100 + ); + + //Images tab + $groupId = (int)$categorySetup->getAttributeGroupByCode( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'image-management', + 'attribute_group_id' + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + $groupId, + 'image', + 1 + ); + $categorySetup->updateAttributeGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + $groupId, + 'attribute_group_name', + 'Images' + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'image', + 'frontend_label', + 'Base' + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'small_image', + 'frontend_label', + 'Small' + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'image', + 'frontend_input_renderer', + null + ); + + //Design tab + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'page_layout', + 'frontend_label', + 'Layout' + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'custom_layout_update', + 'frontend_label', + 'Layout Update XML', + 10 + ); + + //Schedule Design Update tab + $categorySetup->addAttributeGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Schedule Design Update', + 55 + ); + $categorySetup->updateAttributeGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Schedule Design Update', + 'tab_group_code', + 'advanced' + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Schedule Design Update', + 'custom_design_from', + 20 + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Schedule Design Update', + 'custom_design_to', + 30 + ); + $categorySetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'custom_design', + 'frontend_label', + 'New Theme', + 40 + ); + $categorySetup->addAttributeToGroup( + \Magento\Catalog\Model\Product::ENTITY, + $attributeSetId, + 'Schedule Design Update', + 'custom_design' + ); + $categorySetup->addAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'custom_layout', + [ + 'type' => 'varchar', + 'label' => 'New Layout', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Product\Attribute\Source\Layout::class, + 'required' => false, + 'sort_order' => 50, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'Schedule Design Update', + 'is_used_in_grid' => true, + 'is_visible_in_grid' => false, + 'is_filterable_in_grid' => false + ] + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateMediaAttributesBackendTypes::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.5'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductMetaDescription.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductMetaDescription.php new file mode 100644 index 00000000000..0c8f248d1d5 --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateProductMetaDescription.php @@ -0,0 +1,88 @@ +moduleDataSetup = $moduleDataSetup; + $this->eavSetupFactory = $eavSetupFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); + + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'meta_description', + [ + 'note' => 'Maximum 255 chars. Meta Description should optimally be between 150-160 characters' + ] + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateProductAttributes::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.7'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Catalog/Setup/UpgradeWebsiteAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php similarity index 79% rename from app/code/Magento/Catalog/Setup/UpgradeWebsiteAttributes.php rename to app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php index 3d300d9c849..f9d6abbc374 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeWebsiteAttributes.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWebsiteAttributes.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Catalog\Setup; +namespace Magento\Catalog\Setup\Patch\Data; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Api\Data\ProductInterface; @@ -12,14 +12,16 @@ use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; /** * Class UpgradeWebsiteAttributes - * @package Magento\Catalog\Setup + * @package Magento\Catalog\Setup\Patch * * IMPORTANT: This class const/methods can not be reused because it needs to be isolated */ -class UpgradeWebsiteAttributes +class UpgradeWebsiteAttributes implements DataPatchInterface, PatchVersionInterface { /** * ATTENTION: These constants must not be reused anywhere outside @@ -79,48 +81,55 @@ class UpgradeWebsiteAttributes */ private $linkFields = []; + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + /** * UpgradeWebsiteAttributes constructor. * @param Generator $batchQueryGenerator * @param MetadataPool $metadataPool + * @param ModuleDataSetupInterface $moduleDataSetup */ - public function __construct(Generator $batchQueryGenerator, MetadataPool $metadataPool) - { + public function __construct( + Generator $batchQueryGenerator, + MetadataPool $metadataPool, + ModuleDataSetupInterface $moduleDataSetup + ) { $this->batchQueryGenerator = $batchQueryGenerator; $this->metaDataPool = $metadataPool; + $this->moduleDataSetup = $moduleDataSetup; } /** - * @param ModuleDataSetupInterface $setup - * @return void + * {@inheritdoc} */ - public function upgrade(ModuleDataSetupInterface $setup) + public function apply() { foreach (array_keys($this->tableMetaDataClass) as $tableName) { - $this->upgradeTable($setup, $tableName); + $this->upgradeTable($tableName); } } /** - * @param ModuleDataSetupInterface $setup * @param string $tableName * @return void */ - private function upgradeTable(ModuleDataSetupInterface $setup, $tableName) + private function upgradeTable($tableName) { - foreach ($this->fetchAttributeValues($setup, $tableName) as $attributeValueItems) { - $this->processAttributeValues($setup, $attributeValueItems, $tableName); + foreach ($this->fetchAttributeValues($tableName) as $attributeValueItems) { + $this->processAttributeValues($attributeValueItems, $tableName); } } /** * Aligns website attribute values - * @param ModuleDataSetupInterface $setup * @param array $attributeValueItems * @param string $tableName * @return void */ - private function processAttributeValues(ModuleDataSetupInterface $setup, array $attributeValueItems, $tableName) + private function processAttributeValues(array $attributeValueItems, $tableName) { $this->resetProcessedAttributeValues(); @@ -129,9 +138,9 @@ private function processAttributeValues(ModuleDataSetupInterface $setup, array $ continue; } - $insertions = $this->generateAttributeValueInsertions($setup, $attributeValueItem, $tableName); + $insertions = $this->generateAttributeValueInsertions($attributeValueItem, $tableName); if (!empty($insertions)) { - $this->executeInsertions($setup, $insertions, $tableName); + $this->executeInsertions($insertions, $tableName); } $this->markAttributeValueProcessed($attributeValueItem, $tableName); @@ -141,32 +150,31 @@ private function processAttributeValues(ModuleDataSetupInterface $setup, array $ /** * Yields batch of AttributeValues * - * @param ModuleDataSetupInterface $setup * @param string $tableName * @yield array - * @return void + * @return \Generator */ - private function fetchAttributeValues(ModuleDataSetupInterface $setup, $tableName) + private function fetchAttributeValues($tableName) { - $connection = $setup->getConnection(); + $connection = $this->moduleDataSetup->getConnection(); $batchSelectIterator = $this->batchQueryGenerator->generate( 'value_id', $connection ->select() ->from( - ['cpei' => $setup->getTable($tableName)], + ['cpei' => $this->moduleDataSetup->getTable($tableName)], '*' ) ->join( [ - 'cea' => $setup->getTable('catalog_eav_attribute'), + 'cea' => $this->moduleDataSetup->getTable('catalog_eav_attribute'), ], 'cpei.attribute_id = cea.attribute_id', '' ) ->join( [ - 'st' => $setup->getTable('store'), + 'st' => $this->moduleDataSetup->getTable('store'), ], 'st.store_id = cpei.store_id', 'st.website_id' @@ -187,20 +195,19 @@ private function fetchAttributeValues(ModuleDataSetupInterface $setup, $tableNam } /** - * @param ModuleDataSetupInterface $setup * @return array */ - private function getGroupedStoreViews(ModuleDataSetupInterface $setup) + private function getGroupedStoreViews() { if (!empty($this->groupedStoreViews)) { return $this->groupedStoreViews; } - $connection = $setup->getConnection(); + $connection = $this->moduleDataSetup->getConnection(); $query = $connection ->select() ->from( - $setup->getTable('store'), + $this->moduleDataSetup->getTable('store'), '*' ); @@ -274,17 +281,15 @@ private function getAttributeValueKey($entityId, $attributeId, $websiteId) } /** - * @param ModuleDataSetupInterface $setup * @param array $attributeValue * @param string $tableName * @return array|null */ private function generateAttributeValueInsertions( - ModuleDataSetupInterface $setup, array $attributeValue, $tableName ) { - $groupedStoreViews = $this->getGroupedStoreViews($setup); + $groupedStoreViews = $this->getGroupedStoreViews(); if (empty($groupedStoreViews[$attributeValue['website_id']])) { return null; } @@ -305,12 +310,11 @@ private function generateAttributeValueInsertions( } /** - * @param ModuleDataSetupInterface $setup * @param array $insertions * @param string $tableName * @return void */ - private function executeInsertions(ModuleDataSetupInterface $setup, array $insertions, $tableName) + private function executeInsertions(array $insertions, $tableName) { $rawQuery = sprintf( 'INSERT INTO @@ -318,12 +322,12 @@ private function executeInsertions(ModuleDataSetupInterface $setup, array $inser VALUES %s ON duplicate KEY UPDATE `value` = VALUES(`value`)', - $setup->getTable($tableName), + $this->moduleDataSetup->getTable($tableName), $this->getTableLinkField($tableName), $this->prepareInsertValuesStatement($insertions) ); - $setup->getConnection()->query($rawQuery, $this->getPlaceholderValues($insertions)); + $this->moduleDataSetup->getConnection()->query($rawQuery, $this->getPlaceholderValues($insertions)); } /** @@ -386,4 +390,30 @@ private function getTableLinkField($tableName) return $this->linkFields[$tableName]; } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpgradeWidgetData::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.2.2'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/Catalog/Setup/UpgradeWidgetData.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWidgetData.php similarity index 71% rename from app/code/Magento/Catalog/Setup/UpgradeWidgetData.php rename to app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWidgetData.php index f9eba413f54..8f72f943199 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeWidgetData.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpgradeWidgetData.php @@ -3,19 +3,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Catalog\Setup; -use Magento\Framework\DB\Select\QueryModifierFactory; -use Magento\Widget\Setup\LayoutUpdateConverter; +namespace Magento\Catalog\Setup\Patch\Data; + use Magento\Eav\Setup\EavSetup; -use Magento\Framework\DB\FieldToConvert; +use Magento\Eav\Setup\EavSetupFactory; use Magento\Framework\DB\AggregatedFieldDataConverter; +use Magento\Framework\DB\FieldToConvert; +use Magento\Framework\DB\Select\QueryModifierFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\Widget\Setup\LayoutUpdateConverter; /** - * Convert serialized widget data for categories and products tables to JSON + * Class UpgradeWidgetData. + * + * @package Magento\Catalog\Setup\Patch */ -class UpgradeWidgetData +class UpgradeWidgetData implements DataPatchInterface, PatchVersionInterface { + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + /** * @var EavSetup */ @@ -27,29 +39,33 @@ class UpgradeWidgetData private $queryModifierFactory; /** - * Constructor - * - * @param EavSetup $eavSetup + * @var AggregatedFieldDataConverter + */ + private $aggregatedFieldDataConverter; + + /** + * PrepareInitialConfig constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param EavSetupFactory $eavSetupFactory * @param QueryModifierFactory $queryModifierFactory * @param AggregatedFieldDataConverter $aggregatedFieldDataConverter */ public function __construct( - EavSetup $eavSetup, + ModuleDataSetupInterface $moduleDataSetup, + EavSetupFactory $eavSetupFactory, QueryModifierFactory $queryModifierFactory, AggregatedFieldDataConverter $aggregatedFieldDataConverter ) { - $this->eavSetup = $eavSetup; + $this->moduleDataSetup = $moduleDataSetup; + $this->eavSetup = $eavSetupFactory->create(['setup' => $moduleDataSetup]); $this->queryModifierFactory = $queryModifierFactory; $this->aggregatedFieldDataConverter = $aggregatedFieldDataConverter; } /** - * Convert category and product layout update - * - * @return void - * @throws \InvalidArgumentException + * {@inheritdoc} */ - public function upgrade() + public function apply() { $categoryTypeId = $this->eavSetup->getEntityTypeId(\Magento\Catalog\Model\Category::ENTITY); $categoryLayoutUpdateAttribute = $this->eavSetup->getAttribute($categoryTypeId, 'custom_layout_update'); @@ -117,4 +133,30 @@ public function upgrade() $this->eavSetup->getSetup()->getConnection() ); } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + DisallowUsingHtmlForProductName::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.2.1'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/Catalog/Setup/UpgradeData.php b/app/code/Magento/Catalog/Setup/UpgradeData.php deleted file mode 100644 index a290d4870bd..00000000000 --- a/app/code/Magento/Catalog/Setup/UpgradeData.php +++ /dev/null @@ -1,439 +0,0 @@ -categorySetupFactory = $categorySetupFactory; - $this->eavSetupFactory = $eavSetupFactory; - $this->upgradeWidgetData = $upgradeWidgetData; - $this->upgradeWebsiteAttributes = $upgradeWebsiteAttributes; - } - - /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $setup->startSetup(); - if ($context->getVersion() - && version_compare($context->getVersion(), '2.0.1') < 0 - ) { - $select = $setup->getConnection()->select() - ->from( - $setup->getTable('catalog_product_entity_group_price'), - [ - 'entity_id', - 'all_groups', - 'customer_group_id', - new \Zend_Db_Expr('1'), - 'value', - 'website_id' - ] - ); - $select = $setup->getConnection()->insertFromSelect( - $select, - $setup->getTable('catalog_product_entity_tier_price'), - [ - 'entity_id', - 'all_groups', - 'customer_group_id', - 'qty', - 'value', - 'website_id' - ] - ); - $setup->getConnection()->query($select); - - $categorySetupManager = $this->categorySetupFactory->create(); - $categorySetupManager->removeAttribute(\Magento\Catalog\Model\Product::ENTITY, 'group_price'); - } - - if (version_compare($context->getVersion(), '2.0.2') < 0) { - // set new resource model paths - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Category::ENTITY, - 'entity_model', - \Magento\Catalog\Model\ResourceModel\Category::class - ); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Category::ENTITY, - 'attribute_model', - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class - ); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Category::ENTITY, - 'entity_attribute_collection', - \Magento\Catalog\Model\ResourceModel\Category\Attribute\Collection::class - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Category::ENTITY, - 'custom_design_from', - 'attribute_model', - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class - ); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Product::ENTITY, - 'entity_model', - \Magento\Catalog\Model\ResourceModel\Product::class - ); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Product::ENTITY, - 'attribute_model', - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class - ); - $categorySetup->updateEntityType( - \Magento\Catalog\Model\Product::ENTITY, - 'entity_attribute_collection', - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class - ); - } - - if (version_compare($context->getVersion(), '2.0.3') < 0) { - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - $categorySetup->updateAttribute(3, 54, 'default_value', 1); - } - - if (version_compare($context->getVersion(), '2.0.4') < 0) { - $mediaBackendType = 'static'; - $mediaBackendModel = null; - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - $categorySetup->updateAttribute( - 'catalog_product', - 'media_gallery', - 'backend_type', - $mediaBackendType - ); - $categorySetup->updateAttribute( - 'catalog_product', - 'media_gallery', - 'backend_model', - $mediaBackendModel - ); - } - - if (version_compare($context->getVersion(), '2.0.5', '<')) { - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - - //Product Details tab - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'status', - 'frontend_label', - 'Enable Product', - 5 - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'name', - 'frontend_label', - 'Product Name' - ); - $attributeSetId = $categorySetup->getDefaultAttributeSetId(\Magento\Catalog\Model\Product::ENTITY); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Product Details', - 'visibility', - 80 - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Product Details', - 'news_from_date', - 90 - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Product Details', - 'news_to_date', - 100 - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Product Details', - 'country_of_manufacture', - 110 - ); - - //Content tab - $categorySetup->addAttributeGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Content', - 15 - ); - $categorySetup->updateAttributeGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Content', - 'tab_group_code', - 'basic' - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Content', - 'description' - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Content', - 'short_description', - 100 - ); - - //Images tab - $groupId = (int)$categorySetup->getAttributeGroupByCode( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'image-management', - 'attribute_group_id' - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - $groupId, - 'image', - 1 - ); - $categorySetup->updateAttributeGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - $groupId, - 'attribute_group_name', - 'Images' - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'image', - 'frontend_label', - 'Base' - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'small_image', - 'frontend_label', - 'Small' - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'image', - 'frontend_input_renderer', - null - ); - - //Design tab - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'page_layout', - 'frontend_label', - 'Layout' - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'custom_layout_update', - 'frontend_label', - 'Layout Update XML', - 10 - ); - - //Schedule Design Update tab - $categorySetup->addAttributeGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Schedule Design Update', - 55 - ); - $categorySetup->updateAttributeGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Schedule Design Update', - 'tab_group_code', - 'advanced' - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Schedule Design Update', - 'custom_design_from', - 20 - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Schedule Design Update', - 'custom_design_to', - 30 - ); - $categorySetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'custom_design', - 'frontend_label', - 'New Theme', - 40 - ); - $categorySetup->addAttributeToGroup( - \Magento\Catalog\Model\Product::ENTITY, - $attributeSetId, - 'Schedule Design Update', - 'custom_design' - ); - $categorySetup->addAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'custom_layout', - [ - 'type' => 'varchar', - 'label' => 'New Layout', - 'input' => 'select', - 'source' => \Magento\Catalog\Model\Product\Attribute\Source\Layout::class, - 'required' => false, - 'sort_order' => 50, - 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, - 'group' => 'Schedule Design Update', - 'is_used_in_grid' => true, - 'is_visible_in_grid' => false, - 'is_filterable_in_grid' => false - ] - ); - } - - if (version_compare($context->getVersion(), '2.0.7') < 0) { - /** @var EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); - - $eavSetup->updateAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'meta_description', - [ - 'note' => 'Maximum 255 chars. Meta Description should optimally be between 150-160 characters' - ] - ); - } - - if (version_compare($context->getVersion(), '2.1.3') < 0) { - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - $this->changePriceAttributeDefaultScope($categorySetup); - } - - if (version_compare($context->getVersion(), '2.1.5') < 0) { - $this->disallowUsingHtmlForProductName($setup); - } - - if ($context->getVersion() && version_compare($context->getVersion(), '2.2.1') < 0) { - $this->upgradeWidgetData->upgrade(); - } - - if (version_compare($context->getVersion(), '2.2.2') < 0) { - $this->upgradeWebsiteAttributes->upgrade($setup); - } - - $setup->endSetup(); - } - - /** - * Set to 'No' 'Is Allowed Html on Store Front' option on product name attribute, because product name - * is multi entity field (used in order, quote) and cannot be conditionally escaped in all places - * - * @param ModuleDataSetupInterface $setup - * @return void - */ - private function disallowUsingHtmlForProductName(ModuleDataSetupInterface $setup) - { - /** @var CategorySetup $categorySetup */ - $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]); - $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); - $attribute = $categorySetup->getAttribute($entityTypeId, 'name'); - - $setup->getConnection() - ->update( - $setup->getTable('catalog_eav_attribute'), - ['is_html_allowed_on_front' => 0], - $setup->getConnection()->quoteInto('attribute_id = ?', $attribute['attribute_id']) - ); - } - - /** - * @param CategorySetup $categorySetup - * @return void - */ - private function changePriceAttributeDefaultScope($categorySetup) - { - $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); - foreach (['price', 'cost', 'special_price'] as $attributeCode) { - $attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode); - if (isset($attribute['attribute_id'])) { - $categorySetup->updateAttribute( - $entityTypeId, - $attribute['attribute_id'], - 'is_global', - \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL - ); - } - } - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php index e0b5d6ef399..dc152aaf058 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php @@ -5,49 +5,43 @@ */ namespace Magento\Catalog\Test\Unit\Block\Product; +use Magento\Catalog\Block\Product\ImageBuilder; +use Magento\Catalog\Block\Product\ImageFactory; +use Magento\Catalog\Helper\Image; +use Magento\Catalog\Model\Product; + class ImageBuilderTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Product\ImageBuilder + * @var ImageBuilder */ - protected $model; + private $model; /** * @var \Magento\Catalog\Helper\ImageFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $helperFactory; + private $helperFactory; /** - * @var \Magento\Catalog\Block\Product\ImageFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ImageFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $imageFactory; + private $imageFactory; protected function setUp() { - $this->helperFactory = $this->getMockBuilder(\Magento\Catalog\Helper\ImageFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->imageFactory = $this->getMockBuilder(\Magento\Catalog\Block\Product\ImageFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->model = new \Magento\Catalog\Block\Product\ImageBuilder( - $this->helperFactory, - $this->imageFactory - ); + $this->helperFactory = $this->createPartialMock(\Magento\Catalog\Helper\ImageFactory::class, ['create']); + + $this->imageFactory = $this->createPartialMock(ImageFactory::class, ['create']); + + $this->model = new ImageBuilder($this->helperFactory, $this->imageFactory); } public function testSetProduct() { - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); + $productMock = $this->createMock(Product::class); $this->assertInstanceOf( - \Magento\Catalog\Block\Product\ImageBuilder::class, + ImageBuilder::class, $this->model->setProduct($productMock) ); } @@ -57,7 +51,7 @@ public function testSetImageId() $imageId = 'test_image_id'; $this->assertInstanceOf( - \Magento\Catalog\Block\Product\ImageBuilder::class, + ImageBuilder::class, $this->model->setImageId($imageId) ); } @@ -68,7 +62,7 @@ public function testSetAttributes() 'name' => 'value', ]; $this->assertInstanceOf( - \Magento\Catalog\Block\Product\ImageBuilder::class, + ImageBuilder::class, $this->model->setAttributes($attributes) ); } @@ -81,13 +75,9 @@ public function testCreate($data, $expected) { $imageId = 'test_image_id'; - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); + $productMock = $this->createMock(Product::class); - $helperMock = $this->getMockBuilder(\Magento\Catalog\Helper\Image::class) - ->disableOriginalConstructor() - ->getMock(); + $helperMock = $this->createMock(Image::class); $helperMock->expects($this->once()) ->method('init') ->with($productMock, $imageId) @@ -116,9 +106,7 @@ public function testCreate($data, $expected) ->method('create') ->willReturn($helperMock); - $imageMock = $this->getMockBuilder(\Magento\Catalog\Block\Product\Image::class) - ->disableOriginalConstructor() - ->getMock(); + $imageMock = $this->createMock(\Magento\Catalog\Block\Product\Image::class); $this->imageFactory->expects($this->once()) ->method('create') @@ -131,61 +119,173 @@ public function testCreate($data, $expected) $this->assertInstanceOf(\Magento\Catalog\Block\Product\Image::class, $this->model->create()); } + /** + * Check if custom attributes will be overridden when builder used few times + * @param array $data + * @dataProvider createMultipleCallsDataProvider + */ + public function testCreateMultipleCalls($data) + { + list ($firstCall, $secondCall) = array_values($data); + + $imageId = 'test_image_id'; + + $productMock = $this->createMock(Product::class); + + $helperMock = $this->createMock(Image::class); + $helperMock->expects($this->exactly(2)) + ->method('init') + ->with($productMock, $imageId) + ->willReturnSelf(); + + $helperMock->expects($this->exactly(2)) + ->method('getFrame') + ->willReturnOnConsecutiveCalls($firstCall['data']['frame'], $secondCall['data']['frame']); + $helperMock->expects($this->exactly(2)) + ->method('getUrl') + ->willReturnOnConsecutiveCalls($firstCall['data']['url'], $secondCall['data']['url']); + $helperMock->expects($this->exactly(4)) + ->method('getWidth') + ->willReturnOnConsecutiveCalls( + $firstCall['data']['width'], + $firstCall['data']['width'], + $secondCall['data']['width'], + $secondCall['data']['width'] + ); + $helperMock->expects($this->exactly(4)) + ->method('getHeight') + ->willReturnOnConsecutiveCalls( + $firstCall['data']['height'], + $firstCall['data']['height'], + $secondCall['data']['height'], + $secondCall['data']['height'] + ); + $helperMock->expects($this->exactly(2)) + ->method('getLabel') + ->willReturnOnConsecutiveCalls($firstCall['data']['label'], $secondCall['data']['label']); + $helperMock->expects($this->exactly(2)) + ->method('getResizedImageInfo') + ->willReturnOnConsecutiveCalls($firstCall['data']['imagesize'], $secondCall['data']['imagesize']); + $this->helperFactory->expects($this->exactly(2)) + ->method('create') + ->willReturn($helperMock); + + $imageMock = $this->createMock(\Magento\Catalog\Block\Product\Image::class); + + $this->imageFactory->expects($this->at(0)) + ->method('create') + ->with($firstCall['expected']) + ->willReturn($imageMock); + + $this->imageFactory->expects($this->at(1)) + ->method('create') + ->with($secondCall['expected']) + ->willReturn($imageMock); + + $this->model->setProduct($productMock); + $this->model->setImageId($imageId); + $this->model->setAttributes($firstCall['data']['custom_attributes']); + + $this->assertInstanceOf(\Magento\Catalog\Block\Product\Image::class, $this->model->create()); + + $this->model->setProduct($productMock); + $this->model->setImageId($imageId); + $this->model->setAttributes($secondCall['data']['custom_attributes']); + $this->assertInstanceOf(\Magento\Catalog\Block\Product\Image::class, $this->model->create()); + } + + /** + * @return array + */ + public function createDataProvider(): array + { + return [ + $this->getTestDataWithoutAttributes(), + $this->getTestDataWithAttributes(), + ]; + } + /** * @return array */ - public function createDataProvider() + public function createMultipleCallsDataProvider(): array { return [ [ + [ + 'without_attributes' => $this->getTestDataWithoutAttributes(), + 'with_attributes' => $this->getTestDataWithAttributes(), + ], + ], + [ + [ + 'with_attributes' => $this->getTestDataWithAttributes(), + 'without_attributes' => $this->getTestDataWithoutAttributes(), + ], + ], + ]; + } + + /** + * @return array + */ + private function getTestDataWithoutAttributes(): array + { + return [ + 'data' => [ + 'frame' => 0, + 'url' => 'test_url_1', + 'width' => 100, + 'height' => 100, + 'label' => 'test_label', + 'custom_attributes' => [], + 'imagesize' => [100, 100], + ], + 'expected' => [ 'data' => [ - 'frame' => 0, - 'url' => 'test_url_1', + 'template' => 'Magento_Catalog::product/image_with_borders.phtml', + 'image_url' => 'test_url_1', 'width' => 100, 'height' => 100, 'label' => 'test_label', - 'custom_attributes' => [], - 'imagesize' => [100, 100], + 'ratio' => 1, + 'custom_attributes' => '', + 'resized_image_width' => 100, + 'resized_image_height' => 100, ], - 'expected' => [ - 'data' => [ - 'template' => 'Magento_Catalog::product/image_with_borders.phtml', - 'image_url' => 'test_url_1', - 'width' => 100, - 'height' => 100, - 'label' => 'test_label', - 'ratio' => 1, - 'custom_attributes' => '', - 'resized_image_width' => 100, - 'resized_image_height' => 100, - ], + ], + ]; + } + + /** + * @return array + */ + private function getTestDataWithAttributes(): array + { + return [ + 'data' => [ + 'frame' => 1, + 'url' => 'test_url_2', + 'width' => 100, + 'height' => 50, + 'label' => 'test_label_2', + 'custom_attributes' => [ + 'name_1' => 'value_1', + 'name_2' => 'value_2', ], + 'imagesize' => [120, 70], ], - [ + 'expected' => [ 'data' => [ - 'frame' => 1, - 'url' => 'test_url_2', + 'template' => 'Magento_Catalog::product/image.phtml', + 'image_url' => 'test_url_2', 'width' => 100, 'height' => 50, 'label' => 'test_label_2', - 'custom_attributes' => [ - 'name_1' => 'value_1', - 'name_2' => 'value_2', - ], - 'imagesize' => [120, 70], - ], - 'expected' => [ - 'data' => [ - 'template' => 'Magento_Catalog::product/image.phtml', - 'image_url' => 'test_url_2', - 'width' => 100, - 'height' => 50, - 'label' => 'test_label_2', - 'ratio' => 0.5, - 'custom_attributes' => 'name_1="value_1" name_2="value_2"', - 'resized_image_width' => 120, - 'resized_image_height' => 70, - ], + 'ratio' => 0.5, + 'custom_attributes' => 'name_1="value_1" name_2="value_2"', + 'resized_image_width' => 120, + 'resized_image_height' => 70, ], ], ]; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index d4e09714d05..aef9d761c61 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Test\Unit\Model\Category\Attribute\Backend; +use Magento\Framework\App\Filesystem\DirectoryList; + class ImageTest extends \PHPUnit\Framework\TestCase { /** @@ -27,6 +29,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $logger; + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $filesystem; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -59,6 +66,9 @@ protected function setUp() \Magento\Catalog\Model\ImageUploader::class, ['moveFileFromTmp'] ); + + $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class)->disableOriginalConstructor() + ->getMock(); } /** @@ -144,6 +154,38 @@ public function testBeforeSaveAttributeFileName() $this->assertEquals('test123.jpg', $object->getTestAttribute()); } + public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() + { + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class, [ + 'filesystem' => $this->filesystem + ]); + + $model->setAttribute($this->attribute); + + $this->filesystem + ->expects($this->once()) + ->method('getUri') + ->with(DirectoryList::MEDIA) + ->willReturn('pub/media'); + + $object = new \Magento\Framework\DataObject([ + 'test_attribute' => [ + [ + 'name' => '/test123.jpg', + 'url' => '/pub/media/wysiwyg/test123.jpg', + ] + ] + ]); + + $model->beforeSave($object); + + $this->assertEquals('/pub/media/wysiwyg/test123.jpg', $object->getTestAttribute()); + $this->assertEquals( + [['name' => '/pub/media/wysiwyg/test123.jpg', 'url' => '/pub/media/wysiwyg/test123.jpg']], + $object->getData('_additional_data_test_attribute') + ); + } + public function testBeforeSaveTemporaryAttribute() { $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php index 0e8777b5c6f..8ca823127e6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php @@ -10,6 +10,7 @@ use Magento\Framework\File\Mime; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\Directory\ReadInterface; class FileInfoTest extends \PHPUnit\Framework\TestCase { @@ -28,6 +29,11 @@ class FileInfoTest extends \PHPUnit\Framework\TestCase */ private $mediaDirectory; + /** + * @var ReadInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $baseDirectory; + /** * @var FileInfo */ @@ -38,6 +44,9 @@ protected function setUp() $this->mediaDirectory = $this->getMockBuilder(WriteInterface::class) ->getMockForAbstractClass(); + $this->baseDirectory = $this->getMockBuilder(ReadInterface::class) + ->getMockForAbstractClass(); + $this->filesystem = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() ->getMock(); @@ -46,10 +55,20 @@ protected function setUp() ->with(DirectoryList::MEDIA) ->willReturn($this->mediaDirectory); + $this->filesystem->expects($this->any()) + ->method('getDirectoryRead') + ->with(DirectoryList::ROOT) + ->willReturn($this->baseDirectory); + $this->mime = $this->getMockBuilder(Mime::class) ->disableOriginalConstructor() ->getMock(); + $this->baseDirectory->expects($this->any()) + ->method('getAbsolutePath') + ->with(null) + ->willReturn('/a/b/c'); + $this->model = new FileInfo( $this->filesystem, $this->mime @@ -58,16 +77,24 @@ protected function setUp() public function testGetMimeType() { - $mediaPath = '/catalog/category'; - $fileName = '/filename.ext1'; - $absoluteFilePath = '/absolute_path/catalog/category/filename.ext1'; + $absoluteFilePath = '/a/b/c/pub/media/catalog/category/filename.ext1'; $expected = 'ext1'; - $this->mediaDirectory->expects($this->once()) + $this->mediaDirectory->expects($this->at(0)) + ->method('getAbsolutePath') + ->with(null) + ->willReturn('/a/b/c/pub/media'); + + $this->mediaDirectory->expects($this->at(1)) + ->method('getAbsolutePath') + ->with(null) + ->willReturn('/a/b/c/pub/media'); + + $this->mediaDirectory->expects($this->at(2)) ->method('getAbsolutePath') - ->with($mediaPath. '/' . ltrim($fileName, '/')) + ->with('/catalog/category/filename.ext1') ->willReturn($absoluteFilePath); $this->mime->expects($this->once()) @@ -86,6 +113,11 @@ public function testGetStat() $expected = ['size' => 1]; + $this->mediaDirectory->expects($this->any()) + ->method('getAbsolutePath') + ->with(null) + ->willReturn('/a/b/c/pub/media'); + $this->mediaDirectory->expects($this->once()) ->method('stat') ->with($mediaPath . $fileName) @@ -104,6 +136,11 @@ public function testIsExist() $fileName = '/filename.ext1'; + $this->mediaDirectory->expects($this->any()) + ->method('getAbsolutePath') + ->with(null) + ->willReturn('/a/b/c/pub/media'); + $this->mediaDirectory->expects($this->once()) ->method('isExist') ->with($mediaPath . $fileName) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php index 6c8951c3ca6..b42262f1f03 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php @@ -145,7 +145,7 @@ public function testDeleteByIdsWithCouldNotSaveException() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Category does not contain specified product + * @expectedExceptionMessage The category doesn't contain the specified product. */ public function testDeleteWithInputException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php index d96ac4bfaab..9f5f3313c68 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php @@ -7,7 +7,7 @@ namespace Magento\Catalog\Test\Unit\Model; use Magento\Catalog\Model\Indexer; -use Magento\Catalog\Model\Category; +use Magento\Eav\Model\Entity\GetCustomAttributeCodesInterface; /** * @SuppressWarnings(PHPMD.TooManyFields) @@ -120,6 +120,11 @@ class CategoryTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @var GetCustomAttributeCodesInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $getCustomAttributeCodes; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -160,6 +165,10 @@ protected function setUp() ); $this->attributeValueFactory = $this->getMockBuilder(\Magento\Framework\Api\AttributeValueFactory::class) ->disableOriginalConstructor()->getMock(); + $this->getCustomAttributeCodes = $this->getMockBuilder(GetCustomAttributeCodesInterface::class) + ->setMethods(['execute']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->category = $this->getCategoryModel(); } @@ -309,6 +318,7 @@ protected function getCategoryModel() 'indexerRegistry' => $this->indexerRegistry, 'metadataService' => $this->metadataServiceMock, 'customAttributeFactory' => $this->attributeValueFactory, + 'getCustomAttributeCodes' => $this->getCustomAttributeCodes ] ); } @@ -433,25 +443,15 @@ public function testGetCustomAttributes() { $nameAttributeCode = 'name'; $descriptionAttributeCode = 'description'; - $interfaceAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class); - $interfaceAttribute->expects($this->once()) - ->method('getAttributeCode') - ->willReturn($nameAttributeCode); - $descriptionAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class); - $descriptionAttribute->expects($this->once()) - ->method('getAttributeCode') - ->willReturn($descriptionAttributeCode); - $customAttributesMetadata = [$interfaceAttribute, $descriptionAttribute]; - - $this->metadataServiceMock->expects($this->once()) - ->method('getCustomAttributesMetadata') - ->willReturn($customAttributesMetadata); + $this->getCustomAttributeCodes->expects($this->exactly(3)) + ->method('execute') + ->willReturn([$descriptionAttributeCode]); $this->category->setData($nameAttributeCode, "sub"); - //The color attribute is not set, expect empty custom attribute array + //The description attribute is not set, expect empty custom attribute array $this->assertEquals([], $this->category->getCustomAttributes()); - //Set the color attribute; + //Set the description attribute; $this->category->setData($descriptionAttributeCode, "description"); $attributeValue = new \Magento\Framework\Api\AttributeValue(); $attributeValue2 = new \Magento\Framework\Api\AttributeValue(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php index 9df0a6bc1ea..d8931cbbfcf 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php @@ -101,7 +101,7 @@ public function testGetCollection() * Test exception when collection provider is not configured for product link type. * * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Collection provider is not registered + * @expectedExceptionMessage The collection provider isn't registered. */ public function testGetCollectionWithMissingProviders() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetCategoryCustomAttributeCodesTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetCategoryCustomAttributeCodesTest.php new file mode 100644 index 00000000000..465063dccd3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetCategoryCustomAttributeCodesTest.php @@ -0,0 +1,66 @@ +baseCustomAttributeCodes = $this->getMockBuilder(GetCustomAttributeCodesInterface::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMockForAbstractClass(); + $objectManager = new ObjectManager($this); + $this->getCategoryCustomAttributeCodes = $objectManager->getObject( + GetCategoryCustomAttributeCodes::class, + ['baseCustomAttributeCodes' => $this->baseCustomAttributeCodes] + ); + } + + /** + * Test GetCategoryCustomAttributeCodes::execute() will return only custom category attribute codes. + */ + public function testExecute() + { + /** @var MetadataServiceInterface|\PHPUnit_Framework_MockObject_MockObject $metadataService */ + $metadataService = $this->getMockBuilder(MetadataServiceInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->baseCustomAttributeCodes->expects($this->once()) + ->method('execute') + ->with($this->identicalTo($metadataService)) + ->willReturn(['test_custom_attribute_code', 'name']); + $this->assertEquals( + ['test_custom_attribute_code'], + $this->getCategoryCustomAttributeCodes->execute($metadataService) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetProductCustomAttributeCodesTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetProductCustomAttributeCodesTest.php new file mode 100644 index 00000000000..a37e1c6df09 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Entity/GetProductCustomAttributeCodesTest.php @@ -0,0 +1,66 @@ +baseCustomAttributeCodes = $this->getMockBuilder(GetCustomAttributeCodesInterface::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMockForAbstractClass(); + $objectManager = new ObjectManager($this); + $this->getProductCustomAttributeCodes = $objectManager->getObject( + GetProductCustomAttributeCodes::class, + ['baseCustomAttributeCodes' => $this->baseCustomAttributeCodes] + ); + } + + /** + * Test GetProductCustomAttributeCodes::execute() will return only custom product attribute codes. + */ + public function testExecute() + { + /** @var MetadataServiceInterface|\PHPUnit_Framework_MockObject_MockObject $metadataService */ + $metadataService = $this->getMockBuilder(MetadataServiceInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->baseCustomAttributeCodes->expects($this->once()) + ->method('execute') + ->with($this->identicalTo($metadataService)) + ->willReturn(['test_custom_attribute_code', 'name']); + $this->assertEquals( + ['test_custom_attribute_code'], + $this->getProductCustomAttributeCodes->execute($metadataService) + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Locator/RegistryLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Locator/RegistryLocatorTest.php index fceb29295b6..b2810ddea24 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Locator/RegistryLocatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Locator/RegistryLocatorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Locator; use Magento\Catalog\Api\Data\ProductInterface; @@ -83,7 +84,7 @@ public function testGetStore() /** * @expectedException \Magento\Framework\Exception\NotFoundException - * @expectedExceptionMessage Product was not registered + * @expectedExceptionMessage The product wasn't registered. */ public function testGetProductWithException() { @@ -92,7 +93,7 @@ public function testGetProductWithException() /** * @expectedException \Magento\Framework\Exception\NotFoundException - * @expectedExceptionMessage Store was not registered + * @expectedExceptionMessage The store wasn't registered. Verify the store and try again. */ public function testGetStoreWithException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/ImageTest.php index 115a333a38b..3ceedddc2b7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/ImageTest.php @@ -3,45 +3,71 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Product\Attribute\Frontend; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Frontend\Image; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; -class ImageTest extends \PHPUnit\Framework\TestCase +class ImageTest extends TestCase { /** - * @var \Magento\Catalog\Model\Product\Attribute\Frontend\Image + * @var Image */ private $model; - public function testGetUrl() + /** + * @dataProvider getUrlDataProvider + * @param string $expectedImage + * @param string $productImage + */ + public function testGetUrl(string $expectedImage, string $productImage) + { + $this->assertEquals($expectedImage, $this->model->getUrl($this->getMockedProduct($productImage))); + } + + /** + * Data provider for testGetUrl + * + * @return array + */ + public function getUrlDataProvider(): array { - $this->assertEquals('catalog/product/img.jpg', $this->model->getUrl($this->getMockedProduct())); + return [ + ['catalog/product/img.jpg', 'img.jpg'], + ['catalog/product/img.jpg', '/img.jpg'], + ]; } protected function setUp() { $helper = new ObjectManager($this); $this->model = $helper->getObject( - \Magento\Catalog\Model\Product\Attribute\Frontend\Image::class, + Image::class, ['storeManager' => $this->getMockedStoreManager()] ); $this->model->setAttribute($this->getMockedAttribute()); } /** - * @return \Magento\Catalog\Model\Product + * @param string $productImage + * @return Product */ - private function getMockedProduct() + private function getMockedProduct(string $productImage): Product { - $mockBuilder = $this->getMockBuilder(\Magento\Catalog\Model\Product::class); + $mockBuilder = $this->getMockBuilder(Product::class); $mock = $mockBuilder->setMethods(['getData', 'getStore', '__wakeup']) ->disableOriginalConstructor() ->getMock(); $mock->expects($this->any()) ->method('getData') - ->will($this->returnValue('img.jpg')); + ->will($this->returnValue($productImage)); $mock->expects($this->any()) ->method('getStore'); @@ -50,13 +76,13 @@ private function getMockedProduct() } /** - * @return \Magento\Store\Model\StoreManagerInterface + * @return StoreManagerInterface */ - private function getMockedStoreManager() + private function getMockedStoreManager(): StoreManagerInterface { $mockedStore = $this->getMockedStore(); - $mockBuilder = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class); + $mockBuilder = $this->getMockBuilder(StoreManagerInterface::class); $mock = $mockBuilder->setMethods(['getStore']) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -69,11 +95,11 @@ private function getMockedStoreManager() } /** - * @return \Magento\Store\Model\Store + * @return Store */ - private function getMockedStore() + private function getMockedStore(): Store { - $mockBuilder = $this->getMockBuilder(\Magento\Store\Model\Store::class); + $mockBuilder = $this->getMockBuilder(Store::class); $mock = $mockBuilder->setMethods(['getBaseUrl', '__wakeup']) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -86,11 +112,11 @@ private function getMockedStore() } /** - * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + * @return AbstractAttribute */ - private function getMockedAttribute() + private function getMockedAttribute(): AbstractAttribute { - $mockBuilder = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class); + $mockBuilder = $this->getMockBuilder(AbstractAttribute::class); $mockBuilder->setMethods(['getAttributeCode', '__wakeup']); $mockBuilder->disableOriginalConstructor(); $mock = $mockBuilder->getMockForAbstractClass(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php index 03bac570c36..3cc6f94d58c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/RepositoryTest.php @@ -207,7 +207,7 @@ public function testSaveNoSuchEntityException() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage frontend_label is a required field. + * @expectedExceptionMessage "frontend_label" is required. Enter and try again. */ public function testSaveInputExceptionRequiredField() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php index 57168f2c127..1f4c4dd3782 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/SetManagementTest.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Product\Attribute; class SetManagementTest extends \PHPUnit\Framework\TestCase @@ -71,7 +72,6 @@ public function testCreate() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Can not create attribute set based on non product attribute set. */ public function testCreateNonProductAttributeSet() { @@ -91,5 +91,9 @@ public function testCreateNonProductAttributeSet() ->willReturn($typeMock); $skeletonSetMock->expects($this->once())->method('getEntityTypeId')->willReturn(3); $this->model->create($attributeSetMock, $skeletonId); + + $this->expectExceptionMessage( + "The attribute set couldn't be created because it's based on a non-product attribute set." + ); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php index f5c71e45a66..0246ba337db 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Source/InputtypeTest.php @@ -27,27 +27,37 @@ protected function setUp() $this->inputtypeModel = $this->objectManagerHelper->getObject( \Magento\Catalog\Model\Product\Attribute\Source\Inputtype::class, [ - 'coreRegistry' => $this->registry + 'coreRegistry' => $this->registry, + 'optionsArray' => $this->getInputTypeSet() ] ); } public function testToOptionArray() { - $inputTypesSet = [ + + $extraValues = [ + ['value' => 'price', 'label' => 'Price'], + ['value' => 'media_image', 'label' => 'Media Image'] + ]; + $inputTypesSet = $this->getInputTypeSet(); + $inputTypesSet = array_merge($inputTypesSet, $extraValues); + + $this->registry->expects($this->once())->method('registry'); + $this->registry->expects($this->once())->method('register'); + $this->assertEquals($inputTypesSet, $this->inputtypeModel->toOptionArray()); + } + + private function getInputTypeSet() + { + return [ ['value' => 'text', 'label' => 'Text Field'], ['value' => 'textarea', 'label' => 'Text Area'], ['value' => 'texteditor', 'label' => 'Text Editor'], ['value' => 'date', 'label' => 'Date'], ['value' => 'boolean', 'label' => 'Yes/No'], ['value' => 'multiselect', 'label' => 'Multiple Select'], - ['value' => 'select', 'label' => 'Dropdown'], - ['value' => 'price', 'label' => 'Price'], - ['value' => 'media_image', 'label' => 'Media Image'], + ['value' => 'select', 'label' => 'Dropdown'] ]; - - $this->registry->expects($this->once())->method('registry'); - $this->registry->expects($this->once())->method('register'); - $this->assertEquals($inputTypesSet, $this->inputtypeModel->toOptionArray()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php index cf49d10416d..9fafbc9d967 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php @@ -65,7 +65,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The image content is not valid. + * @expectedExceptionMessage The image content is invalid. Verify the content and try again. */ public function testCreateWithInvalidImageException() { @@ -82,7 +82,7 @@ public function testCreateWithInvalidImageException() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Cannot save product. + * @expectedExceptionMessage The product can't be saved. */ public function testCreateWithCannotSaveException() { @@ -136,7 +136,7 @@ public function testCreate() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage There is no image with provided ID. + * @expectedExceptionMessage No image with the provided ID was found. Verify the ID and try again. */ public function testUpdateWithNonExistingImage() { @@ -157,7 +157,7 @@ public function testUpdateWithNonExistingImage() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Cannot save product. + * @expectedExceptionMessage The product can't be saved. */ public function testUpdateWithCannotSaveException() { @@ -216,7 +216,7 @@ public function testUpdate() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage There is no image with provided ID. + * @expectedExceptionMessage No image with the provided ID was found. Verify the ID and try again. */ public function testRemoveWithNonExistingImage() { @@ -253,7 +253,7 @@ public function testRemove() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Such product doesn't exist + * @expectedExceptionMessage The product doesn't exist. Verify and try again. */ public function testGetWithNonExistingProduct() { @@ -266,7 +266,7 @@ public function testGetWithNonExistingProduct() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionText Such image doesn't exist + * @expectedExceptionText The image doesn't exist. Verify and try again. */ public function testGetWithNonExistingImage() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php index f918692cb27..627aa184850 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php @@ -5,12 +5,10 @@ */ namespace Magento\Catalog\Test\Unit\Model\Product; -use Magento\Catalog\Model\View\Asset\Image\ContextFactory; use Magento\Catalog\Model\View\Asset\ImageFactory; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\View\Asset\ContextInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -73,10 +71,24 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $viewAssetPlaceholderFactory; + /** + * @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + + /** + * @var \Magento\Framework\App\CacheInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $cacheManager; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->context = $this->createMock(\Magento\Framework\Model\Context::class); + $this->cacheManager = $this->getMockBuilder(\Magento\Framework\App\CacheInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->context->expects($this->any())->method('getCacheManager')->will($this->returnValue($this->cacheManager)); $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) ->disableOriginalConstructor() @@ -112,17 +124,36 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); + $this->serializer = $this->getMockBuilder( + \Magento\Framework\Serialize\SerializerInterface::class + )->getMockForAbstractClass(); + $this->serializer->expects($this->any()) + ->method('serialize') + ->willReturnCallback( + function ($value) { + return json_encode($value); + } + ); + $this->serializer->expects($this->any()) + ->method('unserialize') + ->willReturnCallback( + function ($value) { + return json_decode($value, true); + } + ); $this->image = $objectManager->getObject( \Magento\Catalog\Model\Product\Image::class, [ + 'context' => $this->context, 'storeManager' => $this->storeManager, 'catalogProductMediaConfig' => $this->config, 'coreFileStorageDatabase' => $this->coreFileHelper, 'filesystem' => $this->filesystem, 'imageFactory' => $this->factory, 'viewAssetImageFactory' => $this->viewAssetImageFactory, - 'viewAssetPlaceholderFactory' => $this->viewAssetPlaceholderFactory + 'viewAssetPlaceholderFactory' => $this->viewAssetPlaceholderFactory, + 'serializer' => $this->serializer ] ); @@ -354,12 +385,16 @@ public function testIsCached() $this->testSetGetBaseFile(); $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/watermark/somefile.png'; $this->imageAsset->expects($this->any())->method('getPath')->willReturn($absolutePath); + $this->cacheManager->expects($this->once())->method('load')->willReturn( + json_encode(['size' => ['image data']]) + ); $this->assertTrue($this->image->isCached()); } public function testClearCache() { $this->coreFileHelper->expects($this->once())->method('deleteFolder')->will($this->returnValue(true)); + $this->cacheManager->expects($this->once())->method('clean'); $this->image->clearCache(); } @@ -383,4 +418,24 @@ public function testIsBaseFilePlaceholder() { $this->assertFalse($this->image->isBaseFilePlaceholder()); } + + public function testGetResizedImageInfoWithCache() + { + $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/watermark/somefile.png'; + $this->imageAsset->expects($this->any())->method('getPath')->willReturn($absolutePath); + $this->cacheManager->expects($this->once())->method('load')->willReturn( + json_encode(['size' => ['image data']]) + ); + $this->cacheManager->expects($this->never())->method('save'); + $this->assertEquals(['image data'], $this->image->getResizedImageInfo()); + } + + public function testGetResizedImageInfoEmptyCache() + { + $absolutePath = dirname(dirname(__DIR__)) . '/_files/catalog/product/watermark/somefile.png'; + $this->imageAsset->expects($this->any())->method('getPath')->willReturn($absolutePath); + $this->cacheManager->expects($this->once())->method('load')->willReturn(false); + $this->cacheManager->expects($this->once())->method('save'); + $this->assertTrue(is_array($this->image->getResizedImageInfo())); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php index ec6aa9d8db8..fd728390386 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Product\Option; use \Magento\Catalog\Model\Product\Option\Repository; @@ -199,7 +200,7 @@ public function testDeleteByIdentifierWhenCannotRemoveOption() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage ProductSku should be specified + * @expectedExceptionMessage The ProductSku is empty. Set the ProductSku and try again. */ public function testSaveCouldNotSaveException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php index c2337d1abdb..e2bd4e9c10b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php @@ -178,7 +178,7 @@ public function testUpdateWithInvalidSku() ->with( 1, __( - 'Requested product doesn\'t exist. ' + 'The product that was requested doesn\'t exist. Verify the product and try again. ' . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', [ 'SKU' => 'sku_1', diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php index 1d0f89f3207..754d80302d4 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php @@ -54,7 +54,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedMessage This product doesn't have tier price + * @expectedMessage Tier price is unavailable for this product. */ public function testRemoveWhenTierPricesNotExists() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php index c57040f2426..371696d08d0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php @@ -192,7 +192,7 @@ public function testSuccessDeleteTierPrice() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @message Such product doesn't exist + * @message The product doesn't exist. Verify and try again. */ public function testDeleteTierPriceFromNonExistingProduct() { @@ -328,7 +328,7 @@ public function testSetUpdatedPriceWithGlobalPriceScope() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Values of following attributes are invalid: attr1, attr2 + * @expectedExceptionMessage Values in the attr1, attr2 attributes are invalid. Verify the values and try again. */ public function testSetThrowsExceptionIfDoesntValidate() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductAttributeGroupRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductAttributeGroupRepositoryTest.php index 14f0cbdf9ab..5c3e9a42931 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductAttributeGroupRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductAttributeGroupRepositoryTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model; class ProductAttributeGroupRepositoryTest extends \PHPUnit\Framework\TestCase @@ -121,7 +122,7 @@ public function testDelete() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Attribute group that contains system attributes can not be deleted + * @expectedExceptionMessage The attribute group can't be deleted because it contains system attributes. */ public function testDeleteThrowsExceptionIfGroupHasSystemAttributes() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php index f9b3af4c7a3..ab52d87f562 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php @@ -79,7 +79,7 @@ public function testGetLinkedItemsByType() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Unknown link type: bad type + * @expectedExceptionMessage The "bad type" link type is unknown. Verify the type and try again. */ public function testGetLinkedItemsByTypeWithWrongType() { @@ -132,7 +132,7 @@ public function testSetProductLinks() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage linkType is a required field. + * @expectedExceptionMessage "linkType" is required. Enter and try again. */ public function testSetProductLinksWithoutLinkTypeInLink() { @@ -154,7 +154,7 @@ public function testSetProductLinksWithoutLinkTypeInLink() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Provided link type "bad type" does not exist + * @expectedExceptionMessage The "bad type" link type wasn't found. Verify the type and try again. */ public function testSetProductLinksThrowExceptionIfProductLinkTypeDoesNotExist() { @@ -181,7 +181,7 @@ public function testSetProductLinksThrowExceptionIfProductLinkTypeDoesNotExist() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Requested product doesn't exist + * @expectedExceptionMessage The product that was requested doesn't exist. Verify the product and try again. */ public function testSetProductLinksNoProductException() { @@ -205,7 +205,9 @@ public function testSetProductLinksNoProductException() ->method('get') ->will( $this->throwException( - new \Magento\Framework\Exception\NoSuchEntityException(__('Requested product doesn\'t exist')) + new \Magento\Framework\Exception\NoSuchEntityException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ) ) ); $this->model->setProductLinks($productSku, $links); @@ -213,7 +215,7 @@ public function testSetProductLinksNoProductException() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Invalid data provided for linked products + * @expectedExceptionMessage The linked products data is invalid. Verify the data and try again. */ public function testSetProductLinksInvalidDataException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/RepositoryTest.php index 56aca8d2053..88815f562b5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/RepositoryTest.php @@ -137,7 +137,7 @@ public function testSave() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Invalid data provided for linked products + * @expectedExceptionMessage The linked products data is invalid. Verify the data and try again. */ public function testSaveWithException() { @@ -208,7 +208,7 @@ public function testDelete() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Invalid data provided for linked products + * @expectedExceptionMessage The linked products data is invalid. Verify the data and try again. */ public function testDeleteWithInvalidDataException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 7470f1fc71c..8d65153d7ba 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -301,7 +301,7 @@ function ($value) { /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Requested product doesn't exist + * @expectedExceptionMessage The product that was requested doesn't exist. Verify the product and try again. */ public function testGetAbsentProduct() { @@ -367,7 +367,7 @@ public function testGetWithSetStoreId() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Requested product doesn't exist + * @expectedExceptionMessage The product that was requested doesn't exist. Verify the product and try again. */ public function testGetByIdAbsentProduct() { @@ -592,7 +592,7 @@ public function testSaveNew() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Unable to save product + * @expectedExceptionMessage The product was unable to be saved. Please try again. */ public function testSaveUnableToSaveException() { @@ -707,7 +707,7 @@ public function testDelete() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Unable to remove product product-42 + * @expectedExceptionMessage The "product-42" product couldn't be removed. */ public function testDeleteException() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 8d7e3dfb3f2..74a71a2828e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\Data\ProductExtensionFactory; use Magento\Catalog\Api\Data\ProductExtensionInterface; use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Entity\GetCustomAttributeCodesInterface; use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\Api\ExtensionAttributesFactory; @@ -198,6 +199,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ private $extensionAttributes; + /** + * @var GetCustomAttributeCodesInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $getCustomAttributeCodes; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -369,6 +375,10 @@ protected function setUp() ->expects($this->any()) ->method('create') ->willReturn($this->extensionAttributes); + $this->getCustomAttributeCodes = $this->getMockBuilder(GetCustomAttributeCodesInterface::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMockForAbstractClass(); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->model = $this->objectManagerHelper->getObject( @@ -398,7 +408,8 @@ protected function setUp() 'catalogProductMediaConfig' => $this->mediaConfig, '_filesystem' => $this->filesystemMock, '_collectionFactory' => $this->collectionFactoryMock, - 'data' => ['id' => 1] + 'data' => ['id' => 1], + 'getCustomAttributeCodes' => $this->getCustomAttributeCodes ] ); } @@ -1269,19 +1280,9 @@ public function testGetCustomAttributes() { $priceCode = 'price'; $colorAttributeCode = 'color'; - $interfaceAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class); - $interfaceAttribute->expects($this->once()) - ->method('getAttributeCode') - ->willReturn($priceCode); - $colorAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class); - $colorAttribute->expects($this->once()) - ->method('getAttributeCode') - ->willReturn($colorAttributeCode); - $customAttributesMetadata = [$interfaceAttribute, $colorAttribute]; - - $this->metadataServiceMock->expects($this->once()) - ->method('getCustomAttributesMetadata') - ->willReturn($customAttributesMetadata); + $this->getCustomAttributeCodes->expects($this->exactly(3)) + ->method('execute') + ->willReturn([$colorAttributeCode]); $this->model->setData($priceCode, 10); //The color attribute is not set, expect empty custom attribute array 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 ee6d483c9d4..3dc95b02b75 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 @@ -31,6 +31,7 @@ use Magento\Ui\Component\Form\Fieldset; use Magento\Ui\DataProvider\Mapper\FormElement as FormElementMapper; use Magento\Ui\DataProvider\Mapper\MetaProperties as MetaPropertiesMapper; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav\CompositeConfigProcessor; /** * Class Eav @@ -187,6 +188,11 @@ class Eav extends AbstractModifier */ private $localeCurrency; + /** + * @var CompositeConfigProcessor + */ + private $wysiwygConfigProcessor; + /** * @param LocatorInterface $locator * @param CatalogEavValidationRules $catalogEavValidationRules @@ -207,6 +213,7 @@ class Eav extends AbstractModifier * @param DataPersistorInterface $dataPersistor * @param array $attributesToDisable * @param array $attributesToEliminate + * @param CompositeConfigProcessor|null $wysiwygConfigProcessor * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -228,7 +235,8 @@ public function __construct( ScopeOverriddenValue $scopeOverriddenValue, DataPersistorInterface $dataPersistor, $attributesToDisable = [], - $attributesToEliminate = [] + $attributesToEliminate = [], + CompositeConfigProcessor $wysiwygConfigProcessor = null ) { $this->locator = $locator; $this->catalogEavValidationRules = $catalogEavValidationRules; @@ -249,6 +257,8 @@ public function __construct( $this->dataPersistor = $dataPersistor; $this->attributesToDisable = $attributesToDisable; $this->attributesToEliminate = $attributesToEliminate; + $this->wysiwygConfigProcessor = $wysiwygConfigProcessor ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(CompositeConfigProcessor::class); } /** @@ -779,13 +789,7 @@ private function customizeWysiwyg(ProductAttributeInterface $attribute, array $m $meta['arguments']['data']['config']['formElement'] = WysiwygElement::NAME; $meta['arguments']['data']['config']['wysiwyg'] = true; - $meta['arguments']['data']['config']['wysiwygConfigData'] = [ - 'add_variables' => false, - 'add_widgets' => false, - 'add_directives' => true, - 'use_container' => true, - 'container_class' => 'hor-scroll', - ]; + $meta['arguments']['data']['config']['wysiwygConfigData'] = $this->wysiwygConfigProcessor->process($attribute); return $meta; } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/CompositeConfigProcessor.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/CompositeConfigProcessor.php new file mode 100644 index 00000000000..5513af9d98e --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/CompositeConfigProcessor.php @@ -0,0 +1,58 @@ +logger = $logger; + $this->eavWysiwygDataProcessors = $eavWysiwygDataProcessors; + } + + /** + * {@inheritdoc} + */ + public function process(\Magento\Catalog\Api\Data\ProductAttributeInterface $attribute) + { + $wysiwygConfigData = []; + + foreach ($this->eavWysiwygDataProcessors as $processor) { + if (!$processor instanceof WysiwygConfigDataProcessorInterface) { + $this->logger->critical( + __( + 'Processor %1 doesn\'t implement WysiwygConfigDataProcessorInterface. It will be skipped', + get_class($processor) + ) + ); + continue; + } + + $wysiwygConfigData = array_merge_recursive($wysiwygConfigData, $processor->process($attribute)); + } + + return $wysiwygConfigData; + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessor.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessor.php new file mode 100644 index 00000000000..d301a45d14f --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessor.php @@ -0,0 +1,29 @@ + false, + 'add_widgets' => false, + 'add_directives' => true, + 'use_container' => true, + 'container_class' => 'hor-scroll', + ]; + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessorInterface.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessorInterface.php new file mode 100644 index 00000000000..64faef7ba27 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/WysiwygConfigDataProcessorInterface.php @@ -0,0 +1,23 @@ +_productLimitationFilters->setUsePriceIndex(false); + return $this->_productLimitationPrice(true); + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 34d08958090..7578b608d9f 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -78,6 +78,11 @@ + + + \Magento\Catalog\Ui\DataProvider\Product\ProductCollection + + @@ -86,6 +91,7 @@ Magento\Catalog\Ui\DataProvider\Product\AddStoreFieldToCollection + \Magento\Catalog\Ui\DataProvider\Product\ProductCollectionFactory @@ -187,4 +193,11 @@ + + + + Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav\WysiwygConfigDataProcessor + + + diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 5160eeff9ab..f39a78d922f 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index f28d2cbcdff..a7502d12e1f 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -143,6 +143,12 @@ Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy Magento\Catalog\Model\Product\Link\Proxy + Magento\Catalog\Model\Entity\GetProductCustomAttributeCodes + + + + + Magento\Catalog\Model\Entity\GetCategoryCustomAttributeCodes @@ -816,6 +822,9 @@ Magento\Catalog\Model\ResourceModel\AttributePersistor + + + diff --git a/app/code/Magento/Catalog/etc/events.xml b/app/code/Magento/Catalog/etc/events.xml index 3fdb554e65b..63bd5748943 100644 --- a/app/code/Magento/Catalog/etc/events.xml +++ b/app/code/Magento/Catalog/etc/events.xml @@ -56,7 +56,6 @@ - diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml index 26ed173420a..96deaa08bbf 100644 --- a/app/code/Magento/Catalog/etc/module.xml +++ b/app/code/Magento/Catalog/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + 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 57474f83512..1d587e21313 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 @@ -151,29 +151,32 @@ true - + category - ui/form/element/uploader/uploader + ui/form/element/uploader/image string true false - + false Magento_Catalog/image-preview + Media Gallery + jpg jpeg gif png + 4194304 - + diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml index 96055b73d36..1e608239297 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/design_config_form.xml @@ -22,10 +22,10 @@ Allowed file types: jpeg, gif, png. - fileUploader + imageUploader - + jpg jpeg gif png 2097152 @@ -33,7 +33,7 @@ theme/design_config_fileUploader/save - + @@ -80,12 +80,11 @@ - Allowed file types: jpeg, gif, png. - fileUploader + imageUploader - + jpg jpeg gif png 2097152 @@ -93,7 +92,7 @@ theme/design_config_fileUploader/save - + @@ -140,12 +139,11 @@ - Allowed file types: jpeg, gif, png. - fileUploader + imageUploader - + jpg jpeg gif png 2097152 @@ -153,7 +151,7 @@ theme/design_config_fileUploader/save - + diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js index 5aa0a163c0d..4d0448f8f2a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js @@ -166,7 +166,7 @@ define([ if (request.length === 0) { if (!massActionTrigger) { alert({ - content: $.mage.__('Please select items.') + content: $.mage.__('An item needs to be selected. Select and try again.') }); } diff --git a/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html b/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html index a6a1b3e5b05..04b4990f9ca 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html +++ b/app/code/Magento/Catalog/view/adminhtml/web/template/image-preview.html @@ -5,8 +5,9 @@ */ -->
-
- +
+ +
@@ -30,7 +32,7 @@
- x + x,
diff --git a/app/code/Magento/CatalogAnalytics/etc/module.xml b/app/code/Magento/CatalogAnalytics/etc/module.xml index 7974598e17a..613af18d183 100644 --- a/app/code/Magento/CatalogAnalytics/etc/module.xml +++ b/app/code/Magento/CatalogAnalytics/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogGraphQl/etc/module.xml b/app/code/Magento/CatalogGraphQl/etc/module.xml index e1830103535..1f7aca76674 100644 --- a/app/code/Magento/CatalogGraphQl/etc/module.xml +++ b/app/code/Magento/CatalogGraphQl/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index cf8707b4721..58e2b13b192 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1712,7 +1712,7 @@ protected function _saveProducts() foreach ($rowImages as $column => $columnImages) { foreach ($columnImages as $columnImageKey => $columnImage) { if (!isset($uploadedImages[$columnImage])) { - $uploadedFile = $this->uploadMediaFiles($columnImage, true); + $uploadedFile = $this->uploadMediaFiles($columnImage); $uploadedFile = $uploadedFile ?: $this->getSystemFile($columnImage); if ($uploadedFile) { $uploadedImages[$columnImage] = $uploadedFile; diff --git a/app/code/Magento/CatalogImportExport/etc/module.xml b/app/code/Magento/CatalogImportExport/etc/module.xml index 517ffc0fa39..9e2c801d27b 100644 --- a/app/code/Magento/CatalogImportExport/etc/module.xml +++ b/app/code/Magento/CatalogImportExport/etc/module.xml @@ -6,6 +6,6 @@ */ --> - + diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index 3fb0790640f..8e0c749be2d 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -5,6 +5,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model\Quote\Item; use Magento\CatalogInventory\Api\Data\StockItemInterface; @@ -114,7 +115,7 @@ public function validate(Observer $observer) /* @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ $stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId()); if (!$stockItem instanceof StockItemInterface) { - throw new LocalizedException(__('The stock item for Product is not valid.')); + throw new LocalizedException(__('The Product stock item is invalid. Verify the stock item and try again.')); } /* @var \Magento\CatalogInventory\Api\Data\StockStatusInterface $stockStatus */ diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Product/StockStatusBaseSelectProcessor.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Product/StockStatusBaseSelectProcessor.php index 9f89d380a8f..b80c26c53a7 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Product/StockStatusBaseSelectProcessor.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Product/StockStatusBaseSelectProcessor.php @@ -56,7 +56,9 @@ public function process(Select $select) ['stock' => $stockStatusTable], sprintf('stock.product_id = %s.entity_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS), [] - )->where('stock.stock_status = ?', Stock::STOCK_IN_STOCK); + ) + ->where('stock.stock_status = ?', Stock::STOCK_IN_STOCK) + ->where('stock.website_id = ?', 0); } return $select; diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index e5154a10f0a..6928ab99470 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model\Stock; use Magento\Catalog\Model\ProductFactory; @@ -183,7 +184,7 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc $this->resource->save($stockItem); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Unable to save Stock Item'), $exception); + throw new CouldNotSaveException(__('The stock item was unable to be saved. Please try again.'), $exception); } return $stockItem; } @@ -196,7 +197,9 @@ public function get($stockItemId) $stockItem = $this->stockItemFactory->create(); $this->resource->load($stockItem, $stockItemId); if (!$stockItem->getItemId()) { - throw new NoSuchEntityException(__('Stock Item with id "%1" does not exist.', $stockItemId)); + throw new NoSuchEntityException( + __('The stock item with the "%1" ID wasn\'t found. Verify the ID and try again.', $stockItemId) + ); } return $stockItem; } @@ -225,7 +228,10 @@ public function delete(StockItemInterface $stockItem) $this->getStockRegistryStorage()->removeStockStatus($stockItem->getProductId()); } catch (\Exception $exception) { throw new CouldNotDeleteException( - __('Unable to remove Stock Item with id "%1"', $stockItem->getItemId()), + __( + 'The stock item with the "%1" ID wasn\'t found. Verify the ID and try again.', + $stockItem->getItemId() + ), $exception ); } @@ -242,7 +248,7 @@ public function deleteById($id) $this->delete($stockItem); } catch (\Exception $exception) { throw new CouldNotDeleteException( - __('Unable to remove Stock Item with id "%1"', $id), + __('The stock item with the "%1" ID wasn\'t found. Verify the ID and try again.', $id), $exception ); } diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php index 0e7add8a951..d4d82dd35ee 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model\Stock; use Magento\CatalogInventory\Api\Data\StockCollectionInterfaceFactory; @@ -84,7 +85,7 @@ public function save(StockInterface $stock) try { $this->resource->save($stock); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Unable to save Stock'), $exception); + throw new CouldNotSaveException(__('The stock was unable to be saved. Please try again.'), $exception); } return $stock; } @@ -99,7 +100,9 @@ public function get($stockId) $stock = $this->stockFactory->create(); $this->resource->load($stock, $stockId); if (!$stock->getId()) { - throw new NoSuchEntityException(__('Stock with id "%1" does not exist.', $stockId)); + throw new NoSuchEntityException( + __('The stock with the "%1" ID wasn\'t found. Verify the ID and try again.', $stockId) + ); } return $stock; } diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php index 3dfc4c49a5d..4db1b07ea51 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockStatusRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model\Stock; use Magento\CatalogInventory\Api\Data\StockStatusCollectionInterfaceFactory; @@ -82,7 +83,10 @@ public function save(StockStatusInterface $stockStatus) try { $this->resource->save($stockStatus); } catch (\Exception $exception) { - throw new CouldNotSaveException(__('Unable to save Stock Status'), $exception); + throw new CouldNotSaveException( + __('The stock status was unable to be saved. Please try again.'), + $exception + ); } return $stockStatus; } diff --git a/app/code/Magento/CatalogInventory/Model/StockItemValidator.php b/app/code/Magento/CatalogInventory/Model/StockItemValidator.php index 2cc4832159d..5d218a4f065 100644 --- a/app/code/Magento/CatalogInventory/Model/StockItemValidator.php +++ b/app/code/Magento/CatalogInventory/Model/StockItemValidator.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model; use Magento\Catalog\Api\Data\ProductInterface; @@ -53,21 +54,34 @@ public function validate(ProductInterface $product, StockItemInterface $stockIte $stockId = $stockItem->getStockId(); if ($stockId !== null && $stockId != $defaultStockId) { throw new LocalizedException( - __('Invalid stock id: %1. Only default stock with id %2 allowed', $stockId, $defaultStockId) + __( + 'The "%1" value is invalid for stock ID. Enter stock with a default value of %2 to try again.', + $stockId, + $defaultStockId + ) ); } $stockItemId = $stockItem->getItemId(); if ($stockItemId !== null && (!is_numeric($stockItemId) || $stockItemId <= 0)) { throw new LocalizedException( - __('Invalid stock item id: %1. Should be null or numeric value greater than 0', $stockItemId) + __( + 'The "%1" value is invalid for stock item ID. ' + . 'Enter either zero or a number than zero to try again.', + $stockItemId + ) ); } $defaultStockItemId = $this->stockRegistry->getStockItem($product->getId())->getItemId(); if ($defaultStockItemId && $stockItemId !== null && $defaultStockItemId != $stockItemId) { throw new LocalizedException( - __('Invalid stock item id: %1. Assigned stock item id is %2', $stockItemId, $defaultStockItemId) + __( + 'The "%1" value is invalid for stock item ID. ' + . 'Use the stock item ID\'s assigned "%2" value and try again.', + $stockItemId, + $defaultStockItemId + ) ); } } diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php index d688132fdb9..f60edee3301 100644 --- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php +++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model; use Magento\Catalog\Model\ProductFactory; @@ -192,7 +193,7 @@ protected function resolveProductId($productSku) if (!$productId) { throw new \Magento\Framework\Exception\NoSuchEntityException( __( - 'Product with SKU "%1" does not exist', + 'The Product with the "%1" SKU doesn\'t exist.', $productSku ) ); diff --git a/app/code/Magento/CatalogInventory/Model/System/Config/Backend/Qtyincrements.php b/app/code/Magento/CatalogInventory/Model/System/Config/Backend/Qtyincrements.php index f34d765b0e5..3c4586c9edd 100644 --- a/app/code/Magento/CatalogInventory/Model/System/Config/Backend/Qtyincrements.php +++ b/app/code/Magento/CatalogInventory/Model/System/Config/Backend/Qtyincrements.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Model\System\Config\Backend; use Magento\Framework\Exception\LocalizedException; @@ -22,7 +23,9 @@ public function beforeSave() { $value = $this->getValue(); if (floor($value) != $value) { - throw new LocalizedException(__('Decimal qty increments is not allowed.')); + throw new LocalizedException( + __("Quantity increments can't use decimals. Enter a new increment and try again.") + ); } } } diff --git a/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php index e473f714bd2..cea19c098b9 100644 --- a/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php @@ -97,7 +97,7 @@ private function prepareQuantityAndStockStatus(StockItemInterface $stockItem, ar ) { unset($quantityAndStockStatus['is_in_stock']); } - if (isset($quantityAndStockStatus['qty']) + if (array_key_exists('qty', $quantityAndStockStatus) && $stockItem->getQty() == $quantityAndStockStatus['qty'] ) { unset($quantityAndStockStatus['qty']); diff --git a/app/code/Magento/CatalogInventory/Setup/Patch/Data/ConvertSerializedDataToJson.php b/app/code/Magento/CatalogInventory/Setup/Patch/Data/ConvertSerializedDataToJson.php new file mode 100644 index 00000000000..d0ea3da59c5 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Setup/Patch/Data/ConvertSerializedDataToJson.php @@ -0,0 +1,127 @@ +moduleDataSetup = $moduleDataSetup; + $this->fieldDataConverterFactory = $fieldDataConverterFactory; + $this->queryModifierFactory = $queryModifierFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $select = $this->moduleDataSetup->getConnection() + ->select() + ->from( + $this->moduleDataSetup->getTable('core_config_data'), + ['config_id', 'value'] + ) + ->where('path = ?', 'cataloginventory/item_options/min_sale_qty'); + + $rows = $this->moduleDataSetup->getConnection()->fetchAssoc($select); + $serializedRows = array_filter($rows, function ($row) { + return $this->isSerialized($row['value']); + }); + + $fieldDataConverter = $this->fieldDataConverterFactory->create(SerializedToJson::class); + $queryModifier = $this->queryModifierFactory->create( + 'in', + [ + 'values' => [ + 'config_id' => array_keys($serializedRows) + ] + ] + ); + + $fieldDataConverter->convert( + $this->moduleDataSetup->getConnection(), + $this->moduleDataSetup->getTable('core_config_data'), + 'config_id', + 'value', + $queryModifier + ); + } + + /** + * Check if value is a serialized string + * + * @param string $value + * @return boolean + */ + private function isSerialized($value) + { + return (boolean) preg_match('/^((s|i|d|b|a|O|C):|N;)/', $value); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateStockItemsWebsite::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.2.1'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/CatalogInventory/Setup/InstallData.php b/app/code/Magento/CatalogInventory/Setup/Patch/Data/CreateDefaultStock.php similarity index 50% rename from app/code/Magento/CatalogInventory/Setup/InstallData.php rename to app/code/Magento/CatalogInventory/Setup/Patch/Data/CreateDefaultStock.php index 6fd17455742..ceb353a8091 100644 --- a/app/code/Magento/CatalogInventory/Setup/InstallData.php +++ b/app/code/Magento/CatalogInventory/Setup/Patch/Data/CreateDefaultStock.php @@ -4,57 +4,88 @@ * See COPYING.txt for license details. */ -namespace Magento\CatalogInventory\Setup; +namespace Magento\CatalogInventory\Setup\Patch\Data; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * @codeCoverageIgnore + * Class CreateDefaultStock + * @package Magento\CatalogInventory\Setup\Patch */ -class InstallData implements InstallDataInterface +class CreateDefaultStock implements DataPatchInterface, PatchVersionInterface { /** - * EAV setup factory - * + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** * @var EavSetupFactory */ private $eavSetupFactory; /** - * Init - * + * PrepareInitialConfig constructor. + * @param ModuleDataSetupInterface $resourceConnection * @param EavSetupFactory $eavSetupFactory */ - public function __construct(EavSetupFactory $eavSetupFactory) - { + public function __construct( + ModuleDataSetupInterface $resourceConnection, + EavSetupFactory $eavSetupFactory + ) { + $this->moduleDataSetup = $resourceConnection; $this->eavSetupFactory = $eavSetupFactory; } /** * {@inheritdoc} */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { - $setup->getConnection() + $this->moduleDataSetup->getConnection() ->insertForce( - $setup->getTable('cataloginventory_stock'), + $this->moduleDataSetup->getTable('cataloginventory_stock'), ['stock_id' => 1, 'stock_name' => 'Default'] ); /** @var EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); $groupName = 'Product Details'; $entityTypeId = $eavSetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY); $attributeSetId = $eavSetup->getAttributeSetId($entityTypeId, 'Default'); - $attribute = $eavSetup->getAttribute($entityTypeId, 'quantity_and_stock_status'); if ($attribute) { $eavSetup->addAttributeToGroup($entityTypeId, $attributeSetId, $groupName, $attribute['attribute_id'], 60); $eavSetup->updateAttribute($entityTypeId, $attribute['attribute_id'], 'default_value', 1); } } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/CatalogInventory/Setup/Patch/Data/UpdateStockItemsWebsite.php b/app/code/Magento/CatalogInventory/Setup/Patch/Data/UpdateStockItemsWebsite.php new file mode 100644 index 00000000000..9c73da8915b --- /dev/null +++ b/app/code/Magento/CatalogInventory/Setup/Patch/Data/UpdateStockItemsWebsite.php @@ -0,0 +1,98 @@ +moduleDataSetup = $moduleDataSetup; + $this->stockConfiguration = $stockConfiguration; + $this->storeManager = $storeManager; + $this->indexerProcessor = $indexerProcessor; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $this->moduleDataSetup->getConnection()->update( + $this->moduleDataSetup->getTable('cataloginventory_stock_item'), + ['website_id' => $this->stockConfiguration->getDefaultScopeId()], + ['website_id = ?' => $this->storeManager->getWebsite()->getId()] + ); + $this->indexerProcessor->getIndexer()->invalidate(); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + CreateDefaultStock::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.2.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/CatalogInventory/Setup/UpgradeData.php b/app/code/Magento/CatalogInventory/Setup/UpgradeData.php deleted file mode 100644 index 8c99861308b..00000000000 --- a/app/code/Magento/CatalogInventory/Setup/UpgradeData.php +++ /dev/null @@ -1,152 +0,0 @@ -configuration = $configuration; - $this->storeManager = $storeManager; - $this->indexerProcessor = $indexerProcessor; - $this->fieldDataConverterFactory = $fieldDataConverterFactory; - $this->queryModifierFactory = $queryModifierFactory; - } - - /** - * {@inheritdoc} - */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $setup->startSetup(); - if (version_compare($context->getVersion(), '2.2.0') < 0) { - $this->upgradeCatalogInventoryStockItem($setup); - } - - if (version_compare($context->getVersion(), '2.2.1', '<')) { - $this->convertSerializedDataToJson($setup); - } - $setup->endSetup(); - } - - /** - * @param ModuleDataSetupInterface $setup - * @return void - */ - private function upgradeCatalogInventoryStockItem($setup) - { - $setup->getConnection()->update( - $setup->getTable('cataloginventory_stock_item'), - ['website_id' => $this->configuration->getDefaultScopeId()], - ['website_id = ?' => $this->storeManager->getWebsite()->getId()] - ); - $this->indexerProcessor->getIndexer()->invalidate(); - } - - /** - * Upgrade data to version 2.2.1, converts row data in the core_config_data table that uses the - * path cataloginventory/item_options/min_sale_qty from serialized to JSON. Stored value may not be - * serialized, so validate data format before executing update. - * - * @param ModuleDataSetupInterface $setup - * @return void - */ - private function convertSerializedDataToJson(ModuleDataSetupInterface $setup) - { - $select = $setup->getConnection() - ->select() - ->from( - $setup->getTable('core_config_data'), - ['config_id', 'value'] - ) - ->where('path = ?', 'cataloginventory/item_options/min_sale_qty'); - - $rows = $setup->getConnection()->fetchAssoc($select); - $serializedRows = array_filter($rows, function ($row) { - return $this->isSerialized($row['value']); - }); - - $fieldDataConverter = $this->fieldDataConverterFactory->create(SerializedToJson::class); - $queryModifier = $this->queryModifierFactory->create( - 'in', - [ - 'values' => [ - 'config_id' => array_keys($serializedRows) - ] - ] - ); - - $fieldDataConverter->convert( - $setup->getConnection(), - $setup->getTable('core_config_data'), - 'config_id', - 'value', - $queryModifier - ); - } - - /** - * Check if value is a serialized string - * - * @param string $value - * @return boolean - */ - private function isSerialized($value) - { - return (boolean) preg_match('/^((s|i|d|b|a|O|C):|N;)/', $value); - } -} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/ResourceModel/Product/StockStatusBaseSelectProcessorTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/ResourceModel/Product/StockStatusBaseSelectProcessorTest.php index 9a46dc99ee0..0598fe7e9fe 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/ResourceModel/Product/StockStatusBaseSelectProcessorTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/ResourceModel/Product/StockStatusBaseSelectProcessorTest.php @@ -56,9 +56,13 @@ public function testProcess() [] ) ->willReturnSelf(); - $this->select->expects($this->once()) + + $this->select->expects($this->exactly(2)) ->method('where') - ->with('stock.stock_status = ?', Stock::STOCK_IN_STOCK) + ->withConsecutive( + ['stock.stock_status = ?', Stock::STOCK_IN_STOCK, null], + ['stock.website_id = ?', 0, null] + ) ->willReturnSelf(); $this->stockStatusBaseSelectProcessor->process($this->select); diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index 293874bb32b..089f6c42a17 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogInventory\Test\Unit\Model\Stock; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; @@ -251,7 +252,7 @@ public function testDeleteById() /** * @expectedException \Magento\Framework\Exception\CouldNotDeleteException - * @expectedExceptionMessage Unable to remove Stock Item with id "1" + * @expectedExceptionMessage The stock item with the "1" ID wasn't found. Verify the ID and try again. */ public function testDeleteByIdException() { diff --git a/app/code/Magento/CatalogInventory/etc/db_schema.xml b/app/code/Magento/CatalogInventory/etc/db_schema.xml index a5395ae0a2c..82372f7f605 100644 --- a/app/code/Magento/CatalogInventory/etc/db_schema.xml +++ b/app/code/Magento/CatalogInventory/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 04935b11ce0..2a55d745e11 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -78,11 +78,6 @@ - - - Magento\CatalogInventory\Model\Indexer\Stock\Processor - - diff --git a/app/code/Magento/CatalogInventory/etc/module.xml b/app/code/Magento/CatalogInventory/etc/module.xml index b9cddf838b9..d643c501513 100644 --- a/app/code/Magento/CatalogInventory/etc/module.xml +++ b/app/code/Magento/CatalogInventory/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php b/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php index a3fa9a8f67a..2655c0fa964 100644 --- a/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php +++ b/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogRule\Model; use Magento\CatalogRule\Api\Data; @@ -55,7 +56,9 @@ public function save(Data\RuleInterface $rule) } catch (ValidatorException $e) { throw new CouldNotSaveException(__($e->getMessage())); } catch (\Exception $e) { - throw new CouldNotSaveException(__('Unable to save rule %1', $rule->getRuleId())); + throw new CouldNotSaveException( + __('The "%1" rule was unable to be saved. Please try again.', $rule->getRuleId()) + ); } return $rule; } @@ -72,7 +75,9 @@ public function get($ruleId) /* TODO: change to resource model after entity manager will be fixed */ $rule->load($ruleId); if (!$rule->getRuleId()) { - throw new NoSuchEntityException(__('Rule with specified ID "%1" not found.', $ruleId)); + throw new NoSuchEntityException( + __('The rule with the "%1" ID wasn\'t found. Verify the ID and try again.', $ruleId) + ); } $this->rules[$ruleId] = $rule; } @@ -90,7 +95,7 @@ public function delete(Data\RuleInterface $rule) } catch (ValidatorException $e) { throw new CouldNotSaveException(__($e->getMessage())); } catch (\Exception $e) { - throw new CouldNotDeleteException(__('Unable to remove rule %1', $rule->getRuleId())); + throw new CouldNotDeleteException(__('The "%1" rule couldn\'t be removed.', $rule->getRuleId())); } return true; } diff --git a/app/code/Magento/CatalogRule/Setup/InstallData.php b/app/code/Magento/CatalogRule/Setup/InstallData.php deleted file mode 100644 index 47591f838d1..00000000000 --- a/app/code/Magento/CatalogRule/Setup/InstallData.php +++ /dev/null @@ -1,45 +0,0 @@ -createMigrationSetup(); - $setup->startSetup(); - - $installer->appendClassAliasReplace( - 'catalogrule', - 'conditions_serialized', - \Magento\Framework\Module\Setup\Migration::ENTITY_TYPE_MODEL, - \Magento\Framework\Module\Setup\Migration::FIELD_CONTENT_TYPE_SERIALIZED, - ['rule_id'] - ); - $installer->appendClassAliasReplace( - 'catalogrule', - 'actions_serialized', - \Magento\Framework\Module\Setup\Migration::ENTITY_TYPE_MODEL, - \Magento\Framework\Module\Setup\Migration::FIELD_CONTENT_TYPE_SERIALIZED, - ['rule_id'] - ); - - $installer->doUpdateClassAliases(); - - $setup->endSetup(); - } -} diff --git a/app/code/Magento/CatalogRule/Setup/Patch/Data/ConvertSerializedDataToJson.php b/app/code/Magento/CatalogRule/Setup/Patch/Data/ConvertSerializedDataToJson.php new file mode 100644 index 00000000000..111d7acd530 --- /dev/null +++ b/app/code/Magento/CatalogRule/Setup/Patch/Data/ConvertSerializedDataToJson.php @@ -0,0 +1,101 @@ +moduleDataSetup = $moduleDataSetup; + $this->metadataPool = $metadataPool; + $this->aggregatedFieldDataConverter = $aggregatedFieldDataConverter; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $metadata = $this->metadataPool->getMetadata(RuleInterface::class); + $this->aggregatedFieldDataConverter->convert( + [ + new FieldToConvert( + SerializedToJson::class, + $this->moduleDataSetup->getTable('catalogrule'), + $metadata->getLinkField(), + 'conditions_serialized' + ), + new FieldToConvert( + SerializedToJson::class, + $this->moduleDataSetup->getTable('catalogrule'), + $metadata->getLinkField(), + 'actions_serialized' + ), + ], + $this->moduleDataSetup->getConnection() + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdateClassAliasesForCatalogRules::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.3'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/CatalogRule/Setup/Patch/Data/UpdateClassAliasesForCatalogRules.php b/app/code/Magento/CatalogRule/Setup/Patch/Data/UpdateClassAliasesForCatalogRules.php new file mode 100644 index 00000000000..ce1d76876f6 --- /dev/null +++ b/app/code/Magento/CatalogRule/Setup/Patch/Data/UpdateClassAliasesForCatalogRules.php @@ -0,0 +1,80 @@ +dataSetup = $dataSetup; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $installer = $this->dataSetup->createMigrationSetup(); + $installer->appendClassAliasReplace( + 'catalogrule', + 'conditions_serialized', + \Magento\Framework\Module\Setup\Migration::ENTITY_TYPE_MODEL, + \Magento\Framework\Module\Setup\Migration::FIELD_CONTENT_TYPE_SERIALIZED, + ['rule_id'] + ); + $installer->appendClassAliasReplace( + 'catalogrule', + 'actions_serialized', + \Magento\Framework\Module\Setup\Migration::ENTITY_TYPE_MODEL, + \Magento\Framework\Module\Setup\Migration::FIELD_CONTENT_TYPE_SERIALIZED, + ['rule_id'] + ); + $installer->doUpdateClassAliases(); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/CatalogRule/Setup/UpgradeData.php b/app/code/Magento/CatalogRule/Setup/UpgradeData.php deleted file mode 100644 index 7f75b7e41df..00000000000 --- a/app/code/Magento/CatalogRule/Setup/UpgradeData.php +++ /dev/null @@ -1,86 +0,0 @@ -aggregatedFieldConverter = $aggregatedFieldConverter; - $this->metadataPool = $metadataPool; - } - - /** - * @inheritdoc - */ - public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) - { - $setup->startSetup(); - - if (version_compare($context->getVersion(), '2.0.3', '<')) { - $this->convertSerializedDataToJson($setup); - } - - $setup->endSetup(); - } - - /** - * Convert metadata from serialized to JSON format: - * - * @param ModuleDataSetupInterface $setup - * - * @return void - */ - public function convertSerializedDataToJson($setup) - { - $metadata = $this->metadataPool->getMetadata(RuleInterface::class); - $this->aggregatedFieldConverter->convert( - [ - new FieldToConvert( - SerializedToJson::class, - $setup->getTable('catalogrule'), - $metadata->getLinkField(), - 'conditions_serialized' - ), - new FieldToConvert( - SerializedToJson::class, - $setup->getTable('catalogrule'), - $metadata->getLinkField(), - 'actions_serialized' - ), - ], - $setup->getConnection() - ); - } -} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php index 32c6896625a..7db805908ae 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php @@ -65,7 +65,7 @@ public function testEditRule() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Unable to save rule 1 + * @expectedExceptionMessage The "1" rule was unable to be saved. Please try again. */ public function testEnableSaveRule() { @@ -106,7 +106,7 @@ public function testDeleteRuleById() /** * @expectedException \Magento\Framework\Exception\CouldNotDeleteException - * @expectedExceptionMessage Unable to remove rule 1 + * @expectedExceptionMessage The "1" rule couldn't be removed. */ public function testUnableDeleteRule() { @@ -132,7 +132,7 @@ public function testGetRule() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Rule with specified ID "1" not found. + * @expectedExceptionMessage The rule with the "1" ID wasn't found. Verify the ID and try again. */ public function testGetNonExistentRule() { diff --git a/app/code/Magento/CatalogRule/etc/db_schema.xml b/app/code/Magento/CatalogRule/etc/db_schema.xml index 726c92e252f..883a992d8c7 100644 --- a/app/code/Magento/CatalogRule/etc/db_schema.xml +++ b/app/code/Magento/CatalogRule/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/CatalogRule/etc/module.xml b/app/code/Magento/CatalogRule/etc/module.xml index 1dc0f27b137..c2acce2ff99 100644 --- a/app/code/Magento/CatalogRule/etc/module.xml +++ b/app/code/Magento/CatalogRule/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml index 2990c03ae01..4b1166941bd 100644 --- a/app/code/Magento/CatalogRule/etc/mview.xml +++ b/app/code/Magento/CatalogRule/etc/mview.xml @@ -18,7 +18,6 @@
-
diff --git a/app/code/Magento/CatalogRuleConfigurable/etc/module.xml b/app/code/Magento/CatalogRuleConfigurable/etc/module.xml index 3552af8ceb3..0f4d5742fb7 100644 --- a/app/code/Magento/CatalogRuleConfigurable/etc/module.xml +++ b/app/code/Magento/CatalogRuleConfigurable/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogSearch/Block/SearchTermsLog.php b/app/code/Magento/CatalogSearch/Block/SearchTermsLog.php new file mode 100644 index 00000000000..0be43ce6ff1 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Block/SearchTermsLog.php @@ -0,0 +1,40 @@ +response = $response; + } + + /** + * Check is current page cacheable + * + * @return bool + */ + public function isPageCacheable() + { + $pragma = $this->response->getHeader('pragma')->getFieldValue(); + return ($pragma == 'cache'); + } +} diff --git a/app/code/Magento/CatalogSearch/Controller/Result/Index.php b/app/code/Magento/CatalogSearch/Controller/Result/Index.php index f3990da3a32..22958b64d44 100644 --- a/app/code/Magento/CatalogSearch/Controller/Result/Index.php +++ b/app/code/Magento/CatalogSearch/Controller/Result/Index.php @@ -9,9 +9,9 @@ use Magento\Catalog\Model\Layer\Resolver; use Magento\Catalog\Model\Session; use Magento\Framework\App\Action\Context; -use Magento\Framework\App\ResourceConnection; use Magento\Store\Model\StoreManagerInterface; use Magento\Search\Model\QueryFactory; +use Magento\Search\Model\PopularSearchTerms; class Index extends \Magento\Framework\App\Action\Action { @@ -64,34 +64,88 @@ public function __construct( * Display search result * * @return void + * + * @throws \Magento\Framework\Exception\LocalizedException */ public function execute() { $this->layerResolver->create(Resolver::CATALOG_LAYER_SEARCH); + /* @var $query \Magento\Search\Model\Query */ $query = $this->_queryFactory->get(); - $query->setStoreId($this->_storeManager->getStore()->getId()); + $storeId = $this->_storeManager->getStore()->getId(); + $query->setStoreId($storeId); + + $queryText = $query->getQueryText(); + + if ($queryText != '') { + $catalogSearchHelper = $this->_objectManager->get(\Magento\CatalogSearch\Helper\Data::class); - if ($query->getQueryText() != '') { - if ($this->_objectManager->get(\Magento\CatalogSearch\Helper\Data::class)->isMinQueryLength()) { - $query->setId(0)->setIsActive(1)->setIsProcessed(1); + $getAdditionalRequestParameters = $this->getRequest()->getParams(); + unset($getAdditionalRequestParameters[QueryFactory::QUERY_VAR_NAME]); + + if (empty($getAdditionalRequestParameters) && + $this->_objectManager->get(PopularSearchTerms::class)->isCacheable($queryText, $storeId) + ) { + $this->getCacheableResult($catalogSearchHelper, $query); } else { - $query->saveIncrementalPopularity(); + $this->getNotCacheableResult($catalogSearchHelper, $query); + } + } else { + $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); + } + } - $redirect = $query->getRedirect(); - if ($redirect && $this->_url->getCurrentUrl() !== $redirect) { - $this->getResponse()->setRedirect($redirect); - return; - } + /** + * Return cacheable result + * + * @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper + * @param \Magento\Search\Model\Query $query + * @return void + */ + private function getCacheableResult($catalogSearchHelper, $query) + { + if (!$catalogSearchHelper->isMinQueryLength()) { + $redirect = $query->getRedirect(); + if ($redirect && $this->_url->getCurrentUrl() !== $redirect) { + $this->getResponse()->setRedirect($redirect); + return; } + } - $this->_objectManager->get(\Magento\CatalogSearch\Helper\Data::class)->checkNotes(); + $catalogSearchHelper->checkNotes(); + + $this->_view->loadLayout(); + $this->_view->renderLayout(); + } - $this->_view->loadLayout(); - $this->_view->renderLayout(); + /** + * Return not cacheable result + * + * @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper + * @param \Magento\Search\Model\Query $query + * @return void + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getNotCacheableResult($catalogSearchHelper, $query) + { + if ($catalogSearchHelper->isMinQueryLength()) { + $query->setId(0)->setIsActive(1)->setIsProcessed(1); } else { - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); + $query->saveIncrementalPopularity(); + $redirect = $query->getRedirect(); + if ($redirect && $this->_url->getCurrentUrl() !== $redirect) { + $this->getResponse()->setRedirect($redirect); + return; + } } + + $catalogSearchHelper->checkNotes(); + + $this->_view->loadLayout(); + $this->getResponse()->setNoCacheHeaders(); + $this->_view->renderLayout(); } } diff --git a/app/code/Magento/CatalogSearch/Controller/SearchTermsLog/Save.php b/app/code/Magento/CatalogSearch/Controller/SearchTermsLog/Save.php new file mode 100644 index 00000000000..a4a843c636c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Controller/SearchTermsLog/Save.php @@ -0,0 +1,95 @@ +storeManager = $storeManager; + $this->catalogSearchHelper = $catalogSearchHelper; + $this->queryFactory = $queryFactory; + $this->resultJsonFactory = $resultJsonFactory; + } + + /** + * Save search term + * + * @return Json + */ + public function execute() + { + /* @var $query \Magento\Search\Model\Query */ + $query = $this->queryFactory->get(); + + $query->setStoreId($this->storeManager->getStore()->getId()); + + if ($query->getQueryText() != '') { + try { + if ($this->catalogSearchHelper->isMinQueryLength()) { + $query->setId(0)->setIsActive(1)->setIsProcessed(1); + } else { + $query->saveIncrementalPopularity(); + } + $responseContent = ['success' => true, 'error_message' => '']; + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $responseContent = ['success' => false, 'error_message' => $e]; + } + } else { + $responseContent = ['success' => false, 'error_message' => __('Search term is empty')]; + } + + /** @var Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + return $resultJson->setData($responseContent); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Advanced.php b/app/code/Magento/CatalogSearch/Model/Advanced.php index d158bdf40b4..28f67a7829e 100644 --- a/app/code/Magento/CatalogSearch/Model/Advanced.php +++ b/app/code/Magento/CatalogSearch/Model/Advanced.php @@ -226,7 +226,7 @@ public function addFilters($values) $this->_registry->register('advanced_search_conditions', $allConditions); $this->getProductCollection()->addFieldsToFilter($allConditions); } else { - throw new LocalizedException(__('Please specify at least one search term.')); + throw new LocalizedException(__('Enter a search term and try again.')); } return $this; diff --git a/app/code/Magento/CatalogSearch/Model/Autocomplete/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Autocomplete/DataProvider.php index c4413d002e1..c1c9997bc83 100644 --- a/app/code/Magento/CatalogSearch/Model/Autocomplete/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Autocomplete/DataProvider.php @@ -10,9 +10,16 @@ use Magento\Search\Model\QueryFactory; use Magento\Search\Model\Autocomplete\DataProviderInterface; use Magento\Search\Model\Autocomplete\ItemFactory; +use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfig; +use Magento\Store\Model\ScopeInterface; class DataProvider implements DataProviderInterface { + /** + * Autocomplete limit + */ + const CONFIG_AUTOCOMPLETE_LIMIT = 'catalog/search/autocomplete_limit'; + /** * Query factory * @@ -27,16 +34,29 @@ class DataProvider implements DataProviderInterface */ protected $itemFactory; + /** + * Limit + * + * @var int + */ + protected $limit; + /** * @param QueryFactory $queryFactory * @param ItemFactory $itemFactory */ public function __construct( QueryFactory $queryFactory, - ItemFactory $itemFactory + ItemFactory $itemFactory, + ScopeConfig $scopeConfig ) { $this->queryFactory = $queryFactory; $this->itemFactory = $itemFactory; + + $this->limit = (int) $scopeConfig->getValue( + self::CONFIG_AUTOCOMPLETE_LIMIT, + ScopeInterface::SCOPE_STORE + ); } /** @@ -58,7 +78,7 @@ public function getItems() $result[] = $resultItem; } } - return $result; + return ($this->limit) ? array_splice($result, 0, $this->limit) : $result; } /** diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php index 02bc3e2bc24..57896ba5a79 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php @@ -187,7 +187,7 @@ protected function _renderFiltersBefore() } catch (NonExistingRequestNameException $e) { $this->_logger->error($e->getMessage()); throw new LocalizedException( - __('Sorry, something went wrong. You can find out more in the error log.') + __('An error occurred. For details, see the error log.') ); } $temporaryStorage = $this->temporaryStorageFactory->create(); diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 68274ee5043..0408957e511 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -347,7 +347,7 @@ protected function _renderFiltersBefore() $this->searchResult = $this->searchResultFactory->create()->setItems([]); } catch (NonExistingRequestNameException $e) { $this->_logger->error($e->getMessage()); - throw new LocalizedException(__('Sorry, something went wrong. You can find out more in the error log.')); + throw new LocalizedException(__('An error occurred. For details, see the error log.')); } $temporaryStorage = $this->temporaryStorageFactory->create(); @@ -441,7 +441,7 @@ public function getFacetedData($field) $result[$metrics['value']] = $metrics; } } else { - throw new StateException(__('Bucket does not exist')); + throw new StateException(__("The bucket doesn't exist.")); } } return $result; diff --git a/app/code/Magento/CatalogSearch/Setup/InstallData.php b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php similarity index 57% rename from app/code/Magento/CatalogSearch/Setup/InstallData.php rename to app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php index 8a2754f1903..e266e67804e 100644 --- a/app/code/Magento/CatalogSearch/Setup/InstallData.php +++ b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php @@ -3,15 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\CatalogSearch\Setup; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; +namespace Magento\CatalogSearch\Setup\Patch\Data; + +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; use Magento\Framework\Indexer\IndexerInterfaceFactory; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; -class InstallData implements InstallDataInterface +/** + * Class SetInitialSearchWeightForAttributes + * @package Magento\CatalogSearch\Setup\Patch + */ +class SetInitialSearchWeightForAttributes implements DataPatchInterface, PatchVersionInterface { /** * @var IndexerInterfaceFactory @@ -24,6 +28,7 @@ class InstallData implements InstallDataInterface private $attributeRepository; /** + * SetInitialSearchWeightForAttributes constructor. * @param IndexerInterfaceFactory $indexerFactory * @param ProductAttributeRepositoryInterface $attributeRepository */ @@ -36,33 +41,43 @@ public function __construct( } /** - * Installs data for a module - * - * @param ModuleDataSetupInterface $setup - * @param ModuleContextInterface $context - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * {@inheritdoc} */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { $this->setWeight('sku', 6); $this->setWeight('name', 5); - $this->getIndexer('catalogsearch_fulltext')->reindexAll(); } /** - * @param string $indexerId - * @return \Magento\Framework\Indexer\IndexerInterface + * {@inheritdoc} */ - private function getIndexer($indexerId) + public static function getDependencies() { - return $this->indexerFactory->create()->load($indexerId); + return []; } /** - * @param string $attributeCode - * @param int $weight - * @return void + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } + + /** + * Set attribute search weight. + * + * @param $attributeCode + * @param $weight */ private function setWeight($attributeCode, $weight) { diff --git a/app/code/Magento/CatalogSearch/Setup/RecurringData.php b/app/code/Magento/CatalogSearch/Setup/RecurringData.php new file mode 100644 index 00000000000..0c2aee800b6 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Setup/RecurringData.php @@ -0,0 +1,62 @@ +indexerInterfaceFactory = $indexerInterfaceFactory; + $this->state = $state; + } + + /** + * {@inheritdoc} + */ + public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + $this->state->emulateAreaCode( + \Magento\Framework\App\Area::AREA_CRONTAB, + [$this, 'reindex'] + ); + } + + /** + * Run reindex. + * + * @return void + */ + public function reindex() + { + $this->indexerInterfaceFactory->create()->load('catalogsearch_fulltext')->reindexAll(); + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Autocomplete/DataProviderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Autocomplete/DataProviderTest.php index 75daf438f7b..bb8fc848bb2 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Autocomplete/DataProviderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Autocomplete/DataProviderTest.php @@ -30,6 +30,11 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $suggestCollection; + /** + * @var integer + */ + private $limit = 3; + protected function setUp() { $helper = new ObjectManager($this); @@ -60,11 +65,20 @@ protected function setUp() ->setMethods(['create']) ->getMock(); + $scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->setMethods(['getValue']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $scopeConfig->expects($this->any()) + ->method('getValue') + ->willReturn($this->limit); + $this->model = $helper->getObject( \Magento\CatalogSearch\Model\Autocomplete\DataProvider::class, [ 'queryFactory' => $queryFactory, - 'itemFactory' => $this->itemFactory + 'itemFactory' => $this->itemFactory, + 'scopeConfig' => $scopeConfig ] ); } @@ -103,8 +117,10 @@ public function testGetItems() ->will($this->returnValue($expected)); $this->itemFactory->expects($this->any())->method('create')->willReturn($itemMock); + $result = $this->model->getItems(); $this->assertEquals($expected, $result[0]->toArray()); + $this->assertEquals($this->limit, count($result)); } private function buildCollection(array $data) diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index 0eeb6ab3387..18d2cdf5427 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -27,6 +27,15 @@ validate-digits + + + Number of popular search terms to be cached for faster response. Use “0” to cache all results after a term is searched for the second time. + validate-digits + + + + validate-digits + diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index d5ff194813b..d2b50fe9f53 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -15,6 +15,8 @@ mysql 1 128 + 100 + 8 diff --git a/app/code/Magento/CatalogSearch/etc/db_schema.xml b/app/code/Magento/CatalogSearch/etc/db_schema.xml index 2f6c0ba2b72..ae9217b0632 100644 --- a/app/code/Magento/CatalogSearch/etc/db_schema.xml +++ b/app/code/Magento/CatalogSearch/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/CatalogSearch/etc/module.xml b/app/code/Magento/CatalogSearch/etc/module.xml index fd31faa0839..db530edbdd7 100644 --- a/app/code/Magento/CatalogSearch/etc/module.xml +++ b/app/code/Magento/CatalogSearch/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml index e54b4509358..8dd8e3ed728 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml +++ b/app/code/Magento/CatalogSearch/view/frontend/layout/catalogsearch_result_index.xml @@ -9,16 +9,16 @@ - - + + positions:list-secondary - - + + product_list_toolbar @@ -36,6 +36,11 @@ + + + Magento\CatalogSearch\Block\SearchTermsLog + + diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml new file mode 100644 index 00000000000..61609bdf66b --- /dev/null +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml @@ -0,0 +1,18 @@ + +getSearchTermsLog()->isPageCacheable()): ?> + + diff --git a/app/code/Magento/CatalogSearch/view/frontend/web/js/search-terms-log.js b/app/code/Magento/CatalogSearch/view/frontend/web/js/search-terms-log.js new file mode 100644 index 00000000000..8638a837f56 --- /dev/null +++ b/app/code/Magento/CatalogSearch/view/frontend/web/js/search-terms-log.js @@ -0,0 +1,21 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'mageUtils' +], function ($, utils) { + 'use strict'; + + return function (data) { + $.ajax({ + method: 'GET', + url: data.url, + data: { + 'q': utils.getUrlParameters(window.location.href).q + } + }); + }; +}); diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index e4ccd0b869d..c4d67f447e2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -50,13 +50,6 @@ public function execute(\Magento\Framework\Event\Observer $observer) || $product->getIsChangedWebsites() || $product->dataHasChangedFor('visibility') ) { - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $product->getStoreId() - ]); - if ($product->isVisibleInSiteVisibility()) { $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } diff --git a/app/code/Magento/CatalogUrlRewrite/Setup/InstallData.php b/app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/CreateUrlAttributes.php similarity index 70% rename from app/code/Magento/CatalogUrlRewrite/Setup/InstallData.php rename to app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/CreateUrlAttributes.php index bbc5f497843..22e87174e1f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Setup/InstallData.php +++ b/app/code/Magento/CatalogUrlRewrite/Setup/Patch/Data/CreateUrlAttributes.php @@ -4,43 +4,51 @@ * See COPYING.txt for license details. */ -namespace Magento\CatalogUrlRewrite\Setup; +namespace Magento\CatalogUrlRewrite\Setup\Patch\Data; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * @codeCoverageIgnore + * Class CreateUrlAttributes + * @package Magento\CatalogUrlRewrite\Setup\Patch */ -class InstallData implements InstallDataInterface +class CreateUrlAttributes implements DataPatchInterface, PatchVersionInterface { /** - * EAV setup factory - * + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** * @var EavSetupFactory */ private $eavSetupFactory; /** - * Init - * + * CreateUrlAttributes constructor. + * @param ModuleDataSetupInterface $moduleDataSetup * @param EavSetupFactory $eavSetupFactory */ - public function __construct(EavSetupFactory $eavSetupFactory) - { + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + EavSetupFactory $eavSetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; $this->eavSetupFactory = $eavSetupFactory; } /** * {@inheritdoc} */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { /** @var EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); $eavSetup->addAttribute( \Magento\Catalog\Model\Category::ENTITY, 'url_key', @@ -67,7 +75,6 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'group' => 'General Information', ] ); - $eavSetup->addAttribute( \Magento\Catalog\Model\Product::ENTITY, 'url_key', @@ -85,7 +92,6 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'is_filterable_in_grid' => true, ] ); - $eavSetup->addAttribute( \Magento\Catalog\Model\Product::ENTITY, 'url_path', @@ -98,4 +104,28 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index d294f6d022e..39317b42af9 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -103,7 +103,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedDeleteCount' => 1, 'expectedReplaceCount' => 1, ], @@ -113,7 +112,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedDeleteCount' => 0, 'expectedReplaceCount' => 0 ], 'visibility changed' => [ @@ -122,7 +120,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedDeleteCount' => 1, 'expectedReplaceCount' => 1 ], 'websites changed' => [ @@ -131,7 +128,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => true, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedDeleteCount' => 1, 'expectedReplaceCount' => 1 ], 'categories changed' => [ @@ -140,7 +136,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => true, 'visibilityResult' => true, - 'expectedDeleteCount' => 1, 'expectedReplaceCount' => 1 ], 'url changed invisible' => [ @@ -149,7 +144,6 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => false, - 'expectedDeleteCount' => 1, 'expectedReplaceCount' => 0 ], ]; @@ -161,7 +155,6 @@ public function urlKeyDataProvider() * @param bool $isChangedWebsites * @param bool $isChangedCategories * @param bool $visibilityResult - * @param int $expectedDeleteCount * @param int $expectedReplaceCount * * @dataProvider urlKeyDataProvider @@ -172,7 +165,6 @@ public function testExecuteUrlKey( $isChangedWebsites, $isChangedCategories, $visibilityResult, - $expectedDeleteCount, $expectedReplaceCount ) { $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(12)); @@ -194,13 +186,6 @@ public function testExecuteUrlKey( ->method('getIsChangedCategories') ->will($this->returnValue($isChangedCategories)); - $this->urlPersist->expects($this->exactly($expectedDeleteCount))->method('deleteByData')->with([ - UrlRewrite::ENTITY_ID => $this->product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $this->product->getStoreId() - ]); - $this->product->expects($this->any()) ->method('isVisibleInSiteVisibility') ->will($this->returnValue($visibilityResult)); diff --git a/app/code/Magento/CatalogUrlRewrite/etc/db_schema.xml b/app/code/Magento/CatalogUrlRewrite/etc/db_schema.xml index bb83af6a75c..174173fa201 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/db_schema.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
- + diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml index d17a3cdb45c..be4bb9fcd70 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml @@ -6,5 +6,5 @@ */ --> - + diff --git a/app/code/Magento/CatalogWidget/etc/module.xml b/app/code/Magento/CatalogWidget/etc/module.xml index 8954f11f954..b3724d4b91f 100644 --- a/app/code/Magento/CatalogWidget/etc/module.xml +++ b/app/code/Magento/CatalogWidget/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Checkout/Controller/Cart/Configure.php b/app/code/Magento/Checkout/Controller/Cart/Configure.php index 132fbf7d199..6d409144ff6 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Configure.php +++ b/app/code/Magento/Checkout/Controller/Cart/Configure.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Controller\Cart; use Magento\Framework; @@ -63,7 +64,7 @@ public function execute() try { if (!$quoteItem || $productId != $quoteItem->getProduct()->getId()) { - $this->messageManager->addError(__("We can't find the quote item.")); + $this->messageManager->addError(__("The quote item isn't found. Verify the item and try again.")); return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('checkout/cart'); } diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php index 11861126322..d7cb94f3da6 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Controller\Cart; class UpdateItemOptions extends \Magento\Checkout\Controller\Cart @@ -35,7 +36,9 @@ public function execute() $quoteItem = $this->cart->getQuote()->getItemById($id); if (!$quoteItem) { - throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t find the quote item.')); + throw new \Magento\Framework\Exception\LocalizedException( + __("The quote item isn't found. Verify the item and try again.") + ); } $item = $this->cart->updateItem($id, new \Magento\Framework\DataObject($params)); diff --git a/app/code/Magento/Checkout/Controller/Onepage/SaveOrder.php b/app/code/Magento/Checkout/Controller/Onepage/SaveOrder.php index 3597962b838..12b725a8f6d 100644 --- a/app/code/Magento/Checkout/Controller/Onepage/SaveOrder.php +++ b/app/code/Magento/Checkout/Controller/Onepage/SaveOrder.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Controller\Onepage; use Magento\Framework\DataObject; @@ -37,7 +38,10 @@ public function execute() $result->setData('error', true); $result->setData( 'error_messages', - __('Please agree to all the terms and conditions before placing the order.') + __( + "The order wasn't placed. " + . "First, agree to the terms and conditions, then try placing your order again." + ) ); return $this->resultJsonFactory->create()->setData($result->getData()); } diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php index f7072ab9743..d1a55aee4db 100644 --- a/app/code/Magento/Checkout/Model/Cart.php +++ b/app/code/Magento/Checkout/Model/Cart.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Model; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -299,21 +300,30 @@ protected function _getProduct($productInfo) if ($productInfo instanceof Product) { $product = $productInfo; if (!$product->getId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t find the product.')); + throw new \Magento\Framework\Exception\LocalizedException( + __("The product wasn't found. Verify the product and try again.") + ); } } elseif (is_int($productInfo) || is_string($productInfo)) { $storeId = $this->_storeManager->getStore()->getId(); try { $product = $this->productRepository->getById($productInfo, false, $storeId); } catch (NoSuchEntityException $e) { - throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t find the product.'), $e); + throw new \Magento\Framework\Exception\LocalizedException( + __("The product wasn't found. Verify the product and try again."), + $e + ); } } else { - throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t find the product.')); + throw new \Magento\Framework\Exception\LocalizedException( + __("The product wasn't found. Verify the product and try again.") + ); } $currentWebsiteId = $this->_storeManager->getStore()->getWebsiteId(); if (!is_array($product->getWebsiteIds()) || !in_array($currentWebsiteId, $product->getWebsiteIds())) { - throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t find the product.')); + throw new \Magento\Framework\Exception\LocalizedException( + __("The product wasn't found. Verify the product and try again.") + ); } return $product; } diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index 6779da354fa..e18940626a3 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -95,7 +95,7 @@ public function savePaymentInformationAndPlaceOrder( } catch (\Exception $e) { $this->getLogger()->critical($e); throw new CouldNotSaveException( - __('An error occurred on the server. Please try to place the order again.'), + __('A server error stopped your order from being placed. Please try to place your order again.'), $e ); } diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 3d6b0aa0cdc..164109177d4 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Model; use Magento\Framework\Exception\CouldNotSaveException; @@ -89,7 +90,7 @@ public function savePaymentInformationAndPlaceOrder( } catch (\Exception $e) { $this->getLogger()->critical($e); throw new CouldNotSaveException( - __('An error occurred on the server. Please try to place the order again.'), + __('A server error stopped your order from being placed. Please try to place your order again.'), $e ); } diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index d8142d033f7..381ee2b9015 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Model; use Magento\Framework\Exception\InputException; @@ -151,7 +152,7 @@ public function saveAddressInformation( } if (!$address->getCountryId()) { - throw new StateException(__('Shipping address is not set')); + throw new StateException(__('The shipping address is missing. Set the address and try again.')); } /** @var \Magento\Quote\Model\Quote $quote */ @@ -169,7 +170,9 @@ public function saveAddressInformation( $this->quoteRepository->save($quote); } catch (\Exception $e) { $this->logger->critical($e); - throw new InputException(__('Unable to save shipping information. Please check input data.')); + throw new InputException( + __('The shipping information was unable to be saved. Verify the input data and try again.') + ); } $shippingAddress = $quote->getShippingAddress(); @@ -198,7 +201,9 @@ public function saveAddressInformation( protected function validateQuote(\Magento\Quote\Model\Quote $quote) { if (0 == $quote->getItemsCount()) { - throw new InputException(__('Shipping method is not applicable for empty cart')); + throw new InputException( + __("The shipping method can't be set for an empty cart. Add an item to cart and try again.") + ); } } diff --git a/app/code/Magento/Checkout/Model/Sidebar.php b/app/code/Magento/Checkout/Model/Sidebar.php index 1a0e3aa8183..6f6655508f1 100644 --- a/app/code/Magento/Checkout/Model/Sidebar.php +++ b/app/code/Magento/Checkout/Model/Sidebar.php @@ -85,7 +85,7 @@ public function checkQuoteItem($itemId) { $item = $this->cart->getQuote()->getItemById($itemId); if (!$item instanceof CartItemInterface) { - throw new LocalizedException(__('We can\'t find the quote item.')); + throw new LocalizedException(__("The quote item isn't found. Verify the item and try again.")); } return $this; } diff --git a/app/code/Magento/Checkout/Setup/InstallData.php b/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php similarity index 78% rename from app/code/Magento/Checkout/Setup/InstallData.php rename to app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php index 38879e06d65..bc38809d070 100644 --- a/app/code/Magento/Checkout/Setup/InstallData.php +++ b/app/code/Magento/Checkout/Setup/Patch/Data/PrepareInitialCheckoutConfiguration.php @@ -4,42 +4,46 @@ * See COPYING.txt for license details. */ -namespace Magento\Checkout\Setup; +namespace Magento\Checkout\Setup\Patch\Data; -use Magento\Customer\Helper\Address; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * @codeCoverageIgnore + * Class PrepareInitialCheckoutConfiguration + * @package Magento\Checkout\Setup\Patch */ -class InstallData implements InstallDataInterface +class PrepareInitialCheckoutConfiguration implements DataPatchInterface, PatchVersionInterface { /** - * EAV setup factory - * + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** * @var EavSetupFactory */ private $eavSetupFactory; /** - * Customer address - * - * @var Address + * @var \Magento\Customer\Helper\Address */ private $customerAddress; /** - * Init - * - * @param EavSetupFactory $eavSetupFactory - * @param Address $customerAddress + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup */ - public function __construct(EavSetupFactory $eavSetupFactory, Address $customerAddress) - { + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + EavSetupFactory $eavSetupFactory, + \Magento\Customer\Helper\Address $customerAddress + ) { + $this->moduleDataSetup = $moduleDataSetup; $this->eavSetupFactory = $eavSetupFactory; $this->customerAddress = $customerAddress; } @@ -50,17 +54,17 @@ public function __construct(EavSetupFactory $eavSetupFactory, Address $customerA * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { /** @var EavSetup $eavSetup */ - $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); - - $setup->startSetup(); + $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); + + $this->moduleDataSetup->getConnection()->startSetup(); - $connection = $setup->getConnection(); + $connection = $this->moduleDataSetup->getConnection(); $select = $connection->select()->from( - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getTable('core_config_data'), 'COUNT(*)' )->where( 'path=?', @@ -73,7 +77,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface || $connection->fetchOne($select) > 0; $select = $connection->select()->from( - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getTable('core_config_data'), 'COUNT(*)' )->where( 'path=?', @@ -82,14 +86,11 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'value NOT LIKE ?', '0' ); - $showMiddlename = (bool)$this->customerAddress->getConfig( - 'middlename_show' - ) || $connection->fetchOne( - $select - ) > 0; + $showMiddlename = (bool)$this->customerAddress->getConfig('middlename_show') + || $connection->fetchOne($select) > 0; $select = $connection->select()->from( - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getTable('core_config_data'), 'COUNT(*)' )->where( 'path=?', @@ -102,7 +103,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface || $connection->fetchOne($select) > 0; $select = $connection->select()->from( - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getTable('core_config_data'), 'COUNT(*)' )->where( 'path=?', @@ -115,7 +116,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface || $connection->fetchOne($select) > 0; $select = $connection->select()->from( - $setup->getTable('core_config_data'), + $this->moduleDataSetup->getTable('core_config_data'), 'COUNT(*)' )->where( 'path=?', @@ -137,7 +138,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface */ $connection->insert( - $setup->getTable('eav_form_type'), + $this->moduleDataSetup->getTable('eav_form_type'), [ 'code' => 'checkout_onepage_register', 'label' => 'checkout_onepage_register', @@ -146,21 +147,21 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'store_id' => 0 ] ); - $formTypeId = $connection->lastInsertId($setup->getTable('eav_form_type')); + $formTypeId = $connection->lastInsertId($this->moduleDataSetup->getTable('eav_form_type')); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $customerEntityTypeId] ); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $addressEntityTypeId] ); $elementSort = 0; if ($showPrefix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -170,7 +171,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -180,7 +181,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showMiddlename) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -190,7 +191,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -200,7 +201,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showSuffix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -210,7 +211,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -219,7 +220,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -228,7 +229,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -237,7 +238,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -246,7 +247,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -255,7 +256,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -264,7 +265,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -273,7 +274,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -282,7 +283,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -292,7 +293,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showDob) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -303,7 +304,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface } if ($showTaxVat) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -320,7 +321,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface */ $connection->insert( - $setup->getTable('eav_form_type'), + $this->moduleDataSetup->getTable('eav_form_type'), [ 'code' => 'checkout_onepage_register_guest', 'label' => 'checkout_onepage_register_guest', @@ -329,21 +330,21 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'store_id' => 0 ] ); - $formTypeId = $connection->lastInsertId($setup->getTable('eav_form_type')); + $formTypeId = $connection->lastInsertId($this->moduleDataSetup->getTable('eav_form_type')); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $customerEntityTypeId] ); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $addressEntityTypeId] ); $elementSort = 0; if ($showPrefix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -353,7 +354,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -363,7 +364,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showMiddlename) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -373,7 +374,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -383,7 +384,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showSuffix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -393,7 +394,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -402,7 +403,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -411,7 +412,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -420,7 +421,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -429,7 +430,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -438,7 +439,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -447,7 +448,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -456,7 +457,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -465,7 +466,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -475,7 +476,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showDob) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -486,7 +487,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface } if ($showTaxVat) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -503,7 +504,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface */ $connection->insert( - $setup->getTable('eav_form_type'), + $this->moduleDataSetup->getTable('eav_form_type'), [ 'code' => 'checkout_onepage_billing_address', 'label' => 'checkout_onepage_billing_address', @@ -512,17 +513,17 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'store_id' => 0 ] ); - $formTypeId = $connection->lastInsertId($setup->getTable('eav_form_type')); + $formTypeId = $connection->lastInsertId($this->moduleDataSetup->getTable('eav_form_type')); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $addressEntityTypeId] ); $elementSort = 0; if ($showPrefix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -532,7 +533,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -542,7 +543,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showMiddlename) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -552,7 +553,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -562,7 +563,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showSuffix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -572,7 +573,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -581,7 +582,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -590,7 +591,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -599,7 +600,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -608,7 +609,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -617,7 +618,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -626,7 +627,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -635,7 +636,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -651,7 +652,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface */ $connection->insert( - $setup->getTable('eav_form_type'), + $this->moduleDataSetup->getTable('eav_form_type'), [ 'code' => 'checkout_onepage_shipping_address', 'label' => 'checkout_onepage_shipping_address', @@ -660,17 +661,17 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'store_id' => 0 ] ); - $formTypeId = $connection->lastInsertId($setup->getTable('eav_form_type')); + $formTypeId = $connection->lastInsertId($this->moduleDataSetup->getTable('eav_form_type')); $connection->insert( - $setup->getTable('eav_form_type_entity'), + $this->moduleDataSetup->getTable('eav_form_type_entity'), ['type_id' => $formTypeId, 'entity_type_id' => $addressEntityTypeId] ); $elementSort = 0; if ($showPrefix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -680,7 +681,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -690,7 +691,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showMiddlename) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -700,7 +701,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -710,7 +711,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); if ($showSuffix) { $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -720,7 +721,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ); } $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -729,7 +730,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -738,7 +739,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -747,7 +748,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -756,7 +757,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -765,7 +766,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -774,7 +775,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -783,7 +784,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); $connection->insert( - $setup->getTable('eav_form_element'), + $this->moduleDataSetup->getTable('eav_form_element'), [ 'type_id' => $formTypeId, 'fieldset_id' => null, @@ -792,7 +793,7 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); - $table = $setup->getTable('core_config_data'); + $table = $this->moduleDataSetup->getTable('core_config_data'); $select = $connection->select()->from( $table, @@ -821,6 +822,30 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface } } - $setup->endSetup(); + $connection->endSetup(); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index 5fa12521fc3..ba6bba6d633 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Test\Unit\Model; /** @@ -98,7 +99,6 @@ public function testSavePaymentInformationAndPlaceOrder() } /** - * @expectedExceptionMessage An error occurred on the server. Please try to place the order again. * @expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testSavePaymentInformationAndPlaceOrderException() @@ -118,6 +118,10 @@ public function testSavePaymentInformationAndPlaceOrderException() $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); + + $this->expectExceptionMessage( + 'A server error stopped your order from being placed. Please try to place your order again.' + ); } public function testSavePaymentInformation() diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index b40b2b244ac..77c15fccfa8 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Test\Unit\Model; /** @@ -83,7 +84,6 @@ public function testSavePaymentInformationAndPlaceOrder() } /** - * @expectedExceptionMessage An error occurred on the server. Please try to place the order again. * @expectedException \Magento\Framework\Exception\CouldNotSaveException */ public function testSavePaymentInformationAndPlaceOrderException() @@ -99,6 +99,10 @@ public function testSavePaymentInformationAndPlaceOrderException() $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); + + $this->expectExceptionMessage( + 'A server error stopped your order from being placed. Please try to place your order again.' + ); } public function testSavePaymentInformationAndPlaceOrderIfBillingAddressNotExist() diff --git a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php index 5c6314b2a35..dd88b7161ac 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Test\Unit\Model; /** @@ -157,7 +158,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Shipping method is not applicable for empty cart + * @expectedExceptionMessage The shipping method can't be set for an empty cart. Add an item to cart and try again. */ public function testSaveAddressInformationIfCartIsEmpty() { @@ -238,7 +239,7 @@ private function setShippingAssignmentsMocks($shippingMethod) /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedExceptionMessage Shipping address is not set + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. */ public function testSaveAddressInformationIfShippingAddressNotSet() { @@ -263,7 +264,7 @@ public function testSaveAddressInformationIfShippingAddressNotSet() /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Unable to save shipping information. Please check input data. + * @expectedExceptionMessage The shipping information was unable to be saved. Verify the input data and try again. */ public function testSaveAddressInformationIfCanNotSaveQuote() { diff --git a/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php b/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php index 29537e8ec05..c07413c8611 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Test\Unit\Model; use Magento\Checkout\Model\Sidebar; @@ -94,7 +95,7 @@ public function testCheckQuoteItem() /** * @expectedException \Magento\Framework\Exception\LocalizedException - * @exceptedExceptionMessage We can't find the quote item. + * @exceptedExceptionMessage The quote item isn't found. Verify the item and try again. */ public function testCheckQuoteItemWithException() { diff --git a/app/code/Magento/Checkout/etc/module.xml b/app/code/Magento/Checkout/etc/module.xml index 219ca87337f..153de5ef2a7 100644 --- a/app/code/Magento/Checkout/etc/module.xml +++ b/app/code/Magento/Checkout/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml index b41d548e95b..c1db2f7775c 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml @@ -23,7 +23,7 @@ value="" title="" class="input-text qty" - data-validate="{'required-number':true,digits:true}"/> + data-validate="escapeHtml(json_encode($block->getQuantityValidators())) ?>"/> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 619de95e467..a7cb7f7e7de 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -266,7 +266,9 @@ define([ field; if (!quote.shippingMethod()) { - this.errorValidationMessage($t('Please specify a shipping method.')); + this.errorValidationMessage( + $t('The shipping method is missing. Select the shipping method and try again.') + ); return false; } diff --git a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php index 4176334bb4a..fbceca09067 100644 --- a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php +++ b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/GuestValidation.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CheckoutAgreements\Model\Checkout\Plugin; use Magento\CheckoutAgreements\Model\AgreementsProvider; @@ -113,7 +114,10 @@ private function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $pa if (!$this->agreementsValidator->isValid($agreements)) { throw new \Magento\Framework\Exception\CouldNotSaveException( - __('Please agree to all the terms and conditions before placing the order.') + __( + "The order wasn't placed. " + . "First, agree to the terms and conditions, then try placing your order again." + ) ); } } diff --git a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php index b8a6afff4fc..67e2a6c9ec3 100644 --- a/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php +++ b/app/code/Magento/CheckoutAgreements/Model/Checkout/Plugin/Validation.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CheckoutAgreements\Model\Checkout\Plugin; use Magento\CheckoutAgreements\Model\AgreementsProvider; @@ -105,7 +106,10 @@ protected function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $ if (!$this->agreementsValidator->isValid($agreements)) { throw new \Magento\Framework\Exception\CouldNotSaveException( - __('Please agree to all the terms and conditions before placing the order.') + __( + "The order wasn't placed. " + . "First, agree to the terms and conditions, then try placing your order again." + ) ); } } diff --git a/app/code/Magento/CheckoutAgreements/Model/CheckoutAgreementsRepository.php b/app/code/Magento/CheckoutAgreements/Model/CheckoutAgreementsRepository.php index 9379b73a1b9..60cc1f98264 100644 --- a/app/code/Magento/CheckoutAgreements/Model/CheckoutAgreementsRepository.php +++ b/app/code/Magento/CheckoutAgreements/Model/CheckoutAgreementsRepository.php @@ -138,7 +138,7 @@ public function save(\Magento\CheckoutAgreements\Api\Data\AgreementInterface $da $this->resourceModel->save($data); } catch (\Exception $e) { throw new \Magento\Framework\Exception\CouldNotSaveException( - __('Unable to save checkout agreement %1', $data->getAgreementId()) + __('The "%1" checkout agreement couldn\'t be saved.', $data->getAgreementId()) ); } return $data; @@ -153,7 +153,7 @@ public function delete(\Magento\CheckoutAgreements\Api\Data\AgreementInterface $ $this->resourceModel->delete($data); } catch (\Exception $e) { throw new \Magento\Framework\Exception\CouldNotDeleteException( - __('Unable to remove checkout agreement %1', $data->getAgreementId()) + __('The "%1" checkout agreement couldn\'t be removed.', $data->getAgreementId()) ); } return true; @@ -178,7 +178,9 @@ public function get($id, $storeId = null) $agreement = $this->agreementFactory->create(); $this->resourceModel->load($agreement, $id); if (!$agreement->getId()) { - throw new NoSuchEntityException(__('Checkout agreement with specified ID "%1" not found.', $id)); + throw new NoSuchEntityException( + __('A checkout agreement with the "%1" specified ID wasn\'t found. Verify the ID and try again.', $id) + ); } return $agreement; } diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php index baaaad1bb6a..3d7b910c7ab 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/GuestValidationTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CheckoutAgreements\Test\Unit\Model\Checkout\Plugin; use Magento\CheckoutAgreements\Model\AgreementsProvider; @@ -119,7 +120,6 @@ public function testBeforeSavePaymentInformationAndPlaceOrder() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Please agree to all the terms and conditions before placing the order. */ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotValid() { @@ -151,6 +151,10 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali $this->paymentMock, $this->addressMock ); + + $this->expectExceptionMessage( + "The order wasn't placed. First, agree to the terms and conditions, then try placing your order again." + ); } public function testBeforeSavePaymentInformation() diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php index b77cab203eb..7f11fad2024 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/Checkout/Plugin/ValidationTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CheckoutAgreements\Test\Unit\Model\Checkout\Plugin; use Magento\CheckoutAgreements\Model\AgreementsProvider; @@ -112,7 +113,6 @@ public function testBeforeSavePaymentInformationAndPlaceOrder() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Please agree to all the terms and conditions before placing the order. */ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotValid() { @@ -137,6 +137,10 @@ public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotVali ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); $this->model->beforeSavePaymentInformation($this->subjectMock, $cartId, $this->paymentMock, $this->addressMock); + + $this->expectExceptionMessage( + "The order wasn't placed. First, agree to the terms and conditions, then try placing your order again." + ); } public function testBeforeSavePaymentInformation() diff --git a/app/code/Magento/CheckoutAgreements/etc/db_schema.xml b/app/code/Magento/CheckoutAgreements/etc/db_schema.xml index 41bd8dffb4b..31b3111df98 100644 --- a/app/code/Magento/CheckoutAgreements/etc/db_schema.xml +++ b/app/code/Magento/CheckoutAgreements/etc/db_schema.xml @@ -6,7 +6,7 @@ */ --> + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
diff --git a/app/code/Magento/CheckoutAgreements/etc/module.xml b/app/code/Magento/CheckoutAgreements/etc/module.xml index 42a373eaad4..1ea97b556be 100644 --- a/app/code/Magento/CheckoutAgreements/etc/module.xml +++ b/app/code/Magento/CheckoutAgreements/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php index f1e1b3797d8..44c2f3ca2f8 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Content.php @@ -92,7 +92,10 @@ protected function _construct() */ public function getContentsUrl() { - return $this->getUrl('cms/*/contents', ['type' => $this->getRequest()->getParam('type')]); + return $this->getUrl('cms/*/contents', [ + 'type' => $this->getRequest()->getParam('type'), + 'use_storage_root' => (int) $this->getRequest()->getParam('use_storage_root'), + ]); } /** @@ -140,7 +143,9 @@ public function getNewfolderUrl() */ protected function getDeletefolderUrl() { - return $this->getUrl('cms/*/deleteFolder'); + return $this->getUrl('cms/*/deleteFolder', [ + 'use_storage_root' => (int) $this->getRequest()->getParam('use_storage_root'), + ]); } /** @@ -160,7 +165,9 @@ public function getDeleteFilesUrl() */ public function getOnInsertUrl() { - return $this->getUrl('cms/*/onInsert'); + return $this->getUrl('cms/*/onInsert', [ + 'use_storage_root' => (int) $this->getRequest()->getParam('use_storage_root'), + ]); } /** diff --git a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php index 5471bbfb8b0..7bed7cee308 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php @@ -69,12 +69,21 @@ public function getTreeJson() ); $jsonArray = []; foreach ($collection as $item) { - $jsonArray[] = [ + $data = [ 'text' => $this->_cmsWysiwygImages->getShortFilename($item->getBasename(), 20), 'id' => $this->_cmsWysiwygImages->convertPathToId($item->getFilename()), 'path' => substr($item->getFilename(), strlen($storageRoot)), 'cls' => 'folder', ]; + + $hasNestedDirectories = count(glob($item->getFilename() . '/*', GLOB_ONLYDIR)) > 0; + + // if no nested directories inside dir, add 'leaf' state so that jstree hides dropdown arrow next to dir + if (!$hasNestedDirectories) { + $data['state'] = 'leaf'; + } + + $jsonArray[] = $data; } return $this->serializer->serialize($jsonArray); } @@ -86,7 +95,10 @@ public function getTreeJson() */ public function getTreeLoaderUrl() { - return $this->getUrl('cms/*/treeJson'); + return $this->getUrl( + 'cms/*/treeJson', + ['use_storage_root' => (int) $this->getRequest()->getParam('use_storage_root')] + ); } /** diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php index 2daaf39d58d..3244a7d14f0 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php @@ -34,17 +34,26 @@ public function __construct( */ public function execute() { - $helper = $this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); - $storeId = $this->getRequest()->getParam('store'); + $imagesHelper = $this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); + $request = $this->getRequest(); - $filename = $this->getRequest()->getParam('filename'); - $filename = $helper->idDecode($filename); - $asIs = $this->getRequest()->getParam('as_is'); + $storeId = $request->getParam('store'); + + $filename = $request->getParam('filename'); + $filename = $imagesHelper->idDecode($filename); + + $asIs = $request->getParam('as_is'); + + $forceStaticPath = $request->getParam('force_static_path'); $this->_objectManager->get(\Magento\Catalog\Helper\Data::class)->setStoreId($storeId); - $helper->setStoreId($storeId); + $imagesHelper->setStoreId($storeId); - $image = $helper->getImageHtmlDeclaration($filename, $asIs); + if ($forceStaticPath) { + $image = parse_url($imagesHelper->getCurrentUrl() . $filename, PHP_URL_PATH); + } else { + $image = $imagesHelper->getImageHtmlDeclaration($filename, $asIs); + } /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); diff --git a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php index 0fe36e0d74e..3e410799906 100644 --- a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php +++ b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php @@ -9,10 +9,16 @@ /** * Wysiwyg Images Helper - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Images extends \Magento\Framework\App\Helper\AbstractHelper { + /** + * Image directory subpath relative to media directory + * + * @var string + */ + private $imageDirectorySubpath; + /** * Current directory path * @var string @@ -80,7 +86,7 @@ public function __construct( $this->escaper = $escaper; $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); - $this->_directory->create(\Magento\Cms\Model\Wysiwyg\Config::IMAGE_DIRECTORY); + $this->_directory->create($this->getStorageRoot()); } /** @@ -102,7 +108,19 @@ public function setStoreId($store) */ public function getStorageRoot() { - return $this->_directory->getAbsolutePath(\Magento\Cms\Model\Wysiwyg\Config::IMAGE_DIRECTORY); + return $this->_directory->getAbsolutePath($this->getStorageRootSubpath()); + } + + /** + * Get image storage root subpath. User is unable to traverse outside of this subpath in media gallery + * + * @return string + */ + public function getStorageRootSubpath() + { + return $this->_getRequest()->getParam('use_storage_root') + ? '' + : \Magento\Cms\Model\Wysiwyg\Config::IMAGE_DIRECTORY; } /** @@ -159,7 +177,7 @@ public function convertIdToPath($id) */ public function isUsingStaticUrlsAllowed() { - $checkResult = new \stdClass(); + $checkResult = (object) []; $checkResult->isAllowed = false; $this->_eventManager->dispatch( 'cms_wysiwyg_images_static_urls_allowed', @@ -189,7 +207,13 @@ public function getImageHtmlDeclaration($filename, $renderAsTag = false) $html = $fileUrl; } else { $directive = $this->urlEncoder->encode($directive); - $html = $this->_backendData->getUrl('cms/wysiwyg/directive', ['___directive' => $directive]); + $html = $this->_backendData->getUrl( + 'cms/wysiwyg/directive', + [ + '___directive' => $directive, + '_escape_params' => false, + ] + ); } } return $html; @@ -205,7 +229,7 @@ public function getImageHtmlDeclaration($filename, $renderAsTag = false) public function getCurrentPath() { if (!$this->_currentPath) { - $currentPath = $this->_directory->getAbsolutePath() . \Magento\Cms\Model\Wysiwyg\Config::IMAGE_DIRECTORY; + $currentPath = $this->getStorageRoot(); $path = $this->_getRequest()->getParam($this->getTreeNodeName()); if ($path) { $path = $this->convertIdToPath($path); @@ -241,7 +265,7 @@ public function getCurrentUrl() )->getBaseUrl( \Magento\Framework\UrlInterface::URL_TYPE_MEDIA ); - $this->_currentUrl = $mediaUrl . $this->_directory->getRelativePath($path) . '/'; + $this->_currentUrl = rtrim($mediaUrl . $this->_directory->getRelativePath($path), '/') . '/'; } return $this->_currentUrl; } @@ -283,4 +307,15 @@ public function getShortFilename($filename, $maxLength = 20) } return substr($filename, 0, $maxLength) . '...'; } + + /** + * Set user-traversable image directory subpath relative to media directory and relative to nested storage root + * + * @var string $subpath + * @return void + */ + public function setImageDirectorySubpath($subpath) + { + $this->imageDirectorySubpath = $subpath; + } } diff --git a/app/code/Magento/Cms/Model/BlockRepository.php b/app/code/Magento/Cms/Model/BlockRepository.php index e724c1581e8..fa29cc9ff76 100644 --- a/app/code/Magento/Cms/Model/BlockRepository.php +++ b/app/code/Magento/Cms/Model/BlockRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Cms\Model; use Magento\Cms\Api\BlockRepositoryInterface; @@ -134,7 +135,7 @@ public function getById($blockId) $block = $this->blockFactory->create(); $this->resource->load($block, $blockId); if (!$block->getId()) { - throw new NoSuchEntityException(__('CMS Block with id "%1" does not exist.', $blockId)); + throw new NoSuchEntityException(__('The CMS block with the "%1" ID doesn\'t exist.', $blockId)); } return $block; } diff --git a/app/code/Magento/Cms/Model/GetBlockByIdentifier.php b/app/code/Magento/Cms/Model/GetBlockByIdentifier.php index 587785ce3d8..071e6225c82 100644 --- a/app/code/Magento/Cms/Model/GetBlockByIdentifier.php +++ b/app/code/Magento/Cms/Model/GetBlockByIdentifier.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Cms\Model; use Magento\Cms\Api\GetBlockByIdentifierInterface; @@ -46,7 +47,7 @@ public function execute(string $identifier, int $storeId) : BlockInterface $this->blockResource->load($block, $identifier, BlockInterface::IDENTIFIER); if (!$block->getId()) { - throw new NoSuchEntityException(__('CMS Block with identifier "%1" does not exist.', $identifier)); + throw new NoSuchEntityException(__('The CMS block with the "%1" ID doesn\'t exist.', $identifier)); } return $block; diff --git a/app/code/Magento/Cms/Model/GetPageByIdentifier.php b/app/code/Magento/Cms/Model/GetPageByIdentifier.php index d12e7749f7d..d3ad65fb011 100644 --- a/app/code/Magento/Cms/Model/GetPageByIdentifier.php +++ b/app/code/Magento/Cms/Model/GetPageByIdentifier.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Cms\Model; use Magento\Cms\Api\Data\PageInterface; @@ -46,7 +47,7 @@ public function execute(string $identifier, int $storeId) : PageInterface $this->pageResource->load($page, $identifier, PageInterface::IDENTIFIER); if (!$page->getId()) { - throw new NoSuchEntityException(__('CMS Page with identifier "%1" does not exist.', $identifier)); + throw new NoSuchEntityException(__('The CMS page with the "%1" ID doesn\'t exist.', $identifier)); } return $page; diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 9c9e18211aa..65b23bce7e9 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Cms\Model; use Magento\Cms\Api\Data; @@ -137,7 +138,7 @@ public function getById($pageId) $page = $this->pageFactory->create(); $page->load($pageId); if (!$page->getId()) { - throw new NoSuchEntityException(__('CMS Page with id "%1" does not exist.', $pageId)); + throw new NoSuchEntityException(__('The CMS page with the "%1" ID doesn\'t exist.', $pageId)); } return $page; } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page.php b/app/code/Magento/Cms/Model/ResourceModel/Page.php index b836cf19963..079d64d2882 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Page.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Page.php @@ -117,13 +117,16 @@ protected function _beforeSave(AbstractModel $object) if (!$this->isValidPageIdentifier($object)) { throw new LocalizedException( - __('The page URL key contains capital letters or disallowed symbols.') + __( + "The page URL key can't use capital letters or disallowed symbols. " + . "Remove the letters and symbols and try again." + ) ); } if ($this->isNumericPageIdentifier($object)) { throw new LocalizedException( - __('The page URL key cannot be made of only numbers.') + __("The page URL key can't use only numbers. Add letters or words and try again.") ); } return parent::_beforeSave($object); diff --git a/app/code/Magento/Cms/Model/Wysiwyg/CompositeConfigProvider.php b/app/code/Magento/Cms/Model/Wysiwyg/CompositeConfigProvider.php index b2f32132ad3..a97529a7d4e 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/CompositeConfigProvider.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/CompositeConfigProvider.php @@ -130,12 +130,16 @@ public function processWysiwygConfig($config) /** * Returns active editor path * + * @param \Magento\Framework\DataObject $config * @return string */ - private function getActiveEditorPath() + private function getActiveEditorPath($config) { - if (!isset($this->activeEditorPath)) { - $this->activeEditorPath = $this->activeEditor->getWysiwygAdapterPath(); + if (!isset($this->activeEditorPath) || $this->activeEditorPath !== $config->getData('activeEditorPath')) { + $this->activeEditorPath = $config->getData('activeEditorPath') + ? $config->getData('activeEditorPath') + : $this->activeEditor->getWysiwygAdapterPath(); + $config->setData('activeEditorPath', $this->activeEditorPath); } return $this->activeEditorPath; } @@ -149,7 +153,7 @@ private function getActiveEditorPath() */ private function updateConfig($config, array $configProviders) { - $adapterType = $this->getActiveEditorPath(); + $adapterType = $this->getActiveEditorPath($config); //Extension point to update plugin settings by adapter type $providerClass = isset($configProviders[$adapterType]) ? $configProviders[$adapterType] diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Config.php b/app/code/Magento/Cms/Model/Wysiwyg/Config.php index 9fdb27f0f02..5db3933dd11 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Config.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Config.php @@ -201,13 +201,13 @@ public function getConfig($data = []) $config->setData('directives_url_quoted', preg_quote($config->getData('directives_url'))); - if ($this->_authorization->isAllowed('Magento_Cms::media_gallery')) { - $this->configProvider->processGalleryConfig($config); - } - if (is_array($data)) { $config->addData($data); } + + if ($this->_authorization->isAllowed('Magento_Cms::media_gallery')) { + $this->configProvider->processGalleryConfig($config); + } if ($config->getData('add_widgets')) { $this->configProvider->processWidgetConfig($config); } diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 0688534dc4a..0c8ff7d0b2b 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -241,10 +241,12 @@ protected function getConditionsForExcludeDirs() protected function removeItemFromCollection($collection, $conditions) { $regExp = $conditions['reg_exp'] ? '~' . implode('|', array_keys($conditions['reg_exp'])) . '~i' : null; - $storageRootLength = strlen($this->_cmsWysiwygImages->getStorageRoot()); + $storageRoot = $this->_cmsWysiwygImages->getStorageRoot(); + $storageRootLength = strlen($storageRoot); foreach ($collection as $key => $value) { - $rootChildParts = explode('/', substr($value->getFilename(), $storageRootLength)); + $mediaSubPathname = substr($value->getFilename(), $storageRootLength); + $rootChildParts = explode('/', '/' . ltrim($mediaSubPathname, '/')); if (array_key_exists($rootChildParts[1], $conditions['plain']) || ($regExp && preg_match($regExp, $value->getFilename()))) { @@ -318,6 +320,8 @@ public function getFilesCollection($path, $type = null) $item->setName($item->getBasename()); $item->setShortName($this->_cmsWysiwygImages->getShortFilename($item->getBasename())); $item->setUrl($this->_cmsWysiwygImages->getCurrentUrl() . $item->getBasename()); + $item->setSize(filesize($item->getFilename())); + $item->setMimeType(\mime_content_type($item->getFilename())); if ($this->isImage($item->getBasename())) { $thumbUrl = $this->getThumbnailUrl($item->getFilename(), true); diff --git a/app/code/Magento/Cms/Setup/Patch/Data/ConvertWidgetConditionsToJson.php b/app/code/Magento/Cms/Setup/Patch/Data/ConvertWidgetConditionsToJson.php new file mode 100644 index 00000000000..61f26c9bd07 --- /dev/null +++ b/app/code/Magento/Cms/Setup/Patch/Data/ConvertWidgetConditionsToJson.php @@ -0,0 +1,158 @@ +moduleDataSetup = $moduleDataSetup; + $this->queryModifierFactory = $queryModifierFactory; + $this->metadataPool = $metadataPool; + $this->aggregatedFieldDataConverter = $aggregatedFieldDataConverter; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + $queryModifier = $this->queryModifierFactory->create( + 'like', + [ + 'values' => [ + 'content' => '%conditions_encoded%' + ] + ] + ); + $layoutUpdateXmlFieldQueryModifier = $this->queryModifierFactory->create( + 'like', + [ + 'values' => [ + 'layout_update_xml' => '%conditions_encoded%' + ] + ] + ); + $customLayoutUpdateXmlFieldQueryModifier = $this->queryModifierFactory->create( + 'like', + [ + 'values' => [ + 'custom_layout_update_xml' => '%conditions_encoded%' + ] + ] + ); + $blockMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + $pageMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $this->aggregatedFieldDataConverter->convert( + [ + new FieldToConvert( + ContentConverter::class, + $this->moduleDataSetup->getTable('cms_block'), + $blockMetadata->getIdentifierField(), + 'content', + $queryModifier + ), + new FieldToConvert( + ContentConverter::class, + $this->moduleDataSetup->getTable('cms_page'), + $pageMetadata->getIdentifierField(), + 'content', + $queryModifier + ), + new FieldToConvert( + LayoutUpdateConverter::class, + $this->moduleDataSetup->getTable('cms_page'), + $pageMetadata->getIdentifierField(), + 'layout_update_xml', + $layoutUpdateXmlFieldQueryModifier + ), + new FieldToConvert( + LayoutUpdateConverter::class, + $this->moduleDataSetup->getTable('cms_page'), + $pageMetadata->getIdentifierField(), + 'custom_layout_update_xml', + $customLayoutUpdateXmlFieldQueryModifier + ), + ], + $this->moduleDataSetup->getConnection() + ); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + UpdatePrivacyPolicyPage::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.2'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Cms/Setup/InstallData.php b/app/code/Magento/Cms/Setup/Patch/Data/CreateDefaultPages.php similarity index 92% rename from app/code/Magento/Cms/Setup/InstallData.php rename to app/code/Magento/Cms/Setup/Patch/Data/CreateDefaultPages.php index 57e9aadd691..615cdb144fd 100644 --- a/app/code/Magento/Cms/Setup/InstallData.php +++ b/app/code/Magento/Cms/Setup/Patch/Data/CreateDefaultPages.php @@ -4,42 +4,47 @@ * See COPYING.txt for license details. */ -namespace Magento\Cms\Setup; +namespace Magento\Cms\Setup\Patch\Data; -use Magento\Cms\Model\Page; -use Magento\Cms\Model\PageFactory; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; use Magento\Framework\Module\Setup\Migration; -use Magento\Framework\Setup\InstallDataInterface; -use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; /** - * @codeCoverageIgnore + * Class CreateDefaultPages + * @package Magento\Cms\Setup\Patch */ -class InstallData implements InstallDataInterface +class CreateDefaultPages implements DataPatchInterface, PatchVersionInterface { /** - * Page factory - * - * @var PageFactory + * @var \Magento\Cms\Model\PageFactory */ private $pageFactory; /** - * Init - * - * @param PageFactory $pageFactory + * @var ModuleDataSetupInterface */ - public function __construct(PageFactory $pageFactory) - { + private $moduleDataSetup; + + /** + * CreateDefaultPages constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Cms\Model\PageFactory $pageFactory + */ + public function __construct( + \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup, + \Magento\Cms\Model\PageFactory $pageFactory + ) { $this->pageFactory = $pageFactory; + $this->moduleDataSetup = $moduleDataSetup; } /** * {@inheritdoc} * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + public function apply() { $cmsPages = [ [ @@ -101,7 +106,6 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface foreach ($cmsPages as $data) { $this->createPage()->setData($data)->save(); } - $pageContent = <<
@@ -314,7 +318,6 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface
EOD; - $privacyPageData = [ 'title' => 'Privacy and Cookie Policy', 'content_heading' => 'Privacy and Cookie Policy', @@ -325,11 +328,8 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface 'stores' => [0], 'sort_order' => 0, ]; - $this->createPage()->setData($privacyPageData)->save(); - $footerLinksBlock = $this->createPage()->load('footer_links', 'identifier'); - if ($footerLinksBlock->getId()) { $content = $footerLinksBlock->getContent(); if (preg_match('/