diff --git a/.travis.yml b/.travis.yml
index cc730ca5a2cd4..bd2b94865cafd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,6 +33,7 @@ env:
- TEST_SUITE=integration INTEGRATION_INDEX=2
- TEST_SUITE=integration INTEGRATION_INDEX=3
- TEST_SUITE=functional
+ - TEST_SUITE=graphql-api-functional
matrix:
exclude:
- php: 7.1
@@ -43,6 +44,8 @@ matrix:
env: TEST_SUITE=js GRUNT_COMMAND=static
- php: 7.1
env: TEST_SUITE=functional
+ - php: 7.1
+ env: TEST_SUITE=graphql-api-functional
cache:
apt: true
directories:
@@ -61,5 +64,6 @@ script:
# The scripts for grunt/phpunit type tests
- if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi
- - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi
+ - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js"] && [ $TEST_SUITE != "graphql-api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi
- if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi
+ - if [ $TEST_SUITE == "graphql-api-functional" ]; then phpunit -c dev/tests/api-functional; fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc722b6d61b33..b86c7b79a0cbd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -157,7 +157,7 @@ To get detailed information about changes in Magento 2.1.0, please visit [Magent
* Updated styles
* Sample Data:
* Improved sample data installation UX
- * Updated sample data with Product Heros, color swatches, MAP and rule based product relations
+ * Updated sample data with Product Heroes, color swatches, MAP and rule based product relations
* Improved sample data upgrade flow
* Added the ability to log errors and set the error flag during sample data installation
* Various improvements:
@@ -2284,7 +2284,7 @@ Tests:
* Fixed an issue where no results were found for Coupons reports
* Fixed an issue with incremental Qty setting
* Fixed an issue with allowing importing of negative weight values
- * Fixed an issue with Inventory - Only X left Treshold being not dependent on Qty for Item's Status to Become Out of Stock
+ * Fixed an issue with Inventory - Only X left Threshold being not dependent on Qty for Item's Status to Become Out of Stock
* Fixed an issue where the "Catalog Search Index index was rebuilt." message was displayed when reindexing the Catalog Search index
* Search module:
* Integrated the Search library to the advanced search functionality
@@ -2706,7 +2706,7 @@ Tests:
* Ability to support extensible service data objects
* No Code Duplication in Root Templates
* Fixed bugs:
- * Persistance session application. Loggin out the customer
+ * Persistence session application. Logging out the customer
* Placing the order with two terms and conditions
* Saving of custom option by service catalogProductCustomOptionsWriteServiceV1
* Placing the order on frontend if enter in the street address line 1 and 2 255 symbols
@@ -2965,7 +2965,7 @@ Tests:
* Fixed an issue with incorrect items label for the cases when there are more than one item in the category
* Fixed an issue when configurable product was out of stock in Google Shopping while being in stock in the Magento backend
* Fixed an issue when swipe gesture in menu widget was not supported on mobile
- * Fixed an issue when it was impossible to enter alpha-numeric zip code on the stage of estimating shipping and tax rates
+ * Fixed an issue when it was impossible to enter alphanumeric zip code on the stage of estimating shipping and tax rates
* Fixed an issue when custom price was not applied when editing an order
* Fixed an issue when items were not returned to stock after unsuccessful order was placed
* Fixed an issue when error message appeared "Cannot save the credit memo” while creating credit memo
diff --git a/README.md b/README.md
index c292f1100f336..979e60a167748 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,11 @@
Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results.
## Magento system requirements
-[Magento system requirements](http://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html).
+[Magento system requirements](https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html).
## Install Magento
-* [Installation guide](http://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html).
+* [Installation guide](https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html).
Contributing to the Magento 2 code base
Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions.
@@ -21,10 +21,10 @@ To learn about issues, click [here][2]. To open an issue, click [here][3].
To suggest documentation improvements, click [here][4].
-[1]:
-[2]:
+[1]:
+[2]:
[3]:
-[4]:
+[4]:
Community Maintainers
The members of this team have been recognized for their outstanding commitment to maintaining and improving Magento. Magento has granted them permission to accept, merge, and reject pull requests, as well as review issues, and thanks these Community Maintainers for their valuable contributions.
@@ -53,9 +53,9 @@ Stay up-to-date on the latest security news and patches for Magento by signing u
Each Magento source file included in this distribution is licensed under OSL 3.0 or the Magento Enterprise Edition (MEE) license.
-http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
-Please see LICENSE.txt for the full text of the OSL 3.0 license or contact license@magentocommerce.com for a copy.
+[Open Software License (OSL 3.0)](https://opensource.org/licenses/osl-3.0.php).
+Please see [LICENSE.txt](https://github.com/magento/magento2/blob/2.3-develop/LICENSE.txt) for the full text of the OSL 3.0 license or contact license@magentocommerce.com for a copy.
Subject to Licensee's payment of fees and compliance with the terms and conditions of the MEE License, the MEE License supersedes the OSL 3.0 license for each source file.
-Please see LICENSE_EE.txt for the full text of the MEE License or visit http://magento.com/legal/terms/enterprise.
+Please see LICENSE_EE.txt for the full text of the MEE License or visit https://magento.com/legal/terms/enterprise.
diff --git a/app/code/Magento/AdminNotification/Block/Window.php b/app/code/Magento/AdminNotification/Block/Window.php
index 9563626ee2577..e9b4bfa44893d 100644
--- a/app/code/Magento/AdminNotification/Block/Window.php
+++ b/app/code/Magento/AdminNotification/Block/Window.php
@@ -8,6 +8,8 @@
namespace Magento\AdminNotification\Block;
/**
+ * Admin notification window block
+ *
* @api
* @since 100.0.2
*/
@@ -40,7 +42,7 @@ class Window extends \Magento\Backend\Block\Template
protected $_criticalCollection;
/**
- * @var \Magento\Adminnotification\Model\Inbox
+ * @var \Magento\AdminNotification\Model\Inbox
*/
protected $_latestItem;
@@ -92,7 +94,7 @@ protected function _toHtml()
/**
* Retrieve latest critical item
*
- * @return bool|\Magento\Adminnotification\Model\Inbox
+ * @return bool|\Magento\AdminNotification\Model\Inbox
*/
protected function _getLatestItem()
{
diff --git a/app/code/Magento/AdminNotification/etc/db_schema.xml b/app/code/Magento/AdminNotification/etc/db_schema.xml
index 35e6045b607d1..29d928ced2084 100644
--- a/app/code/Magento/AdminNotification/etc/db_schema.xml
+++ b/app/code/Magento/AdminNotification/etc/db_schema.xml
@@ -21,16 +21,16 @@
default="0" comment="Flag if notification read"/>
-
+
-
+
-
+
-
+
@@ -40,7 +40,7 @@
default="0" comment="Problem type"/>
-
+
diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php
index b46e286e75007..d78c4f5e61af3 100644
--- a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php
+++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/WebsiteTest.php
@@ -103,13 +103,13 @@ public function testGetAllWebsitesValue()
$this->webSiteModel->expects($this->once())->method('getBaseCurrency')->willReturn($currency);
$expectedResult = AdvancedPricing::VALUE_ALL_WEBSITES . ' [' . $currencyCode . ']';
- $this->websiteString = $this->getMockBuilder(
+ $websiteString = $this->getMockBuilder(
\Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website::class
)
->setMethods(['_clearMessages', '_addMessages'])
->setConstructorArgs([$this->storeResolver, $this->webSiteModel])
->getMock();
- $result = $this->websiteString->getAllWebsitesValue();
+ $result = $websiteString->getAllWebsitesValue();
$this->assertEquals($expectedResult, $result);
}
diff --git a/app/code/Magento/AdvancedSearch/etc/db_schema.xml b/app/code/Magento/AdvancedSearch/etc/db_schema.xml
index 9fae40411098c..2dd8c68e2d5fd 100644
--- a/app/code/Magento/AdvancedSearch/etc/db_schema.xml
+++ b/app/code/Magento/AdvancedSearch/etc/db_schema.xml
@@ -14,13 +14,13 @@
default="0" comment="Query Id"/>
-
+
-
-
diff --git a/app/code/Magento/Analytics/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Analytics/Test/Mftf/Page/AdminConfigPage.xml
new file mode 100644
index 0000000000000..c4ced12e67e07
--- /dev/null
+++ b/app/code/Magento/Analytics/Test/Mftf/Page/AdminConfigPage.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigAdvancedReportingSection.xml
similarity index 86%
rename from app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigSection.xml
rename to app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigAdvancedReportingSection.xml
index f8554a4ea115b..2e5f2b762a7b1 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigSection.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Section/AdminConfigAdvancedReportingSection.xml
@@ -6,8 +6,8 @@
*/
-->
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml
index ff89ca9b663ee..914cb59b64e4e 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationBlankIndustryTest.xml
@@ -18,15 +18,14 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml
index 1706383fc7866..1c1a3b27b06af 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationEnableDisableAnalyticsTest.xml
@@ -18,27 +18,24 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml
index dcfdca9e8edd7..624aa952fbce9 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationIndustryTest.xml
@@ -17,15 +17,17 @@
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml
index 5414e9c2a5c18..58e809ec45c4a 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml
@@ -19,44 +19,47 @@
-
-
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
index 3f17df108b50b..58e62500b8203 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml
@@ -19,17 +19,16 @@
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml
index 4e21648d00ce8..c7da840b7e665 100644
--- a/app/code/Magento/Analytics/etc/adminhtml/system.xml
+++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml
@@ -36,6 +36,9 @@
Magento\Analytics\Model\Config\Source\Vertical
Magento\Analytics\Model\Config\Backend\Vertical
Magento\Analytics\Block\Adminhtml\System\Config\Vertical
+
+ 1
+
Enabled Template Path Hints for Admin
@@ -338,15 +338,26 @@
Images Upload Configuration
-
+
+ Enable Frontend Resize
+ Magento\Config\Model\Config\Source\Yesno
+ Resize performed via javascript before file upload.
+
+
Maximum Width
validate-greater-than-zero validate-number required-entry
Maximum allowed width for uploaded image.
+
+ 1
+
-
+
Maximum Height
validate-greater-than-zero validate-number required-entry
Maximum allowed height for uploaded image.
+
+ 1
+
diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml
index 45d283ad3ff22..8283fa18dd370 100644
--- a/app/code/Magento/Backend/etc/config.xml
+++ b/app/code/Magento/Backend/etc/config.xml
@@ -29,6 +29,7 @@
1
+ 1
1920
1200
diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml
index 966372773f295..4d9ba6a8c4bad 100644
--- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml
+++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml
@@ -13,8 +13,9 @@
data-mage-init='{
"Magento_Backend/js/media-uploader" : {
"maxFileSize": = /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?>,
- "maxWidth":= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?> ,
- "maxHeight": = /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>
+ "maxWidth": = /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?>,
+ "maxHeight": = /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>,
+ "isResizeEnabled": = /* @noEscape */ $block->getImageUploadConfigData()->getIsResizeEnabled() ?>
}
}'
>
diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js
index 7e0b6bdfb46dd..119e7a35747cb 100644
--- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js
+++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js
@@ -33,9 +33,20 @@ define([
* @private
*/
_create: function () {
- var
- self = this,
- progressTmpl = mageTemplate('[data-template="uploader"]');
+ var self = this,
+ progressTmpl = mageTemplate('[data-template="uploader"]'),
+ isResizeEnabled = this.options.isResizeEnabled,
+ resizeConfiguration = {
+ action: 'resize',
+ maxWidth: this.options.maxWidth,
+ maxHeight: this.options.maxHeight
+ };
+
+ if (!isResizeEnabled) {
+ resizeConfiguration = {
+ action: 'resize'
+ };
+ }
this.element.find('input[type=file]').fileupload({
dataType: 'json',
@@ -52,8 +63,7 @@ define([
* @param {Object} data
*/
add: function (e, data) {
- var
- fileSize,
+ var fileSize,
tmpl;
$.each(data.files, function (index, file) {
@@ -123,11 +133,10 @@ define([
this.element.find('input[type=file]').fileupload('option', {
process: [{
action: 'load',
- fileTypes: /^image\/(gif|jpeg|png)$/,
- maxFileSize: this.options.maxFileSize
- }, {
- action: 'resize'
- }, {
+ fileTypes: /^image\/(gif|jpeg|png)$/
+ },
+ resizeConfiguration,
+ {
action: 'save'
}]
});
diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/validate-store.js b/app/code/Magento/Backend/view/adminhtml/web/js/validate-store.js
index 0a692a9b868cc..c2a0d4dab1fb3 100644
--- a/app/code/Magento/Backend/view/adminhtml/web/js/validate-store.js
+++ b/app/code/Magento/Backend/view/adminhtml/web/js/validate-store.js
@@ -67,6 +67,7 @@ define([
* 'Confirm' action handler.
*/
confirm: function () {
+ $('body').trigger('processStart');
dataPost().postData(requestData);
}
}
diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index.php b/app/code/Magento/Backup/Controller/Adminhtml/Index.php
index dcafbc7370d2d..94dca327195f3 100644
--- a/app/code/Magento/Backup/Controller/Adminhtml/Index.php
+++ b/app/code/Magento/Backup/Controller/Adminhtml/Index.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Backup\Controller\Adminhtml;
+use Magento\Backend\App\Action;
+use Magento\Framework\App\Action\HttpGetActionInterface;
+
/**
* Backup admin controller
*
@@ -12,14 +15,14 @@
* @api
* @since 100.0.2
*/
-abstract class Index extends \Magento\Backend\App\Action
+abstract class Index extends Action implements HttpGetActionInterface
{
/**
* Authorization level of a basic admin session
*
* @see _isAllowed()
*/
- const ADMIN_RESOURCE = 'Magento_Backend::backup';
+ const ADMIN_RESOURCE = 'Magento_Backup::backup';
/**
* Core registry
diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php
index ca252aabe54a9..14ec829d98024 100644
--- a/app/code/Magento/Braintree/Controller/Paypal/Review.php
+++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php
@@ -12,11 +12,12 @@
use Magento\Braintree\Gateway\Config\PayPal\Config;
use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class Review
*/
-class Review extends AbstractAction
+class Review extends AbstractAction implements HttpPostActionInterface
{
/**
* @var QuoteUpdater
@@ -60,7 +61,7 @@ public function execute()
try {
$this->validateQuote($quote);
- if ($this->validateRequestData($requestData)) {
+ if ($requestData && $this->validateRequestData($requestData)) {
$this->quoteUpdater->execute(
$requestData['nonce'],
$requestData['details'],
@@ -91,6 +92,8 @@ public function execute()
}
/**
+ * Validate request data
+ *
* @param array $requestData
* @return boolean
*/
diff --git a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php
index a035c84b4cafd..4d63ee4125b74 100644
--- a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php
+++ b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php
@@ -49,6 +49,8 @@ public function build(array $buildSubject)
$payment = $paymentDO->getPayment();
$data = $payment->getAdditionalInformation();
+ // the payment token could be stored only if a customer checks the Vault flow on storefront
+ // see https://developers.braintreepayments.com/guides/paypal/vault/javascript/v2#invoking-the-vault-flow
if (!empty($data[VaultConfigProvider::IS_ACTIVE_CODE])) {
$result[self::$optionsKey] = [
self::$storeInVaultOnSuccess => true
diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php
index 4280663178efb..950634ba2d9e2 100644
--- a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php
+++ b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php
@@ -6,6 +6,8 @@
namespace Magento\Braintree\Gateway\Request;
use Magento\Braintree\Gateway\SubjectReader;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Payment\Gateway\Command\CommandException;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Magento\Payment\Helper\Formatter;
@@ -41,6 +43,9 @@ public function build(array $buildSubject)
$payment = $paymentDO->getPayment();
$extensionAttributes = $payment->getExtensionAttributes();
$paymentToken = $extensionAttributes->getVaultPaymentToken();
+ if ($paymentToken === null) {
+ throw new CommandException(__('The Payment Token is not available to perform the request.'));
+ }
return [
'amount' => $this->formatPrice($this->subjectReader->readAmount($buildSubject)),
'paymentMethodToken' => $paymentToken->getGatewayToken()
diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php
index fe5895541543d..aa23fa767d1ed 100644
--- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php
+++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php
@@ -148,7 +148,7 @@ private function updateBillingAddress(Quote $quote, array $details)
{
$billingAddress = $quote->getBillingAddress();
- if ($this->config->isRequiredBillingAddress()) {
+ if ($this->config->isRequiredBillingAddress() && !empty($details['billingAddress'])) {
$this->updateAddressData($billingAddress, $details['billingAddress']);
} else {
$this->updateAddressData($billingAddress, $details['shippingAddress']);
diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml
index 114c79189101a..2b5d60bea24e1 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml
+++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml
@@ -65,7 +65,7 @@
-
diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php
index 25ccd8b32d10e..d4e1f2745e3f3 100644
--- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php
+++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php
@@ -3,10 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Braintree\Test\Unit\Gateway\Request;
-use Magento\Braintree\Gateway\SubjectReader;
use Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder;
+use Magento\Braintree\Gateway\SubjectReader;
use Magento\Payment\Gateway\Data\PaymentDataObjectInterface;
use Magento\Sales\Api\Data\OrderPaymentExtension;
use Magento\Sales\Model\Order\Payment;
@@ -26,47 +28,46 @@ class VaultCaptureDataBuilderTest extends \PHPUnit\Framework\TestCase
/**
* @var PaymentDataObjectInterface|MockObject
*/
- private $paymentDOMock;
+ private $paymentDO;
/**
* @var Payment|MockObject
*/
- private $paymentMock;
+ private $payment;
/**
- * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject
+ * @var SubjectReader|MockObject
*/
- private $subjectReaderMock;
+ private $subjectReader;
/**
* @inheritdoc
*/
- protected function setUp()
+ protected function setUp(): void
{
- $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class);
- $this->paymentMock = $this->getMockBuilder(Payment::class)
+ $this->paymentDO = $this->createMock(PaymentDataObjectInterface::class);
+ $this->payment = $this->getMockBuilder(Payment::class)
->disableOriginalConstructor()
->getMock();
- $this->paymentDOMock->expects(static::once())
- ->method('getPayment')
- ->willReturn($this->paymentMock);
+ $this->paymentDO->method('getPayment')
+ ->willReturn($this->payment);
- $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class)
+ $this->subjectReader = $this->getMockBuilder(SubjectReader::class)
->disableOriginalConstructor()
->getMock();
- $this->builder = new VaultCaptureDataBuilder($this->subjectReaderMock);
+ $this->builder = new VaultCaptureDataBuilder($this->subjectReader);
}
/**
- * \Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder::build
+ * Checks the result after builder execution.
*/
- public function testBuild()
+ public function testBuild(): void
{
$amount = 30.00;
$token = '5tfm4c';
$buildSubject = [
- 'payment' => $this->paymentDOMock,
+ 'payment' => $this->paymentDO,
'amount' => $amount,
];
@@ -75,36 +76,68 @@ public function testBuild()
'paymentMethodToken' => $token,
];
- $this->subjectReaderMock->expects(self::once())
- ->method('readPayment')
+ $this->subjectReader->method('readPayment')
->with($buildSubject)
- ->willReturn($this->paymentDOMock);
- $this->subjectReaderMock->expects(self::once())
- ->method('readAmount')
+ ->willReturn($this->paymentDO);
+ $this->subjectReader->method('readAmount')
->with($buildSubject)
->willReturn($amount);
- $paymentExtensionMock = $this->getMockBuilder(OrderPaymentExtension::class)
+ /** @var OrderPaymentExtension|MockObject $paymentExtension */
+ $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class)
->setMethods(['getVaultPaymentToken'])
->disableOriginalConstructor()
->getMockForAbstractClass();
- $paymentTokenMock = $this->getMockBuilder(PaymentToken::class)
+ /** @var PaymentToken|MockObject $paymentToken */
+ $paymentToken = $this->getMockBuilder(PaymentToken::class)
->disableOriginalConstructor()
->getMock();
- $paymentExtensionMock->expects(static::once())
- ->method('getVaultPaymentToken')
- ->willReturn($paymentTokenMock);
- $this->paymentMock->expects(static::once())
- ->method('getExtensionAttributes')
- ->willReturn($paymentExtensionMock);
+ $paymentExtension->method('getVaultPaymentToken')
+ ->willReturn($paymentToken);
+ $this->payment->method('getExtensionAttributes')
+ ->willReturn($paymentExtension);
- $paymentTokenMock->expects(static::once())
- ->method('getGatewayToken')
+ $paymentToken->method('getGatewayToken')
->willReturn($token);
$result = $this->builder->build($buildSubject);
self::assertEquals($expected, $result);
}
+
+ /**
+ * Checks a builder execution if Payment Token doesn't exist.
+ *
+ * @expectedException \Magento\Payment\Gateway\Command\CommandException
+ * @expectedExceptionMessage The Payment Token is not available to perform the request.
+ */
+ public function testBuildWithoutPaymentToken(): void
+ {
+ $amount = 30.00;
+ $buildSubject = [
+ 'payment' => $this->paymentDO,
+ 'amount' => $amount,
+ ];
+
+ $this->subjectReader->method('readPayment')
+ ->with($buildSubject)
+ ->willReturn($this->paymentDO);
+ $this->subjectReader->method('readAmount')
+ ->with($buildSubject)
+ ->willReturn($amount);
+
+ /** @var OrderPaymentExtension|MockObject $paymentExtension */
+ $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class)
+ ->setMethods(['getVaultPaymentToken'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->payment->method('getExtensionAttributes')
+ ->willReturn($paymentExtension);
+ $paymentExtension->method('getVaultPaymentToken')
+ ->willReturn(null);
+
+ $this->builder->build($buildSubject);
+ }
}
diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php
index 62452228b6186..a2b5380d2884b 100644
--- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php
+++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php
@@ -3,23 +3,24 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Braintree\Test\Unit\Model\Paypal\Helper;
-use Magento\Quote\Model\Quote;
-use Magento\Quote\Model\Quote\Address;
-use Magento\Quote\Model\Quote\Payment;
-use Magento\Quote\Api\CartRepositoryInterface;
-use Magento\Braintree\Model\Ui\PayPal\ConfigProvider;
-use Magento\Braintree\Observer\DataAssignObserver;
use Magento\Braintree\Gateway\Config\PayPal\Config;
use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater;
+use Magento\Braintree\Model\Ui\PayPal\ConfigProvider;
+use Magento\Braintree\Observer\DataAssignObserver;
+use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartExtensionInterface;
+use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\Quote\Address;
+use Magento\Quote\Model\Quote\Payment;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
/**
* Class QuoteUpdaterTest
*
- * @see \Magento\Braintree\Model\Paypal\Helper\QuoteUpdater
- *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase
@@ -27,24 +28,24 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase
const TEST_NONCE = '3ede7045-2aea-463e-9754-cd658ffeeb48';
/**
- * @var Config|\PHPUnit_Framework_MockObject_MockObject
+ * @var Config|MockObject
*/
- private $configMock;
+ private $config;
/**
- * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var CartRepositoryInterface|MockObject
*/
- private $quoteRepositoryMock;
+ private $quoteRepository;
/**
- * @var Address|\PHPUnit_Framework_MockObject_MockObject
+ * @var Address|MockObject
*/
- private $billingAddressMock;
+ private $billingAddress;
/**
- * @var Address|\PHPUnit_Framework_MockObject_MockObject
+ * @var Address|MockObject
*/
- private $shippingAddressMock;
+ private $shippingAddress;
/**
* @var QuoteUpdater
@@ -52,17 +53,17 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase
private $quoteUpdater;
/**
- * @return void
+ * @inheritdoc
*/
protected function setUp()
{
- $this->configMock = $this->getMockBuilder(Config::class)
+ $this->config = $this->getMockBuilder(Config::class)
->disableOriginalConstructor()
->getMock();
- $this->quoteRepositoryMock = $this->getMockBuilder(CartRepositoryInterface::class)
+ $this->quoteRepository = $this->getMockBuilder(CartRepositoryInterface::class)
->getMockForAbstractClass();
- $this->billingAddressMock = $this->getMockBuilder(Address::class)
+ $this->billingAddress = $this->getMockBuilder(Address::class)
->setMethods(
[
'setLastname',
@@ -77,9 +78,10 @@ protected function setUp()
'setShouldIgnoreValidation',
'getEmail'
]
- )->disableOriginalConstructor()
+ )
+ ->disableOriginalConstructor()
->getMock();
- $this->shippingAddressMock = $this->getMockBuilder(Address::class)
+ $this->shippingAddress = $this->getMockBuilder(Address::class)
->setMethods(
[
'setLastname',
@@ -93,61 +95,61 @@ protected function setUp()
'setPostcode',
'setShouldIgnoreValidation'
]
- )->disableOriginalConstructor()
+ )
+ ->disableOriginalConstructor()
->getMock();
$this->quoteUpdater = new QuoteUpdater(
- $this->configMock,
- $this->quoteRepositoryMock
+ $this->config,
+ $this->quoteRepository
);
}
/**
- * @return void
+ * Checks if quote details can be update by the response from Braintree.
+ *
* @throws \Magento\Framework\Exception\LocalizedException
*/
- public function testExecute()
+ public function testExecute(): void
{
$details = $this->getDetails();
- $quoteMock = $this->getQuoteMock();
- $paymentMock = $this->getPaymentMock();
+ $quote = $this->getQuoteMock();
+ $payment = $this->getPaymentMock();
- $quoteMock->expects(self::once())
- ->method('getPayment')
- ->willReturn($paymentMock);
+ $quote->method('getPayment')
+ ->willReturn($payment);
- $paymentMock->expects(self::once())
- ->method('setMethod')
+ $payment->method('setMethod')
->with(ConfigProvider::PAYPAL_CODE);
- $paymentMock->expects(self::once())
- ->method('setAdditionalInformation')
+ $payment->method('setAdditionalInformation')
->with(DataAssignObserver::PAYMENT_METHOD_NONCE, self::TEST_NONCE);
- $this->updateQuoteStep($quoteMock, $details);
+ $this->updateQuoteStep($quote, $details);
- $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quoteMock);
+ $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quote);
}
/**
+ * Disables quote's addresses validation.
+ *
* @return void
*/
- private function disabledQuoteAddressValidationStep()
+ private function disabledQuoteAddressValidationStep(): void
{
- $this->billingAddressMock->expects(self::once())
- ->method('setShouldIgnoreValidation')
+ $this->billingAddress->method('setShouldIgnoreValidation')
->with(true);
- $this->shippingAddressMock->expects(self::once())
- ->method('setShouldIgnoreValidation')
+ $this->shippingAddress->method('setShouldIgnoreValidation')
->with(true);
- $this->billingAddressMock->expects(self::once())
- ->method('getEmail')
+ $this->billingAddress->method('getEmail')
->willReturn('bt_buyer_us@paypal.com');
}
/**
+ * Gets quote's details.
+ *
* @return array
*/
- private function getDetails()
+ private function getDetails(): array
{
return [
'email' => 'bt_buyer_us@paypal.com',
@@ -177,54 +179,51 @@ private function getDetails()
}
/**
+ * Updates shipping address details.
+ *
* @param array $details
*/
- private function updateShippingAddressStep(array $details)
+ private function updateShippingAddressStep(array $details): void
{
- $this->shippingAddressMock->expects(self::once())
- ->method('setLastname')
+ $this->shippingAddress->method('setLastname')
->with($details['lastName']);
- $this->shippingAddressMock->expects(self::once())
- ->method('setFirstname')
+ $this->shippingAddress->method('setFirstname')
->with($details['firstName']);
- $this->shippingAddressMock->expects(self::once())
- ->method('setEmail')
+ $this->shippingAddress->method('setEmail')
->with($details['email']);
- $this->shippingAddressMock->expects(self::once())
- ->method('setCollectShippingRates')
+ $this->shippingAddress->method('setCollectShippingRates')
->with(true);
- $this->updateAddressDataStep($this->shippingAddressMock, $details['shippingAddress']);
+ $this->updateAddressDataStep($this->shippingAddress, $details['shippingAddress']);
}
/**
- * @param \PHPUnit_Framework_MockObject_MockObject $addressMock
+ * Updates address details.
+ *
+ * @param MockObject $address
* @param array $addressData
*/
- private function updateAddressDataStep(\PHPUnit_Framework_MockObject_MockObject $addressMock, array $addressData)
+ private function updateAddressDataStep(MockObject $address, array $addressData): void
{
- $addressMock->expects(self::once())
- ->method('setStreet')
+ $address->method('setStreet')
->with([$addressData['streetAddress'], $addressData['extendedAddress']]);
- $addressMock->expects(self::once())
- ->method('setCity')
+ $address->method('setCity')
->with($addressData['locality']);
- $addressMock->expects(self::once())
- ->method('setRegionCode')
+ $address->method('setRegionCode')
->with($addressData['region']);
- $addressMock->expects(self::once())
- ->method('setCountryId')
+ $address->method('setCountryId')
->with($addressData['countryCodeAlpha2']);
- $addressMock->expects(self::once())
- ->method('setPostcode')
+ $address->method('setPostcode')
->with($addressData['postalCode']);
}
/**
- * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock
+ * Updates quote's address details.
+ *
+ * @param MockObject $quoteMock
* @param array $details
*/
- private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details)
+ private function updateQuoteAddressStep(MockObject $quoteMock, array $details): void
{
$quoteMock->expects(self::exactly(2))
->method('getIsVirtual')
@@ -235,64 +234,61 @@ private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject
}
/**
+ * Updates billing address details.
+ *
* @param array $details
*/
- private function updateBillingAddressStep(array $details)
+ private function updateBillingAddressStep(array $details): void
{
- $this->configMock->expects(self::once())
- ->method('isRequiredBillingAddress')
+ $this->config->method('isRequiredBillingAddress')
->willReturn(true);
- $this->updateAddressDataStep($this->billingAddressMock, $details['billingAddress']);
+ $this->updateAddressDataStep($this->billingAddress, $details['billingAddress']);
- $this->billingAddressMock->expects(self::once())
- ->method('setLastname')
+ $this->billingAddress->method('setLastname')
->with($details['lastName']);
- $this->billingAddressMock->expects(self::once())
- ->method('setFirstname')
+ $this->billingAddress->method('setFirstname')
->with($details['firstName']);
- $this->billingAddressMock->expects(self::once())
- ->method('setEmail')
+ $this->billingAddress->method('setEmail')
->with($details['email']);
}
/**
- * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock
+ * Updates quote details.
+ *
+ * @param MockObject $quote
* @param array $details
*/
- private function updateQuoteStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details)
+ private function updateQuoteStep(MockObject $quote, array $details): void
{
- $quoteMock->expects(self::once())
- ->method('setMayEditShippingAddress')
+ $quote->method('setMayEditShippingAddress')
->with(false);
- $quoteMock->expects(self::once())
- ->method('setMayEditShippingMethod')
+ $quote->method('setMayEditShippingMethod')
->with(true);
- $quoteMock->expects(self::exactly(2))
- ->method('getShippingAddress')
- ->willReturn($this->shippingAddressMock);
- $quoteMock->expects(self::exactly(2))
+ $quote->method('getShippingAddress')
+ ->willReturn($this->shippingAddress);
+ $quote->expects(self::exactly(2))
->method('getBillingAddress')
- ->willReturn($this->billingAddressMock);
+ ->willReturn($this->billingAddress);
- $this->updateQuoteAddressStep($quoteMock, $details);
+ $this->updateQuoteAddressStep($quote, $details);
$this->disabledQuoteAddressValidationStep();
- $quoteMock->expects(self::once())
- ->method('collectTotals');
+ $quote->method('collectTotals');
- $this->quoteRepositoryMock->expects(self::once())
- ->method('save')
- ->with($quoteMock);
+ $this->quoteRepository->method('save')
+ ->with($quote);
}
/**
- * @return Quote|\PHPUnit_Framework_MockObject_MockObject
+ * Creates a mock for Quote object.
+ *
+ * @return Quote|MockObject
*/
- private function getQuoteMock()
+ private function getQuoteMock(): MockObject
{
- $quoteMock = $this->getMockBuilder(Quote::class)
+ $quote = $this->getMockBuilder(Quote::class)
->setMethods(
[
'getIsVirtual',
@@ -304,25 +300,27 @@ private function getQuoteMock()
'getBillingAddress',
'getExtensionAttributes'
]
- )->disableOriginalConstructor()
+ )
+ ->disableOriginalConstructor()
->getMock();
- $cartExtensionMock = $this->getMockBuilder(CartExtensionInterface::class)
+ $cartExtension = $this->getMockBuilder(CartExtensionInterface::class)
->setMethods(['setShippingAssignments'])
->disableOriginalConstructor()
->getMockForAbstractClass();
- $quoteMock->expects(self::any())
- ->method('getExtensionAttributes')
- ->willReturn($cartExtensionMock);
+ $quote->method('getExtensionAttributes')
+ ->willReturn($cartExtension);
- return $quoteMock;
+ return $quote;
}
/**
- * @return Payment|\PHPUnit_Framework_MockObject_MockObject
+ * Creates a mock for Payment object.
+ *
+ * @return Payment|MockObject
*/
- private function getPaymentMock()
+ private function getPaymentMock(): MockObject
{
return $this->getMockBuilder(Payment::class)
->disableOriginalConstructor()
diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv
index 6bf677151ed0d..7bd305f546dc6 100644
--- a/app/code/Magento/Braintree/i18n/en_US.csv
+++ b/app/code/Magento/Braintree/i18n/en_US.csv
@@ -192,3 +192,4 @@ Currency,Currency
"Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later."
"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later."
"Braintree Settlement","Braintree Settlement"
+"The Payment Token is not available to perform the request.","The Payment Token is not available to perform the request."
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
index 62a2fa1c47e1e..fa488b073f515 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php
@@ -7,6 +7,7 @@
use Magento\Bundle\Model\Option;
use Magento\Catalog\Model\Product;
+use Magento\Framework\DataObject;
/**
* Catalog bundle product info block
@@ -170,7 +171,7 @@ public function getJsonConfig()
$defaultValues = [];
$preConfiguredFlag = $currentProduct->hasPreconfiguredValues();
- /** @var \Magento\Framework\DataObject|null $preConfiguredValues */
+ /** @var DataObject|null $preConfiguredValues */
$preConfiguredValues = $preConfiguredFlag ? $currentProduct->getPreconfiguredValues() : null;
$position = 0;
@@ -193,12 +194,13 @@ public function getJsonConfig()
$options[$optionId]['selections'][$configValue]['qty'] = $configQty;
}
}
+ $options = $this->processOptions($optionId, $options, $preConfiguredValues);
}
$position++;
}
$config = $this->getConfigData($currentProduct, $options);
- $configObj = new \Magento\Framework\DataObject(
+ $configObj = new DataObject(
[
'config' => $config,
]
@@ -403,4 +405,30 @@ private function getConfigData(Product $product, array $options)
];
return $config;
}
+
+ /**
+ * Set preconfigured quantities and selections to options.
+ *
+ * @param string $optionId
+ * @param array $options
+ * @param DataObject $preConfiguredValues
+ * @return array
+ */
+ private function processOptions(string $optionId, array $options, DataObject $preConfiguredValues)
+ {
+ $preConfiguredQtys = $preConfiguredValues->getData("bundle_option_qty/${optionId}") ?? [];
+ $selections = $options[$optionId]['selections'];
+ array_walk($selections, function (&$selection, $selectionId) use ($preConfiguredQtys) {
+ if (is_array($preConfiguredQtys) && isset($preConfiguredQtys[$selectionId])) {
+ $selection['qty'] = $preConfiguredQtys[$selectionId];
+ } else {
+ if ((int)$preConfiguredQtys > 0) {
+ $selection['qty'] = $preConfiguredQtys;
+ }
+ }
+ });
+ $options[$optionId]['selections'] = $selections;
+
+ return $options;
+ }
}
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php
index 5d326e7c01d19..7c63af0bd0e2e 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php
@@ -167,7 +167,9 @@ protected function _getSelectedOptions()
*/
protected function assignSelection(\Magento\Bundle\Model\Option $option, $selectionId)
{
- if ($selectionId && $option->getSelectionById($selectionId)) {
+ if (is_array($selectionId)) {
+ $this->_selectedOptions = $selectionId;
+ } else if ($selectionId && $option->getSelectionById($selectionId)) {
$this->_selectedOptions = $selectionId;
} elseif (!$option->getRequired()) {
$this->_selectedOptions = 'None';
@@ -228,6 +230,8 @@ public function getProduct()
}
/**
+ * Get bundle option price title.
+ *
* @param \Magento\Catalog\Model\Product $selection
* @param bool $includeContainer
* @return string
diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php
index 59e658b08df28..0b96ea8d5b789 100644
--- a/app/code/Magento/Bundle/Model/OptionRepository.php
+++ b/app/code/Magento/Bundle/Model/OptionRepository.php
@@ -90,7 +90,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function get($sku, $optionId)
{
@@ -106,23 +106,24 @@ public function get($sku, $optionId)
$productLinks = $this->linkManagement->getChildren($product->getSku(), $optionId);
- /** @var \Magento\Bundle\Api\Data\OptionInterface $option */
+ /** @var \Magento\Bundle\Api\Data\OptionInterface $optionDataObject */
$optionDataObject = $this->optionFactory->create();
$this->dataObjectHelper->populateWithArray(
$optionDataObject,
$option->getData(),
\Magento\Bundle\Api\Data\OptionInterface::class
);
- $optionDataObject->setOptionId($option->getId())
- ->setTitle($option->getTitle() === null ? $option->getDefaultTitle() : $option->getTitle())
- ->setSku($product->getSku())
- ->setProductLinks($productLinks);
+
+ $optionDataObject->setOptionId($option->getId());
+ $optionDataObject->setTitle($option->getTitle() === null ? $option->getDefaultTitle() : $option->getTitle());
+ $optionDataObject->setSku($product->getSku());
+ $optionDataObject->setProductLinks($productLinks);
return $optionDataObject;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getList($sku)
{
@@ -131,6 +132,8 @@ public function getList($sku)
}
/**
+ * Return list of product options
+ *
* @param ProductInterface $product
* @return \Magento\Bundle\Api\Data\OptionInterface[]
*/
@@ -140,7 +143,7 @@ public function getListByProduct(ProductInterface $product)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function delete(\Magento\Bundle\Api\Data\OptionInterface $option)
{
@@ -156,20 +159,19 @@ public function delete(\Magento\Bundle\Api\Data\OptionInterface $option)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function deleteById($sku, $optionId)
{
- $product = $this->getProduct($sku);
- $optionCollection = $this->type->getOptionsCollection($product);
- $optionCollection->setIdFilter($optionId);
- $hasBeenDeleted = $this->delete($optionCollection->getFirstItem());
+ /** @var \Magento\Bundle\Api\Data\OptionInterface $option */
+ $option = $this->get($sku, $optionId);
+ $hasBeenDeleted = $this->delete($option);
return $hasBeenDeleted;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function save(
\Magento\Catalog\Api\Data\ProductInterface $product,
@@ -189,6 +191,9 @@ public function save(
* @param \Magento\Catalog\Api\Data\ProductInterface $product
* @param \Magento\Bundle\Api\Data\OptionInterface $option
* @return $this
+ * @throws InputException
+ * @throws NoSuchEntityException
+ * @throws \Magento\Framework\Exception\CouldNotSaveException
*/
protected function updateOptionSelection(
\Magento\Catalog\Api\Data\ProductInterface $product,
@@ -228,9 +233,12 @@ protected function updateOptionSelection(
}
/**
+ * Retrieve product by SKU
+ *
* @param string $sku
* @return \Magento\Catalog\Api\Data\ProductInterface
- * @throws \Magento\Framework\Exception\InputException
+ * @throws InputException
+ * @throws NoSuchEntityException
*/
private function getProduct($sku)
{
diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php
index 92bada8094c7e..b61df8d7cb125 100644
--- a/app/code/Magento/Bundle/Model/Product/Type.php
+++ b/app/code/Magento/Bundle/Model/Product/Type.php
@@ -308,8 +308,11 @@ public function getSku($product)
$selectionIds = $this->serializer->unserialize($customOption->getValue());
if (!empty($selectionIds)) {
$selections = $this->getSelectionsByIds($selectionIds, $product);
- foreach ($selections->getItems() as $selection) {
- $skuParts[] = $selection->getSku();
+ foreach ($selectionIds as $selectionId) {
+ $entity = $selections->getItemByColumnValue('selection_id', $selectionId);
+ if (isset($entity) && $entity->getEntityId()) {
+ $skuParts[] = $entity->getSku();
+ }
}
}
}
@@ -736,7 +739,7 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
$price = $product->getPriceModel()
->getSelectionFinalTotalPrice($product, $selection, 0, $qty);
$attributes = [
- 'price' => $this->priceCurrency->convert($price),
+ 'price' => $price,
'qty' => $qty,
'option_label' => $selection->getOption()
->getTitle(),
diff --git a/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php b/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php
new file mode 100644
index 0000000000000..d5aafb8ad2b61
--- /dev/null
+++ b/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php
@@ -0,0 +1,55 @@
+serializer = $serializer;
+ }
+
+ /**
+ * Update price on quote item options level
+ *
+ * @param OrigQuoteItem $subject
+ * @param AbstractItem $result
+ * @return AbstractItem
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterCalcRowTotal(OrigQuoteItem $subject, AbstractItem $result)
+ {
+ $bundleAttributes = $result->getProduct()->getCustomOption('bundle_selection_attributes');
+ if ($bundleAttributes !== null) {
+ $actualPrice = $result->getPrice();
+ $parsedValue = $this->serializer->unserialize($bundleAttributes->getValue());
+ if (is_array($parsedValue) && array_key_exists('price', $parsedValue)) {
+ $parsedValue['price'] = $actualPrice;
+ }
+ $bundleAttributes->setValue($this->serializer->serialize($parsedValue));
+ }
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml
new file mode 100644
index 0000000000000..441303e8f1b84
--- /dev/null
+++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml
index 8d9f29814f762..946992f1efe04 100644
--- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml
+++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml
@@ -24,10 +24,13 @@
+
+
+
diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
index 735571375866e..fae1ec331b667 100644
--- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
+++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
@@ -13,5 +13,6 @@
+
diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml
new file mode 100644
index 0000000000000..ded8bb3c83337
--- /dev/null
+++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml
index f94cd83f4e7d7..58806126aee30 100644
--- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml
+++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml
@@ -94,9 +94,8 @@
-
-
-
+
+
diff --git a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php
index b4a466b413af0..2450f63c38933 100644
--- a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php
+++ b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php
@@ -8,6 +8,7 @@
namespace Magento\Bundle\Test\Unit\Model;
use Magento\Bundle\Model\OptionRepository;
+use Magento\Framework\Exception\NoSuchEntityException;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -84,7 +85,7 @@ protected function setUp()
->getMock();
$this->optionResourceMock = $this->createPartialMock(
\Magento\Bundle\Model\ResourceModel\Option::class,
- ['delete', '__wakeup', 'save', 'removeOptionSelections']
+ ['get', 'delete', '__wakeup', 'save', 'removeOptionSelections']
);
$this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class);
$this->linkManagementMock = $this->createMock(\Magento\Bundle\Api\ProductLinkManagementInterface::class);
@@ -227,32 +228,92 @@ public function testDeleteThrowsExceptionIfCannotDelete()
$this->model->delete($optionMock);
}
+ /**
+ * Test successful delete action for given $optionId
+ */
public function testDeleteById()
{
$productSku = 'sku';
$optionId = 100;
- $productMock = $this->createMock(\Magento\Catalog\Api\Data\ProductInterface::class);
+
+ $optionMock = $this->createMock(\Magento\Bundle\Model\Option::class);
+ $optionMock->expects($this->exactly(2))
+ ->method('getId')
+ ->willReturn($optionId);
+
+ $optionMock->expects($this->once())
+ ->method('getData')
+ ->willReturn([
+ 'title' => 'Option title',
+ 'option_id' => $optionId
+ ]);
+
+ $this->optionFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($optionMock);
+
+ $productMock = $this->createPartialMock(
+ \Magento\Catalog\Model\Product::class,
+ ['getTypeId', 'getTypeInstance', 'getStoreId', 'getPriceType', '__wakeup', 'getSku']
+ );
$productMock->expects($this->once())
->method('getTypeId')
->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
- $this->productRepositoryMock->expects($this->once())
+ $productMock->expects($this->exactly(2))->method('getSku')->willReturn($productSku);
+
+ $this->productRepositoryMock
+ ->expects($this->once())
->method('get')
->with($productSku)
->willReturn($productMock);
+ $optCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class);
+ $optCollectionMock->expects($this->once())->method('getItemById')->with($optionId)->willReturn($optionMock);
+ $this->typeMock->expects($this->once())
+ ->method('getOptionsCollection')
+ ->with($productMock)
+ ->willReturn($optCollectionMock);
+
+ $this->assertTrue($this->model->deleteById($productSku, $optionId));
+ }
+
+ /**
+ * Tests if NoSuchEntityException thrown when provided $optionId not found
+ */
+ public function testDeleteByIdException()
+ {
+ $productSku = 'sku';
+ $optionId = null;
+
$optionMock = $this->createMock(\Magento\Bundle\Model\Option::class);
+ $optionMock->expects($this->exactly(1))
+ ->method('getId')
+ ->willReturn($optionId);
+
+ $productMock = $this->createPartialMock(
+ \Magento\Catalog\Model\Product::class,
+ ['getTypeId', 'getTypeInstance', 'getStoreId', 'getPriceType', '__wakeup', 'getSku']
+ );
+ $productMock->expects($this->once())
+ ->method('getTypeId')
+ ->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
+
+ $this->productRepositoryMock
+ ->expects($this->once())
+ ->method('get')
+ ->with($productSku)
+ ->willReturn($productMock);
$optCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class);
+ $optCollectionMock->expects($this->once())->method('getItemById')->with($optionId)->willReturn($optionMock);
$this->typeMock->expects($this->once())
->method('getOptionsCollection')
->with($productMock)
->willReturn($optCollectionMock);
- $optCollectionMock->expects($this->once())->method('setIdFilter')->with($optionId)->willReturnSelf();
- $optCollectionMock->expects($this->once())->method('getFirstItem')->willReturn($optionMock);
+ $this->expectException(NoSuchEntityException::class);
- $this->optionResourceMock->expects($this->once())->method('delete')->with($optionMock)->willReturnSelf();
- $this->assertTrue($this->model->deleteById($productSku, $optionId));
+ $this->model->deleteById($productSku, $optionId);
}
/**
diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php
index 4bbf5641c55d3..59f7f008ed3ee 100644
--- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php
+++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php
@@ -513,10 +513,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
->method('getSelectionId')
->willReturn(314);
- $this->priceCurrency->expects($this->once())
- ->method('convert')
- ->willReturn(3.14);
-
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
$this->assertEquals([$product, $productType], $result);
}
@@ -737,10 +733,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
->method('prepareForCart')
->willReturn([]);
- $this->priceCurrency->expects($this->once())
- ->method('convert')
- ->willReturn(3.14);
-
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
$this->assertEquals('We can\'t add this item to your shopping cart right now.', $result);
}
@@ -961,10 +953,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
->method('prepareForCart')
->willReturn('string');
- $this->priceCurrency->expects($this->once())
- ->method('convert')
- ->willReturn(3.14);
-
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
$this->assertEquals('string', $result);
}
@@ -1595,7 +1583,7 @@ public function testGetSkuWithoutType()
->disableOriginalConstructor()
->getMock();
$selectionItemMock = $this->getMockBuilder(\Magento\Framework\DataObject::class)
- ->setMethods(['getSku', '__wakeup'])
+ ->setMethods(['getSku', 'getEntityId', '__wakeup'])
->disableOriginalConstructor()
->getMock();
@@ -1623,9 +1611,12 @@ public function testGetSkuWithoutType()
->will($this->returnValue($serializeIds));
$selectionMock = $this->getSelectionsByIdsMock($selectionIds, $productMock, 5, 6);
$selectionMock->expects(($this->any()))
- ->method('getItems')
- ->will($this->returnValue([$selectionItemMock]));
- $selectionItemMock->expects($this->any())
+ ->method('getItemByColumnValue')
+ ->will($this->returnValue($selectionItemMock));
+ $selectionItemMock->expects($this->at(0))
+ ->method('getEntityId')
+ ->will($this->returnValue(1));
+ $selectionItemMock->expects($this->once())
->method('getSku')
->will($this->returnValue($itemSku));
diff --git a/app/code/Magento/Bundle/etc/db_schema.xml b/app/code/Magento/Bundle/etc/db_schema.xml
index f1c1e7ae782dc..33738cd252d61 100644
--- a/app/code/Magento/Bundle/etc/db_schema.xml
+++ b/app/code/Magento/Bundle/etc/db_schema.xml
@@ -18,13 +18,13 @@
-
+
-
-
+
@@ -39,13 +39,13 @@
-
+
-
-
+
@@ -73,19 +73,19 @@
comment="Selection Qty"/>
-
+
-
-
-
+
-
+
@@ -101,19 +101,19 @@
nullable="false" default="0" comment="Selection Price Value"/>
-
+
-
-
-
+
@@ -129,24 +129,24 @@
comment="Min Price"/>
-
+
-
-
-
-
+
-
+
@@ -162,7 +162,7 @@
default="0" comment="Option Id"/>
-
+
@@ -197,7 +197,7 @@
comment="Tier Price"/>
-
+
@@ -231,7 +231,7 @@
comment="Tier Price"/>
-
+
@@ -257,7 +257,7 @@
comment="Price"/>
-
+
@@ -285,7 +285,7 @@
comment="Price"/>
-
+
@@ -313,7 +313,7 @@
comment="Tier Price"/>
-
+
@@ -340,7 +340,7 @@
comment="Tier Price"/>
-
+
diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml
index 733b089dccd4b..6f0cc04790cc2 100644
--- a/app/code/Magento/Bundle/etc/di.xml
+++ b/app/code/Magento/Bundle/etc/di.xml
@@ -123,6 +123,9 @@
+
+
+
diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php
index f55028a7d1a5b..184f7177a995c 100644
--- a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php
+++ b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php
@@ -7,7 +7,7 @@
namespace Magento\BundleGraphQl\Model\Resolver;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\BundleGraphQl\Model\Resolver\Links\Collection;
use Magento\Framework\GraphQl\Config\Element\Field;
@@ -47,7 +47,7 @@ public function __construct(
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!isset($value['option_id']) || !isset($value['parent_id'])) {
- throw new GraphQlInputException(__('"option_id" and "parent_id" values should be specified'));
+ throw new LocalizedException(__('"option_id" and "parent_id" values should be specified'));
}
$this->linkCollection->addIdFilters((int)$value['option_id'], (int)$value['parent_id']);
diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php
index bcddd5d084629..de72b18982c12 100644
--- a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php
+++ b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php
@@ -7,7 +7,7 @@
namespace Magento\BundleGraphQl\Model\Resolver\Options;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Deferred\Product as ProductDataProvider;
use Magento\Framework\GraphQl\Config\Element\Field;
@@ -50,7 +50,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['sku'])) {
- throw new GraphQlInputException(__('"sku" value should be specified'));
+ throw new LocalizedException(__('"sku" value should be specified'));
}
$this->product->addProductSku($value['sku']);
diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php
index 3ed7e144ddd5a..81a47d72602b7 100644
--- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php
+++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php
@@ -20,6 +20,7 @@
/**
* Class Bundle
+ *
* @package Magento\BundleImportExport\Model\Import\Product\Type
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*/
@@ -349,6 +350,8 @@ protected function populateSelectionTemplate($selection, $optionId, $parentId, $
}
/**
+ * Deprecated method for retrieving mapping between skus and products.
+ *
* @deprecated Misspelled method
* @see retrieveProductsByCachedSkus
*/
@@ -600,6 +603,7 @@ protected function insertOptions()
/**
* Populate array for insert option values
+ *
* @param array $optionIds
* @return array
*/
@@ -779,7 +783,7 @@ protected function clear()
*/
private function getStoreIdByCode(string $storeCode): int
{
- if (!isset($this->storeIdToCode[$storeCode])) {
+ if (!isset($this->storeCodeToId[$storeCode])) {
/** @var $store \Magento\Store\Model\Store */
foreach ($this->storeManager->getStores() as $store) {
$this->storeCodeToId[$store->getCode()] = $store->getId();
diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php
new file mode 100644
index 0000000000000..415f022a7364d
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php
@@ -0,0 +1,137 @@
+helperMock = $this->createMock(Data::class);
+ $this->messageManagerMock = $this->createMock(ManagerInterface::class);
+ $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class);
+ $this->requestMock = $this->createMock(RequestInterface::class);
+
+ $this->observer = new CheckUserLoginBackendObserver(
+ $this->helperMock,
+ $this->captchaStringResolverMock,
+ $this->requestMock
+ );
+ }
+
+ /**
+ * Test check user login in backend with correct captcha
+ *
+ * @dataProvider requiredCaptchaDataProvider
+ * @param bool $isRequired
+ * @return void
+ */
+ public function testCheckOnBackendLoginWithCorrectCaptcha(bool $isRequired): void
+ {
+ $formId = 'backend_login';
+ $login = 'admin';
+ $captchaValue = 'captcha-value';
+
+ /** @var Observer|MockObject $observerMock */
+ $observerMock = $this->createPartialMock(Observer::class, ['getEvent']);
+ $eventMock = $this->createPartialMock(Event::class, ['getUsername']);
+ $captcha = $this->createMock(DefaultModel::class);
+
+ $eventMock->method('getUsername')->willReturn('admin');
+ $observerMock->method('getEvent')->willReturn($eventMock);
+ $captcha->method('isRequired')->with($login)->willReturn($isRequired);
+ $captcha->method('isCorrect')->with($captchaValue)->willReturn(true);
+ $this->helperMock->method('getCaptcha')->with($formId)->willReturn($captcha);
+ $this->captchaStringResolverMock->method('resolve')->with($this->requestMock, $formId)
+ ->willReturn($captchaValue);
+
+ $this->observer->execute($observerMock);
+ }
+
+ /**
+ * @return array
+ */
+ public function requiredCaptchaDataProvider(): array
+ {
+ return [
+ [true],
+ [false]
+ ];
+ }
+
+ /**
+ * Test check user login in backend with wrong captcha
+ *
+ * @return void
+ * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException
+ */
+ public function testCheckOnBackendLoginWithWrongCaptcha(): void
+ {
+ $formId = 'backend_login';
+ $login = 'admin';
+ $captchaValue = 'captcha-value';
+
+ /** @var Observer|MockObject $observerMock */
+ $observerMock = $this->createPartialMock(Observer::class, ['getEvent']);
+ $eventMock = $this->createPartialMock(Event::class, ['getUsername']);
+ $captcha = $this->createMock(DefaultModel::class);
+
+ $eventMock->method('getUsername')->willReturn($login);
+ $observerMock->method('getEvent')->willReturn($eventMock);
+ $captcha->method('isRequired')->with($login)->willReturn(true);
+ $captcha->method('isCorrect')->with($captchaValue)->willReturn(false);
+ $this->helperMock->method('getCaptcha')->with($formId)->willReturn($captcha);
+ $this->captchaStringResolverMock->method('resolve')->with($this->requestMock, $formId)
+ ->willReturn($captchaValue);
+
+ $this->observer->execute($observerMock);
+ }
+}
diff --git a/app/code/Magento/Captcha/etc/db_schema.xml b/app/code/Magento/Captcha/etc/db_schema.xml
index fa9a14abb8963..b8987363569ca 100644
--- a/app/code/Magento/Captcha/etc/db_schema.xml
+++ b/app/code/Magento/Captcha/etc/db_schema.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
index e1208e25cc7c8..063503682f4db 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php
@@ -13,10 +13,12 @@
*/
namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery;
+use Magento\Framework\App\ObjectManager;
use Magento\Backend\Block\Media\Uploader;
use Magento\Framework\View\Element\AbstractBlock;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\FileSystemException;
+use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider;
/**
* Block for gallery content.
@@ -43,21 +45,30 @@ class Content extends \Magento\Backend\Block\Widget
*/
private $imageHelper;
+ /**
+ * @var ImageUploadConfigDataProvider
+ */
+ private $imageUploadConfigDataProvider;
+
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
* @param array $data
+ * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
- array $data = []
+ array $data = [],
+ ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null
) {
$this->_jsonEncoder = $jsonEncoder;
$this->_mediaConfig = $mediaConfig;
parent::__construct($context, $data);
+ $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider
+ ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class);
}
/**
@@ -67,7 +78,11 @@ public function __construct(
*/
protected function _prepareLayout()
{
- $this->addChild('uploader', \Magento\Backend\Block\Media\Uploader::class);
+ $this->addChild(
+ 'uploader',
+ \Magento\Backend\Block\Media\Uploader::class,
+ ['image_upload_config_data' => $this->imageUploadConfigDataProvider]
+ );
$this->getUploader()->getConfig()->setUrl(
$this->_urlBuilder->addSessionParam()->getUrl('catalog/product_gallery/upload')
diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
index 6c54aa4e171ef..76f5dbd1bea88 100644
--- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
+++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php
@@ -122,12 +122,7 @@ public function __construct(
*/
public function getAddToWishlistParams($product)
{
- $continueUrl = $this->urlEncoder->encode($this->getUrl('customer/account'));
- $urlParamName = Action::PARAM_NAME_URL_ENCODED;
-
- $continueUrlParams = [$urlParamName => $continueUrl];
-
- return $this->_wishlistHelper->getAddParams($product, $continueUrlParams);
+ return $this->_wishlistHelper->getAddParams($product);
}
/**
diff --git a/app/code/Magento/Catalog/Block/Product/Image.php b/app/code/Magento/Catalog/Block/Product/Image.php
index 20a556ab41451..7a7f9c0affc7d 100644
--- a/app/code/Magento/Catalog/Block/Product/Image.php
+++ b/app/code/Magento/Catalog/Block/Product/Image.php
@@ -6,6 +6,8 @@
namespace Magento\Catalog\Block\Product;
/**
+ * Product image block
+ *
* @api
* @method string getImageUrl()
* @method string getWidth()
@@ -13,6 +15,7 @@
* @method string getLabel()
* @method float getRatio()
* @method string getCustomAttributes()
+ * @method string getClass()
* @since 100.0.2
*/
class Image extends \Magento\Framework\View\Element\Template
diff --git a/app/code/Magento/Catalog/Block/Product/ImageFactory.php b/app/code/Magento/Catalog/Block/Product/ImageFactory.php
index f9a576367ddeb..aa303af656a5b 100644
--- a/app/code/Magento/Catalog/Block/Product/ImageFactory.php
+++ b/app/code/Magento/Catalog/Block/Product/ImageFactory.php
@@ -77,16 +77,29 @@ private function getStringCustomAttributes(array $attributes): string
{
$result = [];
foreach ($attributes as $name => $value) {
- $result[] = $name . '="' . $value . '"';
+ if ($name != 'class') {
+ $result[] = $name . '="' . $value . '"';
+ }
}
return !empty($result) ? implode(' ', $result) : '';
}
+ /**
+ * Retrieve image class for HTML element
+ *
+ * @param array $attributes
+ * @return string
+ */
+ private function getClass(array $attributes): string
+ {
+ return $attributes['class'] ?? 'product-image-photo';
+ }
+
/**
* Calculate image ratio
*
- * @param $width
- * @param $height
+ * @param int $width
+ * @param int $height
* @return float
*/
private function getRatio(int $width, int $height): float
@@ -98,8 +111,9 @@ private function getRatio(int $width, int $height): float
}
/**
- * @param Product $product
+ * Get image label
*
+ * @param Product $product
* @param string $imageType
* @return string
*/
@@ -114,6 +128,7 @@ private function getLabel(Product $product, string $imageType): string
/**
* Create image block from product
+ *
* @param Product $product
* @param string $imageId
* @param array|null $attributes
@@ -154,6 +169,7 @@ public function create(Product $product, string $imageId, array $attributes = nu
'label' => $this->getLabel($product, $imageMiscParams['image_type']),
'ratio' => $this->getRatio($imageMiscParams['image_width'], $imageMiscParams['image_height']),
'custom_attributes' => $this->getStringCustomAttributes($attributes),
+ 'class' => $this->getClass($attributes),
'product_id' => $product->getId()
],
];
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
index 0b8d2d6c89e72..c530ba4785ad9 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
@@ -7,6 +7,8 @@
use Magento\Catalog\Helper\Product\ProductList;
use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel;
+use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer;
+use Magento\Framework\App\ObjectManager;
/**
* Product list toolbar
@@ -77,6 +79,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
/**
* @var bool $_paramsMemorizeAllowed
+ * @deprecated
*/
protected $_paramsMemorizeAllowed = true;
@@ -96,6 +99,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
* Catalog session
*
* @var \Magento\Catalog\Model\Session
+ * @deprecated
*/
protected $_catalogSession;
@@ -104,6 +108,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template
*/
protected $_toolbarModel;
+ /**
+ * @var ToolbarMemorizer
+ */
+ private $toolbarMemorizer;
+
/**
* @var ProductList
*/
@@ -119,6 +128,16 @@ class Toolbar extends \Magento\Framework\View\Element\Template
*/
protected $_postDataHelper;
+ /**
+ * @var \Magento\Framework\App\Http\Context
+ */
+ private $httpContext;
+
+ /**
+ * @var \Magento\Framework\Data\Form\FormKey
+ */
+ private $formKey;
+
/**
* @param \Magento\Framework\View\Element\Template\Context $context
* @param \Magento\Catalog\Model\Session $catalogSession
@@ -128,6 +147,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template
* @param ProductList $productListHelper
* @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper
* @param array $data
+ * @param ToolbarMemorizer|null $toolbarMemorizer
+ * @param \Magento\Framework\App\Http\Context|null $httpContext
+ * @param \Magento\Framework\Data\Form\FormKey|null $formKey
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
@@ -137,7 +161,10 @@ public function __construct(
\Magento\Framework\Url\EncoderInterface $urlEncoder,
ProductList $productListHelper,
\Magento\Framework\Data\Helper\PostHelper $postDataHelper,
- array $data = []
+ array $data = [],
+ ToolbarMemorizer $toolbarMemorizer = null,
+ \Magento\Framework\App\Http\Context $httpContext = null,
+ \Magento\Framework\Data\Form\FormKey $formKey = null
) {
$this->_catalogSession = $catalogSession;
$this->_catalogConfig = $catalogConfig;
@@ -145,6 +172,15 @@ public function __construct(
$this->urlEncoder = $urlEncoder;
$this->_productListHelper = $productListHelper;
$this->_postDataHelper = $postDataHelper;
+ $this->toolbarMemorizer = $toolbarMemorizer ?: ObjectManager::getInstance()->get(
+ ToolbarMemorizer::class
+ );
+ $this->httpContext = $httpContext ?: ObjectManager::getInstance()->get(
+ \Magento\Framework\App\Http\Context::class
+ );
+ $this->formKey = $formKey ?: ObjectManager::getInstance()->get(
+ \Magento\Framework\Data\Form\FormKey::class
+ );
parent::__construct($context, $data);
}
@@ -152,6 +188,7 @@ public function __construct(
* Disable list state params memorizing
*
* @return $this
+ * @deprecated
*/
public function disableParamsMemorizing()
{
@@ -165,6 +202,7 @@ public function disableParamsMemorizing()
* @param string $param parameter name
* @param mixed $value parameter value
* @return $this
+ * @deprecated
*/
protected function _memorizeParam($param, $value)
{
@@ -244,13 +282,13 @@ public function getCurrentOrder()
$defaultOrder = $keys[0];
}
- $order = $this->_toolbarModel->getOrder();
+ $order = $this->toolbarMemorizer->getOrder();
if (!$order || !isset($orders[$order])) {
$order = $defaultOrder;
}
- if ($order != $defaultOrder) {
- $this->_memorizeParam('sort_order', $order);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, $defaultOrder);
}
$this->setData('_current_grid_order', $order);
@@ -270,13 +308,13 @@ public function getCurrentDirection()
}
$directions = ['asc', 'desc'];
- $dir = strtolower($this->_toolbarModel->getDirection());
+ $dir = strtolower($this->toolbarMemorizer->getDirection());
if (!$dir || !in_array($dir, $directions)) {
$dir = $this->_direction;
}
- if ($dir != $this->_direction) {
- $this->_memorizeParam('sort_direction', $dir);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $dir, $this->_direction);
}
$this->setData('_current_grid_direction', $dir);
@@ -392,6 +430,8 @@ public function getPagerUrl($params = [])
}
/**
+ * Get pager encoded url.
+ *
* @param array $params
* @return string
*/
@@ -412,11 +452,15 @@ public function getCurrentMode()
return $mode;
}
$defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes());
- $mode = $this->_toolbarModel->getMode();
+ $mode = $this->toolbarMemorizer->getMode();
if (!$mode || !isset($this->_availableMode[$mode])) {
$mode = $defaultMode;
}
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, $defaultMode);
+ }
+
$this->setData('_current_grid_mode', $mode);
return $mode;
}
@@ -568,13 +612,13 @@ public function getLimit()
$defaultLimit = $keys[0];
}
- $limit = $this->_toolbarModel->getLimit();
+ $limit = $this->toolbarMemorizer->getLimit();
if (!$limit || !isset($limits[$limit])) {
$limit = $defaultLimit;
}
- if ($limit != $defaultLimit) {
- $this->_memorizeParam('limit_page', $limit);
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, $defaultLimit);
}
$this->setData('_current_limit', $limit);
@@ -582,6 +626,8 @@ public function getLimit()
}
/**
+ * Check if limit is current used in toolbar.
+ *
* @param int $limit
* @return bool
*/
@@ -591,6 +637,8 @@ public function isLimitCurrent($limit)
}
/**
+ * Pager number of items from which products started on current page.
+ *
* @return int
*/
public function getFirstNum()
@@ -600,6 +648,8 @@ public function getFirstNum()
}
/**
+ * Pager number of items products finished on current page.
+ *
* @return int
*/
public function getLastNum()
@@ -609,6 +659,8 @@ public function getLastNum()
}
/**
+ * Total number of products in current category.
+ *
* @return int
*/
public function getTotalNum()
@@ -617,6 +669,8 @@ public function getTotalNum()
}
/**
+ * Check if current page is the first.
+ *
* @return bool
*/
public function isFirstPage()
@@ -625,6 +679,8 @@ public function isFirstPage()
}
/**
+ * Return last page number.
+ *
* @return int
*/
public function getLastPageNum()
@@ -692,6 +748,8 @@ public function getWidgetOptionsJson(array $customOptions = [])
'orderDefault' => $this->getOrderField(),
'limitDefault' => $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode),
'url' => $this->getPagerUrl(),
+ 'formKey' => $this->formKey->getFormKey(),
+ 'post' => $this->toolbarMemorizer->isMemorizingAllowed() ? true : false
];
$options = array_replace_recursive($options, $customOptions);
return json_encode(['productListToolbarForm' => $options]);
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
index d82f4a04fb252..f11d16755ef0d 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php
@@ -19,6 +19,8 @@
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter;
/**
+ * Product helper
+ *
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @since 100.0.2
@@ -365,6 +367,8 @@ private function overwriteValue($optionId, $option, $overwriteOptions)
}
/**
+ * Get link resolver instance
+ *
* @return LinkResolver
* @deprecated 101.0.0
*/
@@ -377,6 +381,8 @@ private function getLinkResolver()
}
/**
+ * Get DateTimeFilter instance
+ *
* @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime
* @deprecated 101.0.0
*/
@@ -391,6 +397,7 @@ private function getDateTimeFilter()
/**
* Remove ids of non selected websites from $websiteIds array and return filtered data
+ *
* $websiteIds parameter expects array with website ids as keys and 1 (selected) or 0 (non selected) as values
* Only one id (default website ID) will be set to $websiteIds array when the single store mode is turned on
*
@@ -463,6 +470,7 @@ private function fillProductOptions(Product $product, array $productOptions)
private function convertSpecialFromDateStringToObject($productData)
{
if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') {
+ $productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']);
$productData['special_from_date'] = new \DateTime($productData['special_from_date']);
}
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
index b7655f7ee2862..9d7273fb3f23c 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php
@@ -14,6 +14,7 @@
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
/**
+ * Updates status for a batch of products.
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class MassStatus extends \Magento\Catalog\Controller\Adminhtml\Product implements HttpPostActionInterface
@@ -87,7 +88,7 @@ public function execute()
$filterRequest = $this->getRequest()->getParam('filters', null);
$status = (int) $this->getRequest()->getParam('status');
- if (null !== $storeId && null !== $filterRequest) {
+ if (null === $storeId && null !== $filterRequest) {
$storeId = (isset($filterRequest['store_id'])) ? (int) $filterRequest['store_id'] : 0;
}
diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php
index 19243aabb1b71..2088bb5ea77cd 100644
--- a/app/code/Magento/Catalog/Controller/Category/View.php
+++ b/app/code/Magento/Catalog/Controller/Category/View.php
@@ -10,6 +10,7 @@
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\Layer\Resolver;
+use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Action;
@@ -74,6 +75,11 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter
*/
protected $categoryRepository;
+ /**
+ * @var ToolbarMemorizer
+ */
+ private $toolbarMemorizer;
+
/**
* Constructor
*
@@ -87,6 +93,7 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter
* @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory
* @param Resolver $layerResolver
* @param CategoryRepositoryInterface $categoryRepository
+ * @param ToolbarMemorizer|null $toolbarMemorizer
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -99,7 +106,8 @@ public function __construct(
PageFactory $resultPageFactory,
\Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory,
Resolver $layerResolver,
- CategoryRepositoryInterface $categoryRepository
+ CategoryRepositoryInterface $categoryRepository,
+ ToolbarMemorizer $toolbarMemorizer = null
) {
parent::__construct($context);
$this->_storeManager = $storeManager;
@@ -111,6 +119,7 @@ public function __construct(
$this->resultForwardFactory = $resultForwardFactory;
$this->layerResolver = $layerResolver;
$this->categoryRepository = $categoryRepository;
+ $this->toolbarMemorizer = $toolbarMemorizer ?: $context->getObjectManager()->get(ToolbarMemorizer::class);
}
/**
@@ -135,6 +144,7 @@ protected function _initCategory()
}
$this->_catalogSession->setLastVisitedCategoryId($category->getId());
$this->_coreRegistry->register('current_category', $category);
+ $this->toolbarMemorizer->memorizeParams();
try {
$this->_eventManager->dispatch(
'catalog_controller_category_init_after',
@@ -198,7 +208,7 @@ public function execute()
if ($layoutUpdates && is_array($layoutUpdates)) {
foreach ($layoutUpdates as $layoutUpdate) {
$page->addUpdate($layoutUpdate);
- $page->addPageLayoutHandles(['layout_update' => md5($layoutUpdate)], null, false);
+ $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false);
}
}
diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php
index ae20cda460796..3e96763632830 100644
--- a/app/code/Magento/Catalog/Helper/Data.php
+++ b/app/code/Magento/Catalog/Helper/Data.php
@@ -7,6 +7,7 @@
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Store\Model\ScopeInterface;
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Pricing\PriceCurrencyInterface;
@@ -273,7 +274,8 @@ public function setStoreId($store)
/**
* Return current category path or get it from current category
- * and creating array of categories|product paths for breadcrumbs
+ *
+ * Creating array of categories|product paths for breadcrumbs
*
* @return array
*/
@@ -382,6 +384,7 @@ public function getLastViewedUrl()
/**
* Split SKU of an item by dashes and spaces
+ *
* Words will not be broken, unless this length is greater than $length
*
* @param string $sku
@@ -410,14 +413,15 @@ public function getAttributeHiddenFields()
/**
* Retrieve Catalog Price Scope
*
- * @return int
+ * @return int|null
*/
- public function getPriceScope()
+ public function getPriceScope(): ?int
{
- return $this->scopeConfig->getValue(
+ $priceScope = $this->scopeConfig->getValue(
self::XML_PATH_PRICE_SCOPE,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE
);
+ return isset($priceScope) ? (int)$priceScope : null;
}
/**
@@ -439,7 +443,7 @@ public function isUsingStaticUrlsAllowed()
{
return $this->scopeConfig->isSetFlag(
self::CONFIG_USE_STATIC_URLS,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE
);
}
@@ -454,7 +458,7 @@ public function isUrlDirectivesParsingAllowed()
{
return $this->scopeConfig->isSetFlag(
self::CONFIG_PARSE_URL_DIRECTIVES,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ ScopeInterface::SCOPE_STORE,
$this->_storeId
);
}
@@ -472,6 +476,7 @@ public function getPageTemplateProcessor()
/**
* Whether to display items count for each filter option
+ *
* @param int $storeId Store view ID
* @return bool
*/
@@ -479,12 +484,14 @@ public function shouldDisplayProductCountOnLayer($storeId = null)
{
return $this->scopeConfig->isSetFlag(
self::XML_PATH_DISPLAY_PRODUCT_COUNT,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ ScopeInterface::SCOPE_STORE,
$storeId
);
}
/**
+ * Convert tax address array to address data object with country id and postcode
+ *
* @param array $taxAddress
* @return \Magento\Customer\Api\Data\AddressInterface|null
*/
diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php
index 758e59790d241..170f1209ad9e6 100644
--- a/app/code/Magento/Catalog/Helper/Image.php
+++ b/app/code/Magento/Catalog/Helper/Image.php
@@ -298,6 +298,7 @@ public function resize($width, $height = null)
*
* @param int $quality
* @return $this
+ * @deprecated
*/
public function setQuality($quality)
{
@@ -406,7 +407,8 @@ public function rotate($angle)
/**
* Add watermark to image
- * size param in format 100x200
+ *
+ * Size param in format 100x200
*
* @param string $fileName
* @param string $position
@@ -533,6 +535,8 @@ public function getUrl()
}
/**
+ * Save changes
+ *
* @return $this
*/
public function save()
@@ -553,6 +557,8 @@ public function getResizedImageInfo()
}
/**
+ * Getter for placeholder url
+ *
* @param null|string $placeholder
* @return string
*/
@@ -655,7 +661,8 @@ protected function getWatermarkPosition()
/**
* Set watermark size
- * param size in format 100x200
+ *
+ * Param size in format 100x200
*
* @param string $size
* @return $this
diff --git a/app/code/Magento/Catalog/Model/AbstractModel.php b/app/code/Magento/Catalog/Model/AbstractModel.php
index 007635b124331..78a49cd1e8b14 100644
--- a/app/code/Magento/Catalog/Model/AbstractModel.php
+++ b/app/code/Magento/Catalog/Model/AbstractModel.php
@@ -179,7 +179,7 @@ public function isLockedAttribute($attributeCode)
*
* @param string|array $key
* @param mixed $value
- * @return \Magento\Framework\DataObject
+ * @return $this
*/
public function setData($key, $value = null)
{
@@ -282,9 +282,9 @@ public function getWebsiteStoreIds()
*
* Default value existing is flag for using store value in data
*
- * @param string $attributeCode
- * @param mixed $value
- * @return $this
+ * @param string $attributeCode
+ * @param mixed $value
+ * @return $this
*
* @deprecated 101.0.0
*/
@@ -332,11 +332,10 @@ public function getAttributeDefaultValue($attributeCode)
}
/**
- * Set attribute code flag if attribute has value in current store and does not use
- * value of default store as value
+ * Set attribute code flag if attribute has value in current store and does not use value of default store as value
*
- * @param string $attributeCode
- * @return $this
+ * @param string $attributeCode
+ * @return $this
*
* @deprecated 101.0.0
*/
diff --git a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php
index f70bab73d0830..66a9132ae44b8 100644
--- a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php
+++ b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php
@@ -38,6 +38,7 @@ class ProductCategoryCondition implements CustomConditionInterface
/**
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
+ * @param \Magento\Catalog\Model\CategoryRepository $categoryRepository
*/
public function __construct(
\Magento\Framework\App\ResourceConnection $resourceConnection,
@@ -104,7 +105,7 @@ private function getCategoryIds(Filter $filter): array
}
}
- return array_unique(array_merge($categoryIds, ...$childCategoryIds));
+ return array_map('intval', array_unique(array_merge($categoryIds, ...$childCategoryIds)));
}
/**
diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php
index bd7cdabb40856..853bbeac8eb38 100644
--- a/app/code/Magento/Catalog/Model/Design.php
+++ b/app/code/Magento/Catalog/Model/Design.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Catalog\Model;
+use \Magento\Framework\TranslateInterface;
+
/**
* Catalog Custom Category design Model
*
@@ -31,14 +33,20 @@ class Design extends \Magento\Framework\Model\AbstractModel
*/
protected $_localeDate;
+ /**
+ * @var TranslateInterface
+ */
+ private $translator;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
* @param \Magento\Framework\View\DesignInterface $design
- * @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 TranslateInterface|null $translator
*/
public function __construct(
\Magento\Framework\Model\Context $context,
@@ -47,10 +55,13 @@ public function __construct(
\Magento\Framework\View\DesignInterface $design,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
- array $data = []
+ array $data = [],
+ TranslateInterface $translator = null
) {
$this->_localeDate = $localeDate;
$this->_design = $design;
+ $this->translator = $translator ?:
+ \Magento\Framework\App\ObjectManager::getInstance()->get(TranslateInterface::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
@@ -63,6 +74,7 @@ public function __construct(
public function applyCustomDesign($design)
{
$this->_design->setDesignTheme($design);
+ $this->translator->loadData(null, true);
return $this;
}
diff --git a/app/code/Magento/Catalog/Model/FilterProductCustomAttribute.php b/app/code/Magento/Catalog/Model/FilterProductCustomAttribute.php
index b83bb97301b9c..497ed2fd49953 100644
--- a/app/code/Magento/Catalog/Model/FilterProductCustomAttribute.php
+++ b/app/code/Magento/Catalog/Model/FilterProductCustomAttribute.php
@@ -27,11 +27,12 @@ public function __construct(array $blackList = [])
/**
* Delete custom attribute
- * @param array $attributes
+ *
+ * @param array $attributes set objects attributes @example ['attribute_code'=>'attribute_object']
* @return array
*/
public function execute(array $attributes): array
{
- return array_diff($attributes, $this->blackList);
+ return array_diff_key($attributes, array_flip($this->blackList));
}
}
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php
index 64a8f930d83ee..a62e3d8f83b85 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Catalog\Model\Indexer\Category\Flat\Action;
+/**
+ * Class for full reindex flat categories
+ */
class Full extends \Magento\Catalog\Model\Indexer\Category\Flat\AbstractAction
{
/**
@@ -92,6 +95,7 @@ protected function populateFlatTables(array $stores)
/**
* Create table and add attributes as fields for specified store.
+ *
* This routine assumes that DDL operations are allowed
*
* @param int $store
@@ -109,6 +113,7 @@ protected function createTable($store)
/**
* Create category flat tables and add attributes as fields.
+ *
* Tables are created only if DDL operations are allowed
*
* @param \Magento\Store\Model\Store[] $stores if empty, create tables for all stores of the application
@@ -167,6 +172,44 @@ protected function switchTables(array $stores = [])
return $this;
}
+ /**
+ * Retrieve all actual Catalog Product Flat Table names
+ *
+ * @return string[]
+ */
+ private function getActualStoreTablesForCategoryFlat(): array
+ {
+ $actualStoreTables = [];
+ foreach ($this->storeManager->getStores() as $store) {
+ $actualStoreTables[] = sprintf(
+ '%s_store_%s',
+ $this->connection->getTableName('catalog_category_flat'),
+ $store->getId()
+ );
+ }
+
+ return $actualStoreTables;
+ }
+
+ /**
+ * Delete all category flat tables for not existing stores
+ *
+ * @return void
+ */
+ private function deleteAbandonedStoreCategoryFlatTables(): void
+ {
+ $existentTables = $this->connection->getTables(
+ $this->connection->getTableName('catalog_category_flat_store_%')
+ );
+ $actualStoreTables = $this->getActualStoreTablesForCategoryFlat();
+
+ $tablesToDelete = array_diff($existentTables, $actualStoreTables);
+
+ foreach ($tablesToDelete as $table) {
+ $this->connection->dropTable($table);
+ }
+ }
+
/**
* Transactional rebuild flat data from eav
*
@@ -182,7 +225,7 @@ public function reindexAll()
$stores = $this->storeManager->getStores();
$this->populateFlatTables($stores);
$this->switchTables($stores);
-
+ $this->deleteAbandonedStoreCategoryFlatTables();
$this->allowTableChanges = true;
return $this;
diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
index 6a499883ac723..178f4172ce6fa 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php
@@ -123,6 +123,11 @@ abstract class AbstractAction
*/
private $queryGenerator;
+ /**
+ * @var int
+ */
+ private $currentStoreId = 0;
+
/**
* @param ResourceConnection $resource
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -164,6 +169,7 @@ protected function reindex()
{
foreach ($this->storeManager->getStores() as $store) {
if ($this->getPathFromCategoryId($store->getRootCategoryId())) {
+ $this->currentStoreId = $store->getId();
$this->reindexRootCategory($store);
$this->reindexAnchorCategories($store);
$this->reindexNonAnchorCategories($store);
@@ -586,6 +592,8 @@ protected function createAnchorSelect(Store $store)
}
/**
+ * Get temporary table name
+ *
* Get temporary table name for concurrent indexing in persistent connection
* Temp table name is NOT shared between action instances and each action has it's own temp tree index
*
@@ -597,7 +605,7 @@ protected function getTemporaryTreeIndexTableName()
if (empty($this->tempTreeIndexTableName)) {
$this->tempTreeIndexTableName = $this->connection->getTableName('temp_catalog_category_tree_index')
. '_'
- . substr(md5(time() . random_int(0, 999999999)), 0, 8);
+ . substr(sha1(time() . random_int(0, 999999999)), 0, 8);
}
return $this->tempTreeIndexTableName;
@@ -641,7 +649,6 @@ protected function makeTempCategoryTreeIndex()
['child_id'],
['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX]
);
-
// Drop the temporary table in case it already exists on this (persistent?) connection.
$this->connection->dropTemporaryTable($temporaryName);
$this->connection->createTemporaryTable($temporaryTable);
@@ -659,11 +666,31 @@ protected function makeTempCategoryTreeIndex()
*/
protected function fillTempCategoryTreeIndex($temporaryName)
{
+ $isActiveAttributeId = $this->config->getAttribute(
+ \Magento\Catalog\Model\Category::ENTITY,
+ 'is_active'
+ )->getId();
+ $categoryMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
+ $categoryLinkField = $categoryMetadata->getLinkField();
$selects = $this->prepareSelectsByRange(
$this->connection->select()
->from(
['c' => $this->getTable('catalog_category_entity')],
['entity_id', 'path']
+ )->joinInner(
+ ['ccacd' => $this->getTable('catalog_category_entity_int')],
+ 'ccacd.' . $categoryLinkField . ' = c.' . $categoryLinkField . ' AND ccacd.store_id = 0' .
+ ' AND ccacd.attribute_id = ' . $isActiveAttributeId,
+ []
+ )->joinLeft(
+ ['ccacs' => $this->getTable('catalog_category_entity_int')],
+ 'ccacs.' . $categoryLinkField . ' = c.' . $categoryLinkField
+ . ' AND ccacs.attribute_id = ccacd.attribute_id AND ccacs.store_id = ' .
+ $this->currentStoreId,
+ []
+ )->where(
+ $this->connection->getIfNullSql('ccacs.value', 'ccacd.value') . ' = ?',
+ 1
),
'entity_id'
);
diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php
index cf6b509822ad3..44c5c891f4d6a 100644
--- a/app/code/Magento/Catalog/Model/Product.php
+++ b/app/code/Magento/Catalog/Model/Product.php
@@ -512,13 +512,17 @@ protected function _getResource()
protected function getCustomAttributesCodes()
{
if ($this->customAttributesCodes === null) {
- $this->customAttributesCodes = array_keys($this->eavConfig->getEntityAttributes(
- self::ENTITY,
- $this
- ));
-
- $this->customAttributesCodes = $this->filterCustomAttribute->execute($this->customAttributesCodes);
- $this->customAttributesCodes = array_diff($this->customAttributesCodes, ProductInterface::ATTRIBUTES);
+ $this->customAttributesCodes = array_diff(
+ array_keys(
+ $this->filterCustomAttribute->execute(
+ $this->eavConfig->getEntityAttributes(
+ self::ENTITY,
+ $this
+ )
+ )
+ ),
+ ProductInterface::ATTRIBUTES
+ );
}
return $this->customAttributesCodes;
@@ -596,7 +600,6 @@ public function getPrice()
* @see \Magento\Catalog\Model\Product\Visibility
*
* @return int
- * @codeCoverageIgnoreStart
*/
public function getVisibility()
{
@@ -1053,7 +1056,7 @@ public function reindex()
*
* Register indexing event before delete product
*
- * @return $this
+ * @return \Magento\Catalog\Model\Product
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeDelete()
@@ -1567,6 +1570,7 @@ public function hasGalleryAttribute()
* @param bool $move if true, it will move source file
* @param bool $exclude mark image as disabled in product page view
* @return \Magento\Catalog\Model\Product
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function addImageToMediaGallery($file, $mediaAttribute = null, $move = false, $exclude = true)
{
@@ -1818,7 +1822,7 @@ public function formatUrlKey($str)
/**
* Save current attribute with code $code and assign new value
*
- * @param string $code Attribute code
+ * @param string $code Attribute code
* @param mixed $value New attribute value
* @param int $store Store ID
* @return void
@@ -2230,9 +2234,9 @@ public function getPreconfiguredValues()
}
/**
- * Prepare product custom options
+ * Prepare product custom options.
*
- * To be sure that all product custom options does not has ID and has product instance.
+ * To be sure that all product custom options does not has ID and has product instance
*
* @return \Magento\Catalog\Model\Product
*/
@@ -2568,7 +2572,7 @@ public function setTypeId($typeId)
/**
* @inheritdoc
*
- * @return \Magento\Catalog\Api\Data\ProductExtensionInterface
+ * @return \Magento\Framework\Api\ExtensionAttributesInterface
*/
public function getExtensionAttributes()
{
@@ -2609,7 +2613,7 @@ protected function convertToMediaGalleryInterface(array $mediaGallery)
}
/**
- * Returns media gallery entries
+ * Get media gallery entries
*
* @return \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface[]|null
* @throws \Magento\Framework\Exception\LocalizedException
@@ -2671,7 +2675,7 @@ public function setId($value)
}
/**
- * Returns link repository instance
+ * Get link repository
*
* @return ProductLinkRepositoryInterface
*/
@@ -2685,7 +2689,7 @@ private function getLinkRepository()
}
/**
- * Returns media gallery processor instance
+ * Get media gallery processor
*
* @return Product\Gallery\Processor
*/
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 a652d0ef90213..98738e055ca8f 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php
@@ -4,15 +4,13 @@
* See COPYING.txt for license details.
*/
-/**
- * Catalog product SKU backend attribute model
- *
- * @author Magento Core Team
- */
namespace Magento\Catalog\Model\Product\Attribute\Backend;
use Magento\Catalog\Model\Product;
+/**
+ * Catalog product SKU backend attribute model.
+ */
class Sku extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
{
/**
@@ -97,6 +95,7 @@ protected function _generateUniqueSku($object)
public function beforeSave($object)
{
$this->_generateUniqueSku($object);
+ $this->trimValue($object);
return parent::beforeSave($object);
}
@@ -127,4 +126,19 @@ protected function _getLastSimilarAttributeValueIncrement($attribute, $object)
$data = $connection->fetchOne($select, $bind);
return abs((int)str_replace($value, '', $data));
}
+
+ /**
+ * Remove extra spaces from attribute value before save.
+ *
+ * @param Product $object
+ * @return void
+ */
+ private function trimValue($object)
+ {
+ $attrCode = $this->getAttribute()->getAttributeCode();
+ $value = $object->getData($attrCode);
+ if ($value) {
+ $object->setData($attrCode, trim($value));
+ }
+ }
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
index b4d6dc2c19e51..aef3e87586015 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php
@@ -86,10 +86,10 @@ public function execute($entity, $arguments = [])
__('Tier prices data should be array, but actually other type is received')
);
}
- $websiteId = $this->storeManager->getStore($entity->getStoreId())->getWebsiteId();
+ $websiteId = (int)$this->storeManager->getStore($entity->getStoreId())->getWebsiteId();
$isGlobal = $attribute->isScopeGlobal() || $websiteId === 0;
$identifierField = $this->metadataPoll->getMetadata(ProductInterface::class)->getLinkField();
- $productId = (int) $entity->getData($identifierField);
+ $productId = (int)$entity->getData($identifierField);
// prepare original data to compare
$origPrices = [];
@@ -98,7 +98,7 @@ public function execute($entity, $arguments = [])
$origPrices = $entity->getOrigData($attribute->getName());
}
- $old = $this->prepareOriginalDataToCompare($origPrices, $isGlobal);
+ $old = $this->prepareOldTierPriceToCompare($origPrices);
// prepare data for save
$new = $this->prepareNewDataForSave($priceRows, $isGlobal);
@@ -271,21 +271,18 @@ private function isWebsiteGlobal(int $websiteId): bool
}
/**
- * Prepare original data to compare.
+ * Prepare old data to compare.
*
* @param array|null $origPrices
- * @param bool $isGlobal
* @return array
*/
- private function prepareOriginalDataToCompare(?array $origPrices, bool $isGlobal = true): array
+ private function prepareOldTierPriceToCompare(?array $origPrices): array
{
$old = [];
if (is_array($origPrices)) {
foreach ($origPrices as $data) {
- if ($isGlobal === $this->isWebsiteGlobal((int)$data['website_id'])) {
- $key = $this->getPriceKey($data);
- $old[$key] = $data;
- }
+ $key = $this->getPriceKey($data);
+ $old[$key] = $data;
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
index 92b9a2e4239b2..e346c912dccaa 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
@@ -13,6 +13,9 @@
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
+/**
+ * Backend model for Tierprice attribute
+ */
class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice
{
/**
@@ -159,8 +162,22 @@ protected function validatePrice(array $priceRow)
*/
protected function modifyPriceData($object, $data)
{
+ /** @var \Magento\Catalog\Model\Product $object */
$data = parent::modifyPriceData($object, $data);
$price = $object->getPrice();
+
+ $specialPrice = $object->getSpecialPrice();
+ $specialPriceFromDate = $object->getSpecialFromDate();
+ $specialPriceToDate = $object->getSpecialToDate();
+ $today = time();
+
+ if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) {
+ if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) ||
+ $today >= strtotime($specialPriceFromDate) && $specialPriceToDate === null) {
+ $price = $specialPrice;
+ }
+ }
+
foreach ($data as $key => $tierPrice) {
$percentageValue = $this->getPercentage($tierPrice);
if ($percentageValue) {
@@ -172,6 +189,10 @@ protected function modifyPriceData($object, $data)
}
/**
+ * Update Price values in DB
+ *
+ * Updates price values in DB from array comparing to old values. Returns bool if updated
+ *
* @param array $valuesToUpdate
* @param array $oldValues
* @return boolean
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
index f6d3ca36c1e1e..99edfe5bc7208 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
@@ -11,6 +11,8 @@
use Magento\Framework\Exception\NoSuchEntityException;
/**
+ * Product attribute repository
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Repository implements \Magento\Catalog\Api\ProductAttributeRepositoryInterface
@@ -78,7 +80,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function get($attributeCode)
{
@@ -89,7 +91,7 @@ public function get($attributeCode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
{
@@ -100,12 +102,17 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attribute)
{
+ $attribute->setEntityTypeId(
+ $this->eavConfig
+ ->getEntityType(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE)
+ ->getId()
+ );
if ($attribute->getAttributeId()) {
$existingModel = $this->get($attribute->getAttributeCode());
@@ -144,11 +151,6 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
$attribute->setBackendModel(
$this->productHelper->getAttributeBackendModelByInputType($attribute->getFrontendInput())
);
- $attribute->setEntityTypeId(
- $this->eavConfig
- ->getEntityType(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE)
- ->getId()
- );
$attribute->setIsUserDefined(1);
}
if (!empty($attribute->getData(AttributeInterface::OPTIONS))) {
@@ -180,7 +182,7 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function delete(\Magento\Catalog\Api\Data\ProductAttributeInterface $attribute)
{
@@ -189,7 +191,7 @@ public function delete(\Magento\Catalog\Api\Data\ProductAttributeInterface $attr
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function deleteById($attributeCode)
{
@@ -200,7 +202,7 @@ public function deleteById($attributeCode)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function getCustomAttributesMetadata($dataObjectClassName = null)
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php
index 4d274a071d087..0e08b0af92862 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php
@@ -1,6 +1,5 @@
contentValidator->isValid($entryContent)) {
throw new InputException(__('The image content is invalid. Verify the content and try again.'));
}
- $product = $this->productRepository->get($sku);
+ $product = $this->productRepository->get($sku, true);
$existingMediaGalleryEntries = $product->getMediaGalleryEntries();
$existingEntryIds = [];
@@ -84,11 +86,11 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry)
{
- $product = $this->productRepository->get($sku);
+ $product = $this->productRepository->get($sku, true);
$existingMediaGalleryEntries = $product->getMediaGalleryEntries();
if ($existingMediaGalleryEntries == null) {
throw new NoSuchEntityException(
@@ -125,11 +127,11 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function remove($sku, $entryId)
{
- $product = $this->productRepository->get($sku);
+ $product = $this->productRepository->get($sku, true);
$existingMediaGalleryEntries = $product->getMediaGalleryEntries();
if ($existingMediaGalleryEntries == null) {
throw new NoSuchEntityException(
@@ -155,7 +157,7 @@ public function remove($sku, $entryId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function get($sku, $entryId)
{
@@ -176,7 +178,7 @@ public function get($sku, $entryId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getList($sku)
{
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
index c6c7fbda7e9ec..0912324745360 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php
@@ -6,9 +6,10 @@
namespace Magento\Catalog\Model\Product\Gallery;
+use Magento\Framework\Api\Data\ImageContentInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
-use Magento\Framework\Filesystem\DriverInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Catalog product Media Gallery attribute processor.
@@ -56,28 +57,39 @@ class Processor
*/
protected $resourceModel;
+ /**
+ * @var \Magento\Framework\File\Mime
+ */
+ private $mime;
+
/**
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
* @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb
* @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel
+ * @param \Magento\Framework\File\Mime|null $mime
+ * @throws \Magento\Framework\Exception\FileSystemException
*/
public function __construct(
\Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
\Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb,
\Magento\Catalog\Model\Product\Media\Config $mediaConfig,
\Magento\Framework\Filesystem $filesystem,
- \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel
+ \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel,
+ \Magento\Framework\File\Mime $mime = null
) {
$this->attributeRepository = $attributeRepository;
$this->fileStorageDb = $fileStorageDb;
$this->mediaConfig = $mediaConfig;
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->resourceModel = $resourceModel;
+ $this->mime = $mime ?: ObjectManager::getInstance()->get(\Magento\Framework\File\Mime::class);
}
/**
+ * Return media_gallery attribute
+ *
* @return \Magento\Catalog\Api\Data\ProductAttributeInterface
* @since 101.0.0
*/
@@ -183,6 +195,13 @@ public function addImage(
$attrCode = $this->getAttribute()->getAttributeCode();
$mediaGalleryData = $product->getData($attrCode);
$position = 0;
+
+ $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($file);
+ $imageMimeType = $this->mime->getMimeType($absoluteFilePath);
+ $imageContent = $this->mediaDirectory->readFile($absoluteFilePath);
+ $imageBase64 = base64_encode($imageContent);
+ $imageName = $pathinfo['filename'];
+
if (!is_array($mediaGalleryData)) {
$mediaGalleryData = ['images' => []];
}
@@ -197,9 +216,17 @@ public function addImage(
$mediaGalleryData['images'][] = [
'file' => $fileName,
'position' => $position,
- 'media_type' => 'image',
'label' => '',
'disabled' => (int)$exclude,
+ 'media_type' => 'image',
+ 'types' => $mediaAttribute,
+ 'content' => [
+ 'data' => [
+ ImageContentInterface::NAME => $imageName,
+ ImageContentInterface::BASE64_ENCODED_DATA => $imageBase64,
+ ImageContentInterface::TYPE => $imageMimeType,
+ ]
+ ]
];
$product->setData($attrCode, $mediaGalleryData);
@@ -358,7 +385,8 @@ public function setMediaAttribute(\Magento\Catalog\Model\Product $product, $medi
}
/**
- * get media attribute codes
+ * Get media attribute codes
+ *
* @return array
* @since 101.0.0
*/
@@ -368,6 +396,8 @@ public function getMediaAttributeCodes()
}
/**
+ * Trim .tmp ending from filename
+ *
* @param string $file
* @return string
* @since 101.0.0
@@ -489,7 +519,6 @@ public function getAffectedFields($object)
/**
* Attribute value is not to be saved in a conventional way, separate table is used to store the complex value
*
- * {@inheritdoc}
* @since 101.0.0
*/
public function isScalar()
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
index c785d08e64b7f..a3726207b3024 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php
@@ -47,6 +47,8 @@ public function __construct(
}
/**
+ * Execute read handler for catalog product gallery
+ *
* @param Product $entity
* @param array $arguments
* @return object
@@ -55,9 +57,6 @@ public function __construct(
*/
public function execute($entity, $arguments = [])
{
- $value = [];
- $value['images'] = [];
-
$mediaEntries = $this->resourceModel->loadProductGalleryByAttributeId(
$entity,
$this->getAttribute()->getAttributeId()
@@ -72,6 +71,8 @@ public function execute($entity, $arguments = [])
}
/**
+ * Add media data to product
+ *
* @param Product $product
* @param array $mediaEntries
* @return void
@@ -79,40 +80,18 @@ public function execute($entity, $arguments = [])
*/
public function addMediaDataToProduct(Product $product, array $mediaEntries)
{
- $attrCode = $this->getAttribute()->getAttributeCode();
- $value = [];
- $value['images'] = [];
- $value['values'] = [];
-
- foreach ($mediaEntries as $mediaEntry) {
- $mediaEntry = $this->substituteNullsWithDefaultValues($mediaEntry);
- $value['images'][$mediaEntry['value_id']] = $mediaEntry;
- }
- $product->setData($attrCode, $value);
- }
-
- /**
- * @param array $rawData
- * @return array
- */
- private function substituteNullsWithDefaultValues(array $rawData)
- {
- $processedData = [];
- foreach ($rawData as $key => $rawValue) {
- if (null !== $rawValue) {
- $processedValue = $rawValue;
- } elseif (isset($rawData[$key . '_default'])) {
- $processedValue = $rawData[$key . '_default'];
- } else {
- $processedValue = null;
- }
- $processedData[$key] = $processedValue;
- }
-
- return $processedData;
+ $product->setData(
+ $this->getAttribute()->getAttributeCode(),
+ [
+ 'images' => array_column($mediaEntries, null, 'value_id'),
+ 'values' => []
+ ]
+ );
}
/**
+ * Get attribute
+ *
* @return \Magento\Catalog\Api\Data\ProductAttributeInterface
* @since 101.0.0
*/
@@ -126,6 +105,8 @@ public function getAttribute()
}
/**
+ * Find default value
+ *
* @param string $key
* @param string[] &$image
* @return string
diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php
index f1ae9ac62dc9b..a0be36c5a327c 100644
--- a/app/code/Magento/Catalog/Model/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/Product/Image.php
@@ -15,6 +15,8 @@
use Magento\Catalog\Model\Product\Image\ParamsBuilder;
/**
+ * Image operations
+ *
* @method string getFile()
* @method string getLabel()
* @method string getPosition()
@@ -24,6 +26,11 @@
*/
class Image extends \Magento\Framework\Model\AbstractModel
{
+ /**
+ * Config path for the jpeg image quality value
+ */
+ const XML_PATH_JPEG_QUALITY = 'system/upload_configuration/jpeg_quality';
+
/**
* @var int
*/
@@ -38,8 +45,9 @@ class Image extends \Magento\Framework\Model\AbstractModel
* Default quality value (for JPEG images only).
*
* @var int
+ * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY
*/
- protected $_quality = 80;
+ protected $_quality = null;
/**
* @var bool
@@ -203,13 +211,13 @@ class Image extends \Magento\Framework\Model\AbstractModel
* @param \Magento\Framework\Image\Factory $imageFactory
* @param \Magento\Framework\View\Asset\Repository $assetRepo
* @param \Magento\Framework\View\FileSystem $viewFileSystem
+ * @param ImageFactory $viewAssetImageFactory
+ * @param PlaceholderFactory $viewAssetPlaceholderFactory
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
- * @param ImageFactory|null $viewAssetImageFactory
- * @param PlaceholderFactory|null $viewAssetPlaceholderFactory
- * @param SerializerInterface|null $serializer
+ * @param SerializerInterface $serializer
* @param ParamsBuilder $paramsBuilder
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
@@ -249,6 +257,8 @@ public function __construct(
}
/**
+ * Set image width property
+ *
* @param int $width
* @return $this
*/
@@ -259,6 +269,8 @@ public function setWidth($width)
}
/**
+ * Get image width property
+ *
* @return int
*/
public function getWidth()
@@ -267,6 +279,8 @@ public function getWidth()
}
/**
+ * Set image height property
+ *
* @param int $height
* @return $this
*/
@@ -277,6 +291,8 @@ public function setHeight($height)
}
/**
+ * Get image height property
+ *
* @return int
*/
public function getHeight()
@@ -289,6 +305,7 @@ public function getHeight()
*
* @param int $quality
* @return $this
+ * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY
*/
public function setQuality($quality)
{
@@ -303,10 +320,14 @@ public function setQuality($quality)
*/
public function getQuality()
{
- return $this->_quality;
+ return $this->_quality === null
+ ? $this->_scopeConfig->getValue(self::XML_PATH_JPEG_QUALITY)
+ : $this->_quality;
}
/**
+ * Set _keepAspectRatio property
+ *
* @param bool $keep
* @return $this
*/
@@ -317,6 +338,8 @@ public function setKeepAspectRatio($keep)
}
/**
+ * Set _keepFrame property
+ *
* @param bool $keep
* @return $this
*/
@@ -327,6 +350,8 @@ public function setKeepFrame($keep)
}
/**
+ * Set _keepTransparency
+ *
* @param bool $keep
* @return $this
*/
@@ -337,6 +362,8 @@ public function setKeepTransparency($keep)
}
/**
+ * Set _constrainOnly
+ *
* @param bool $flag
* @return $this
*/
@@ -347,6 +374,8 @@ public function setConstrainOnly($flag)
}
/**
+ * Set background color
+ *
* @param int[] $rgbArray
* @return $this
*/
@@ -357,6 +386,8 @@ public function setBackgroundColor(array $rgbArray)
}
/**
+ * Set size
+ *
* @param string $size
* @return $this
*/
@@ -411,6 +442,8 @@ public function setBaseFile($file)
}
/**
+ * Get base filename
+ *
* @return string
*/
public function getBaseFile()
@@ -419,6 +452,8 @@ public function getBaseFile()
}
/**
+ * Get new file
+ *
* @deprecated 101.1.0
* @return bool|string
*/
@@ -438,6 +473,8 @@ public function isBaseFilePlaceholder()
}
/**
+ * Set image processor
+ *
* @param MagentoImage $processor
* @return $this
*/
@@ -448,6 +485,8 @@ public function setImageProcessor($processor)
}
/**
+ * Get image processor
+ *
* @return MagentoImage
*/
public function getImageProcessor()
@@ -461,11 +500,13 @@ public function getImageProcessor()
$this->_processor->keepTransparency($this->_keepTransparency);
$this->_processor->constrainOnly($this->_constrainOnly);
$this->_processor->backgroundColor($this->_backgroundColor);
- $this->_processor->quality($this->_quality);
+ $this->_processor->quality($this->getQuality());
return $this->_processor;
}
/**
+ * Resize image
+ *
* @see \Magento\Framework\Image\Adapter\AbstractAdapter
* @return $this
*/
@@ -479,6 +520,8 @@ public function resize()
}
/**
+ * Rotate image
+ *
* @param int $angle
* @return $this
*/
@@ -505,7 +548,8 @@ public function setAngle($angle)
/**
* Add watermark to image
- * size param in format 100x200
+ *
+ * Size param in format 100x200
*
* @param string $file
* @param string $position
@@ -564,6 +608,8 @@ public function setWatermark(
}
/**
+ * Save file
+ *
* @return $this
*/
public function saveFile()
@@ -578,6 +624,8 @@ public function saveFile()
}
/**
+ * Get url
+ *
* @return string
*/
public function getUrl()
@@ -586,6 +634,8 @@ public function getUrl()
}
/**
+ * Set destination subdir
+ *
* @param string $dir
* @return $this
*/
@@ -596,6 +646,8 @@ public function setDestinationSubdir($dir)
}
/**
+ * Get destination subdir
+ *
* @return string
*/
public function getDestinationSubdir()
@@ -604,6 +656,8 @@ public function getDestinationSubdir()
}
/**
+ * Check is image cached
+ *
* @return bool
*/
public function isCached()
@@ -636,7 +690,8 @@ public function getWatermarkFile()
/**
* Get relative watermark file path
- * or false if file not found
+ *
+ * Return false if file not found
*
* @return string | bool
*/
@@ -771,7 +826,10 @@ public function getWatermarkHeight()
}
/**
+ * Clear cache
+ *
* @return void
+ * @throws \Magento\Framework\Exception\FileSystemException
*/
public function clearCache()
{
@@ -784,6 +842,7 @@ public function clearCache()
/**
* First check this file on FS
+ *
* If it doesn't exist - try to download it from DB
*
* @param string $filename
@@ -802,6 +861,7 @@ protected function _fileExists($filename)
/**
* Return resized product image information
+ *
* @return array
* @throws NotLoadInfoImageException
*/
@@ -843,7 +903,7 @@ private function getMiscParams()
'transparency' => $this->_keepTransparency,
'background' => $this->_backgroundColor,
'angle' => $this->_angle,
- 'quality' => $this->_quality
+ 'quality' => $this->getQuality()
]
);
}
diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
index dd8d352fecebc..f6be7f7392b5e 100644
--- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
+++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php
@@ -10,17 +10,13 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\View\ConfigInterface;
use Magento\Store\Model\ScopeInterface;
+use Magento\Catalog\Model\Product\Image;
/**
* Builds parameters array used to build Image Asset
*/
class ParamsBuilder
{
- /**
- * @var int
- */
- private $defaultQuality = 80;
-
/**
* @var array
*/
@@ -69,6 +65,8 @@ public function __construct(
}
/**
+ * Build image params
+ *
* @param array $imageArguments
* @return array
* @SuppressWarnings(PHPMD.NPathComplexity)
@@ -89,6 +87,8 @@ public function build(array $imageArguments): array
}
/**
+ * Overwrite default values
+ *
* @param array $imageArguments
* @return array
*/
@@ -100,11 +100,12 @@ private function overwriteDefaultValues(array $imageArguments): array
$transparency = $imageArguments['transparency'] ?? $this->defaultKeepTransparency;
$background = $imageArguments['background'] ?? $this->defaultBackground;
$angle = $imageArguments['angle'] ?? $this->defaultAngle;
+ $quality = (int) $this->scopeConfig->getValue(Image::XML_PATH_JPEG_QUALITY);
return [
'background' => (array) $background,
'angle' => $angle,
- 'quality' => $this->defaultQuality,
+ 'quality' => $quality,
'keep_aspect_ratio' => (bool) $aspectRatio,
'keep_frame' => (bool) $frame,
'keep_transparency' => (bool) $transparency,
@@ -113,6 +114,8 @@ private function overwriteDefaultValues(array $imageArguments): array
}
/**
+ * Get watermark
+ *
* @param string $type
* @return array
*/
@@ -153,6 +156,7 @@ private function getWatermark(string $type): array
/**
* Get frame from product_image_white_borders
+ *
* @return bool
*/
private function hasDefaultFrame(): bool
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ExistingValidate.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ExistingValidate.php
new file mode 100644
index 0000000000000..c9afdf023b307
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ExistingValidate.php
@@ -0,0 +1,52 @@
+_messages = [];
+ $this->_errors = [];
+
+ if (!is_string($value)) {
+ $this->_messages[] = __('Full file path is expected.')->render();
+ return false;
+ }
+
+ $result = true;
+ $fileInfo = null;
+ if ($originalName) {
+ $fileInfo = ['name' => $originalName];
+ }
+ foreach ($this->_validators as $element) {
+ $validator = $element['instance'];
+ if ($validator->isValid($value, $fileInfo)) {
+ continue;
+ }
+ $result = false;
+ $messages = $validator->getMessages();
+ $this->_messages = array_merge($this->_messages, $messages);
+ $this->_errors = array_merge($this->_errors, array_keys($messages));
+ if ($element['breakChainOnFailure']) {
+ break;
+ }
+ }
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php
index 32c901afe8e74..a7add0ad87b89 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidateFactory.php
@@ -6,13 +6,18 @@
namespace Magento\Catalog\Model\Product\Option\Type\File;
+/**
+ * Class ValidateFactory. Creates Validator with type "ExistingValidate"
+ */
class ValidateFactory
{
/**
+ * Main factory method
+ *
* @return \Zend_Validate
*/
public function create()
{
- return new \Zend_Validate();
+ return new ExistingValidate();
}
}
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 d6a5cb1cbc29d..fef4999a1174a 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
@@ -10,8 +10,12 @@
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Catalog\Model\Product\Exception as ProductException;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Math\Random;
+use Magento\Framework\App\ObjectManager;
/**
+ * Validator class. Represents logic for validation file given from product option
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ValidatorFile extends Validator
@@ -63,11 +67,19 @@ class ValidatorFile extends Validator
protected $isImageValidator;
/**
+ * @var Random
+ */
+ private $random;
+
+ /**
+ * Constructor method
+ *
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\File\Size $fileSize
* @param \Magento\Framework\HTTP\Adapter\FileTransferFactory $httpFactory
* @param \Magento\Framework\Validator\File\IsImage $isImageValidator
+ * @param Random|null $random
* @throws \Magento\Framework\Exception\FileSystemException
*/
public function __construct(
@@ -75,16 +87,21 @@ public function __construct(
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\File\Size $fileSize,
\Magento\Framework\HTTP\Adapter\FileTransferFactory $httpFactory,
- \Magento\Framework\Validator\File\IsImage $isImageValidator
+ \Magento\Framework\Validator\File\IsImage $isImageValidator,
+ Random $random = null
) {
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->filesystem = $filesystem;
$this->httpFactory = $httpFactory;
$this->isImageValidator = $isImageValidator;
+ $this->random = $random
+ ?? ObjectManager::getInstance()->get(Random::class);
parent::__construct($scopeConfig, $filesystem, $fileSize);
}
/**
+ * Setter method for the product
+ *
* @param Product $product
* @return $this
*/
@@ -95,6 +112,8 @@ public function setProduct(Product $product)
}
/**
+ * Validation method
+ *
* @param \Magento\Framework\DataObject $processingParams
* @param \Magento\Catalog\Model\Product\Option $option
* @return array
@@ -154,8 +173,6 @@ public function validate($processingParams, $option)
$userValue = [];
if ($upload->isUploaded($file) && $upload->isValid($file)) {
- $extension = pathinfo(strtolower($fileInfo['name']), PATHINFO_EXTENSION);
-
$fileName = \Magento\MediaStorage\Model\File\Uploader::getCorrectFileName($fileInfo['name']);
$dispersion = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName);
@@ -163,7 +180,8 @@ public function validate($processingParams, $option)
$tmpDirectory = $this->filesystem->getDirectoryRead(DirectoryList::SYS_TMP);
$fileHash = md5($tmpDirectory->readFile($tmpDirectory->getRelativePath($fileInfo['tmp_name'])));
- $filePath .= '/' . $fileHash . '.' . $extension;
+ $fileRandomName = $this->random->getRandomString(32);
+ $filePath .= '/' .$fileRandomName;
$fileFullPath = $this->mediaDirectory->getAbsolutePath($this->quotePath . $filePath);
$upload->addFilter(new \Zend_Filter_File_Rename(['target' => $fileFullPath, 'overwrite' => true]));
@@ -243,6 +261,8 @@ protected function initFilesystem()
}
/**
+ * Validate contents length method
+ *
* @return bool
* @todo need correctly name
*/
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 37e4c7b310a81..100ad37273cff 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
@@ -6,6 +6,9 @@
namespace Magento\Catalog\Model\Product\Option\Type\File;
+/**
+ * Validator for existing files.
+ */
class ValidatorInfo extends Validator
{
/**
@@ -34,6 +37,8 @@ class ValidatorInfo extends Validator
protected $fileRelativePath;
/**
+ * Construct method
+ *
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\File\Size $fileSize
@@ -53,6 +58,8 @@ public function __construct(
}
/**
+ * Setter method for property "useQuotePath"
+ *
* @param mixed $useQuotePath
* @return $this
*/
@@ -63,6 +70,8 @@ public function setUseQuotePath($useQuotePath)
}
/**
+ * Validate method for the option value depends on an option
+ *
* @param array $optionValue
* @param \Magento\Catalog\Model\Product\Option $option
* @return bool
@@ -90,7 +99,7 @@ public function validate($optionValue, $option)
}
$result = false;
- if ($validatorChain->isValid($this->fileFullPath)) {
+ if ($validatorChain->isValid($this->fileFullPath, $optionValue['title'])) {
$result = $this->rootDirectory->isReadable($this->fileRelativePath)
&& isset($optionValue['secret_key'])
&& $this->buildSecretKey($this->fileRelativePath) == $optionValue['secret_key'];
@@ -109,6 +118,8 @@ public function validate($optionValue, $option)
}
/**
+ * Method for creation secret key for the given file
+ *
* @param string $fileRelativePath
* @return string
*/
@@ -118,6 +129,8 @@ protected function buildSecretKey($fileRelativePath)
}
/**
+ * Calculates path for the file
+ *
* @param array $optionValue
* @return void
*/
diff --git a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php
new file mode 100644
index 0000000000000..9c1a781d594f7
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php
@@ -0,0 +1,179 @@
+toolbarModel = $toolbarModel;
+ $this->catalogSession = $catalogSession;
+ $this->scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Get sort order
+ *
+ * @return string|bool
+ */
+ public function getOrder()
+ {
+ if ($this->order === null) {
+ $this->order = $this->toolbarModel->getOrder() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::ORDER_PARAM_NAME) : null);
+ }
+ return $this->order;
+ }
+
+ /**
+ * Get sort direction
+ *
+ * @return string|bool
+ */
+ public function getDirection()
+ {
+ if ($this->direction === null) {
+ $this->direction = $this->toolbarModel->getDirection() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::DIRECTION_PARAM_NAME) : null);
+ }
+ return $this->direction;
+ }
+
+ /**
+ * Get sort mode
+ *
+ * @return string|bool
+ */
+ public function getMode()
+ {
+ if ($this->mode === null) {
+ $this->mode = $this->toolbarModel->getMode() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::MODE_PARAM_NAME) : null);
+ }
+ return $this->mode;
+ }
+
+ /**
+ * Get products per page limit
+ *
+ * @return string|bool
+ */
+ public function getLimit()
+ {
+ if ($this->limit === null) {
+ $this->limit = $this->toolbarModel->getLimit() ??
+ ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::LIMIT_PARAM_NAME) : null);
+ }
+ return $this->limit;
+ }
+
+ /**
+ * Method to save all catalog parameters in catalog session
+ *
+ * @return void
+ */
+ public function memorizeParams()
+ {
+ if (!$this->catalogSession->getParamsMemorizeDisabled() && $this->isMemorizingAllowed()) {
+ $this->memorizeParam(Toolbar::ORDER_PARAM_NAME, $this->getOrder())
+ ->memorizeParam(Toolbar::DIRECTION_PARAM_NAME, $this->getDirection())
+ ->memorizeParam(Toolbar::MODE_PARAM_NAME, $this->getMode())
+ ->memorizeParam(Toolbar::LIMIT_PARAM_NAME, $this->getLimit());
+ }
+ }
+
+ /**
+ * Check configuration for enabled/disabled toolbar memorizing
+ *
+ * @return bool
+ */
+ public function isMemorizingAllowed()
+ {
+ if ($this->isMemorizingAllowed === null) {
+ $this->isMemorizingAllowed = $this->scopeConfig->isSetFlag(self::XML_PATH_CATALOG_REMEMBER_PAGINATION);
+ }
+ return $this->isMemorizingAllowed;
+ }
+
+ /**
+ * Memorize parameter value for session
+ *
+ * @param string $param parameter name
+ * @param mixed $value parameter value
+ * @return $this
+ */
+ private function memorizeParam($param, $value)
+ {
+ if ($value && $this->catalogSession->getData($param) != $value) {
+ $this->catalogSession->setData($param, $value);
+ }
+ return $this;
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/ProductCategoryList.php b/app/code/Magento/Catalog/Model/ProductCategoryList.php
index 5bbae772d5c2b..c3a88a505c516 100644
--- a/app/code/Magento/Catalog/Model/ProductCategoryList.php
+++ b/app/code/Magento/Catalog/Model/ProductCategoryList.php
@@ -80,7 +80,10 @@ public function getCategoryIds($productId)
Select::SQL_UNION_ALL
);
- $this->categoryIdList[$productId] = $this->productResource->getConnection()->fetchCol($unionSelect);
+ $this->categoryIdList[$productId] = array_map(
+ 'intval',
+ $this->productResource->getConnection()->fetchCol($unionSelect)
+ );
}
return $this->categoryIdList[$productId];
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 0d62d120f80e0..14ae38667d873 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1534,7 +1534,7 @@ public function addPriceData($customerGroupId = null, $websiteId = null)
/**
* Add attribute to filter
*
- * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|string $attribute
+ * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|string|array $attribute
* @param array $condition
* @param string $joinType
* @return $this
@@ -1809,7 +1809,8 @@ protected function _productLimitationJoinWebsite()
}
$conditions[] = $this->getConnection()->quoteInto(
'product_website.website_id IN(?)',
- $filters['website_ids']
+ $filters['website_ids'],
+ 'int'
);
} elseif (isset(
$filters['store_id']
@@ -1821,7 +1822,7 @@ protected function _productLimitationJoinWebsite()
) {
$joinWebsite = true;
$websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId();
- $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId);
+ $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int');
}
$fromPart = $this->getSelect()->getPart(\Magento\Framework\DB\Select::FROM);
@@ -2017,12 +2018,16 @@ protected function _applyProductLimitations()
$conditions = [
'cat_index.product_id=e.entity_id',
- $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id']),
+ $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id'], 'int'),
];
if (isset($filters['visibility']) && !isset($filters['store_table'])) {
- $conditions[] = $this->getConnection()->quoteInto('cat_index.visibility IN(?)', $filters['visibility']);
+ $conditions[] = $this->getConnection()->quoteInto(
+ 'cat_index.visibility IN(?)',
+ $filters['visibility'],
+ 'int'
+ );
}
- $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id']);
+ $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id'], 'int');
if (isset($filters['category_is_anchor'])) {
$conditions[] = $this->getConnection()->quoteInto('cat_index.is_parent=?', $filters['category_is_anchor']);
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
index 2868392f85280..635715a60742f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php
@@ -49,7 +49,8 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
* @since 101.0.0
*/
protected function _construct()
@@ -58,7 +59,8 @@ protected function _construct()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
* @since 101.0.0
*/
public function getConnection()
@@ -67,6 +69,8 @@ public function getConnection()
}
/**
+ * Load data from table by valueId
+ *
* @param string $tableNameAlias
* @param array $ids
* @param int|null $storeId
@@ -111,6 +115,8 @@ public function loadDataFromTableByValueId(
}
/**
+ * Load product gallery by attributeId
+ *
* @param \Magento\Catalog\Model\Product $product
* @param int $attributeId
* @return array
@@ -132,6 +138,8 @@ public function loadProductGalleryByAttributeId($product, $attributeId)
}
/**
+ * Create base load select
+ *
* @param int $entityId
* @param int $storeId
* @param int $attributeId
@@ -151,6 +159,8 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId)
}
/**
+ * Create batch base select
+ *
* @param int $storeId
* @param int $attributeId
* @return \Magento\Framework\DB\Select
@@ -190,7 +200,7 @@ public function createBatchBaseSelect($storeId, $attributeId)
'value.' . $linkField . ' = entity.' . $linkField,
]
),
- ['label', 'position', 'disabled']
+ []
)->joinLeft(
['default_value' => $this->getTable(self::GALLERY_VALUE_TABLE)],
implode(
@@ -201,8 +211,15 @@ public function createBatchBaseSelect($storeId, $attributeId)
'default_value.' . $linkField . ' = entity.' . $linkField,
]
),
- ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled']
- )->where(
+ []
+ )->columns([
+ 'label' => $this->getConnection()->getIfNullSql('`value`.`label`', '`default_value`.`label`'),
+ 'position' => $this->getConnection()->getIfNullSql('`value`.`position`', '`default_value`.`position`'),
+ 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'),
+ 'label_default' => 'default_value.label',
+ 'position_default' => 'default_value.position',
+ 'disabled_default' => 'default_value.disabled'
+ ])->where(
$mainTableAlias . '.attribute_id = ?',
$attributeId
)->where(
@@ -240,6 +257,8 @@ protected function removeDuplicates(&$result)
}
/**
+ * Get main table alias
+ *
* @return string
* @since 101.0.0
*/
@@ -249,6 +268,8 @@ public function getMainTableAlias()
}
/**
+ * Bind value to entity
+ *
* @param int $valueId
* @param int $entityId
* @return int
@@ -266,6 +287,8 @@ public function bindValueToEntity($valueId, $entityId)
}
/**
+ * Save data row
+ *
* @param string $table
* @param array $data
* @param array $fields
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
index 123f358be40c8..77f67480619e0 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
@@ -12,6 +12,9 @@
use Magento\Framework\DB\Select;
use Magento\Framework\App\ResourceConnection;
+/**
+ * Class for retrieval of all product images
+ */
class Image
{
/**
@@ -73,15 +76,24 @@ public function getAllProductImages(): \Generator
/**
* Get the number of unique pictures of products
+ *
* @return int
*/
public function getCountAllProductImages(): int
{
- $select = $this->getVisibleImagesSelect()->reset('columns')->columns('count(*)');
+ $select = $this->getVisibleImagesSelect()
+ ->reset('columns')
+ ->reset('distinct')
+ ->columns(
+ new \Zend_Db_Expr('count(distinct value)')
+ );
+
return (int) $this->connection->fetchOne($select);
}
/**
+ * Return Select to fetch all products images
+ *
* @return Select
*/
private function getVisibleImagesSelect(): Select
diff --git a/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php
new file mode 100644
index 0000000000000..6add542b15554
--- /dev/null
+++ b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php
@@ -0,0 +1,73 @@
+toolbarMemorizer = $toolbarMemorizer;
+ $this->catalogSession = $catalogSession;
+ $this->httpContext = $httpContext;
+ }
+
+ /**
+ * Update http context with catalog sensitive information.
+ *
+ * @return void
+ */
+ public function beforeDispatch()
+ {
+ if ($this->toolbarMemorizer->isMemorizingAllowed()) {
+ $params = [
+ ToolbarModel::ORDER_PARAM_NAME,
+ ToolbarModel::DIRECTION_PARAM_NAME,
+ ToolbarModel::MODE_PARAM_NAME,
+ ToolbarModel::LIMIT_PARAM_NAME
+ ];
+ foreach ($params as $param) {
+ $paramValue = $this->catalogSession->getData($param);
+ if ($paramValue) {
+ $this->httpContext->setValue($param, $paramValue, false);
+ }
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php
index 54a13be864db7..77368517a3155 100644
--- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php
+++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php
@@ -30,7 +30,7 @@ public function getValue()
$this->value = false;
foreach ($this->priceInfo->getPrices() as $price) {
if ($price instanceof BasePriceProviderInterface && $price->getValue() !== false) {
- $this->value = min($price->getValue(), $this->value ?: $price->getValue());
+ $this->value = min($price->getValue(), $this->value !== false ? $this->value: $price->getValue());
}
}
}
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index 76f65381f43fa..57f91b78fcbe9 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -237,8 +237,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
index 1f6c2ab4bb25f..01b31430793cc 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
@@ -276,4 +276,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
index 73025023c23c8..80cadbb6571f2 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml
@@ -13,7 +13,6 @@
-
@@ -25,7 +24,6 @@
-
@@ -42,4 +40,11 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
index 1bd9bb4a09c86..a077eced6d5d5 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
@@ -164,6 +164,13 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
index 301bd4b7a5745..4c7c011028c92 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml
@@ -22,6 +22,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -51,4 +66,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml
new file mode 100644
index 0000000000000..d78c03a51dd75
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ test_set_
+ 7
+ 4
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
new file mode 100644
index 0000000000000..d8ec84013d93b
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ GridPerPageValues
+ RememberCategoryPagination
+
+
+
+ 9,12,20,24
+
+
+
+ 1
+
+
+
+ DefaultCatalogStorefrontFlagZero
+ DefaultListAllowAll
+ DefaultFlatCatalogProduct
+
+
+
+ 0
+
+
+
+ 0
+
+
+
+ 0
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ImageData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ImageData.xml
new file mode 100644
index 0000000000000..a2391dda54809
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ImageData.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ adobe-base.jpg
+ adobe-base
+ jpg
+
+
+
+ adobe-small.jpg
+ adobe-small
+ jpg
+
+
+
+ adobe-thumb.jpg
+ adobe-thumb
+ jpg
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
index c575f1a5db82f..5be2a84f54555 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml
@@ -65,4 +65,20 @@
false
0
+
+
+ Green
+ false
+ 3
+ Option7Store0
+ Option8Store1
+
+
+
+ Red
+ false
+ 3
+ Option9Store0
+ Option10Store1
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
index 6f07c0b9eab30..e5b23fe3a3c34 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
@@ -302,6 +302,20 @@
EavStockItem
CustomAttributeCategoryIds
+
+ testSku
+ simple
+
+ 4
+ testProductName
+ 123.00
+ testurlkey
+ 1
+ 1
+ 100
+ EavStockItem
+ CustomAttributeCategoryIds
+
magento.jpg
@@ -420,6 +434,36 @@
1
EavStock100
+
+ testSku
+ simple
+ 4
+ 4
+ massUpdateProductName
+ massUpdateProductName
+ 123.00
+ masstesturlkey
+ 1
+ 100
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
+
+ testSku
+ simple
+ 4
+ 4
+ massUpdateProductName
+ massUpdateProductName
+ 123.00
+ masstesturlkey
+ 1
+ 100
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
api-simple-product
simple
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
index ce964e2d71503..0e51995ac72e8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml
@@ -56,4 +56,20 @@
1
option6
+
+ 0
+ Green
+
+
+ 1
+ Green
+
+
+ 0
+ Red
+
+
+ 1
+ Red
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml
new file mode 100644
index 0000000000000..9ef7b507812a0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ application/json
+
+ string
+ integer
+
+ integer
+
+
+ application/json
+
+
+ application/json
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml
new file mode 100644
index 0000000000000..b1f2b43220b36
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+ string
+
+
+ string
+
+
+ string
+
+
+ string
+
+
+ string
+
+
+ string
+
+
+ integer
+
+
+ integer
+
+
+ integer
+
+
+ integer
+
+
+ string
+
+
+ integer
+
+
+
+
+
+
+
+
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+
+ integer
+
+
+
+ integer
+
+
+ integer
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
index fc776b49ba213..b3ed3f478f810 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml
@@ -17,5 +17,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
index 8b69a44993f17..9a0dd8f5b387d 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryBasicFieldSection.xml
@@ -32,6 +32,13 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
index ed0325394d591..acee714fe3ec7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml
new file mode 100644
index 0000000000000..daa00eb0a27b7
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
index 17778c76da9b9..324f261f3a50a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml
@@ -23,6 +23,9 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeOptionsSection.xml
new file mode 100644
index 0000000000000..0f438540603d0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeOptionsSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
index 7a9de9670f216..06ff54b2a3997 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml
@@ -28,5 +28,7 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
index afbaba41a9bb7..c7d4cd16d788a 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml
new file mode 100644
index 0000000000000..0314534dcddfb
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
index 03df9d2a88107..249610568aec7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml
@@ -28,6 +28,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
index a7e20e22f1ddc..d12233206ce41 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
@@ -28,6 +28,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagePlaceholderConfigSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagePlaceholderConfigSection.xml
new file mode 100644
index 0000000000000..7558b13d624bb
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductImagePlaceholderConfigSection.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
index d484abf2069ff..03566be55ad2f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml
@@ -9,6 +9,9 @@
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMiniCartSection.xml
new file mode 100644
index 0000000000000..ff2e5f2f36015
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMiniCartSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml
index d8f6bc3e4a749..d38a8e4190c82 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml
@@ -50,9 +50,9 @@
-
+
-
+
@@ -63,7 +63,7 @@
-
+
@@ -75,10 +75,12 @@
+
+
-
+
@@ -87,7 +89,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml
new file mode 100644
index 0000000000000..4d97dee56f059
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $getSmallPlaceholderImageSrc
+ {{placeholderSmallImage.name}}
+
+
+
+
+
+ $getSmallNonPlaceholderImageSrc
+ {{placeholderSmallImage.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $getThumbnailPlaceholderImageSrc
+ {{placeholderThumbnailImage.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $getThumbnailImageSrc
+ {{placeholderThumbnailImage.name}}
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml
index bfb9557910642..8806612c0f5de 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml
@@ -69,4 +69,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml
new file mode 100644
index 0000000000000..525f81de6c48c
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
index 95d74b9653113..6658ad36d7150 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml
@@ -40,4 +40,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
index a4bd507d98f55..a748635ac9a53 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml
@@ -129,8 +129,8 @@
-
+
@@ -152,8 +152,8 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
index 69931395a35d5..1cd0e15780c11 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml
@@ -87,10 +87,9 @@
-
-
+
@@ -115,8 +114,7 @@
-
-
+
@@ -136,9 +134,9 @@
-
-
-
+
+
+
@@ -154,16 +152,16 @@
-
+
-
+
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
index 6b444f1f6663b..c845a27773170 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptions.xml
@@ -103,12 +103,15 @@
-
+
+
+
-
+
+
+
-
-
+
@@ -119,6 +122,7 @@
+
@@ -141,6 +145,7 @@
+
@@ -179,4 +184,4 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
index a03636e52ee97..fcdb92d71f1f8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml
@@ -71,11 +71,17 @@
+
+
+
+
+
-
+
+
@@ -86,6 +92,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml
new file mode 100644
index 0000000000000..0ed61b8636c4f
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
new file mode 100644
index 0000000000000..a8b2df1fcfa67
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php
index 8a42865a3fe4d..95b06e40602bf 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php
@@ -145,7 +145,8 @@ private function getTestDataWithoutAttributes(): array
'label' => 'test_image_label',
'ratio' => 1,
'custom_attributes' => '',
- 'product_id' => null
+ 'product_id' => null,
+ 'class' => 'product-image-photo'
],
],
];
@@ -190,6 +191,7 @@ private function getTestDataWithAttributes(): array
'custom_attributes' => [
'name_1' => 'value_1',
'name_2' => 'value_2',
+ 'class' => 'my-class'
],
],
'expected' => [
@@ -201,7 +203,8 @@ private function getTestDataWithAttributes(): array
'label' => 'test_product_name',
'ratio' => 0.5, // <==
'custom_attributes' => 'name_1="value_1" name_2="value_2"',
- 'product_id' => null
+ 'product_id' => null,
+ 'class' => 'my-class'
],
],
];
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
index ac963326dbfa1..884f4c543c8b8 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
@@ -18,6 +18,11 @@ class ToolbarTest extends \PHPUnit\Framework\TestCase
*/
protected $model;
+ /**
+ * @var \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $memorizer;
+
/**
* @var \Magento\Framework\Url | \PHPUnit_Framework_MockObject_MockObject
*/
@@ -62,6 +67,16 @@ protected function setUp()
'getLimit',
'getCurrentPage'
]);
+ $this->memorizer = $this->createPartialMock(
+ \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer::class,
+ [
+ 'getDirection',
+ 'getOrder',
+ 'getMode',
+ 'getLimit',
+ 'isMemorizingAllowed'
+ ]
+ );
$this->layout = $this->createPartialMock(\Magento\Framework\View\Layout::class, ['getChildName', 'getBlock']);
$this->pagerBlock = $this->createPartialMock(\Magento\Theme\Block\Html\Pager::class, [
'setUseContainer',
@@ -116,6 +131,7 @@ protected function setUp()
'context' => $context,
'catalogConfig' => $this->catalogConfig,
'toolbarModel' => $this->model,
+ 'toolbarMemorizer' => $this->memorizer,
'urlEncoder' => $this->urlEncoder,
'productListHelper' => $this->productListHelper
]
@@ -155,7 +171,7 @@ public function testGetPagerEncodedUrl()
public function testGetCurrentOrder()
{
$order = 'price';
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getOrder')
->will($this->returnValue($order));
$this->catalogConfig->expects($this->once())
@@ -169,7 +185,7 @@ public function testGetCurrentDirection()
{
$direction = 'desc';
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getDirection')
->will($this->returnValue($direction));
@@ -183,7 +199,7 @@ public function testGetCurrentMode()
$this->productListHelper->expects($this->once())
->method('getAvailableViewMode')
->will($this->returnValue(['list' => 'List']));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getMode')
->will($this->returnValue($mode));
@@ -232,11 +248,11 @@ public function testGetLimit()
$mode = 'list';
$limit = 10;
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getMode')
->will($this->returnValue($mode));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getLimit')
->will($this->returnValue($limit));
$this->productListHelper->expects($this->once())
@@ -266,7 +282,7 @@ public function testGetPagerHtml()
$this->productListHelper->expects($this->exactly(2))
->method('getAvailableLimit')
->will($this->returnValue([10 => 10, 20 => 20]));
- $this->model->expects($this->once())
+ $this->memorizer->expects($this->once())
->method('getLimit')
->will($this->returnValue($limit));
$this->pagerBlock->expects($this->once())
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
index ff44a91a64998..c889c58e3df3a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php
@@ -95,6 +95,11 @@ class HelperTest extends \PHPUnit\Framework\TestCase
*/
protected $attributeFilterMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $dateTimeFilterMock;
+
/**
* @inheritdoc
*/
@@ -170,6 +175,11 @@ protected function setUp()
$resolverProperty = $helperReflection->getProperty('linkResolver');
$resolverProperty->setAccessible(true);
$resolverProperty->setValue($this->helper, $this->linkResolverMock);
+
+ $this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class);
+ $dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter');
+ $dateTimeFilterProperty->setAccessible(true);
+ $dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock);
}
/**
@@ -211,6 +221,12 @@ public function testInitialize(
if (!empty($tierPrice)) {
$productData = array_merge($productData, ['tier_price' => $tierPrice]);
}
+
+ $this->dateTimeFilterMock->expects($this->once())
+ ->method('filter')
+ ->with($specialFromDate)
+ ->willReturn($specialFromDate);
+
$attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
->disableOriginalConstructor()
->getMock();
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 9fafbc9d9675b..1d12645019d1e 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
@@ -266,7 +266,7 @@ public function testGetWithNonExistingProduct()
/**
* @expectedException \Magento\Framework\Exception\NoSuchEntityException
- * @expectedExceptionText The image doesn't exist. Verify and try again.
+ * @expectedExceptionMessage The image doesn't exist. Verify and try again.
*/
public function testGetWithNonExistingImage()
{
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 754d80302d410..6029a2b820086 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 Tier price is unavailable for this product.
+ * @expectedExceptionMessage Product hasn't group price with such data: customerGroupId = '1', website = 1, qty = 3
*/
public function testRemoveWhenTierPricesNotExists()
{
@@ -70,7 +70,7 @@ public function testRemoveWhenTierPricesNotExists()
/**
* @expectedException \Magento\Framework\Exception\NoSuchEntityException
- * @expectedMessage For current customerGroupId = '10' with 'qty' = 15 any tier price exist'.
+ * @expectedExceptionMessage Product hasn't group price with such data: customerGroupId = '10', website = 1, qty = 5
*/
public function testRemoveTierPriceForNonExistingCustomerGroup()
{
@@ -81,7 +81,7 @@ public function testRemoveTierPriceForNonExistingCustomerGroup()
->will($this->returnValue($this->prices));
$this->productMock->expects($this->never())->method('setData');
$this->productRepositoryMock->expects($this->never())->method('save');
- $this->priceModifier->removeTierPrice($this->productMock, 10, 15, 1);
+ $this->priceModifier->removeTierPrice($this->productMock, 10, 5, 1);
}
public function testSuccessfullyRemoveTierPriceSpecifiedForAllGroups()
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 f340d0b204b62..ae479a9b34d48 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php
@@ -195,7 +195,7 @@ public function testSuccessDeleteTierPrice()
/**
* @expectedException \Magento\Framework\Exception\NoSuchEntityException
- * @message The product doesn't exist. Verify and try again.
+ * @expectedExceptionMessage No such entity.
*/
public function testDeleteTierPriceFromNonExistingProduct()
{
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
index dfed4e4f37385..47ef3c999125f 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php
@@ -281,6 +281,9 @@ public function testBindValueToEntityRecordExists()
$this->resource->bindValueToEntity($valueId, $entityId);
}
+ /**
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
public function testLoadGallery()
{
$productId = 5;
@@ -329,7 +332,8 @@ public function testLoadGallery()
'main.value_id = entity.value_id',
['entity_id']
)->willReturnSelf();
- $this->product->expects($this->at(0))->method('getData')->with('entity_id')->willReturn($productId);
+ $this->product->expects($this->at(0))->method('getData')
+ ->with('entity_id')->willReturn($productId);
$this->product->expects($this->at(1))->method('getStoreId')->will($this->returnValue($storeId));
$this->connection->expects($this->exactly(2))->method('quoteInto')->withConsecutive(
['value.store_id = ?'],
@@ -338,26 +342,50 @@ public function testLoadGallery()
'value.store_id = ' . $storeId,
'default_value.store_id = ' . 0
);
+ $this->connection->expects($this->any())->method('getIfNullSql')->will(
+ $this->returnValueMap([
+ [
+ '`value`.`label`',
+ '`default_value`.`label`',
+ 'IFNULL(`value`.`label`, `default_value`.`label`)'
+ ],
+ [
+ '`value`.`position`',
+ '`default_value`.`position`',
+ 'IFNULL(`value`.`position`, `default_value`.`position`)'
+ ],
+ [
+ '`value`.`disabled`',
+ '`default_value`.`disabled`',
+ 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)'
+ ]
+ ])
+ );
$this->select->expects($this->at(2))->method('joinLeft')->with(
['value' => $getTableReturnValue],
$quoteInfoReturnValue,
- [
- 'label',
- 'position',
- 'disabled'
- ]
+ []
)->willReturnSelf();
$this->select->expects($this->at(3))->method('joinLeft')->with(
['default_value' => $getTableReturnValue],
$quoteDefaultInfoReturnValue,
- ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled']
+ []
)->willReturnSelf();
- $this->select->expects($this->at(4))->method('where')->with(
+ $this->select->expects($this->at(4))->method('columns')->with([
+ 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)',
+ 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)',
+ 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)',
+ 'label_default' => 'default_value.label',
+ 'position_default' => 'default_value.position',
+ 'disabled_default' => 'default_value.disabled'
+ ])->willReturnSelf();
+ $this->select->expects($this->at(5))->method('where')->with(
'main.attribute_id = ?',
$attributeId
)->willReturnSelf();
- $this->select->expects($this->at(5))->method('where')->with('main.disabled = 0')->willReturnSelf();
- $this->select->expects($this->at(7))->method('where')
+ $this->select->expects($this->at(6))->method('where')
+ ->with('main.disabled = 0')->willReturnSelf();
+ $this->select->expects($this->at(8))->method('where')
->with('entity.entity_id = ?', $productId)
->willReturnSelf();
$this->select->expects($this->once())->method('order')
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
new file mode 100644
index 0000000000000..4fce12dc2de89
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
@@ -0,0 +1,237 @@
+objectManager =
+ new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->connectionMock = $this->createMock(AdapterInterface::class);
+ $this->resourceMock = $this->createMock(ResourceConnection::class);
+ $this->resourceMock->method('getConnection')
+ ->willReturn($this->connectionMock);
+ $this->resourceMock->method('getTableName')
+ ->willReturnArgument(0);
+ $this->generatorMock = $this->createMock(Generator::class);
+ }
+
+ /**
+ * @return MockObject
+ */
+ protected function getVisibleImagesSelectMock(): MockObject
+ {
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $selectMock->expects($this->once())
+ ->method('distinct')
+ ->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('from')
+ ->with(
+ ['images' => Gallery::GALLERY_TABLE],
+ 'value as filepath'
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('where')
+ ->with('disabled = 0')
+ ->willReturnSelf();
+
+ return $selectMock;
+ }
+
+ /**
+ * @param int $imagesCount
+ * @dataProvider dataProvider
+ */
+ public function testGetCountAllProductImages(int $imagesCount): void
+ {
+ $selectMock = $this->getVisibleImagesSelectMock();
+ $selectMock->expects($this->exactly(2))
+ ->method('reset')
+ ->withConsecutive(
+ ['columns'],
+ ['distinct']
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('columns')
+ ->with(new \Zend_Db_Expr('count(distinct value)'))
+ ->willReturnSelf();
+
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($selectMock);
+ $this->connectionMock->expects($this->once())
+ ->method('fetchOne')
+ ->with($selectMock)
+ ->willReturn($imagesCount);
+
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock
+ ]
+ );
+
+ $this->assertSame(
+ $imagesCount,
+ $imageModel->getCountAllProductImages()
+ );
+ }
+
+ /**
+ * @param int $imagesCount
+ * @param int $batchSize
+ * @dataProvider dataProvider
+ */
+ public function testGetAllProductImages(
+ int $imagesCount,
+ int $batchSize
+ ): void {
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($this->getVisibleImagesSelectMock());
+
+ $batchCount = (int)ceil($imagesCount / $batchSize);
+ $fetchResultsCallback = $this->getFetchResultCallbackForBatches($imagesCount, $batchSize);
+ $this->connectionMock->expects($this->exactly($batchCount))
+ ->method('fetchAll')
+ ->will($this->returnCallback($fetchResultsCallback));
+
+ /** @var Select | MockObject $selectMock */
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->generatorMock->expects($this->once())
+ ->method('generate')
+ ->with(
+ 'value_id',
+ $selectMock,
+ $batchSize,
+ BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
+ )->will(
+ $this->returnCallback(
+ $this->getBatchIteratorCallback($selectMock, $batchCount)
+ )
+ );
+
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock,
+ 'batchSize' => $batchSize
+ ]
+ );
+
+ $this->assertCount($imagesCount, $imageModel->getAllProductImages());
+ }
+
+ /**
+ * @param int $imagesCount
+ * @param int $batchSize
+ * @return \Closure
+ */
+ protected function getFetchResultCallbackForBatches(
+ int $imagesCount,
+ int $batchSize
+ ): \Closure {
+ $fetchResultsCallback = function () use (&$imagesCount, $batchSize) {
+ $batchSize =
+ ($imagesCount >= $batchSize) ? $batchSize : $imagesCount;
+ $imagesCount -= $batchSize;
+
+ $getFetchResults = function ($batchSize): array {
+ $result = [];
+ $count = $batchSize;
+ while ($count) {
+ $count--;
+ $result[$count] = $count;
+ }
+
+ return $result;
+ };
+
+ return $getFetchResults($batchSize);
+ };
+
+ return $fetchResultsCallback;
+ }
+
+ /**
+ * @param Select | MockObject $selectMock
+ * @param int $batchCount
+ * @return \Closure
+ */
+ protected function getBatchIteratorCallback(
+ MockObject $selectMock,
+ int $batchCount
+ ): \Closure {
+ $iteratorCallback = function () use ($batchCount, $selectMock): array {
+ $result = [];
+ $count = $batchCount;
+ while ($count) {
+ $count--;
+ $result[$count] = $selectMock;
+ }
+
+ return $result;
+ };
+
+ return $iteratorCallback;
+ }
+
+ /**
+ * Data Provider
+ * @return array
+ */
+ public function dataProvider(): array
+ {
+ return [
+ [300, 300],
+ [300, 100],
+ [139, 100],
+ [67, 10],
+ [154, 47],
+ [0, 100]
+ ];
+ }
+}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php
index 98de8ea347671..6ec1cc6c46d9d 100755
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php
@@ -58,8 +58,11 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * Customize number fields for advanced price and weight fields.
+ *
* @since 101.0.0
+ * @param array $data
+ * @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function modifyData(array $data)
@@ -130,8 +133,11 @@ protected function customizeAdvancedPriceFormat(array $data)
}
/**
- * {@inheritdoc}
+ * Customize product form fields.
+ *
* @since 101.0.0
+ * @param array $meta
+ * @return array
*/
public function modifyMeta(array $meta)
{
@@ -361,7 +367,8 @@ protected function customizeNameListeners(array $meta)
$skuPath . static::META_CONFIG_PATH,
$meta,
[
- 'autoImportIfEmpty' => true
+ 'autoImportIfEmpty' => true,
+ 'validation' => ['no-marginal-whitespace' => true]
]
);
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
index 0eddca3322205..a529580e29239 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php
@@ -45,7 +45,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 101.1.0
*/
public function modifyData(array $data)
@@ -54,8 +54,11 @@ public function modifyData(array $data)
}
/**
- * {@inheritdoc}
+ * Add tier price info to meta array.
+ *
* @since 101.1.0
+ * @param array $meta
+ * @return array
*/
public function modifyMeta(array $meta)
{
@@ -150,8 +153,8 @@ private function getUpdatedTierPriceStructure(array $priceMeta)
'dataType' => Price::NAME,
'addbefore' => '%',
'validation' => [
- 'validate-number' => true,
- 'less-than-equals-to' => 100
+ 'required-entry' => true,
+ 'validate-positive-percent-decimal' => true
],
'visible' => $firstOption
&& $firstOption['value'] == ProductPriceOptionsInterface::VALUE_PERCENT,
diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml
index d6c98b92596fd..7a05601fcd666 100644
--- a/app/code/Magento/Catalog/etc/adminhtml/system.xml
+++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml
@@ -10,6 +10,9 @@
Catalog
+
+ Advanced
+
separator-top
Catalog
@@ -93,6 +96,11 @@
Whether to show "All" option in the "Show X Per Page" dropdown
Magento\Config\Model\Config\Source\Yesno
+
Product Image Placeholders
@@ -194,5 +202,19 @@
+
+ separator-top
+ System
+ advanced
+ Magento_Config::config_system
+
+ Images Upload Configuration
+
+ Quality
+ validate-digits validate-digits-range digits-range-1-100 required-entry
+ Jpeg quality for resized images 1-100%.
+
+
+
diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml
index f52760aa50743..3a842166a3825 100644
--- a/app/code/Magento/Catalog/etc/config.xml
+++ b/app/code/Magento/Catalog/etc/config.xml
@@ -30,6 +30,7 @@
0
position
1
+ 0
@@ -66,6 +67,9 @@
custom_options
+
+ 80
+
diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml
index 860e5fee9f157..17e3dddc41c3b 100644
--- a/app/code/Magento/Catalog/etc/db_schema.xml
+++ b/app/code/Magento/Catalog/etc/db_schema.xml
@@ -22,13 +22,13 @@
comment="Creation Time"/>
-
+
-
+
-
+
@@ -43,27 +43,27 @@
-
+
-
-
-
-
+
-
+
-
+
@@ -79,27 +79,27 @@
default="0" comment="Entity ID"/>
-
+
-
-
-
-
+
-
+
-
+
@@ -115,27 +115,27 @@
default="0" comment="Entity ID"/>
-
+
-
-
-
-
+
-
+
-
+
@@ -150,27 +150,27 @@
-
+
-
-
-
-
+
-
+
-
+
@@ -185,27 +185,27 @@
-
+
-
-
-
-
+
-
+
-
+
@@ -222,30 +222,30 @@
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -267,13 +267,13 @@
comment="Tree Level"/>
-
+
-
+
-
+
@@ -288,30 +288,30 @@
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -327,30 +327,30 @@
default="0" comment="Entity ID"/>
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -366,30 +366,30 @@
default="0" comment="Entity ID"/>
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -404,30 +404,30 @@
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -442,30 +442,30 @@
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -479,22 +479,22 @@
default="0" comment="Product ID"/>
-
+
-
-
-
+
-
+
@@ -512,18 +512,18 @@
default="0" comment="Store ID"/>
-
+
-
+
-
+
@@ -542,29 +542,29 @@
default="0" comment="Product ID"/>
-
+
-
-
-
-
+
-
+
-
+
-
+
@@ -574,17 +574,17 @@
comment="Product ID"/>
-
+
-
-
-
+
@@ -593,7 +593,7 @@
-
+
@@ -607,27 +607,27 @@
default="0" comment="Linked Product ID"/>
-
+
-
-
-
-
+
-
+
-
+
@@ -640,13 +640,13 @@
-
+
-
-
+
@@ -660,21 +660,21 @@
comment="Link ID"/>
-
+
-
-
-
+
-
+
@@ -688,21 +688,21 @@
comment="Link ID"/>
-
+
-
-
-
+
-
+
@@ -715,21 +715,21 @@
-
+
-
-
-
+
-
+
@@ -751,29 +751,29 @@
comment="Website ID"/>
-
+
-
-
-
-
+
-
+
-
+
@@ -788,13 +788,13 @@
comment="Media entry type"/>
-
+
-
-
+
@@ -813,28 +813,33 @@
default="0" comment="Is Disabled"/>
-
+
-
-
-
-
+
-
+
-
+
+
+
+
+
+
@@ -875,20 +880,20 @@
-
+
-
-
-
+
-
+
@@ -901,20 +906,20 @@
-
+
-
-
-
+
-
+
@@ -927,13 +932,13 @@
-
+
-
-
+
@@ -948,21 +953,21 @@
-
+
-
-
-
+
-
+
@@ -975,21 +980,21 @@
-
+
-
-
-
+
-
+
@@ -1037,16 +1042,16 @@
identity="false" default="0" comment="Is Visible in Grid"/>
-
+
-
-
+
-
+
@@ -1055,17 +1060,17 @@
comment="Parent ID"/>
-
+
-
-
-
+
@@ -1081,20 +1086,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1110,20 +1115,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1147,18 +1152,18 @@
comment="Max Price"/>
-
+
-
+
-
+
-
+
@@ -1174,24 +1179,24 @@
comment="Website ID"/>
-
+
-
-
-
-
+
-
+
@@ -1203,12 +1208,12 @@
comment="Default store id for website"/>
-
+
-
-
+
@@ -1226,7 +1231,7 @@
comment="Price"/>
-
+
@@ -1247,7 +1252,7 @@
comment="Price"/>
-
+
@@ -1268,7 +1273,7 @@
comment="Max Price"/>
-
+
@@ -1288,7 +1293,7 @@
comment="Max Price"/>
-
+
@@ -1316,7 +1321,7 @@
comment="Tier Price"/>
-
+
@@ -1344,7 +1349,7 @@
comment="Tier Price"/>
-
+
@@ -1364,7 +1369,7 @@
comment="Max Price"/>
-
+
@@ -1384,7 +1389,7 @@
comment="Max Price"/>
-
+
@@ -1406,7 +1411,7 @@
comment="Max Price"/>
-
+
@@ -1429,7 +1434,7 @@
comment="Max Price"/>
-
+
@@ -1448,20 +1453,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1477,20 +1482,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1506,20 +1511,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1535,20 +1540,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1572,18 +1577,18 @@
comment="Max Price"/>
-
+
-
+
-
+
-
+
@@ -1607,18 +1612,18 @@
comment="Max Price"/>
-
+
-
+
-
+
-
+
@@ -1636,12 +1641,12 @@
default="0" comment="Store ID"/>
-
+
-
+
@@ -1653,14 +1658,14 @@
comment="Value media Entry ID"/>
-
-
-
+
@@ -1677,20 +1682,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1706,20 +1711,20 @@
comment="Value"/>
-
+
-
+
-
+
-
+
@@ -1743,18 +1748,18 @@
comment="Max Price"/>
-
+
-
+
-
+
-
+
@@ -1774,18 +1779,18 @@
default="0" comment="Store ID"/>
-
+
-
+
-
+
@@ -1806,21 +1811,21 @@
comment="Product Id"/>
-
+
-
-
-
+
-
+
diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json
index b1543a6a007f9..d4bd6927d4345 100644
--- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json
+++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json
@@ -484,7 +484,8 @@
"index": {
"CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true,
"CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID": true,
- "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true
+ "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true,
+ "CAT_PRD_ENTT_MDA_GLR_VAL_ENTT_ID_VAL_ID_STORE_ID": true
},
"constraint": {
"PRIMARY": true,
@@ -1121,4 +1122,4 @@
"CATALOG_PRODUCT_FRONTEND_ACTION_CUSTOMER_ID_PRODUCT_ID_TYPE_ID": true
}
}
-}
\ No newline at end of file
+}
diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml
index 55098037191e8..ee9c5b29da894 100644
--- a/app/code/Magento/Catalog/etc/frontend/di.xml
+++ b/app/code/Magento/Catalog/etc/frontend/di.xml
@@ -116,4 +116,8 @@
+
+
+
diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv
index f2a3ab8f83f24..ed27dfd646cb2 100644
--- a/app/code/Magento/Catalog/i18n/en_US.csv
+++ b/app/code/Magento/Catalog/i18n/en_US.csv
@@ -233,7 +233,6 @@ Products,Products
"This attribute set no longer exists.","This attribute set no longer exists."
"You saved the attribute set.","You saved the attribute set."
"Something went wrong while saving the attribute set.","Something went wrong while saving the attribute set."
-"You added product %1 to the comparison list.","You added product %1 to the comparison list."
"You cleared the comparison list.","You cleared the comparison list."
"Something went wrong clearing the comparison list.","Something went wrong clearing the comparison list."
"You removed product %1 from the comparison list.","You removed product %1 from the comparison list."
@@ -808,4 +807,5 @@ Details,Details
"Product Name or SKU", "Product Name or SKU"
"Start typing to find products", "Start typing to find products"
"Product with ID: (%1) doesn't exist", "Product with ID: (%1) doesn't exist"
-"Category with ID: (%1) doesn't exist", "Category with ID: (%1) doesn't exist"
\ No newline at end of file
+"Category with ID: (%1) doesn't exist", "Category with ID: (%1) doesn't exist"
+"You added product %1 to the comparison list .","You added product %1 to the comparison list ."
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml
index 223b3e9888eea..75f04eae82159 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml
@@ -2,4 +2,4 @@
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
- */;
+ */
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 dafea71f872d0..1a54db0d59f0f 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
@@ -331,7 +331,6 @@
- group
-
- true
- - true
@@ -341,6 +340,9 @@
+
+ true
+
true
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/options.js b/app/code/Magento/Catalog/view/adminhtml/web/js/options.js
index 1ec47f14fee88..7adc0dcfdf408 100644
--- a/app/code/Magento/Catalog/view/adminhtml/web/js/options.js
+++ b/app/code/Magento/Catalog/view/adminhtml/web/js/options.js
@@ -182,24 +182,16 @@ define([
});
}
editForm.on('beforeSubmit', function () {
- var optionsValues = [],
- optionContainer = optionPanel.find('table tbody');
+ var optionContainer = optionPanel.find('table tbody'),
+ optionsValues;
if (optionPanel.hasClass(activePanelClass)) {
- optionContainer.find('input')
- .each(function () {
- if (this.disabled) {
- return;
- }
-
- if (this.type === 'checkbox' || this.type === 'radio') {
- if (this.checked) {
- optionsValues.push(this.name + '=' + jQuery(this).val());
- }
- } else {
- optionsValues.push(this.name + '=' + jQuery(this).val());
- }
- });
+ optionsValues = jQuery.map(
+ optionContainer.find('tr'),
+ function (row) {
+ return jQuery(row).find('input, select, textarea').serialize();
+ }
+ );
jQuery(' ')
.attr({
type: 'hidden',
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
index 74a0b2d7cf1a3..8a907bd54aa6a 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
@@ -10,7 +10,7 @@
style="width:= /* @escapeNotVerified */ $block->getWidth() ?>px;">
- getCustomAttributes() ?>
src="= /* @escapeNotVerified */ $block->getImageUrl() ?>"
max-width="= /* @escapeNotVerified */ $block->getWidth() ?>"
diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
index 88be03a04e71a..b8b6ff65be2b4 100644
--- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
+++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js
@@ -27,7 +27,9 @@ define([
directionDefault: 'asc',
orderDefault: 'position',
limitDefault: '9',
- url: ''
+ url: '',
+ formKey: '',
+ post: false
},
/** @inheritdoc */
@@ -89,7 +91,7 @@ define([
baseUrl = urlPaths[0],
urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
paramData = {},
- parameters, i;
+ parameters, i, form, params, key, input, formKey;
for (i = 0; i < urlParams.length; i++) {
parameters = urlParams[i].split('=');
@@ -99,12 +101,38 @@ define([
}
paramData[paramName] = paramValue;
- if (paramValue == defaultValue) { //eslint-disable-line eqeqeq
- delete paramData[paramName];
- }
- paramData = $.param(paramData);
+ if (this.options.post) {
+ form = document.createElement('form');
+ params = [this.options.mode, this.options.direction, this.options.order, this.options.limit];
+
+ for (key in paramData) {
+ if (params.indexOf(key) !== -1) { //eslint-disable-line max-depth
+ input = document.createElement('input');
+ input.name = key;
+ input.value = paramData[key];
+ form.appendChild(input);
+ delete paramData[key];
+ }
+ }
+ formKey = document.createElement('input');
+ formKey.name = 'form_key';
+ formKey.value = this.options.formKey;
+ form.appendChild(formKey);
+
+ paramData = $.param(paramData);
+ baseUrl += paramData.length ? '?' + paramData : '';
- location.href = baseUrl + (paramData.length ? '?' + paramData : '');
+ form.action = baseUrl;
+ form.method = 'POST';
+ document.body.appendChild(form);
+ form.submit();
+ } else {
+ if (paramValue == defaultValue) { //eslint-disable-line eqeqeq
+ delete paramData[paramName];
+ }
+ paramData = $.param(paramData);
+ location.href = baseUrl + (paramData.length ? '?' + paramData : '');
+ }
}
});
diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php
index f0cdab9498abb..d2c1fc8f7be9f 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php
@@ -52,11 +52,11 @@ public function hydrateCategory(Category $category, $basicFieldsOnly = false) :
$categoryData = $category->getData();
} else {
$categoryData = $this->dataObjectProcessor->buildOutputDataArray($category, CategoryInterface::class);
- $categoryData['product_count'] = $category->getProductCount();
}
$categoryData['id'] = $category->getId();
$categoryData['children'] = [];
$categoryData['available_sort_by'] = $category->getAvailableSortBy();
+ $categoryData['model'] = $category;
return $this->flattener->flatten($categoryData);
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php
index 20e5590117556..02594ecfaf8e8 100644
--- a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php
+++ b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php
@@ -31,7 +31,7 @@ public function __construct(array $typeResolvers = [])
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function resolveType(array $data) : string
{
@@ -42,10 +42,6 @@ public function resolveType(array $data) : string
return $resolvedType;
}
}
- if (empty($resolvedType)) {
- throw new GraphQlInputException(
- __('Concrete type for %1 not implemented', ['ProductLinksInterface'])
- );
- }
+ throw new GraphQlInputException(__('Cannot resolve layered filter type'));
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php b/app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php
new file mode 100644
index 0000000000000..0d38490407e7c
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php
@@ -0,0 +1,45 @@
+productRepository = $productRepository;
+ }
+
+ /**
+ * Get product data by id
+ *
+ * @param int $productId
+ * @return array
+ */
+ public function getProductDataById(int $productId): array
+ {
+ $product = $this->productRepository->getById($productId);
+ $productData = $product->toArray();
+ $productData['model'] = $product;
+ return $productData;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php
index 937e3921758dc..c1bf5c0b7bb1c 100644
--- a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php
+++ b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php
@@ -11,7 +11,7 @@
use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface;
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
class ProductLinkTypeResolverComposite implements TypeResolverInterface
{
@@ -29,8 +29,7 @@ public function __construct(array $productLinksTypeNameResolvers = [])
}
/**
- * {@inheritdoc}
- * @throws GraphQlInputException
+ * @inheritdoc
*/
public function resolveType(array $data) : string
{
@@ -48,11 +47,6 @@ public function resolveType(array $data) : string
return $resolvedType;
}
}
-
- if (!$resolvedType) {
- throw new GraphQlInputException(
- __('Concrete type for %1 not implemented', ['ProductLinksInterface'])
- );
- }
+ throw new GraphQlInputException(__('Cannot resolve type'));
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
index de4c7dd71ca36..cb392a7b2295d 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php
@@ -7,7 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Api\Data\CategoryInterface;
use Magento\Catalog\Model\ResourceModel\Category\Collection;
@@ -85,7 +85,7 @@ public function __construct(
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var \Magento\Catalog\Model\Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php
index 9e966a060e5c6..b93c7e279153d 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php
@@ -8,7 +8,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Category;
use Magento\CatalogGraphQl\Model\Resolver\Category\DataProvider\Breadcrumbs as BreadcrumbsDataProvider;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -38,7 +38,7 @@ public function __construct(
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!isset($value['path'])) {
- throw new GraphQlInputException(__('"path" value should be specified'));
+ throw new LocalizedException(__('"path" value should be specified'));
}
$breadcrumbsData = $this->breadcrumbsDataProvider->getData($value['path']);
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
index 557c7e08ff432..7a41f8fc94e74 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php
@@ -92,7 +92,8 @@ public function resolve(
'items' => $searchResult->getProductsSearchResult(),
'page_info' => [
'page_size' => $searchCriteria->getPageSize(),
- 'current_page' => $currentPage
+ 'current_page' => $currentPage,
+ 'total_pages' => $maxPages
]
];
return $data;
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php
new file mode 100644
index 0000000000000..397fd12b7e714
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php
@@ -0,0 +1,70 @@
+catalogProductVisibility = $catalogProductVisibility;
+ $this->searchCriteria = $searchCriteria;
+ $this->stockProcessor = $stockProcessor;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($value['model'])) {
+ throw new GraphQlInputException(__('"model" value should be specified'));
+ }
+ /** @var Category $category */
+ $category = $value['model'];
+ $productsCollection = $category->getProductCollection();
+ $productsCollection->setVisibility($this->catalogProductVisibility->getVisibleInSiteIds());
+ $productsCollection = $this->stockProcessor->process($productsCollection, $this->searchCriteria, []);
+
+ return $productsCollection->getSize();
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php
index 0f1a7d8ee9dab..9047eaee4b568 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php
@@ -8,8 +8,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
use Magento\Catalog\Model\Product;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
@@ -29,7 +29,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/* @var $product Product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php
index a09510be8bc7d..ada3caad5f9f8 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php
@@ -7,7 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product;
@@ -16,9 +16,9 @@
use Magento\Framework\GraphQl\Query\ResolverInterface;
/**
- * Fixed the id related data in the product data
+ * @inheritdoc
*
- * {@inheritdoc}
+ * Fixed the id related data in the product data
*/
class EntityIdToId implements ResolverInterface
{
@@ -46,7 +46,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php
index a54cb62c16527..c8f167da583d3 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php
@@ -7,21 +7,32 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
/**
+ * @inheritdoc
+ *
* Format a product's media gallery information to conform to GraphQL schema representation
*/
class MediaGalleryEntries implements ResolverInterface
{
/**
+ * @inheritdoc
+ *
* Format product's media gallery entry data to conform to GraphQL schema
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return array
*/
public function resolve(
Field $field,
@@ -31,7 +42,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php
index 2fa47f86ecb9d..12016282a3081 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php
@@ -7,21 +7,31 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
/**
+ * @inheritdoc
+ *
* Format the new from and to typo of legacy fields news_from_date and news_to_date
*/
class NewFromTo implements ResolverInterface
{
/**
+ * @inheritdoc
+ *
* Transfer data from legacy news_from_date and news_to_date to new names corespondent fields
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return null|array
*/
public function resolve(
Field $field,
@@ -31,7 +41,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php
index 7c7e4ef117a50..76602288039c5 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php
@@ -7,7 +7,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Option;
@@ -20,9 +21,17 @@
class Options implements ResolverInterface
{
/**
+ * @inheritdoc
+ *
* Format product's option data to conform to GraphQL schema
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return null|array
*/
public function resolve(
Field $field,
@@ -32,7 +41,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php
index 6f4f8553a324a..55d930101fb60 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php
@@ -7,7 +7,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Pricing\Price\FinalPrice;
@@ -47,9 +48,17 @@ public function __construct(
}
/**
+ * @inheritdoc
+ *
* Format product's tier price data to conform to GraphQL schema
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return array
*/
public function resolve(
Field $field,
@@ -59,7 +68,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php
similarity index 82%
rename from app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php
rename to app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php
index 43fb1355c6b4e..2573e92e564b9 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php
@@ -7,17 +7,17 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Catalog\Model\Product;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\Catalog\Model\Product;
use Magento\Catalog\Helper\Output as OutputHelper;
/**
* Resolve rendered content for attributes where HTML content is allowed
*/
-class ProductHtmlAttribute implements ResolverInterface
+class ProductComplexTextAttribute implements ResolverInterface
{
/**
* @var OutputHelper
@@ -42,15 +42,16 @@ public function resolve(
ResolveInfo $info,
array $value = null,
array $args = null
- ) {
+ ): array {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/* @var $product Product */
$product = $value['model'];
$fieldName = $field->getName();
$renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName);
- return $renderedValue;
+
+ return ['html' => $renderedValue ?? ''];
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php
new file mode 100644
index 0000000000000..d1566162472b0
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php
@@ -0,0 +1,44 @@
+getName();
+
+ return [
+ 'model' => $product,
+ 'image_type' => $imageType,
+ ];
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php
new file mode 100644
index 0000000000000..f971e35742628
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php
@@ -0,0 +1,96 @@
+productResource = $productResource;
+ $this->storeManager = $storeManager;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($value['image_type'])) {
+ throw new LocalizedException(__('"image_type" value should be specified'));
+ }
+
+ if (!isset($value['model'])) {
+ throw new LocalizedException(__('"model" value should be specified'));
+ }
+
+ /** @var Product $product */
+ $product = $value['model'];
+ $imageType = $value['image_type'];
+ $imagePath = $product->getData($imageType);
+ $productId = (int)$product->getEntityId();
+
+ // null if image is not set
+ if (null === $imagePath) {
+ return $this->getAttributeValue($productId, 'name');
+ }
+
+ $imageLabel = $this->getAttributeValue($productId, $imageType . '_label');
+ if (null === $imageLabel) {
+ $imageLabel = $this->getAttributeValue($productId, 'name');
+ }
+
+ return $imageLabel;
+ }
+
+ /**
+ * Get attribute value
+ *
+ * @param int $productId
+ * @param string $attributeCode
+ * @return null|string Null if attribute value is not exists
+ */
+ private function getAttributeValue(int $productId, string $attributeCode): ?string
+ {
+ $storeId = $this->storeManager->getStore()->getId();
+
+ $value = $this->productResource->getAttributeRawValue($productId, $attributeCode, $storeId);
+ return is_array($value) && empty($value) ? null : $value;
+ }
+}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php
similarity index 62%
rename from app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php
rename to app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php
index 6830aecb78f10..3c19ce599a9b3 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php
@@ -5,7 +5,7 @@
*/
declare(strict_types=1);
-namespace Magento\CatalogGraphQl\Model\Resolver\Product;
+namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\ImageFactory;
@@ -15,13 +15,11 @@
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
/**
- * Returns product's image. If the image is not set, returns a placeholder
+ * Returns product's image url
*/
-class Image implements ResolverInterface
+class Url implements ResolverInterface
{
/**
- * Product image factory
- *
* @var ImageFactory
*/
private $productImageFactory;
@@ -44,23 +42,36 @@ public function resolve(
ResolveInfo $info,
array $value = null,
array $args = null
- ): array {
+ ) {
+ if (!isset($value['image_type'])) {
+ throw new LocalizedException(__('"image_type" value should be specified'));
+ }
+
if (!isset($value['model'])) {
throw new LocalizedException(__('"model" value should be specified'));
}
+
/** @var Product $product */
$product = $value['model'];
- $imageType = $field->getName();
- $path = $product->getData($imageType);
+ $imagePath = $product->getData($value['image_type']);
+ $imageUrl = $this->getImageUrl($value['image_type'], $imagePath);
+ return $imageUrl;
+ }
+
+ /**
+ * Get image url
+ *
+ * @param string $imageType
+ * @param string|null $imagePath Null if image is not set
+ * @return string
+ */
+ private function getImageUrl(string $imageType, ?string $imagePath): string
+ {
$image = $this->productImageFactory->create();
$image->setDestinationSubdir($imageType)
- ->setBaseFile($path);
+ ->setBaseFile($imagePath);
$imageUrl = $image->getUrl();
-
- return [
- 'url' => $imageUrl,
- 'path' => $path,
- ];
+ return $imageUrl;
}
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php
index 4d5622bd5c6d0..4d1b11a74b9d4 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php
@@ -7,7 +7,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductLink\Link;
@@ -15,9 +16,9 @@
use Magento\Framework\GraphQl\Query\ResolverInterface;
/**
- * Format the product links information to conform to GraphQL schema representation
+ * @inheritdoc
*
- * {@inheritdoc}
+ * Format the product links information to conform to GraphQL schema representation
*/
class ProductLinks implements ResolverInterface
{
@@ -27,9 +28,17 @@ class ProductLinks implements ResolverInterface
private $linkTypes = ['related', 'upsell', 'crosssell'];
/**
+ * @inheritdoc
+ *
* Format product links data to conform to GraphQL schema
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return null|array
*/
public function resolve(
Field $field,
@@ -39,7 +48,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php
index 63599a88c79a0..726ef91c56880 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php
@@ -7,7 +7,8 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\TierPrice;
@@ -15,16 +16,24 @@
use Magento\Framework\GraphQl\Query\ResolverInterface;
/**
- * Format a product's tier price information to conform to GraphQL schema representation
+ * @inheritdoc
*
- * {@inheritdoc}
+ * Format a product's tier price information to conform to GraphQL schema representation
*/
class TierPrices implements ResolverInterface
{
/**
+ * @inheritdoc
+ *
* Format product's tier price data to conform to GraphQL schema
*
- * {@inheritdoc}
+ * @param \Magento\Framework\GraphQl\Config\Element\Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ * @throws \Exception
+ * @return null|array
*/
public function resolve(
Field $field,
@@ -34,7 +43,7 @@ public function resolve(
array $args = null
) {
if (!isset($value['model'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/** @var Product $product */
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php
index 9fe64a16935c7..070c564713a96 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php
@@ -7,7 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
@@ -47,7 +47,7 @@ public function __construct(
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!isset($value['entity_id'])) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
$this->productWebsitesCollection->addIdFilters((int)$value['entity_id']);
$result = function () use ($value) {
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
index c4da59fd2cedf..f9b64f3a3c69b 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Search.php
@@ -12,7 +12,6 @@
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchCriteria\Helper\Filter as FilterHelper;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult;
use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory;
-use Magento\Framework\EntityManager\EntityManager;
use Magento\Search\Api\SearchInterface;
/**
@@ -45,31 +44,42 @@ class Search
*/
private $metadataPool;
+ /**
+ * @var \Magento\Search\Model\Search\PageSizeProvider
+ */
+ private $pageSizeProvider;
+
/**
* @param SearchInterface $search
* @param FilterHelper $filterHelper
* @param Filter $filterQuery
* @param SearchResultFactory $searchResultFactory
+ * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
+ * @param \Magento\Search\Model\Search\PageSizeProvider $pageSize
*/
public function __construct(
SearchInterface $search,
FilterHelper $filterHelper,
Filter $filterQuery,
SearchResultFactory $searchResultFactory,
- \Magento\Framework\EntityManager\MetadataPool $metadataPool
+ \Magento\Framework\EntityManager\MetadataPool $metadataPool,
+ \Magento\Search\Model\Search\PageSizeProvider $pageSize
) {
$this->search = $search;
$this->filterHelper = $filterHelper;
$this->filterQuery = $filterQuery;
$this->searchResultFactory = $searchResultFactory;
$this->metadataPool = $metadataPool;
+ $this->pageSizeProvider = $pageSize;
}
/**
* Return results of full text catalog search of given term, and will return filtered results if filter is specified
*
* @param SearchCriteriaInterface $searchCriteria
+ * @param ResolveInfo $info
* @return SearchResult
+ * @throws \Exception
*/
public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $info) : SearchResult
{
@@ -79,7 +89,8 @@ public function getResult(SearchCriteriaInterface $searchCriteria, ResolveInfo $
$realPageSize = $searchCriteria->getPageSize();
$realCurrentPage = $searchCriteria->getCurrentPage();
// Current page must be set to 0 and page size to max for search to grab all ID's as temporary workaround
- $searchCriteria->setPageSize(PHP_INT_MAX);
+ $pageSize = $this->pageSizeProvider->getMaxPageSize();
+ $searchCriteria->setPageSize($pageSize);
$searchCriteria->setCurrentPage(0);
$itemsResults = $this->search->search($searchCriteria);
diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
index 5d62cb63f1662..45f3a4c83be7b 100644
--- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls
@@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId")
name: String @doc(description: "The product name. Customers use this name to identify the product.")
sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer")
- description: String @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductHtmlAttribute")
- short_description: String @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductHtmlAttribute")
+ description: ComplexTextValue @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute")
+ short_description: ComplexTextValue @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute")
special_price: Float @doc(description: "The discounted price of the product")
special_from_date: String @doc(description: "The beginning date that a product has a special price")
special_to_date: String @doc(description: "The end date that a product has a special price")
@@ -257,16 +257,13 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
meta_title: String @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists")
meta_keyword: String @doc(description: "A comma-separated list of keywords that are visible only to search engines")
meta_description: String @doc(description: "A brief overview of the product for search results listings, maximum 255 characters")
- image: String @doc(description: "The relative path to the main image on the product page")
- small_image: ProductImage @doc(description: "The relative path to the small image, which is used on catalog pages") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Image")
- thumbnail: String @doc(description: "The relative path to the product's thumbnail image")
+ image: ProductImage @doc(description: "The relative path to the main image on the product page") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage")
+ small_image: ProductImage @doc(description: "The relative path to the small image, which is used on catalog pages") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage")
+ thumbnail: ProductImage @doc(description: "The relative path to the product's thumbnail image") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage")
new_from_date: String @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo")
new_to_date: String @doc(description: "The end date for new product listings") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo")
tier_price: Float @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached")
options_container: String @doc(description: "If the product has multiple options, determines where they appear on the product page")
- image_label: String @doc(description: "The label assigned to a product image")
- small_image_label: String @doc(description: "The label assigned to a product's small image")
- thumbnail_label: String @doc(description: "The label assigned to a product's thumbnail image")
created_at: String @doc(description: "Timestamp indicating when the product was created")
updated_at: String @doc(description: "Timestamp indicating when the product was updated")
country_of_manufacture: String @doc(description: "The product's country of origin")
@@ -352,15 +349,16 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the
image_size_y: Int @doc(description: "The maximum height of an image")
}
-type ProductImage @doc(description: "Product image information. Contains image relative path and URL") {
- url: String
- path: String
+type ProductImage @doc(description: "Product image information. Contains image relative path, URL and label") {
+ url: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Url")
+ label: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Label")
}
interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") {
title: String @doc(description: "The display name for this option")
required: Boolean @doc(description: "Indicates whether the option is required")
sort_order: Int @doc(description: "The order in which the option is displayed")
+ option_id: Int @doc(description: "Option ID")
}
interface CustomizableProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "CustomizableProductInterface contains information about customizable product options.") {
@@ -379,7 +377,7 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model
level: Int @doc(description: "Indicates the depth of the category within the tree")
created_at: String @doc(description: "Timestamp indicating when the category was created")
updated_at: String @doc(description: "Timestamp indicating when the category was updated")
- product_count: Int @doc(description: "The number of products in the category")
+ product_count: Int @doc(description: "The number of products in the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\ProductsCount")
default_sort_by: String @doc(description: "The attribute to use for sorting")
products(
pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index 7c56c266a64ef..9080dab0a5c13 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -1635,12 +1635,24 @@ protected function _saveProducts()
continue;
}
if ($this->getErrorAggregator()->hasToBeTerminated()) {
- $this->getErrorAggregator()->addRowToSkip($rowNum);
- continue;
+ $validationStrategy = $this->_parameters[Import::FIELD_NAME_VALIDATION_STRATEGY];
+ if (ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS !== $validationStrategy) {
+ $this->getErrorAggregator()->addRowToSkip($rowNum);
+ continue;
+ }
}
$rowScope = $this->getRowScope($rowData);
- $rowData[self::URL_KEY] = $this->getUrlKey($rowData);
+ $urlKey = $this->getUrlKey($rowData);
+ if (!empty($rowData[self::URL_KEY])) {
+ // If url_key column and its value were in the CSV file
+ $rowData[self::URL_KEY] = $urlKey;
+ } else if ($this->isNeedToChangeUrlKey($rowData)) {
+ // If url_key column was empty or even not declared in the CSV file but by the rules it is need to
+ // be setteed. In case when url_key is generating from name column we have to ensure that the bunch
+ // of products will pass for the event with url_key column.
+ $bunch[$rowNum][self::URL_KEY] = $rowData[self::URL_KEY] = $urlKey;
+ }
$rowSku = $rowData[self::COL_SKU];
@@ -2889,6 +2901,26 @@ protected function getResource()
return $this->_resource;
}
+ /**
+ * Whether a url key is needed to be change.
+ *
+ * @param array $rowData
+ * @return bool
+ */
+ private function isNeedToChangeUrlKey(array $rowData): bool
+ {
+ $urlKey = $this->getUrlKey($rowData);
+ $productExists = $this->isSkuExist($rowData[self::COL_SKU]);
+ $markedToEraseUrlKey = isset($rowData[self::URL_KEY]);
+ // The product isn't new and the url key index wasn't marked for change.
+ if (!$urlKey && $productExists && !$markedToEraseUrlKey) {
+ // Seems there is no need to change the url key
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Get product entity link field
*
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
index ffa0d4aff874b..951989146e67e 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php
@@ -66,7 +66,7 @@ public function __construct(
}
/**
- * Initialize categories to be processed
+ * Initialize categories
*
* @return $this
*/
@@ -106,7 +106,6 @@ protected function initCategories()
*
* @param string $name
* @param int $parentId
- *
* @return int
*/
protected function createCategory($name, $parentId)
@@ -116,22 +115,14 @@ protected function createCategory($name, $parentId)
if (!($parentCategory = $this->getCategoryById($parentId))) {
$parentCategory = $this->categoryFactory->create()->load($parentId);
}
-
- // Set StoreId to 0 to generate URL Keys global and prevent generating url rewrites just for default website
- $category->setStoreId(0);
$category->setPath($parentCategory->getPath());
$category->setParentId($parentId);
$category->setName($this->unquoteDelimiter($name));
$category->setIsActive(true);
$category->setIncludeInMenu(true);
$category->setAttributeSetId($category->getDefaultAttributeSetId());
- try {
- $category->save();
- $this->categoriesCache[$category->getId()] = $category;
- } catch (\Exception $e) {
- $this->addFailedCategory($category, $e);
- }
-
+ $category->save();
+ $this->categoriesCache[$category->getId()] = $category;
return $category->getId();
}
@@ -139,7 +130,6 @@ protected function createCategory($name, $parentId)
* Returns ID of category by string path creating nonexistent ones.
*
* @param string $categoryPath
- *
* @return int
*/
protected function upsertCategory($categoryPath)
@@ -170,7 +160,6 @@ protected function upsertCategory($categoryPath)
*
* @param string $categoriesString
* @param string $categoriesSeparator
- *
* @return array
*/
public function upsertCategories($categoriesString, $categoriesSeparator)
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php
index c7eb722050303..fa2853c738624 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php
@@ -1794,7 +1794,8 @@ protected function _getSpecificTypeData(array $rowData, $optionTypeId, $defaultS
];
$priceData = false;
- if (!empty($rowData[self::COLUMN_ROW_PRICE])) {
+ $customOptionRowPrice = $rowData[self::COLUMN_ROW_PRICE];
+ if (!empty($customOptionRowPrice) || $customOptionRowPrice === '0') {
$priceData = [
'price' => (double)rtrim($rowData[self::COLUMN_ROW_PRICE], '%'),
'price_type' => 'fixed',
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php
index afd018f077d20..3b6caef66ce6c 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php
@@ -195,6 +195,8 @@ public function __construct(
}
/**
+ * Initialize template for error message.
+ *
* @param array $templateCollection
* @return $this
*/
@@ -377,6 +379,8 @@ public function retrieveAttributeFromCache($attributeCode)
}
/**
+ * Adding attribute option.
+ *
* In case we've dynamically added new attribute option during import we need to add it to our cache
* in order to keep it up to date.
*
@@ -508,8 +512,10 @@ public function isSuitable()
}
/**
- * Prepare attributes values for save: exclude non-existent, static or with empty values attributes;
- * set default values if needed
+ * Adding default attribute to product before save.
+ *
+ * Prepare attributes values for save: exclude non-existent, static or with empty values attributes,
+ * set default values if needed.
*
* @param array $rowData
* @param bool $withDefaultValue
@@ -537,9 +543,9 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe
} else {
$resultAttrs[$attrCode] = $rowData[$attrCode];
}
- } elseif (array_key_exists($attrCode, $rowData)) {
+ } elseif (array_key_exists($attrCode, $rowData) && empty($rowData['_store'])) {
$resultAttrs[$attrCode] = $rowData[$attrCode];
- } elseif ($withDefaultValue && null !== $attrParams['default_value']) {
+ } elseif ($withDefaultValue && null !== $attrParams['default_value'] && empty($rowData['_store'])) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
}
@@ -611,7 +617,8 @@ protected function getProductEntityLinkField()
}
/**
- * Clean cached values
+ * Clean cached values.
+ *
* @since 100.2.0
*/
public function __destruct()
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
index 2aa2105991883..4b7416f6ad9a6 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
@@ -7,6 +7,7 @@
use Magento\CatalogImportExport\Model\Import\Product;
use Magento\Framework\Validator\AbstractValidator;
+use Magento\Catalog\Model\Product\Attribute\Backend\Sku;
/**
* Class Validator
@@ -60,6 +61,8 @@ public function __construct(
}
/**
+ * Text validation
+ *
* @param mixed $attrCode
* @param string $type
* @return bool
@@ -69,6 +72,8 @@ protected function textValidation($attrCode, $type)
$val = $this->string->cleanString($this->_rowData[$attrCode]);
if ($type == 'text') {
$valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH;
+ } else if ($attrCode == Product::COL_SKU) {
+ $valid = $this->string->strlen($val) <= SKU::SKU_MAX_LENGTH;
} else {
$valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH;
}
@@ -105,6 +110,8 @@ private function validateOption($attrCode, $possibleOptions, $value)
}
/**
+ * Numeric validation
+ *
* @param mixed $attrCode
* @param string $type
* @return bool
@@ -132,6 +139,8 @@ protected function numericValidation($attrCode, $type)
}
/**
+ * Is required attribute valid
+ *
* @param string $attrCode
* @param array $attributeParams
* @param array $rowData
@@ -159,6 +168,8 @@ public function isRequiredAttributeValid($attrCode, array $attributeParams, arra
}
/**
+ * Is attribute valid
+ *
* @param string $attrCode
* @param array $attrParams
* @param array $rowData
@@ -255,6 +266,8 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData)
}
/**
+ * Set invalid attribute
+ *
* @param string|null $attribute
* @return void
* @since 100.1.0
@@ -265,6 +278,8 @@ protected function setInvalidAttribute($attribute)
}
/**
+ * Get invalid attribute
+ *
* @return string
* @since 100.1.0
*/
@@ -274,6 +289,8 @@ public function getInvalidAttribute()
}
/**
+ * Is valid attributes
+ *
* @return bool
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
@@ -300,7 +317,7 @@ protected function isValidAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function isValid($value)
{
@@ -331,6 +348,8 @@ public function getRowScope(array $rowData)
}
/**
+ * Init
+ *
* @param \Magento\CatalogImportExport\Model\Import\Product $context
* @return $this
*/
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
index e7ffe408cc732..ae4be4a1e62b3 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php
@@ -101,7 +101,7 @@ class Uploader extends \Magento\MediaStorage\Model\File\Uploader
* @param \Magento\MediaStorage\Model\File\Validator\NotProtectedExtension $validator
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\Filesystem\File\ReadFactory $readFactory
- * @param null $filePath
+ * @param null|string $filePath
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function __construct(
@@ -113,15 +113,15 @@ public function __construct(
\Magento\Framework\Filesystem\File\ReadFactory $readFactory,
$filePath = null
) {
- if ($filePath !== null) {
- $this->_setUploadFile($filePath);
- }
$this->_imageFactory = $imageFactory;
$this->_coreFileStorageDb = $coreFileStorageDb;
$this->_coreFileStorage = $coreFileStorage;
$this->_validator = $validator;
$this->_directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
$this->_readFactory = $readFactory;
+ if ($filePath !== null) {
+ $this->_setUploadFile($filePath);
+ }
}
/**
@@ -353,7 +353,7 @@ protected function _moveFile($tmpPath, $destPath)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function chmod($file)
{
diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php
index 69f4a465e3309..919f0cfda7cbe 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/CategoryProcessorTest.php
@@ -34,41 +34,49 @@ class CategoryProcessorTest extends \PHPUnit\Framework\TestCase
*/
protected $product;
+ /**
+ * @var \Magento\Catalog\Model\Category
+ */
+ private $childCategory;
+
+ /**
+ * \Magento\Catalog\Model\Category
+ */
+ private $parentCategory;
+
protected function setUp()
{
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->objectManagerHelper = new ObjectManagerHelper($this);
- $childCategory = $this->getMockBuilder(\Magento\Catalog\Model\Category::class)
+ $this->childCategory = $this->getMockBuilder(\Magento\Catalog\Model\Category::class)
->disableOriginalConstructor()
->getMock();
- $childCategory->method('getId')->will($this->returnValue(self::CHILD_CATEGORY_ID));
- $childCategory->method('getName')->will($this->returnValue(self::CHILD_CATEGORY_NAME));
- $childCategory->method('getPath')->will($this->returnValue(
+ $this->childCategory->method('getId')->will($this->returnValue(self::CHILD_CATEGORY_ID));
+ $this->childCategory->method('getName')->will($this->returnValue(self::CHILD_CATEGORY_NAME));
+ $this->childCategory->method('getPath')->will($this->returnValue(
self::PARENT_CATEGORY_ID . CategoryProcessor::DELIMITER_CATEGORY
. self::CHILD_CATEGORY_ID
));
- $childCategory->method('save')->willThrowException(new \Exception());
-
- $parentCategory = $this->getMockBuilder(\Magento\Catalog\Model\Category::class)
+ $this->parentCategory = $this->getMockBuilder(\Magento\Catalog\Model\Category::class)
->disableOriginalConstructor()
->getMock();
- $parentCategory->method('getId')->will($this->returnValue(self::PARENT_CATEGORY_ID));
- $parentCategory->method('getName')->will($this->returnValue('Parent'));
- $parentCategory->method('getPath')->will($this->returnValue(self::PARENT_CATEGORY_ID));
+ $this->parentCategory->method('getId')->will($this->returnValue(self::PARENT_CATEGORY_ID));
+ $this->parentCategory->method('getName')->will($this->returnValue('Parent'));
+ $this->parentCategory->method('getPath')->will($this->returnValue(self::PARENT_CATEGORY_ID));
$categoryCollection =
$this->objectManagerHelper->getCollectionMock(
\Magento\Catalog\Model\ResourceModel\Category\Collection::class,
[
- self::PARENT_CATEGORY_ID => $parentCategory,
- self::CHILD_CATEGORY_ID => $childCategory,
+ self::PARENT_CATEGORY_ID => $this->parentCategory,
+ self::CHILD_CATEGORY_ID => $this->childCategory,
]
);
$map = [
- [self::PARENT_CATEGORY_ID, $parentCategory],
- [self::CHILD_CATEGORY_ID, $childCategory],
+ [self::PARENT_CATEGORY_ID, $this->parentCategory],
+ [self::CHILD_CATEGORY_ID, $this->childCategory],
];
$categoryCollection->expects($this->any())
->method('getItemById')
@@ -91,7 +99,7 @@ protected function setUp()
$categoryFactory = $this->createPartialMock(\Magento\Catalog\Model\CategoryFactory::class, ['create']);
- $categoryFactory->method('create')->will($this->returnValue($childCategory));
+ $categoryFactory->method('create')->will($this->returnValue($this->childCategory));
$this->categoryProcessor =
new \Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor(
@@ -110,11 +118,13 @@ public function testUpsertCategories()
/**
* Tests case when newly created category save throws exception.
*/
- public function testCreateCategoryException()
+ public function testUpsertCategoriesWithAlreadyExistsException()
{
- $method = new \ReflectionMethod(CategoryProcessor::class, 'createCategory');
- $method->setAccessible(true);
- $method->invoke($this->categoryProcessor, self::CHILD_CATEGORY_NAME, self::PARENT_CATEGORY_ID);
+ $exception = new \Magento\Framework\Exception\AlreadyExistsException();
+ $categoriesSeparator = '/';
+ $categoryName = 'Exception Category';
+ $this->childCategory->method('save')->willThrowException($exception);
+ $this->categoryProcessor->upsertCategories($categoryName, $categoriesSeparator);
$this->assertNotEmpty($this->categoryProcessor->getFailedCategories());
}
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockCollectionInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockCollectionInterface.php
index c2521f77ca24a..9ae22e5e1a364 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockCollectionInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockCollectionInterface.php
@@ -15,6 +15,10 @@
* Interface StockCollectionInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockCollectionInterface extends SearchResultsInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockInterface.php
index 53e95921ea955..087fae6e6568a 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockInterface.php
@@ -11,6 +11,10 @@
* Interface Stock
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockInterface extends ExtensibleDataInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockItemCollectionInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockItemCollectionInterface.php
index 038174c8e52be..59a1f58b74c2c 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockItemCollectionInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockItemCollectionInterface.php
@@ -15,6 +15,10 @@
* Interface StockItemCollectionInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockItemCollectionInterface extends SearchResultsInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
index b876615468ba9..e2e8a744c4bcd 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
@@ -11,6 +11,10 @@
* Interface StockItem
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockItemInterface extends ExtensibleDataInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockStatusCollectionInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockStatusCollectionInterface.php
index 70a2c29ff9a6e..1cc045745a0c1 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockStatusCollectionInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockStatusCollectionInterface.php
@@ -11,6 +11,10 @@
* Stock Status collection interface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStatusCollectionInterface extends SearchResultsInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockStatusInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockStatusInterface.php
index c9ae6a96a3671..66b639fb088d1 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockStatusInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockStatusInterface.php
@@ -11,6 +11,10 @@
* Interface StockStatusInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStatusInterface extends ExtensibleDataInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/RegisterProductSaleInterface.php b/app/code/Magento/CatalogInventory/Api/RegisterProductSaleInterface.php
index 9122fb0038646..6fd1e7466970d 100644
--- a/app/code/Magento/CatalogInventory/Api/RegisterProductSaleInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/RegisterProductSaleInterface.php
@@ -12,6 +12,10 @@
/**
* @api
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface RegisterProductSaleInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/RevertProductSaleInterface.php b/app/code/Magento/CatalogInventory/Api/RevertProductSaleInterface.php
index ed496f3882fc2..552e30da89235 100644
--- a/app/code/Magento/CatalogInventory/Api/RevertProductSaleInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/RevertProductSaleInterface.php
@@ -9,6 +9,10 @@
/**
* @api
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface RevertProductSaleInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockConfigurationInterface.php b/app/code/Magento/CatalogInventory/Api/StockConfigurationInterface.php
index a23d5030b8242..5019e86b7af40 100644
--- a/app/code/Magento/CatalogInventory/Api/StockConfigurationInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockConfigurationInterface.php
@@ -9,6 +9,10 @@
* Interface StockConfigurationInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockConfigurationInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockCriteriaInterface.php b/app/code/Magento/CatalogInventory/Api/StockCriteriaInterface.php
index 969af6481cb4a..eb6fb2e812f2e 100644
--- a/app/code/Magento/CatalogInventory/Api/StockCriteriaInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockCriteriaInterface.php
@@ -9,6 +9,10 @@
* Interface StockCriteriaInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockCriteriaInterface extends \Magento\Framework\Api\CriteriaInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockIndexInterface.php b/app/code/Magento/CatalogInventory/Api/StockIndexInterface.php
index 1521b34c715b0..18bab6571c209 100644
--- a/app/code/Magento/CatalogInventory/Api/StockIndexInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockIndexInterface.php
@@ -9,6 +9,10 @@
* Interface StockIndexInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockIndexInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockItemCriteriaInterface.php b/app/code/Magento/CatalogInventory/Api/StockItemCriteriaInterface.php
index a2fc7801b1d13..1d2cabbb48a11 100644
--- a/app/code/Magento/CatalogInventory/Api/StockItemCriteriaInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockItemCriteriaInterface.php
@@ -9,6 +9,10 @@
* Interface StockItemCriteriaInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockItemCriteriaInterface extends \Magento\Framework\Api\CriteriaInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockItemRepositoryInterface.php b/app/code/Magento/CatalogInventory/Api/StockItemRepositoryInterface.php
index 2732048d1445f..eecf6cbe07632 100644
--- a/app/code/Magento/CatalogInventory/Api/StockItemRepositoryInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockItemRepositoryInterface.php
@@ -9,6 +9,10 @@
* Interface StockItemRepository
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockItemRepositoryInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockManagementInterface.php b/app/code/Magento/CatalogInventory/Api/StockManagementInterface.php
index ddb84abf3d1a5..8796953e32fd0 100644
--- a/app/code/Magento/CatalogInventory/Api/StockManagementInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockManagementInterface.php
@@ -9,6 +9,10 @@
* Interface StockManagementInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockManagementInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
index 2d011e24f4105..5478f90fb7d9f 100644
--- a/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php
@@ -9,6 +9,10 @@
* Interface StockRegistryInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockRegistryInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockRepositoryInterface.php b/app/code/Magento/CatalogInventory/Api/StockRepositoryInterface.php
index 80a7e79289cff..3cfdf45506340 100644
--- a/app/code/Magento/CatalogInventory/Api/StockRepositoryInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockRepositoryInterface.php
@@ -9,6 +9,10 @@
* Interface StockRepositoryInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockRepositoryInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockStateInterface.php b/app/code/Magento/CatalogInventory/Api/StockStateInterface.php
index 8a1f7da5158ed..8be7f5be79f27 100644
--- a/app/code/Magento/CatalogInventory/Api/StockStateInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockStateInterface.php
@@ -9,6 +9,10 @@
* Interface StockStateInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStateInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php b/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
index e504e6355a15a..99ad7005d9da4 100644
--- a/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php
@@ -9,6 +9,10 @@
* Interface StockStatusCriteriaInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaInterface
{
diff --git a/app/code/Magento/CatalogInventory/Api/StockStatusRepositoryInterface.php b/app/code/Magento/CatalogInventory/Api/StockStatusRepositoryInterface.php
index 94d4998c1e318..d29171f557f05 100644
--- a/app/code/Magento/CatalogInventory/Api/StockStatusRepositoryInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/StockStatusRepositoryInterface.php
@@ -9,6 +9,10 @@
* Interface StockStatusRepositoryInterface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStatusRepositoryInterface
{
diff --git a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php
index 15dfbd122e950..d3c165bbde1a8 100644
--- a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php
+++ b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php
@@ -10,6 +10,10 @@
*
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Minsaleqty extends \Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray
{
diff --git a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Stock.php b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Stock.php
index 2c41e1798921a..5378801b6c24b 100644
--- a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Stock.php
+++ b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Stock.php
@@ -14,6 +14,10 @@
/**
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Stock extends \Magento\Framework\Data\Form\Element\Select
{
diff --git a/app/code/Magento/CatalogInventory/Block/Qtyincrements.php b/app/code/Magento/CatalogInventory/Block/Qtyincrements.php
index adaa762f3279b..a12b72cd0a971 100644
--- a/app/code/Magento/CatalogInventory/Block/Qtyincrements.php
+++ b/app/code/Magento/CatalogInventory/Block/Qtyincrements.php
@@ -14,6 +14,10 @@
*
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Qtyincrements extends Template implements IdentityInterface
{
diff --git a/app/code/Magento/CatalogInventory/Block/Stockqty/DefaultStockqty.php b/app/code/Magento/CatalogInventory/Block/Stockqty/DefaultStockqty.php
index c315338be0de0..5a3a3ca6ee983 100644
--- a/app/code/Magento/CatalogInventory/Block/Stockqty/DefaultStockqty.php
+++ b/app/code/Magento/CatalogInventory/Block/Stockqty/DefaultStockqty.php
@@ -11,6 +11,10 @@
*
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class DefaultStockqty extends AbstractStockqty implements \Magento\Framework\DataObject\IdentityInterface
{
diff --git a/app/code/Magento/CatalogInventory/Helper/Stock.php b/app/code/Magento/CatalogInventory/Helper/Stock.php
index 494d440eeed89..798ac4074c188 100644
--- a/app/code/Magento/CatalogInventory/Helper/Stock.php
+++ b/app/code/Magento/CatalogInventory/Helper/Stock.php
@@ -18,6 +18,10 @@
* Class Stock
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @api
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Stock
{
diff --git a/app/code/Magento/CatalogInventory/Model/Adminhtml/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/Adminhtml/Stock/Item.php
index 36721db874887..145b0d1454ae2 100644
--- a/app/code/Magento/CatalogInventory/Model/Adminhtml/Stock/Item.php
+++ b/app/code/Magento/CatalogInventory/Model/Adminhtml/Stock/Item.php
@@ -20,6 +20,10 @@
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Item extends \Magento\CatalogInventory\Model\Stock\Item implements IdentityInterface
{
diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php
index b86c3cf13f31b..502d9532e8a05 100644
--- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php
+++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php
@@ -25,6 +25,10 @@
* @api
* @since 100.0.2
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class QuantityValidator
{
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
index 366cb1c3902a3..ba3b62f554767 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
@@ -18,6 +18,10 @@
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class DefaultStock extends AbstractIndexer implements StockInterface
{
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php
index abe473bd9682b..115002b237645 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/QueryProcessorInterface.php
@@ -11,6 +11,10 @@
/**
* @api
* @since 100.1.0
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface QueryProcessorInterface
{
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/StockInterface.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/StockInterface.php
index 5ced55edf208b..24ed496372817 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/StockInterface.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/StockInterface.php
@@ -9,6 +9,10 @@
* CatalogInventory Stock Indexer Interface
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockInterface
{
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/StockFactory.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/StockFactory.php
index f9738484766b9..0ee162e429f40 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/StockFactory.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/StockFactory.php
@@ -12,6 +12,10 @@
/**
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class StockFactory
{
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
index bc5fda4939adc..402ce5f2f611e 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php
@@ -13,6 +13,10 @@
* CatalogInventory Stock Status per website Resource Model
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @api
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
diff --git a/app/code/Magento/CatalogInventory/Model/Source/Backorders.php b/app/code/Magento/CatalogInventory/Model/Source/Backorders.php
index 1d200d27a4445..0bffb9a9888cd 100644
--- a/app/code/Magento/CatalogInventory/Model/Source/Backorders.php
+++ b/app/code/Magento/CatalogInventory/Model/Source/Backorders.php
@@ -9,6 +9,10 @@
* Back orders source class
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Backorders implements \Magento\Framework\Option\ArrayInterface
{
diff --git a/app/code/Magento/CatalogInventory/Model/Source/Stock.php b/app/code/Magento/CatalogInventory/Model/Source/Stock.php
index 9ed891d1dcc0f..7d44ab782de61 100644
--- a/app/code/Magento/CatalogInventory/Model/Source/Stock.php
+++ b/app/code/Magento/CatalogInventory/Model/Source/Stock.php
@@ -11,6 +11,10 @@
* CatalogInventory Stock source model
* @api
* @since 100.0.2
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
class Stock extends AbstractSource
{
diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json
index 8b55b6f327988..007d744b2296f 100644
--- a/app/code/Magento/CatalogInventory/composer.json
+++ b/app/code/Magento/CatalogInventory/composer.json
@@ -27,5 +27,6 @@
"psr-4": {
"Magento\\CatalogInventory\\": ""
}
- }
+ },
+ "abandoned": "magento/inventory-composer-metapackage"
}
diff --git a/app/code/Magento/CatalogInventory/etc/db_schema.xml b/app/code/Magento/CatalogInventory/etc/db_schema.xml
index 8a6ae8d2d93c6..5ac7fedc5aa18 100644
--- a/app/code/Magento/CatalogInventory/etc/db_schema.xml
+++ b/app/code/Magento/CatalogInventory/etc/db_schema.xml
@@ -13,10 +13,10 @@
-
+
-
+
@@ -71,23 +71,23 @@
identity="false" default="0" comment="Is Divided into Multiple Boxes for Shipping"/>
-
+
-
-
-
+
-
+
-
+
@@ -103,18 +103,18 @@
comment="Qty"/>
-
+
-
+
-
+
-
+
@@ -130,15 +130,15 @@
comment="Qty"/>
-
+
-
+
-
+
@@ -154,15 +154,15 @@
comment="Qty"/>
-
+
-
+
-
+
@@ -178,18 +178,18 @@
comment="Qty"/>
-
+
-
+
-
+
-
+
diff --git a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php
index 169456f7d4bbd..9e10f0b448504 100644
--- a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php
+++ b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php
@@ -11,7 +11,7 @@
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Configuration;
use Magento\Framework\App\Config\ScopeConfigInterface;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -50,7 +50,7 @@ public function __construct(
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/* @var $product ProductInterface */
diff --git a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php
index 2f3d520c2cb8f..354e053efa90f 100644
--- a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php
+++ b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php
@@ -10,7 +10,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
-use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -39,7 +39,7 @@ public function __construct(StockStatusRepositoryInterface $stockStatusRepositor
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
{
if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) {
- throw new GraphQlInputException(__('"model" value should be specified'));
+ throw new LocalizedException(__('"model" value should be specified'));
}
/* @var $product ProductInterface */
diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php
index bad1118e3ae72..0ff12faf54cbf 100644
--- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php
+++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php
@@ -14,6 +14,8 @@
use Magento\Framework\App\Request\DataPersistorInterface;
/**
+ * Save action for catalog rule
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog implements HttpPostActionInterface
@@ -40,7 +42,9 @@ public function __construct(
}
/**
- * @return void
+ * Execute save action from catalog rule
+ *
+ * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function execute()
@@ -61,6 +65,17 @@ public function execute()
['request' => $this->getRequest()]
);
$data = $this->getRequest()->getPostValue();
+
+ $filterValues = ['from_date' => $this->_dateFilter];
+ if ($this->getRequest()->getParam('to_date')) {
+ $filterValues['to_date'] = $this->_dateFilter;
+ }
+ $inputFilter = new \Zend_Filter_Input(
+ $filterValues,
+ [],
+ $data
+ );
+ $data = $inputFilter->getUnescaped();
$id = $this->getRequest()->getParam('rule_id');
if ($id) {
$model = $ruleRepository->get($id);
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml
index cfd41f2ab970c..fe4042e8a2e9f 100644
--- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml
+++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml
@@ -58,4 +58,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml
index 741da96179b8c..716a363ec5d78 100644
--- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml
+++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml
@@ -16,6 +16,9 @@
+
+
+
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml
index befe0b0ce7f98..072385c46645a 100644
--- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml
+++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml
@@ -17,6 +17,9 @@
+
+
+
@@ -74,6 +77,9 @@
+
+
+
@@ -97,6 +103,9 @@
+
+
+
@@ -120,6 +129,9 @@
+
+
+
@@ -143,6 +155,9 @@
+
+
+
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml
index d3546d06492be..83770ffff5eab 100644
--- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml
+++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml
@@ -17,6 +17,9 @@
+
+
+
diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml
new file mode 100644
index 0000000000000..2e09ee7134733
--- /dev/null
+++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml
@@ -0,0 +1,85 @@
+
+
+
+
+