diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7237ebfacf5e1..e17c4e14ffc7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -785,7 +785,7 @@ Tests:
* Refactored controller actions in the Product area
* Moved commands cache.php, indexer.php, log.php, test.php, compiler.php, singletenant\_compiler.php, generator.php, pack.php, deploy.php and file\_assembler.php to the new bin/magento CLI framework
* Data Migration Tool
- * The Data Migraiton Tool is published in the separate [repository](https://github.com/magento/data-migration-tool-ce "Data Migration Tool repository")
+ * The Data Migration Tool is published in the separate [repository](https://github.com/magento/data-migration-tool-ce "Data Migration Tool repository")
* Fixed bugs
* Fixed an issue where error appeared during placing order with virtual product
* Fixed an issue where billing and shipping sections didn't contain address information on order print
diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml
index 505e178f49f43..e660a2eb8d428 100644
--- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml
+++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingButtonTest.xml
@@ -13,6 +13,9 @@
+
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminSuccessLoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminSuccessLoginActionGroup.xml
new file mode 100644
index 0000000000000..844f58c789a15
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminSuccessLoginActionGroup.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageOnAdminLoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageOnAdminLoginActionGroup.xml
new file mode 100644
index 0000000000000..ccc7cd24350c5
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageOnAdminLoginActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml
new file mode 100644
index 0000000000000..6aaa612b249b6
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml
index 1070bc409962a..b2fbadcbe38e2 100644
--- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml
+++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml
@@ -10,11 +10,11 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
-
+
-
-
+
+
diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml
index 441ce886f117b..5b517c7be8a79 100644
--- a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml
+++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml
@@ -10,5 +10,6 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginMessagesSection.xml
new file mode 100644
index 0000000000000..f6ada50ada357
--- /dev/null
+++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginMessagesSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php
new file mode 100644
index 0000000000000..a6c1b088400a7
--- /dev/null
+++ b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php
@@ -0,0 +1,177 @@
+orderManagement = $orderManagement;
+ $this->paymentExtensionFactory = $paymentExtensionFactory;
+ $this->getPaymentNonceCommand = $getPaymentNonceCommand;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function place(array $orderList): array
+ {
+ if (empty($orderList)) {
+ return [];
+ }
+
+ $errorList = [];
+ $firstOrder = $this->orderManagement->place(array_shift($orderList));
+ // get payment token from first placed order
+ $paymentToken = $this->getPaymentToken($firstOrder);
+
+ foreach ($orderList as $order) {
+ try {
+ /** @var OrderInterface $order */
+ $orderPayment = $order->getPayment();
+ $this->setVaultPayment($orderPayment, $paymentToken);
+ $this->orderManagement->place($order);
+ } catch (\Exception $e) {
+ $incrementId = $order->getIncrementId();
+ $errorList[$incrementId] = $e;
+ }
+ }
+
+ return $errorList;
+ }
+
+ /**
+ * Sets vault payment method.
+ *
+ * @param OrderPaymentInterface $orderPayment
+ * @param PaymentTokenInterface $paymentToken
+ * @return void
+ */
+ private function setVaultPayment(OrderPaymentInterface $orderPayment, PaymentTokenInterface $paymentToken): void
+ {
+ $vaultMethod = $this->getVaultPaymentMethod(
+ $orderPayment->getMethod()
+ );
+ $orderPayment->setMethod($vaultMethod);
+
+ $publicHash = $paymentToken->getPublicHash();
+ $customerId = $paymentToken->getCustomerId();
+ $result = $this->getPaymentNonceCommand->execute(
+ ['public_hash' => $publicHash, 'customer_id' => $customerId]
+ )
+ ->get();
+
+ $orderPayment->setAdditionalInformation(
+ DataAssignObserver::PAYMENT_METHOD_NONCE,
+ $result['paymentMethodNonce']
+ );
+ $orderPayment->setAdditionalInformation(
+ PaymentTokenInterface::PUBLIC_HASH,
+ $publicHash
+ );
+ $orderPayment->setAdditionalInformation(
+ PaymentTokenInterface::CUSTOMER_ID,
+ $customerId
+ );
+ }
+
+ /**
+ * Returns vault payment method.
+ *
+ * For placing sequence of orders, we need to replace the original method on the vault method.
+ *
+ * @param string $method
+ * @return string
+ */
+ private function getVaultPaymentMethod(string $method): string
+ {
+ $vaultPaymentMap = [
+ ConfigProvider::CODE => ConfigProvider::CC_VAULT_CODE,
+ PaypalConfigProvider::PAYPAL_CODE => PaypalConfigProvider::PAYPAL_VAULT_CODE
+ ];
+
+ return $vaultPaymentMap[$method] ?? $method;
+ }
+
+ /**
+ * Returns payment token.
+ *
+ * @param OrderInterface $order
+ * @return PaymentTokenInterface
+ * @throws \BadMethodCallException
+ */
+ private function getPaymentToken(OrderInterface $order): PaymentTokenInterface
+ {
+ $orderPayment = $order->getPayment();
+ $extensionAttributes = $this->getExtensionAttributes($orderPayment);
+ $paymentToken = $extensionAttributes->getVaultPaymentToken();
+
+ if ($paymentToken === null) {
+ throw new \BadMethodCallException('Vault Payment Token should be defined for placed order payment.');
+ }
+
+ return $paymentToken;
+ }
+
+ /**
+ * Gets payment extension attributes.
+ *
+ * @param OrderPaymentInterface $payment
+ * @return OrderPaymentExtensionInterface
+ */
+ private function getExtensionAttributes(OrderPaymentInterface $payment): OrderPaymentExtensionInterface
+ {
+ $extensionAttributes = $payment->getExtensionAttributes();
+ if (null === $extensionAttributes) {
+ $extensionAttributes = $this->paymentExtensionFactory->create();
+ $payment->setExtensionAttributes($extensionAttributes);
+ }
+
+ return $extensionAttributes;
+ }
+}
diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json
index 5af56a2afd3fe..2f956076f3846 100644
--- a/app/code/Magento/Braintree/composer.json
+++ b/app/code/Magento/Braintree/composer.json
@@ -21,7 +21,8 @@
"magento/module-quote": "*",
"magento/module-sales": "*",
"magento/module-ui": "*",
- "magento/module-vault": "*"
+ "magento/module-vault": "*",
+ "magento/module-multishipping": "*"
},
"suggest": {
"magento/module-checkout-agreements": "*",
diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml
index 9de4773af023a..fe4cfab9c0e30 100644
--- a/app/code/Magento/Braintree/etc/config.xml
+++ b/app/code/Magento/Braintree/etc/config.xml
@@ -42,7 +42,7 @@
cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision
Magento\Braintree\Model\AvsEmsCodeMapper
Magento\Braintree\Model\CvvEmsCodeMapper
- braintree
+ braintree_group
BraintreePayPalFacade
@@ -68,7 +68,7 @@
processorResponseCode,processorResponseText,paymentId
processorResponseCode,processorResponseText,paymentId,payerEmail
en_US,en_GB,en_AU,da_DK,fr_FR,fr_CA,de_DE,zh_HK,it_IT,nl_NL,no_NO,pl_PL,es_ES,sv_SE,tr_TR,pt_BR,ja_JP,id_ID,ko_KR,pt_PT,ru_RU,th_TH,zh_CN,zh_TW
- braintree
+ braintree_group
BraintreeCreditCardVaultFacade
@@ -78,7 +78,7 @@
Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter
Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider
- braintree
+ braintree_group
BraintreePayPalVaultFacade
@@ -88,7 +88,7 @@
Magento\Braintree\Model\InstantPurchase\PayPal\TokenFormatter
Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider
- braintree
+ braintree_group
diff --git a/app/code/Magento/Braintree/etc/frontend/di.xml b/app/code/Magento/Braintree/etc/frontend/di.xml
index ea417c407dffd..d8d3a93b71dc3 100644
--- a/app/code/Magento/Braintree/etc/frontend/di.xml
+++ b/app/code/Magento/Braintree/etc/frontend/di.xml
@@ -61,4 +61,12 @@
Magento\Braintree\Model\LocaleResolver
+
+
+
+ - Magento\Braintree\Model\Multishipping\PlaceOrder
+ - Magento\Braintree\Model\Multishipping\PlaceOrder
+
+
+
diff --git a/app/code/Magento/Braintree/etc/payment.xml b/app/code/Magento/Braintree/etc/payment.xml
index dbabd91151022..4cae049aaf5a9 100644
--- a/app/code/Magento/Braintree/etc/payment.xml
+++ b/app/code/Magento/Braintree/etc/payment.xml
@@ -8,8 +8,16 @@
-
+
Braintree
+
+
+ 1
+
+
+ 1
+
+
diff --git a/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml
new file mode 100644
index 0000000000000..06390d403e63d
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ - Magento_Braintree::multishipping/form.phtml
+ - Magento_Braintree::multishipping/form_paypal.phtml
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml
new file mode 100644
index 0000000000000..bf8aa8dd09c2c
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml
@@ -0,0 +1,29 @@
+
+
+
diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml
new file mode 100644
index 0000000000000..ea3eb2214c2d8
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml
@@ -0,0 +1,29 @@
+
+
+
diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js
new file mode 100644
index 0000000000000..1ceebc8e66282
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js
@@ -0,0 +1,102 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*browser:true*/
+/*global define*/
+
+define([
+ 'jquery',
+ 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields',
+ 'Magento_Braintree/js/validator',
+ 'Magento_Ui/js/model/messageList',
+ 'mage/translate',
+ 'Magento_Checkout/js/model/full-screen-loader',
+ 'Magento_Checkout/js/action/set-payment-information',
+ 'Magento_Checkout/js/model/payment/additional-validators'
+], function (
+ $,
+ Component,
+ validator,
+ messageList,
+ $t,
+ fullScreenLoader,
+ setPaymentInformationAction,
+ additionalValidators
+) {
+ 'use strict';
+
+ return Component.extend({
+ defaults: {
+ template: 'Magento_Braintree/payment/multishipping/form'
+ },
+
+ /**
+ * Get list of available CC types
+ *
+ * @returns {Object}
+ */
+ getCcAvailableTypes: function () {
+ var availableTypes = validator.getAvailableCardTypes(),
+ billingCountryId;
+
+ billingCountryId = $('#multishipping_billing_country_id').val();
+
+ if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) {
+ return validator.collectTypes(
+ availableTypes, validator.getCountrySpecificCardTypes(billingCountryId)
+ );
+ }
+
+ return availableTypes;
+ },
+
+ /**
+ * @override
+ */
+ placeOrder: function () {
+ var self = this;
+
+ this.validatorManager.validate(self, function () {
+ return self.setPaymentInformation();
+ });
+ },
+
+ /**
+ * @override
+ */
+ setPaymentInformation: function () {
+ if (additionalValidators.validate()) {
+
+ fullScreenLoader.startLoader();
+
+ $.when(
+ setPaymentInformationAction(
+ this.messageContainer,
+ this.getData()
+ )
+ ).done(this.done.bind(this))
+ .fail(this.fail.bind(this));
+ }
+ },
+
+ /**
+ * {Function}
+ */
+ fail: function () {
+ fullScreenLoader.stopLoader();
+
+ return this;
+ },
+
+ /**
+ * {Function}
+ */
+ done: function () {
+ fullScreenLoader.stopLoader();
+ $('#multishipping-billing-form').submit();
+
+ return this;
+ }
+ });
+});
diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js
new file mode 100644
index 0000000000000..6702e58d1214b
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js
@@ -0,0 +1,143 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*browser:true*/
+/*global define*/
+define([
+ 'jquery',
+ 'underscore',
+ 'Magento_Braintree/js/view/payment/method-renderer/paypal',
+ 'Magento_Checkout/js/action/set-payment-information',
+ 'Magento_Checkout/js/model/payment/additional-validators',
+ 'Magento_Checkout/js/model/full-screen-loader',
+ 'mage/translate'
+], function (
+ $,
+ _,
+ Component,
+ setPaymentInformationAction,
+ additionalValidators,
+ fullScreenLoader,
+ $t
+) {
+ 'use strict';
+
+ return Component.extend({
+ defaults: {
+ template: 'Magento_Braintree/payment/multishipping/paypal',
+ submitButtonSelector: '#payment-continue span'
+ },
+
+ /**
+ * @override
+ */
+ onActiveChange: function (isActive) {
+ this.updateSubmitButtonTitle(isActive);
+
+ this._super(isActive);
+ },
+
+ /**
+ * @override
+ */
+ beforePlaceOrder: function (data) {
+ this._super(data);
+
+ this.updateSubmitButtonTitle(true);
+ },
+
+ /**
+ * @override
+ */
+ getShippingAddress: function () {
+ return {};
+ },
+
+ /**
+ * @override
+ */
+ getData: function () {
+ var data = this._super();
+
+ data['additional_data']['is_active_payment_token_enabler'] = true;
+
+ return data;
+ },
+
+ /**
+ * @override
+ */
+ isActiveVault: function () {
+ return true;
+ },
+
+ /**
+ * Skipping order review step on checkout with multiple addresses is not allowed.
+ *
+ * @returns {Boolean}
+ */
+ isSkipOrderReview: function () {
+ return false;
+ },
+
+ /**
+ * Checks if payment method nonce is already received.
+ *
+ * @returns {Boolean}
+ */
+ isPaymentMethodNonceReceived: function () {
+ return this.paymentMethodNonce !== null;
+ },
+
+ /**
+ * Updates submit button title on multi-addresses checkout billing form.
+ *
+ * @param {Boolean} isActive
+ */
+ updateSubmitButtonTitle: function (isActive) {
+ var title = this.isPaymentMethodNonceReceived() || !isActive ?
+ $t('Go to Review Your Order') : $t('Continue to PayPal');
+
+ $(this.submitButtonSelector).html(title);
+ },
+
+ /**
+ * @override
+ */
+ placeOrder: function () {
+ if (!this.isPaymentMethodNonceReceived()) {
+ this.payWithPayPal();
+ } else {
+ fullScreenLoader.startLoader();
+
+ $.when(
+ setPaymentInformationAction(
+ this.messageContainer,
+ this.getData()
+ )
+ ).done(this.done.bind(this))
+ .fail(this.fail.bind(this));
+ }
+ },
+
+ /**
+ * {Function}
+ */
+ fail: function () {
+ fullScreenLoader.stopLoader();
+
+ return this;
+ },
+
+ /**
+ * {Function}
+ */
+ done: function () {
+ fullScreenLoader.stopLoader();
+ $('#multishipping-billing-form').submit();
+
+ return this;
+ }
+ });
+});
diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html
new file mode 100644
index 0000000000000..964e15df166d3
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html
@@ -0,0 +1,106 @@
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html
new file mode 100644
index 0000000000000..722989e41f98f
--- /dev/null
+++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml
new file mode 100644
index 0000000000000..07329e2659876
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AdminLoginWithCaptchaActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaNotVisibleOnCustomerLoginFormActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaNotVisibleOnCustomerLoginFormActionGroup.xml
new file mode 100644
index 0000000000000..a371f177e3552
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaNotVisibleOnCustomerLoginFormActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnAdminLoginFormActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnAdminLoginFormActionGroup.xml
new file mode 100644
index 0000000000000..aa02588000d2b
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnAdminLoginFormActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnContactUsFormActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnContactUsFormActionGroup.xml
new file mode 100644
index 0000000000000..d800c65cabb60
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnContactUsFormActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountCreatePageActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountCreatePageActionGroup.xml
new file mode 100644
index 0000000000000..6c09d1d49381f
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountCreatePageActionGroup.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountInfoActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountInfoActionGroup.xml
new file mode 100644
index 0000000000000..c68ffbfb5be4b
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerAccountInfoActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerLoginFormActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerLoginFormActionGroup.xml
new file mode 100644
index 0000000000000..5616b099c026d
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/AssertCaptchaVisibleOnCustomerLoginFormActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml
index beb2c2bffa135..f3b6eb1d9af84 100644
--- a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/CaptchaFormsDisplayingActionGroup.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailWithCaptchaActionGroup.xml
new file mode 100644
index 0000000000000..8aff3d5482f2c
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailWithCaptchaActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillContactUsFormWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillContactUsFormWithCaptchaActionGroup.xml
new file mode 100644
index 0000000000000..3546fa2e57a33
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillContactUsFormWithCaptchaActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormWithCaptchaActionGroup.xml
new file mode 100644
index 0000000000000..d67ebc1a00768
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormWithCaptchaActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithCaptchaActionGroup.xml b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithCaptchaActionGroup.xml
new file mode 100644
index 0000000000000..5ad727a8fe99d
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithCaptchaActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaConfigData.xml b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaConfigData.xml
new file mode 100644
index 0000000000000..90f48c320f2ac
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaConfigData.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+ customer/captcha/enable
+ 0
+ Yes
+ 1
+
+
+ customer/captcha/enable
+ 0
+ No
+ 0
+
+
+ customer/captcha/forms
+ 0
+ Create user
+ user_create
+
+
+ customer/captcha/forms
+ 0
+ Contact Us
+ contact_us
+
+
+
+ customer/captcha/forms
+ 0
+ Login
+ user_login
+
+
+ customer/captcha/forms
+ 0
+ Change password
+ user_edit
+
+
+
+ customer/captcha/forms
+ 0
+ Forgot password
+ user_forgotpassword
+
+
+ customer/captcha/mode
+ 0
+ Always
+ always
+
+
+
+ customer/captcha/mode
+ 0
+ After number of attempts to login
+ after_fail
+
+
+ customer/captcha/length
+ admin
+ 1
+ 3
+ 3
+
+
+ customer/captcha/symbols
+ admin
+ 1
+ 1
+ 1
+
+
+
+ customer/captcha/length
+ admin
+ 1
+ 4-5
+ 4-5
+
+
+
+ customer/captcha/symbols
+ admin
+ 1
+ ABCDEFGHJKMnpqrstuvwxyz23456789
+ ABCDEFGHJKMnpqrstuvwxyz23456789
+
+
+
+ admin/captcha/enable
+ 0
+ Yes
+ 1
+
+
+ admin/captcha/enable
+ 0
+ No
+ 0
+
+
+ admin/captcha/length
+ admin
+ 1
+ 3
+ 3
+
+
+ admin/captcha/symbols
+ admin
+ 1
+ 1
+ 1
+
+
+
+ admin/captcha/length
+ admin
+ 1
+ 4-5
+ 4-5
+
+
+
+ admin/captcha/symbols
+ admin
+ 1
+ ABCDEFGHJKMnpqrstuvwxyz23456789
+ ABCDEFGHJKMnpqrstuvwxyz23456789
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaData.xml b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaData.xml
new file mode 100644
index 0000000000000..d8fb206b8111c
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaData.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ WrongCAPTCHA
+
+
+
+
+ 111
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml
index 9db8110c0f64b..57a09219fe4db 100644
--- a/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml
+++ b/app/code/Magento/Captcha/Test/Mftf/Data/CaptchaFormsDisplayingData.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
Create user
Login
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/AdminLoginFormSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/AdminLoginFormSection.xml
new file mode 100644
index 0000000000000..2bcc6fc542d82
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/AdminLoginFormSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsCaptchaSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsCaptchaSection.xml
new file mode 100644
index 0000000000000..f587812576ff1
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsCaptchaSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsFormSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsFormSection.xml
new file mode 100644
index 0000000000000..60cf961ba7e8c
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontContactUsFormSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
new file mode 100644
index 0000000000000..a273c8d4abd28
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml
new file mode 100644
index 0000000000000..f48e6124cb214
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
index 7a0557c4a2744..54aa36d1ca267 100644
--- a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInPopupFormSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInPopupFormSection.xml
new file mode 100644
index 0000000000000..7a0557c4a2744
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Section/StorefrontCustomerSignInPopupFormSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml
new file mode 100644
index 0000000000000..e5ee55910df65
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/AdminLoginWithCaptchaTest.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaEditCustomerEmailTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaEditCustomerEmailTest.xml
new file mode 100644
index 0000000000000..54237087227d8
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaEditCustomerEmailTest.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnContactUsTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnContactUsTest.xml
new file mode 100644
index 0000000000000..0c6a3f31c1df2
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnContactUsTest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnCustomerLoginTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnCustomerLoginTest.xml
new file mode 100644
index 0000000000000..5a1be68d3f251
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnCustomerLoginTest.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaRegisterNewCustomerTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaRegisterNewCustomerTest.xml
new file mode 100644
index 0000000000000..2c331f958e467
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaRegisterNewCustomerTest.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml
new file mode 100644
index 0000000000000..36d7989b9acc1
--- /dev/null
+++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
index a67f55235b6df..83ec501592489 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
@@ -71,7 +71,7 @@ public function __construct(
}
/**
- * @return void
+ * @inheritdoc
*/
protected function _construct()
{
@@ -80,7 +80,7 @@ protected function _construct()
}
/**
- * @return $this
+ * @inheritdoc
*/
protected function _prepareLayout()
{
@@ -182,6 +182,8 @@ public function getSuggestedCategoriesJson($namePart)
}
/**
+ * Get add root button html
+ *
* @return string
*/
public function getAddRootButtonHtml()
@@ -190,6 +192,8 @@ public function getAddRootButtonHtml()
}
/**
+ * Get add sub button html
+ *
* @return string
*/
public function getAddSubButtonHtml()
@@ -198,6 +202,8 @@ public function getAddSubButtonHtml()
}
/**
+ * Get expand button html
+ *
* @return string
*/
public function getExpandButtonHtml()
@@ -206,6 +212,8 @@ public function getExpandButtonHtml()
}
/**
+ * Get collapse button html
+ *
* @return string
*/
public function getCollapseButtonHtml()
@@ -214,6 +222,8 @@ public function getCollapseButtonHtml()
}
/**
+ * Get store switcher
+ *
* @return string
*/
public function getStoreSwitcherHtml()
@@ -222,6 +232,8 @@ public function getStoreSwitcherHtml()
}
/**
+ * Get loader tree url
+ *
* @param bool|null $expanded
* @return string
*/
@@ -235,6 +247,8 @@ public function getLoadTreeUrl($expanded = null)
}
/**
+ * Get nodes url
+ *
* @return string
*/
public function getNodesUrl()
@@ -243,6 +257,8 @@ public function getNodesUrl()
}
/**
+ * Get switcher tree url
+ *
* @return string
*/
public function getSwitchTreeUrl()
@@ -254,6 +270,8 @@ public function getSwitchTreeUrl()
}
/**
+ * Get is was expanded
+ *
* @return bool
* @SuppressWarnings(PHPMD.BooleanGetMethodName)
*/
@@ -263,6 +281,8 @@ public function getIsWasExpanded()
}
/**
+ * Get move url
+ *
* @return string
*/
public function getMoveUrl()
@@ -271,6 +291,8 @@ public function getMoveUrl()
}
/**
+ * Get tree
+ *
* @param mixed|null $parenNodeCategory
* @return array
*/
@@ -282,6 +304,8 @@ public function getTree($parenNodeCategory = null)
}
/**
+ * Get tree json
+ *
* @param mixed|null $parenNodeCategory
* @return string
*/
@@ -367,7 +391,7 @@ protected function _getNodeJson($node, $level = 0)
}
}
- if ($isParent || $node->getLevel() < 2) {
+ if ($isParent || $node->getLevel() < 1) {
$item['expanded'] = true;
}
@@ -390,6 +414,8 @@ public function buildNodeName($node)
}
/**
+ * Is category movable
+ *
* @param Node|array $node
* @return bool
*/
@@ -403,6 +429,8 @@ protected function _isCategoryMoveable($node)
}
/**
+ * Is parent selected category
+ *
* @param Node|array $node
* @return bool
*/
diff --git a/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php
new file mode 100644
index 0000000000000..0384c9cd9acce
--- /dev/null
+++ b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php
@@ -0,0 +1,156 @@
+gallery = $gallery;
+ $this->jsonSerializer = $jsonSerializer;
+ parent::__construct($context, $arrayUtils, $data);
+ }
+
+ /**
+ * Retrieve gallery options in JSON format
+ *
+ * @return string
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ * @SuppressWarnings(PHPMD.NPathComplexity)
+ * @SuppressWarnings(PHPMD.ElseExpression)
+ */
+ public function getOptionsJson()
+ {
+ $optionItems = null;
+
+ //Special case for gallery/nav which can be the string "thumbs/false/dots"
+ if (is_bool($this->getVar("gallery/nav"))) {
+ $optionItems['nav'] = $this->getVar("gallery/nav") ? 'true' : 'false';
+ } else {
+ $optionItems['nav'] = $this->escapeHtml($this->getVar("gallery/nav"));
+ }
+
+ $optionItems['loop'] = $this->getVar("gallery/loop");
+ $optionItems['keyboard'] = $this->getVar("gallery/keyboard");
+ $optionItems['arrows'] = $this->getVar("gallery/arrows");
+ $optionItems['allowfullscreen'] = $this->getVar("gallery/allowfullscreen");
+ $optionItems['showCaption'] = $this->getVar("gallery/caption");
+ $optionItems['width'] = (int)$this->escapeHtml(
+ $this->gallery->getImageAttribute('product_page_image_medium', 'width')
+ );
+ $optionItems['thumbwidth'] = (int)$this->escapeHtml(
+ $this->gallery->getImageAttribute('product_page_image_small', 'width')
+ );
+
+ if ($this->gallery->getImageAttribute('product_page_image_small', 'height') ||
+ $this->gallery->getImageAttribute('product_page_image_small', 'width')) {
+ $optionItems['thumbheight'] = (int)$this->escapeHtml(
+ $this->gallery->getImageAttribute('product_page_image_small', 'height') ?:
+ $this->gallery->getImageAttribute('product_page_image_small', 'width')
+ );
+ }
+
+ if ($this->gallery->getImageAttribute('product_page_image_medium', 'height') ||
+ $this->gallery->getImageAttribute('product_page_image_medium', 'width')) {
+ $optionItems['height'] = (int)$this->escapeHtml(
+ $this->gallery->getImageAttribute('product_page_image_medium', 'height') ?:
+ $this->gallery->getImageAttribute('product_page_image_medium', 'width')
+ );
+ }
+
+ if ($this->getVar("gallery/transition/duration")) {
+ $optionItems['transitionduration'] =
+ (int)$this->escapeHtml($this->getVar("gallery/transition/duration"));
+ }
+
+ $optionItems['transition'] = $this->escapeHtml($this->getVar("gallery/transition/effect"));
+ $optionItems['navarrows'] = $this->getVar("gallery/navarrows");
+ $optionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/navtype"));
+ $optionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/navdir"));
+
+ if ($this->getVar("gallery/thumbmargin")) {
+ $optionItems['thumbmargin'] = (int)$this->escapeHtml($this->getVar("gallery/thumbmargin"));
+ }
+
+ return $this->jsonSerializer->serialize($optionItems);
+ }
+
+ /**
+ * Retrieve gallery fullscreen options in JSON format
+ *
+ * @return string
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ * @SuppressWarnings(PHPMD.NPathComplexity)
+ * @SuppressWarnings(PHPMD.ElseExpression)
+ */
+ public function getFSOptionsJson()
+ {
+ $fsOptionItems = null;
+
+ //Special case for gallery/nav which can be the string "thumbs/false/dots"
+ if (is_bool($this->getVar("gallery/fullscreen/nav"))) {
+ $fsOptionItems['nav'] = $this->getVar("gallery/fullscreen/nav") ? 'true' : 'false';
+ } else {
+ $fsOptionItems['nav'] = $this->escapeHtml($this->getVar("gallery/fullscreen/nav"));
+ }
+
+ $fsOptionItems['loop'] = $this->getVar("gallery/fullscreen/loop");
+ $fsOptionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navdir"));
+ $fsOptionItems['navarrows'] = $this->getVar("gallery/fullscreen/navarrows");
+ $fsOptionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navtype"));
+ $fsOptionItems['arrows'] = $this->getVar("gallery/fullscreen/arrows");
+ $fsOptionItems['showCaption'] = $this->getVar("gallery/fullscreen/caption");
+
+ if ($this->getVar("gallery/fullscreen/transition/duration")) {
+ $fsOptionItems['transitionduration'] = (int)$this->escapeHtml(
+ $this->getVar("gallery/fullscreen/transition/duration")
+ );
+ }
+
+ $fsOptionItems['transition'] = $this->escapeHtml($this->getVar("gallery/fullscreen/transition/effect"));
+
+ if ($this->getVar("gallery/fullscreen/keyboard")) {
+ $fsOptionItems['keyboard'] = $this->getVar("gallery/fullscreen/keyboard");
+ }
+
+ if ($this->getVar("gallery/fullscreen/thumbmargin")) {
+ $fsOptionItems['thumbmargin'] =
+ (int)$this->escapeHtml($this->getVar("gallery/fullscreen/thumbmargin"));
+ }
+
+ return $this->jsonSerializer->serialize($fsOptionItems);
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php
index 0941aa2478935..9cb6cda4d0a09 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Catalog\Model\Product\Option;
use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface as OptionRepository;
@@ -58,11 +60,26 @@ public function execute($entity, $arguments = [])
}
}
if ($options) {
- foreach ($options as $option) {
- $this->optionRepository->save($option);
- }
+ $this->processOptionsSaving($options, (bool)$entity->dataHasChangedFor('sku'), (string)$entity->getSku());
}
return $entity;
}
+
+ /**
+ * Save custom options
+ *
+ * @param array $options
+ * @param bool $hasChangedSku
+ * @param string $newSku
+ */
+ private function processOptionsSaving(array $options, bool $hasChangedSku, string $newSku)
+ {
+ foreach ($options as $option) {
+ if ($hasChangedSku && $option->hasData('product_sku')) {
+ $option->setProductSku($newSku);
+ }
+ $this->optionRepository->save($option);
+ }
+ }
}
diff --git a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php
index a597b8fddda9f..ed9f89efc6891 100644
--- a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php
+++ b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php
@@ -3,9 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Catalog\Observer;
use Magento\Framework\Event\ObserverInterface;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
/**
* Set value for Special Price start date
@@ -13,21 +16,20 @@
class SetSpecialPriceStartDate implements ObserverInterface
{
/**
- * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
+ * @var TimezoneInterface
*/
private $localeDate;
/**
- * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
- * @codeCoverageIgnore
+ * @param TimezoneInterface $localeDate
*/
- public function __construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate)
+ public function __construct(TimezoneInterface $localeDate)
{
$this->localeDate = $localeDate;
}
/**
- * Set the current date to Special Price From attribute if it empty
+ * Set the current date to Special Price From attribute if it's empty.
*
* @param \Magento\Framework\Event\Observer $observer
* @return $this
@@ -36,8 +38,8 @@ public function execute(\Magento\Framework\Event\Observer $observer)
{
/** @var $product \Magento\Catalog\Model\Product */
$product = $observer->getEvent()->getProduct();
- if ($product->getSpecialPrice() && !$product->getSpecialFromDate()) {
- $product->setData('special_from_date', $this->localeDate->date());
+ if ($product->getSpecialPrice() && ! $product->getSpecialFromDate()) {
+ $product->setData('special_from_date', $this->localeDate->date()->setTime(0, 0));
}
return $this;
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml
index 2d966dde64c4a..b914d5e20712d 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml
@@ -52,4 +52,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml
index 8a26b6babdbbc..d09880f14afbf 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ConstData.xml
@@ -13,4 +13,8 @@
1
2
+
+ "Pursuit Lumaflex™ Tone Band"
+ "x™"
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
index 383797933074e..ba4a623e35def 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
@@ -35,6 +35,10 @@
EavStockItem
CustomAttributeCategoryIds
+
+ Pursuit Lumaflex™ Tone Band
+ x™
+
100
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
index 14e714cb2b6b7..fba28b3feaff1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml
@@ -16,5 +16,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml
index fc78c25ec49fa..352d219351fb8 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml
@@ -45,5 +45,15 @@
+
-
\ No newline at end of file
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
index 02bdbac313076..07dd26381fe08 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
@@ -17,6 +17,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml
index c6bad0efb3ca7..292b2d7008bc1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontNavigationSection.xml
@@ -12,5 +12,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml
index 3027416ee520b..bcfab6ccfdf1f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml
@@ -34,7 +34,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml
new file mode 100644
index 0000000000000..f3ec225540c75
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml
new file mode 100644
index 0000000000000..79ff7bcade77b
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml
index 060720ab007eb..8316f54c15a52 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml
@@ -65,6 +65,8 @@
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml
index 3086f4398e08d..51af9d78dfd46 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml
index 52022f32fd8ec..d895993217e32 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml
@@ -262,10 +262,10 @@
-
-
-
-
+
+
+
+
@@ -333,8 +333,8 @@
-
-
+
+
@@ -417,9 +417,9 @@
-
-
-
-
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php
new file mode 100644
index 0000000000000..7ed8b13fce750
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php
@@ -0,0 +1,223 @@
+escaper = $objectManager->getObject(Escaper::class);
+ $this->configView = $this->createMock(View::class);
+
+ $this->viewConfig = $this->createConfiguredMock(
+ Config::class,
+ [
+ 'getViewConfig' => $this->configView
+ ]
+ );
+
+ $this->context = $this->createConfiguredMock(
+ Context::class,
+ [
+ 'getEscaper' => $this->escaper,
+ 'getViewConfig' => $this->viewConfig
+ ]
+ );
+
+ $this->gallery = $this->createMock(Gallery::class);
+
+ $this->jsonSerializer = $objectManager->getObject(
+ Json::class
+ );
+
+ $this->model = $objectManager->getObject(GalleryOptions::class, [
+ 'context' => $this->context,
+ 'jsonSerializer' => $this->jsonSerializer,
+ 'gallery' => $this->gallery
+ ]);
+ }
+
+ public function testGetOptionsJson()
+ {
+ $configMap = [
+ ['Magento_Catalog', 'gallery/nav', 'thumbs'],
+ ['Magento_Catalog', 'gallery/loop', false],
+ ['Magento_Catalog', 'gallery/keyboard', true],
+ ['Magento_Catalog', 'gallery/arrows', true],
+ ['Magento_Catalog', 'gallery/caption', false],
+ ['Magento_Catalog', 'gallery/allowfullscreen', true],
+ ['Magento_Catalog', 'gallery/navdir', 'horizontal'],
+ ['Magento_Catalog', 'gallery/navarrows', true],
+ ['Magento_Catalog', 'gallery/navtype', 'slides'],
+ ['Magento_Catalog', 'gallery/thumbmargin', '5'],
+ ['Magento_Catalog', 'gallery/transition/effect', 'slide'],
+ ['Magento_Catalog', 'gallery/transition/duration', '500'],
+ ];
+
+ $imageAttributesMap = [
+ ['product_page_image_medium','height',null, 100],
+ ['product_page_image_medium','width',null, 200],
+ ['product_page_image_small','height',null, 300],
+ ['product_page_image_small','width',null, 400]
+ ];
+
+ $this->configView->expects($this->any())
+ ->method('getVarValue')
+ ->will($this->returnValueMap($configMap));
+ $this->gallery->expects($this->any())
+ ->method('getImageAttribute')
+ ->will($this->returnValueMap($imageAttributesMap));
+
+ $json = $this->model->getOptionsJson();
+
+ $decodedJson = $this->jsonSerializer->unserialize($json);
+
+ $this->assertSame('thumbs', $decodedJson['nav']);
+ $this->assertSame(false, $decodedJson['loop']);
+ $this->assertSame(true, $decodedJson['keyboard']);
+ $this->assertSame(true, $decodedJson['arrows']);
+ $this->assertSame(false, $decodedJson['showCaption']);
+ $this->assertSame(true, $decodedJson['allowfullscreen']);
+ $this->assertSame('horizontal', $decodedJson['navdir']);
+ $this->assertSame(true, $decodedJson['navarrows']);
+ $this->assertSame('slides', $decodedJson['navtype']);
+ $this->assertSame(5, $decodedJson['thumbmargin']);
+ $this->assertSame('slide', $decodedJson['transition']);
+ $this->assertSame(500, $decodedJson['transitionduration']);
+ $this->assertSame(100, $decodedJson['height']);
+ $this->assertSame(200, $decodedJson['width']);
+ $this->assertSame(300, $decodedJson['thumbheight']);
+ $this->assertSame(400, $decodedJson['thumbwidth']);
+ }
+
+ public function testGetFSOptionsJson()
+ {
+ $configMap = [
+ ['Magento_Catalog', 'gallery/fullscreen/nav', false],
+ ['Magento_Catalog', 'gallery/fullscreen/loop', true],
+ ['Magento_Catalog', 'gallery/fullscreen/keyboard', true],
+ ['Magento_Catalog', 'gallery/fullscreen/arrows', false],
+ ['Magento_Catalog', 'gallery/fullscreen/caption', true],
+ ['Magento_Catalog', 'gallery/fullscreen/navdir', 'vertical'],
+ ['Magento_Catalog', 'gallery/fullscreen/navarrows', false],
+ ['Magento_Catalog', 'gallery/fullscreen/navtype', 'thumbs'],
+ ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', '10'],
+ ['Magento_Catalog', 'gallery/fullscreen/transition/effect', 'dissolve'],
+ ['Magento_Catalog', 'gallery/fullscreen/transition/duration', '300']
+ ];
+
+ $this->configView->expects($this->any())
+ ->method('getVarValue')
+ ->will($this->returnValueMap($configMap));
+
+ $json = $this->model->getFSOptionsJson();
+
+ $decodedJson = $this->jsonSerializer->unserialize($json);
+
+ //Note, this tests the special case for nav variable set to false. It
+ //Should not be converted to boolean.
+ $this->assertSame('false', $decodedJson['nav']);
+ $this->assertSame(true, $decodedJson['loop']);
+ $this->assertSame(false, $decodedJson['arrows']);
+ $this->assertSame(true, $decodedJson['keyboard']);
+ $this->assertSame(true, $decodedJson['showCaption']);
+ $this->assertSame('vertical', $decodedJson['navdir']);
+ $this->assertSame(false, $decodedJson['navarrows']);
+ $this->assertSame(10, $decodedJson['thumbmargin']);
+ $this->assertSame('thumbs', $decodedJson['navtype']);
+ $this->assertSame('dissolve', $decodedJson['transition']);
+ $this->assertSame(300, $decodedJson['transitionduration']);
+ }
+
+ public function testGetOptionsJsonOptionals()
+ {
+ $configMap = [
+ ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false],
+ ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false]
+ ];
+
+ $this->configView->expects($this->any())
+ ->method('getVarValue')
+ ->will($this->returnValueMap($configMap));
+
+ $json = $this->model->getOptionsJson();
+
+ $decodedJson = $this->jsonSerializer->unserialize($json);
+
+ $this->assertArrayNotHasKey('thumbmargin', $decodedJson);
+ $this->assertArrayNotHasKey('transitionduration', $decodedJson);
+ }
+
+ public function testGetFSOptionsJsonOptionals()
+ {
+ $configMap = [
+ ['Magento_Catalog', 'gallery/fullscreen/keyboard', false],
+ ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false],
+ ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false]
+ ];
+
+ $this->configView->expects($this->any())
+ ->method('getVarValue')
+ ->will($this->returnValueMap($configMap));
+
+ $json = $this->model->getFSOptionsJson();
+
+ $decodedJson = $this->jsonSerializer->unserialize($json);
+
+ $this->assertArrayNotHasKey('thumbmargin', $decodedJson);
+ $this->assertArrayNotHasKey('keyboard', $decodedJson);
+ $this->assertArrayNotHasKey('transitionduration', $decodedJson);
+ }
+}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
old mode 100755
new mode 100644
index 99f7122efa0a8..8326c3b531892
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
@@ -676,7 +676,7 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC
// TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done
$attributeModel = $this->getAttributeModel($attribute);
if ($attributeModel->usesSource()) {
- $options = $attributeModel->getSource()->getAllOptions();
+ $options = $attributeModel->getSource()->getAllOptions(true, true);
$meta = $this->arrayManager->merge($configPath, $meta, [
'options' => $this->convertOptionsValueToString($options),
]);
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php
index f4334bc25efd8..e5451c8e49847 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php
@@ -5,10 +5,16 @@
*/
namespace Magento\Catalog\Ui\DataProvider\Product;
+use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Eav\Model\Entity\Attribute\AttributeInterface;
+
/**
* Collection which is used for rendering product list in the backend.
*
* Used for product grid and customizes behavior of the default Product collection for grid needs.
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class ProductCollection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
{
@@ -25,4 +31,63 @@ protected function _productLimitationJoinPrice()
$this->_productLimitationFilters->setUsePriceIndex(false);
return $this->_productLimitationPrice(true);
}
+
+ /**
+ * Add attribute filter to collection
+ *
+ * @param AttributeInterface|integer|string|array $attribute
+ * @param null|string|array $condition
+ * @param string $joinType
+ * @return $this
+ * @throws LocalizedException
+ */
+ public function addAttributeToFilter($attribute, $condition = null, $joinType = 'inner')
+ {
+ $storeId = (int)$this->getStoreId();
+ if ($attribute === 'is_saleable'
+ || is_array($attribute)
+ || $storeId !== $this->getDefaultStoreId()
+ ) {
+ return parent::addAttributeToFilter($attribute, $condition, $joinType);
+ }
+
+ if ($attribute instanceof AttributeInterface) {
+ $attributeModel = $attribute;
+ } else {
+ $attributeModel = $this->getEntity()->getAttribute($attribute);
+ if ($attributeModel === false) {
+ throw new LocalizedException(
+ __('Invalid attribute identifier for filter (%1)', get_class($attribute))
+ );
+ }
+ }
+
+ if ($attributeModel->isScopeGlobal() || $attributeModel->getBackend()->isStatic()) {
+ return parent::addAttributeToFilter($attribute, $condition, $joinType);
+ }
+
+ $this->addAttributeToFilterAllStores($attributeModel, $condition);
+
+ return $this;
+ }
+
+ /**
+ * Add attribute to filter by all stores
+ *
+ * @param Attribute $attributeModel
+ * @param array $condition
+ * @return void
+ */
+ private function addAttributeToFilterAllStores(Attribute $attributeModel, array $condition): void
+ {
+ $tableName = $this->getTable($attributeModel->getBackendTable());
+ $entity = $this->getEntity();
+ $fKey = 'e.' . $this->getEntityPkName($entity);
+ $pKey = $tableName . '.' . $this->getEntityPkName($entity);
+ $condition = "({$pKey} = {$fKey}) AND ("
+ . $this->_getConditionSql("{$tableName}.value", $condition)
+ . ')';
+ $selectExistsInAllStores = $this->getConnection()->select()->from($tableName);
+ $this->getSelect()->exists($selectExistsInAllStores, $condition);
+ }
}
diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
index 578281f44c4cf..d689daef4bcab 100644
--- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
+++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml
@@ -132,6 +132,7 @@
true
text
+ ui/grid/cells/html
Name
@@ -154,6 +155,7 @@
text
+ ui/grid/cells/html
SKU
diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml
index 8d3248896b434..4283e3be5a9bf 100644
--- a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml
+++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml
@@ -121,7 +121,11 @@
-
+
+
+ Magento\Catalog\Block\Product\View\GalleryOptions
+
+
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml
index 1bfa30478df8a..a810dc2916152 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml
@@ -45,44 +45,8 @@
"mixins":["magnifier/magnify"],
"magnifierOpts": = /* @escapeNotVerified */ $block->getMagnifier() ?>,
"data": = /* @escapeNotVerified */ $block->getGalleryImagesJson() ?>,
- "options": {
- "nav": "= /* @escapeNotVerified */ $block->getVar("gallery/nav") ?>",
- "loop": = /* @escapeNotVerified */ $block->getVar("gallery/loop") ? 'true' : 'false' ?>,
- "keyboard": = /* @escapeNotVerified */ $block->getVar("gallery/keyboard") ? 'true' : 'false' ?>,
- "arrows": = /* @escapeNotVerified */ $block->getVar("gallery/arrows") ? 'true' : 'false' ?>,
- "allowfullscreen": = /* @escapeNotVerified */ $block->getVar("gallery/allowfullscreen") ? 'true' : 'false' ?>,
- "showCaption": = /* @escapeNotVerified */ $block->getVar("gallery/caption") ? 'true' : 'false' ?>,
- "width": "= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_medium', 'width') ?>",
- "thumbwidth": "= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_small', 'width') ?>",
- getImageAttribute('product_page_image_small', 'height') || $block->getImageAttribute('product_page_image_small', 'width')): ?>
- "thumbheight": getImageAttribute('product_page_image_small', 'height')
- ?: $block->getImageAttribute('product_page_image_small', 'width'); ?>,
-
- getImageAttribute('product_page_image_medium', 'height') || $block->getImageAttribute('product_page_image_medium', 'width')): ?>
- "height": getImageAttribute('product_page_image_medium', 'height')
- ?: $block->getImageAttribute('product_page_image_medium', 'width'); ?>,
-
- getVar("gallery/transition/duration")): ?>
- "transitionduration": = /* @escapeNotVerified */ $block->getVar("gallery/transition/duration") ?>,
-
- "transition": "= /* @escapeNotVerified */ $block->getVar("gallery/transition/effect") ?>",
- "navarrows": = /* @escapeNotVerified */ $block->getVar("gallery/navarrows") ? 'true' : 'false' ?>,
- "navtype": "= /* @escapeNotVerified */ $block->getVar("gallery/navtype") ?>",
- "navdir": "= /* @escapeNotVerified */ $block->getVar("gallery/navdir") ?>"
- },
- "fullscreen": {
- "nav": "= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/nav") ?>",
- "loop": = /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/loop") ? 'true' : 'false' ?>,
- "navdir": "= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navdir") ?>",
- "navarrows": = /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navarrows") ? 'true' : 'false' ?>,
- "navtype": "= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navtype") ?>",
- "arrows": = /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/arrows") ? 'true' : 'false' ?>,
- "showCaption": = /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/caption") ? 'true' : 'false' ?>,
- getVar("gallery/fullscreen/transition/duration")): ?>
- "transitionduration": = /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/transition/duration") ?>,
-
- "transition": "= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/transition/effect") ?>"
- },
+ "options": = /* @noEscape */ $block->getGalleryOptions()->getOptionsJson() ?>,
+ "fullscreen": = /* @noEscape */ $block->getGalleryOptions()->getFSOptionsJson() ?>,
"breakpoints": = /* @escapeNotVerified */ $block->getBreakpoints() ?>
}
}
diff --git a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php
index 31fd5606a9849..6851b05aa56a6 100644
--- a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php
+++ b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php
@@ -119,14 +119,12 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ
$result->setItemIsQtyDecimal($stockItem->getIsQtyDecimal());
if (!$stockItem->getIsQtyDecimal()) {
$result->setHasQtyOptionUpdate(true);
- $qty = (int) $qty;
+ $qty = (int) $qty ?: 1;
/**
* Adding stock data to quote item
*/
$result->setItemQty($qty);
- $qty = $this->getNumber($qty);
- $origQty = (int) $origQty;
- $result->setOrigQty($origQty);
+ $result->setOrigQty((int)$this->getNumber($origQty) ?: 1);
}
if ($stockItem->getMinSaleQty() && $qty < $stockItem->getMinSaleQty()) {
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml
index 5b3679bed77e0..393e25e474f12 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml
@@ -46,7 +46,9 @@
-
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml
index 123d25f92b6b7..9ee9d27de477a 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml
@@ -50,6 +50,8 @@
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index 46f10608bc95e..a849d964eaed5 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -1232,6 +1232,8 @@ public function isPossibleBuyFromList($product)
/**
* Returns array of sub-products for specified configurable product
*
+ * $requiredAttributeIds - one dimensional array, if provided
+ *
* Result array contains all children for specified configurable product
*
* @param \Magento\Catalog\Model\Product $product
@@ -1245,9 +1247,12 @@ public function getUsedProducts($product, $requiredAttributeIds = null)
__METHOD__,
$product->getData($metadata->getLinkField()),
$product->getStoreId(),
- $this->getCustomerSession()->getCustomerGroupId(),
- $requiredAttributeIds
+ $this->getCustomerSession()->getCustomerGroupId()
];
+ if ($requiredAttributeIds !== null) {
+ sort($requiredAttributeIds);
+ $keyParts[] = implode('', $requiredAttributeIds);
+ }
$cacheKey = $this->getUsedProductsCacheKey($keyParts);
return $this->loadUsedProducts($product, $cacheKey);
}
diff --git a/app/code/Magento/Contact/Test/Mftf/ActionGroup/AssertMessageContactUsFormActionGroup.xml b/app/code/Magento/Contact/Test/Mftf/ActionGroup/AssertMessageContactUsFormActionGroup.xml
new file mode 100644
index 0000000000000..eec2194825166
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/ActionGroup/AssertMessageContactUsFormActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontFillContactUsFormActionGroup.xml b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontFillContactUsFormActionGroup.xml
new file mode 100644
index 0000000000000..df4964ea0423d
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontFillContactUsFormActionGroup.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontOpenContactUsPageActionGroup.xml b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontOpenContactUsPageActionGroup.xml
new file mode 100644
index 0000000000000..d333d5d998960
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontOpenContactUsPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontSubmitContactUsFormActionGroup.xml b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontSubmitContactUsFormActionGroup.xml
new file mode 100644
index 0000000000000..f3fe34f20c319
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/ActionGroup/StorefrontSubmitContactUsFormActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/Data/ContactUsData.xml b/app/code/Magento/Contact/Test/Mftf/Data/ContactUsData.xml
new file mode 100644
index 0000000000000..eadf760776c58
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/Data/ContactUsData.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Lorem ipsum dolor sit amet, ne enim aliquando eam, oblique deserunt no usu. Unique:
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/Page/StorefrontContactUsPage.xml b/app/code/Magento/Contact/Test/Mftf/Page/StorefrontContactUsPage.xml
new file mode 100644
index 0000000000000..5e793b2338507
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/Page/StorefrontContactUsPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsFormSection.xml b/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsFormSection.xml
new file mode 100644
index 0000000000000..fdaddf33f5170
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsFormSection.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsMessagesSection.xml b/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsMessagesSection.xml
new file mode 100644
index 0000000000000..0970f1f8f6b20
--- /dev/null
+++ b/app/code/Magento/Contact/Test/Mftf/Section/StorefrontContactUsMessagesSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less
index 0aaec05aa2afe..d79806eecbe9b 100644
--- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less
+++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less
@@ -21,6 +21,16 @@
}
}
+//
+// Desktop
+// _____________________________________________
+
+.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) {
+ .contact-index-index .column:not(.sidebar-additional) .form.contact {
+ min-width: 600px;
+ }
+}
+
// Mobile
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.contact-index-index {
diff --git a/app/code/Magento/Customer/Controller/Address/File/Upload.php b/app/code/Magento/Customer/Controller/Address/File/Upload.php
new file mode 100644
index 0000000000000..adb4c7abd1729
--- /dev/null
+++ b/app/code/Magento/Customer/Controller/Address/File/Upload.php
@@ -0,0 +1,146 @@
+fileUploaderFactory = $fileUploaderFactory;
+ $this->addressMetadataService = $addressMetadataService;
+ $this->logger = $logger;
+ $this->fileProcessorFactory = $fileProcessorFactory;
+ parent::__construct($context);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute()
+ {
+ try {
+ $requestedFiles = $this->getRequest()->getFiles('custom_attributes');
+ if (empty($requestedFiles)) {
+ $result = $this->processError(__('No files for upload.'));
+ } else {
+ $attributeCode = key($requestedFiles);
+ $attributeMetadata = $this->addressMetadataService->getAttributeMetadata($attributeCode);
+
+ /** @var FileUploader $fileUploader */
+ $fileUploader = $this->fileUploaderFactory->create([
+ 'attributeMetadata' => $attributeMetadata,
+ 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS,
+ 'scope' => CustomAttributesDataInterface::CUSTOM_ATTRIBUTES,
+ ]);
+
+ $errors = $fileUploader->validate();
+ if (true !== $errors) {
+ $errorMessage = implode('', $errors);
+ $result = $this->processError(($errorMessage));
+ } else {
+ $result = $fileUploader->upload();
+ $this->moveTmpFileToSuitableFolder($result);
+ }
+ }
+ } catch (LocalizedException $e) {
+ $result = $this->processError($e->getMessage(), $e->getCode());
+ } catch (\Exception $e) {
+ $this->logger->critical($e);
+ $result = $this->processError($e->getMessage(), $e->getCode());
+ }
+
+ /** @var \Magento\Framework\Controller\Result\Json $resultJson */
+ $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON);
+ $resultJson->setData($result);
+ return $resultJson;
+ }
+
+ /**
+ * Move file from temporary folder to the 'customer_address' media folder
+ *
+ * @param array $fileInfo
+ * @throws LocalizedException
+ */
+ private function moveTmpFileToSuitableFolder(&$fileInfo)
+ {
+ $fileName = $fileInfo['file'];
+ $fileProcessor = $this->fileProcessorFactory
+ ->create(['entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS]);
+
+ $newFilePath = $fileProcessor->moveTemporaryFile($fileName);
+ $fileInfo['file'] = $newFilePath;
+ $fileInfo['url'] = $fileProcessor->getViewUrl(
+ $newFilePath,
+ 'file'
+ );
+ }
+
+ /**
+ * Prepare result array for errors
+ *
+ * @param string $message
+ * @param int $code
+ * @return array
+ */
+ private function processError($message, $code = 0)
+ {
+ $result = [
+ 'error' => $message,
+ 'errorcode' => $code,
+ ];
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Customer/Model/Address.php b/app/code/Magento/Customer/Model/Address.php
index e9aa2839095d5..ea9b103f42273 100644
--- a/app/code/Magento/Customer/Model/Address.php
+++ b/app/code/Magento/Customer/Model/Address.php
@@ -122,7 +122,7 @@ public function __construct(
}
/**
- * Init model
+ * Initialize address model
*
* @return void
*/
@@ -167,7 +167,14 @@ public function updateData(AddressInterface $address)
}
/**
- * @inheritdoc
+ * Create address data object based on current address model.
+ *
+ * @param int|null $defaultBillingAddressId
+ * @param int|null $defaultShippingAddressId
+ * @return AddressInterface
+ * Use Api/Data/AddressInterface as a result of service operations. Don't rely on the model to provide
+ * the instance of Api/Data/AddressInterface
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function getDataModel($defaultBillingAddressId = null, $defaultShippingAddressId = null)
{
@@ -257,7 +264,7 @@ public function getDefaultAttributeCodes()
}
/**
- * Clone object handler
+ * Clone address
*
* @return void
*/
@@ -356,7 +363,11 @@ public function reindex()
}
/**
- * @inheritdoc
+ * Get a list of custom attribute codes.
+ *
+ * By default, entity can be extended only using extension attributes functionality.
+ *
+ * @return string[]
* @since 100.0.6
*/
protected function getCustomAttributesCodes()
diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php
index b00f393f53734..1287dbe5df708 100644
--- a/app/code/Magento/Customer/Model/Customer.php
+++ b/app/code/Magento/Customer/Model/Customer.php
@@ -1054,17 +1054,6 @@ public function resetErrors()
return $this;
}
- /**
- * Prepare customer for delete
- *
- * @return $this
- */
- public function beforeDelete()
- {
- //TODO : Revisit and figure handling permissions in MAGETWO-11084 Implementation: Service Context Provider
- return parent::beforeDelete();
- }
-
/**
* Processing object after save data
*
@@ -1305,7 +1294,7 @@ public function getResetPasswordLinkExpirationPeriod()
}
/**
- * Create address instance
+ * Create Address from Factory
*
* @return Address
*/
@@ -1315,7 +1304,7 @@ protected function _createAddressInstance()
}
/**
- * Create address collection instance
+ * Create Address Collection from Factory
*
* @return \Magento\Customer\Model\ResourceModel\Address\Collection
*/
@@ -1325,7 +1314,7 @@ protected function _createAddressCollection()
}
/**
- * Returns templates types
+ * Get Template Types
*
* @return array
*/
diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php
index 6a8472758c169..c16faea284296 100644
--- a/app/code/Magento/Customer/Model/FileProcessor.php
+++ b/app/code/Magento/Customer/Model/FileProcessor.php
@@ -3,8 +3,13 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Customer\Model;
+/**
+ * Processor class for work with uploaded files
+ */
class FileProcessor
{
/**
@@ -232,7 +237,7 @@ public function moveTemporaryFile($fileName)
);
}
- $fileName = $dispersionPath . '/' . $fileName;
+ $fileName = $dispersionPath . '/' . $destinationFileName;
return $fileName;
}
diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
index b25838e245488..ddc0576cb0dae 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
@@ -171,7 +171,14 @@ public function __construct(
}
/**
- * @inheritdoc
+ * Create or update a customer.
+ *
+ * @param \Magento\Customer\Api\Data\CustomerInterface $customer
+ * @param string $passwordHash
+ * @return \Magento\Customer\Api\Data\CustomerInterface
+ * @throws \Magento\Framework\Exception\InputException If bad input is provided
+ * @throws \Magento\Framework\Exception\State\InputMismatchException If the provided email is already used
+ * @throws \Magento\Framework\Exception\LocalizedException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
@@ -304,7 +311,13 @@ private function populateCustomerWithSecureData($customerModel, $passwordHash =
}
/**
- * @inheritdoc
+ * Retrieve customer.
+ *
+ * @param string $email
+ * @param int|null $websiteId
+ * @return \Magento\Customer\Api\Data\CustomerInterface
+ * @throws \Magento\Framework\Exception\NoSuchEntityException If customer with the specified email does not exist.
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function get($email, $websiteId = null)
{
@@ -313,7 +326,12 @@ public function get($email, $websiteId = null)
}
/**
- * @inheritdoc
+ * Get customer by Customer ID.
+ *
+ * @param int $customerId
+ * @return \Magento\Customer\Api\Data\CustomerInterface
+ * @throws \Magento\Framework\Exception\NoSuchEntityException If customer with the specified ID does not exist.
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function getById($customerId)
{
@@ -322,7 +340,15 @@ public function getById($customerId)
}
/**
- * @inheritdoc
+ * Retrieve customers which match a specified criteria.
+ *
+ * This call returns an array of objects, but detailed information about each object’s attributes might not be
+ * included. See http://devdocs.magento.com/codelinks/attributes.html#CustomerRepositoryInterface to determine
+ * which call to use to get detailed information about all attributes for an object.
+ *
+ * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
+ * @return \Magento\Customer\Api\Data\CustomerSearchResultsInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function getList(SearchCriteriaInterface $searchCriteria)
{
@@ -362,7 +388,11 @@ public function getList(SearchCriteriaInterface $searchCriteria)
}
/**
- * @inheritdoc
+ * Delete customer.
+ *
+ * @param \Magento\Customer\Api\Data\CustomerInterface $customer
+ * @return bool true on success
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function delete(CustomerInterface $customer)
{
@@ -370,7 +400,12 @@ public function delete(CustomerInterface $customer)
}
/**
- * @inheritdoc
+ * Delete customer by Customer ID.
+ *
+ * @param int $customerId
+ * @return bool true on success
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function deleteById($customerId)
{
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml
new file mode 100644
index 0000000000000..d9da950fe7115
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerResetPasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerResetPasswordActionGroup.xml
new file mode 100644
index 0000000000000..644254443d129
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerResetPasswordActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerChangeAccountInfoActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerChangeAccountInfoActionGroup.xml
new file mode 100644
index 0000000000000..ab48184ed98a8
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerChangeAccountInfoActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountActionGroup.xml
new file mode 100644
index 0000000000000..65c9b025a9c2d
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerLoginActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerLoginActionGroup.xml
new file mode 100644
index 0000000000000..6b88661985873
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerLoginActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml
deleted file mode 100644
index b12858fc1037e..0000000000000
--- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup.xml
new file mode 100644
index 0000000000000..dfb9d1b2c259a
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignOnCustomerLoginFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignOnCustomerLoginFormActionGroup.xml
new file mode 100644
index 0000000000000..9cd52b841fca4
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignOnCustomerLoginFormActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailActionGroup.xml
new file mode 100644
index 0000000000000..844d13aa1fe43
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerChangeEmailActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerResetPasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerResetPasswordActionGroup.xml
new file mode 100644
index 0000000000000..a28593f1b77b7
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerResetPasswordActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormActionGroup.xml
new file mode 100644
index 0000000000000..62d77d4548cce
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerAccountCreationFormActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormActionGroup.xml
new file mode 100644
index 0000000000000..22883ada7c2b1
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml
similarity index 50%
rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml
rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml
index 23a067cd94eea..0ae470d0497ab 100644
--- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountCreatePageActionGroup.xml
@@ -5,10 +5,11 @@
* See COPYING.txt for license details.
*/
-->
+
-
-
-
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountInfoEditPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountInfoEditPageActionGroup.xml
new file mode 100644
index 0000000000000..c1ea2da8a9519
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerAccountInfoEditPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerLoginPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerLoginPageActionGroup.xml
new file mode 100644
index 0000000000000..0a5c72265528a
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontOpenCustomerLoginPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerEditPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerEditPage.xml
new file mode 100644
index 0000000000000..d4cf90dde08ff
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerEditPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
index b819a78002c62..8a633ec5bc271 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
@@ -11,13 +11,14 @@
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml
index 3a4329969ae2b..c00b54b3964da 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml
@@ -10,5 +10,6 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml
index 078021db062cc..a9859cf58751b 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
index f52b379379ad1..7bc057b8be7b7 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
index 4d7572aedc59b..dab298f6b416b 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
@@ -9,7 +9,10 @@
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml
index 12e603bd3748c..8d4be5fda3c79 100644
--- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml
@@ -20,10 +20,11 @@
-
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml
new file mode 100644
index 0000000000000..3121bd0da9d2d
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontResetCustomerPasswordFailedTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Developer/i18n/de_DE.csv b/app/code/Magento/Developer/i18n/de_DE.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/de_DE.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Developer/i18n/es_ES.csv b/app/code/Magento/Developer/i18n/es_ES.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/es_ES.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Developer/i18n/fr_FR.csv b/app/code/Magento/Developer/i18n/fr_FR.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/fr_FR.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Developer/i18n/nl_NL.csv b/app/code/Magento/Developer/i18n/nl_NL.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/nl_NL.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Developer/i18n/pt_BR.csv b/app/code/Magento/Developer/i18n/pt_BR.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/pt_BR.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Developer/i18n/zh_Hans_CN.csv b/app/code/Magento/Developer/i18n/zh_Hans_CN.csv
deleted file mode 100644
index e9c858101b71c..0000000000000
--- a/app/code/Magento/Developer/i18n/zh_Hans_CN.csv
+++ /dev/null
@@ -1,5 +0,0 @@
-"Front-end development workflow","Front-end development workflow"
-"Workflow type","Workflow type"
-"Server side less compilation","Server side less compilation"
-"Client side less compilation","Client side less compilation"
-"Not available in production mode","Not available in production mode"
diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php
index 875d384a20596..56c84593256be 100644
--- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php
+++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/PriceFieldsProvider.php
@@ -72,13 +72,15 @@ public function __construct(
*/
public function getFields(array $productIds, $storeId)
{
+ $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
+
$priceData = $this->dataProvider->getSearchableAttribute('price')
? $this->resourceIndex->getPriceIndexData($productIds, $storeId)
: [];
$fields = [];
foreach ($productIds as $productId) {
- $fields[$productId] = $this->getProductPriceData($productId, $storeId, $priceData);
+ $fields[$productId] = $this->getProductPriceData($productId, $websiteId, $priceData);
}
return $fields;
diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
index 1fc3257ff2c1e..10b29a50a4064 100644
--- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
+++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php
@@ -408,7 +408,9 @@ protected function _saveValidatedBunches()
if ($source->valid()) {
try {
$rowData = $source->current();
- $skuSet[$rowData['sku']] = true;
+ if (array_key_exists('sku', $rowData)) {
+ $skuSet[$rowData['sku']] = true;
+ }
} catch (\InvalidArgumentException $e) {
$this->addRowError($e->getMessage(), $this->_processedRowsCount);
$this->_processedRowsCount++;
@@ -436,7 +438,7 @@ protected function _saveValidatedBunches()
$source->next();
}
}
- $this->_processedEntitiesCount = count($skuSet);
+ $this->_processedEntitiesCount = (count($skuSet)) ? : $this->_processedRowsCount;
return $this;
}
diff --git a/app/code/Magento/Msrp/i18n/de_DE.csv b/app/code/Magento/Msrp/i18n/de_DE.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/de_DE.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Msrp/i18n/es_ES.csv b/app/code/Magento/Msrp/i18n/es_ES.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/es_ES.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Msrp/i18n/fr_FR.csv b/app/code/Magento/Msrp/i18n/fr_FR.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/fr_FR.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Msrp/i18n/nl_NL.csv b/app/code/Magento/Msrp/i18n/nl_NL.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/nl_NL.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Msrp/i18n/pt_BR.csv b/app/code/Magento/Msrp/i18n/pt_BR.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/pt_BR.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Msrp/i18n/zh_Hans_CN.csv b/app/code/Magento/Msrp/i18n/zh_Hans_CN.csv
deleted file mode 100644
index be8e733a4bfd1..0000000000000
--- a/app/code/Magento/Msrp/i18n/zh_Hans_CN.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Enabling MAP by default will hide all product prices on Storefront.","Enabling MAP by default will hide all product prices on Storefront."
-"Warning!","Warning!"
diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml
index da553b7823da9..4354cfb7c1c3e 100644
--- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml
+++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml
@@ -55,6 +55,10 @@
= /* @noEscape */ $block->getCheckoutData()->getAddressHtml($block->getAddress()); ?>
+
@@ -80,35 +84,44 @@
$block->setMethodFormTemplate($code, $methodsForms[$code]);
}
?>
-
- 1) : ?>
-
- checked="checked"
+
+
+ 1) : ?>
+
+ checked="checked"
+
+ class="radio"/>
+
+
- class="radio"/>
-
-
-
-
- = $block->escapeHtml($_method->getTitle()) ?>
-
-
- getChildHtml('payment.method.' . $code)) : ?>
-
- = /* @noEscape */ $html; ?>
-
-
+
+ = $block->escapeHtml($_method->getTitle()) ?>
+
+
+ getChildHtml('payment.method.' . $code)) : ?>
+
+ = /* @noEscape */ $html; ?>
+
+
+
= $block->getChildHtml('payment_methods_after') ?>
diff --git a/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml
new file mode 100644
index 0000000000000..32810ecef20da
--- /dev/null
+++ b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ - Magento_OfflinePayments::multishipping/checkmo_form.phtml
+
+
+
+
+
diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml
new file mode 100644
index 0000000000000..b96918243a7a7
--- /dev/null
+++ b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml
@@ -0,0 +1,28 @@
+
+
diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl
index 8068447e5ca99..c73c4e39170e6 100644
--- a/app/code/Magento/PageCache/etc/varnish4.vcl
+++ b/app/code/Magento/PageCache/etc/varnish4.vcl
@@ -91,10 +91,11 @@ sub vcl_recv {
}
}
- # Remove Google gclid parameters to minimize the cache objects
- set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
- set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
- set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
+ # Remove all marketing get parameters to minimize the cache objects
+ if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") {
+ set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
+ set req.url = regsub(req.url, "[?|&]+$", "");
+ }
# Static files caching
if (req.url ~ "^/(pub/)?(media|static)/") {
diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl
index 6c8414a5cb641..ea586858eff75 100644
--- a/app/code/Magento/PageCache/etc/varnish5.vcl
+++ b/app/code/Magento/PageCache/etc/varnish5.vcl
@@ -92,10 +92,11 @@ sub vcl_recv {
}
}
- # Remove Google gclid parameters to minimize the cache objects
- set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
- set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
- set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
+ # Remove all marketing get parameters to minimize the cache objects
+ if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") {
+ set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
+ set req.url = regsub(req.url, "[?|&]+$", "");
+ }
# Static files caching
if (req.url ~ "^/(pub/)?(media|static)/") {
diff --git a/app/code/Magento/Paypal/README.md b/app/code/Magento/Paypal/README.md
index 8f4453ae0a058..0ed4f2e90291b 100644
--- a/app/code/Magento/Paypal/README.md
+++ b/app/code/Magento/Paypal/README.md
@@ -1,6 +1,6 @@
Module Magento\PayPal implements integration with the PayPal payment system. Namely, it enables the following payment methods:
-*PayPal Express Checkout
-*PayPal Payments Standard
-*PayPal Payments Pro
-*PayPal Credit
-*PayFlow Payment Gateway
+* PayPal Express Checkout
+* PayPal Payments Standard
+* PayPal Payments Pro
+* PayPal Credit
+* PayFlow Payment Gateway
diff --git a/app/code/Magento/ProductVideo/i18n/de_DE.csv b/app/code/Magento/ProductVideo/i18n/de_DE.csv
deleted file mode 100644
index ca24668bb8d16..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/de_DE.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
diff --git a/app/code/Magento/ProductVideo/i18n/es_ES.csv b/app/code/Magento/ProductVideo/i18n/es_ES.csv
deleted file mode 100644
index ca24668bb8d16..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/es_ES.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
diff --git a/app/code/Magento/ProductVideo/i18n/fr_FR.csv b/app/code/Magento/ProductVideo/i18n/fr_FR.csv
deleted file mode 100644
index ca24668bb8d16..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/fr_FR.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
diff --git a/app/code/Magento/ProductVideo/i18n/nl_NL.csv b/app/code/Magento/ProductVideo/i18n/nl_NL.csv
deleted file mode 100644
index 5ad8386573040..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/nl_NL.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
\ No newline at end of file
diff --git a/app/code/Magento/ProductVideo/i18n/pt_BR.csv b/app/code/Magento/ProductVideo/i18n/pt_BR.csv
deleted file mode 100644
index 5ad8386573040..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/pt_BR.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
\ No newline at end of file
diff --git a/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv b/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv
deleted file mode 100644
index 5ad8386573040..0000000000000
--- a/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-"Add video","Add video"
-"New Video","New Video"
-"Product Video","Product Video"
-"YouTube API key","YouTube API key"
-"You have not entered youtube API key. No information about youtube video will be retrieved.","You have not entered youtube API key. No information about youtube video will be retrieved."
-"Url","Url"
-"Preview Image","Preview Image"
-"Get Video Information","Get Video Information"
-"Youtube or Vimeo supported","Youtube or Vimeo supported"
-"Delete image in all store views","Delete image in all store views"
\ No newline at end of file
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php
index 734843e1be09b..582055bc6e13d 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php
@@ -7,12 +7,13 @@
namespace Magento\QuoteGraphQl\Model\Cart;
-use Magento\Customer\Api\Data\AddressInterface as CustomerAddress;
+use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Quote\Model\Quote\Address as QuoteAddress;
use Magento\Quote\Model\Quote\AddressFactory as BaseQuoteAddressFactory;
-use Magento\QuoteGraphQl\Model\Cart\QuoteAddress\Validator;
/**
* Create QuoteAddress
@@ -23,21 +24,22 @@ class QuoteAddressFactory
* @var BaseQuoteAddressFactory
*/
private $quoteAddressFactory;
+
/**
- * @var Validator
+ * @var GetCustomerAddress
*/
- private $quoteAddressValidator;
+ private $getCustomerAddress;
/**
* @param BaseQuoteAddressFactory $quoteAddressFactory
- * @param Validator $quoteAddressValidator
+ * @param GetCustomerAddress $getCustomerAddress
*/
public function __construct(
BaseQuoteAddressFactory $quoteAddressFactory,
- Validator $quoteAddressValidator
+ GetCustomerAddress $getCustomerAddress
) {
$this->quoteAddressFactory = $quoteAddressFactory;
- $this->quoteAddressValidator = $quoteAddressValidator;
+ $this->getCustomerAddress = $getCustomerAddress;
}
/**
@@ -52,28 +54,29 @@ public function createBasedOnInputData(array $addressInput): QuoteAddress
$quoteAddress = $this->quoteAddressFactory->create();
$quoteAddress->addData($addressInput);
- $this->quoteAddressValidator->validate($quoteAddress);
-
return $quoteAddress;
}
/**
- * Create QuoteAddress based on CustomerAddress
+ * Create Quote Address based on Customer Address
*
- * @param CustomerAddress $customerAddress
+ * @param int $customerAddressId
+ * @param int $customerId
* @return QuoteAddress
* @throws GraphQlInputException
+ * @throws GraphQlAuthorizationException
+ * @throws GraphQlNoSuchEntityException
*/
- public function createBasedOnCustomerAddress(CustomerAddress $customerAddress): QuoteAddress
+ public function createBasedOnCustomerAddress(int $customerAddressId, int $customerId): QuoteAddress
{
+ $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, $customerId);
+
$quoteAddress = $this->quoteAddressFactory->create();
try {
$quoteAddress->importCustomerAddressData($customerAddress);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__($e->getMessage()), $e);
}
- $this->quoteAddressValidator->validate($quoteAddress);
-
return $quoteAddress;
}
}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php
index 31524ea023222..c2bac13c07067 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php
@@ -7,8 +7,9 @@
namespace Magento\QuoteGraphQl\Model\Cart;
-use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress;
use Magento\CustomerGraphQl\Model\Customer\GetCustomer;
+use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
+use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
@@ -29,11 +30,6 @@ class SetBillingAddressOnCart
*/
private $getCustomer;
- /**
- * @var GetCustomerAddress
- */
- private $getCustomerAddress;
-
/**
* @var AssignBillingAddressToCart
*/
@@ -42,18 +38,15 @@ class SetBillingAddressOnCart
/**
* @param QuoteAddressFactory $quoteAddressFactory
* @param GetCustomer $getCustomer
- * @param GetCustomerAddress $getCustomerAddress
* @param AssignBillingAddressToCart $assignBillingAddressToCart
*/
public function __construct(
QuoteAddressFactory $quoteAddressFactory,
GetCustomer $getCustomer,
- GetCustomerAddress $getCustomerAddress,
AssignBillingAddressToCart $assignBillingAddressToCart
) {
$this->quoteAddressFactory = $quoteAddressFactory;
$this->getCustomer = $getCustomer;
- $this->getCustomerAddress = $getCustomerAddress;
$this->assignBillingAddressToCart = $assignBillingAddressToCart;
}
@@ -65,6 +58,8 @@ public function __construct(
* @param array $billingAddressInput
* @return void
* @throws GraphQlInputException
+ * @throws GraphQlAuthenticationException
+ * @throws GraphQlAuthorizationException
* @throws GraphQlNoSuchEntityException
*/
public function execute(ContextInterface $context, CartInterface $cart, array $billingAddressInput): void
@@ -97,8 +92,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b
$billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput);
} else {
$customer = $this->getCustomer->execute($context);
- $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$customer->getId());
- $billingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress);
+ $billingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress(
+ (int)$customerAddressId,
+ (int)$customer->getId()
+ );
}
$this->assignBillingAddressToCart->execute($cart, $billingAddress, $useForShipping);
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
index bbca7721754cb..6b0e2a311bf44 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php
@@ -7,7 +7,6 @@
namespace Magento\QuoteGraphQl\Model\Cart;
-use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress;
use Magento\CustomerGraphQl\Model\Customer\GetCustomer;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
@@ -28,11 +27,6 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface
*/
private $getCustomer;
- /**
- * @var GetCustomerAddress
- */
- private $getCustomerAddress;
-
/**
* @var AssignShippingAddressToCart
*/
@@ -41,18 +35,15 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface
/**
* @param QuoteAddressFactory $quoteAddressFactory
* @param GetCustomer $getCustomer
- * @param GetCustomerAddress $getCustomerAddress
* @param AssignShippingAddressToCart $assignShippingAddressToCart
*/
public function __construct(
QuoteAddressFactory $quoteAddressFactory,
GetCustomer $getCustomer,
- GetCustomerAddress $getCustomerAddress,
AssignShippingAddressToCart $assignShippingAddressToCart
) {
$this->quoteAddressFactory = $quoteAddressFactory;
$this->getCustomer = $getCustomer;
- $this->getCustomerAddress = $getCustomerAddress;
$this->assignShippingAddressToCart = $assignShippingAddressToCart;
}
@@ -86,8 +77,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s
$shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput);
} else {
$customer = $this->getCustomer->execute($context);
- $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$customer->getId());
- $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress);
+ $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress(
+ (int)$customerAddressId,
+ (int)$customer->getId()
+ );
}
$this->assignShippingAddressToCart->execute($cart, $shippingAddress);
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php
index ca69b763929be..8251089abcd60 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php
@@ -11,12 +11,27 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
+use Magento\Quote\Api\CouponManagementInterface;
/**
* @inheritdoc
*/
class AppliedCoupon implements ResolverInterface
{
+ /**
+ * @var CouponManagementInterface
+ */
+ private $couponManagement;
+
+ /**
+ * @param CouponManagementInterface $couponManagement
+ */
+ public function __construct(
+ CouponManagementInterface $couponManagement
+ ) {
+ $this->couponManagement = $couponManagement;
+ }
+
/**
* @inheritdoc
*/
@@ -26,9 +41,9 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
throw new LocalizedException(__('"model" value should be specified'));
}
$cart = $value['model'];
+ $cartId = $cart->getId();
- $appliedCoupon = $cart->getCouponCode();
-
+ $appliedCoupon = $this->couponManagement->get($cartId);
return $appliedCoupon ? ['code' => $appliedCoupon] : null;
}
}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php
new file mode 100644
index 0000000000000..3bd46a664f2ab
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php
@@ -0,0 +1,83 @@
+getCartForUser = $getCartForUser;
+ $this->cartManagement = $cartManagement;
+ $this->orderRepository = $orderRepository;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) {
+ throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
+ }
+ $maskedCartId = $args['input']['cart_id'];
+
+ $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId());
+
+ try {
+ $orderId = $this->cartManagement->placeOrder($cart->getId());
+ $order = $this->orderRepository->get($orderId);
+
+ return [
+ 'order' => [
+ 'order_id' => $order->getIncrementId(),
+ ],
+ ];
+ } catch (NoSuchEntityException $e) {
+ throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Unable to place order: %message', ['message' => $e->getMessage()]), $e);
+ }
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php
index 5a708ceaedc28..f81ea3020d3d0 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php
@@ -62,7 +62,11 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
try {
$this->couponManagement->remove($cartId);
} catch (NoSuchEntityException $e) {
- throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
+ $message = $e->getMessage();
+ if (preg_match('/The "\d+" Cart doesn\'t contain products/', $message)) {
+ $message = 'Cart does not contain products';
+ }
+ throw new GraphQlNoSuchEntityException(__($message), $e);
} catch (CouldNotDeleteException $e) {
throw new LocalizedException(__($e->getMessage()), $e);
}
diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json
index 1bf4d581a5fe3..22ca9cfdfae9a 100644
--- a/app/code/Magento/QuoteGraphQl/composer.json
+++ b/app/code/Magento/QuoteGraphQl/composer.json
@@ -10,7 +10,8 @@
"magento/module-catalog": "*",
"magento/module-store": "*",
"magento/module-customer": "*",
- "magento/module-customer-graph-ql": "*"
+ "magento/module-customer-graph-ql": "*",
+ "magento/module-sales": "*"
},
"suggest": {
"magento/module-graph-ql": "*"
diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
index 79cea3855f6f3..9ec3492f64531 100644
--- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
@@ -17,6 +17,7 @@ type Mutation {
setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart")
setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
+ placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
}
input AddSimpleProductsToCartInput {
@@ -114,6 +115,10 @@ input ShippingMethodInput {
method_code: String!
}
+input PlaceOrderInput {
+ cart_id: String!
+}
+
input SetPaymentMethodOnCartInput {
cart_id: String!
payment_method: PaymentMethodInput!
@@ -144,6 +149,10 @@ type ApplyCouponToCartOutput {
cart: Cart!
}
+type PlaceOrderOutput {
+ order: Order!
+}
+
type Cart {
items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems")
applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon")
@@ -285,3 +294,7 @@ type CartItemSelectedOptionValuePrice {
units: String!
type: PriceTypeEnum!
}
+
+type Order {
+ order_id: String
+}
diff --git a/app/code/Magento/Sales/Block/Adminhtml/Totals.php b/app/code/Magento/Sales/Block/Adminhtml/Totals.php
index 83b155293c2b9..8172a3c0db4ad 100644
--- a/app/code/Magento/Sales/Block/Adminhtml/Totals.php
+++ b/app/code/Magento/Sales/Block/Adminhtml/Totals.php
@@ -5,6 +5,11 @@
*/
namespace Magento\Sales\Block\Adminhtml;
+use Magento\Sales\Model\Order;
+
+/**
+ * Adminhtml sales totals block
+ */
class Totals extends \Magento\Sales\Block\Order\Totals
{
/**
@@ -67,12 +72,16 @@ protected function _initTotals()
if (!$this->getSource()->getIsVirtual() && ((double)$this->getSource()->getShippingAmount() ||
$this->getSource()->getShippingDescription())
) {
+ $shippingLabel = __('Shipping & Handling');
+ if ($this->isFreeShipping($this->getOrder()) && $this->getSource()->getDiscountDescription()) {
+ $shippingLabel .= sprintf(' (%s)', $this->getSource()->getDiscountDescription());
+ }
$this->_totals['shipping'] = new \Magento\Framework\DataObject(
[
'code' => 'shipping',
'value' => $this->getSource()->getShippingAmount(),
'base_value' => $this->getSource()->getBaseShippingAmount(),
- 'label' => __('Shipping & Handling'),
+ 'label' => $shippingLabel,
]
);
}
@@ -109,4 +118,23 @@ protected function _initTotals()
return $this;
}
+
+ /**
+ * Availability of free shipping in at least one order item
+ *
+ * @param Order $order
+ * @return bool
+ */
+ private function isFreeShipping(Order $order): bool
+ {
+ $isFreeShipping = false;
+ foreach ($order->getItems() as $orderItem) {
+ if ($orderItem->getFreeShipping() == '1') {
+ $isFreeShipping = true;
+ break;
+ }
+ }
+
+ return $isFreeShipping;
+ }
}
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Creditmemo/DefaultCreditmemo.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Creditmemo/DefaultCreditmemo.php
index 11908864236f6..48934e24a3795 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Creditmemo/DefaultCreditmemo.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Creditmemo/DefaultCreditmemo.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sales\Model\Order\Pdf\Items\Creditmemo;
/**
@@ -66,11 +68,18 @@ public function draw()
$lines = [];
// draw Product name
- $lines[0] = [['text' => $this->string->split($item->getName(), 35, true, true), 'feed' => 35]];
+ $lines[0] = [
+ [
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($item->getName()), 35, true, true),
+ 'feed' => 35
+ ]
+ ];
// draw SKU
$lines[0][] = [
- 'text' => $this->string->split($this->getSku($item), 17),
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($this->getSku($item)), 17),
'feed' => 255,
'align' => 'right',
];
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
index 8562328025540..23c2c00daadc3 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sales\Model\Order\Pdf\Items\Invoice;
/**
@@ -66,11 +68,18 @@ public function draw()
$lines = [];
// draw Product name
- $lines[0] = [['text' => $this->string->split($item->getName(), 35, true, true), 'feed' => 35]];
+ $lines[0] = [
+ [
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($item->getName()), 35, true, true),
+ 'feed' => 35
+ ]
+ ];
// draw SKU
$lines[0][] = [
- 'text' => $this->string->split($this->getSku($item), 17),
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($this->getSku($item)), 17),
'feed' => 290,
'align' => 'right',
];
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php
index 6007e1dcf2b47..a88b508ba0789 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Shipment/DefaultShipment.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sales\Model\Order\Pdf\Items\Shipment;
/**
@@ -65,14 +67,21 @@ public function draw()
$lines = [];
// draw Product name
- $lines[0] = [['text' => $this->string->split($item->getName(), 60, true, true), 'feed' => 100]];
+ $lines[0] = [
+ [
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($item->getName()), 60, true, true),
+ 'feed' => 100
+ ]
+ ];
// draw QTY
$lines[0][] = ['text' => $item->getQty() * 1, 'feed' => 35];
// draw SKU
$lines[0][] = [
- 'text' => $this->string->split($this->getSku($item), 25),
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'text' => $this->string->split(html_entity_decode($this->getSku($item)), 25),
'feed' => 565,
'align' => 'right',
];
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
index 0061411d576e2..b90bac7e0881b 100644
--- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml
@@ -19,18 +19,15 @@
-
-
-
@@ -38,7 +35,6 @@
-
@@ -50,19 +46,22 @@
-
+
+
+
+
+
+
-
-
@@ -70,4 +69,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/i18n/de_DE.csv b/app/code/Magento/Search/i18n/de_DE.csv
deleted file mode 100644
index 8b4b04aa3b9ec..0000000000000
--- a/app/code/Magento/Search/i18n/de_DE.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Search Engine"
diff --git a/app/code/Magento/Search/i18n/es_ES.csv b/app/code/Magento/Search/i18n/es_ES.csv
deleted file mode 100644
index 8b4b04aa3b9ec..0000000000000
--- a/app/code/Magento/Search/i18n/es_ES.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Search Engine"
diff --git a/app/code/Magento/Search/i18n/fr_FR.csv b/app/code/Magento/Search/i18n/fr_FR.csv
deleted file mode 100644
index 8b4b04aa3b9ec..0000000000000
--- a/app/code/Magento/Search/i18n/fr_FR.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Search Engine"
diff --git a/app/code/Magento/Search/i18n/nl_NL.csv b/app/code/Magento/Search/i18n/nl_NL.csv
deleted file mode 100644
index 8b4b04aa3b9ec..0000000000000
--- a/app/code/Magento/Search/i18n/nl_NL.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Search Engine"
diff --git a/app/code/Magento/Search/i18n/pt_BR.csv b/app/code/Magento/Search/i18n/pt_BR.csv
deleted file mode 100644
index c10566a7c9800..0000000000000
--- a/app/code/Magento/Search/i18n/pt_BR.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Mecanismo de Busca"
diff --git a/app/code/Magento/Search/i18n/zh_Hans_CN.csv b/app/code/Magento/Search/i18n/zh_Hans_CN.csv
deleted file mode 100644
index 8b4b04aa3b9ec..0000000000000
--- a/app/code/Magento/Search/i18n/zh_Hans_CN.csv
+++ /dev/null
@@ -1 +0,0 @@
-"Search Engine","Search Engine"
diff --git a/app/code/Magento/Swatches/i18n/de_DE.csv b/app/code/Magento/Swatches/i18n/de_DE.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/de_DE.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Swatches/i18n/es_ES.csv b/app/code/Magento/Swatches/i18n/es_ES.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/es_ES.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Swatches/i18n/fr_FR.csv b/app/code/Magento/Swatches/i18n/fr_FR.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/fr_FR.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Swatches/i18n/nl_NL.csv b/app/code/Magento/Swatches/i18n/nl_NL.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/nl_NL.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Swatches/i18n/pt_BR.csv b/app/code/Magento/Swatches/i18n/pt_BR.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/pt_BR.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Swatches/i18n/zh_Hans_CN.csv b/app/code/Magento/Swatches/i18n/zh_Hans_CN.csv
deleted file mode 100644
index f0aa34bbef26e..0000000000000
--- a/app/code/Magento/Swatches/i18n/zh_Hans_CN.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-"Swatch","Swatch"
-"Swatches per Product","Swatches per Product"
diff --git a/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php
new file mode 100644
index 0000000000000..a81f29280af96
--- /dev/null
+++ b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php
@@ -0,0 +1,38 @@
+getContent();
+ $script = [];
+ if (strpos($content, '