-
+
= /* @escapeNotVerified */ $block->getActions($_item) ?>
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml
index 5530f7661bb1b..d329e2e8c1770 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml
@@ -14,7 +14,8 @@
getMethods('methods') ?: $block->getMethods('top_methods') ?>
- getMethodHtml($method)): ?>
+ getMethodHtml($method); ?>
+
= /* @escapeNotVerified */ $methodHtml ?>
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
index af652e6f556b9..e6d0260cf2305 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml
@@ -25,7 +25,7 @@
getIsNeedToDisplaySideBar()): ?>
- shopping cart.',
+ $block->getData('product_name'),
+ $block->getData('cart_url')
+), ['a']);
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js
new file mode 100644
index 0000000000000..ce1527b3d72d6
--- /dev/null
+++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js
@@ -0,0 +1,127 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'Magento_Ui/js/modal/alert',
+ 'jquery',
+ 'jquery/ui',
+ 'mage/validation'
+], function (alert, $) {
+ 'use strict';
+
+ $.widget('mage.updateShoppingCart', {
+ options: {
+ validationURL: '',
+ eventName: 'updateCartItemQty'
+ },
+
+ /** @inheritdoc */
+ _create: function () {
+ this._on(this.element, {
+ 'submit': this.onSubmit
+ });
+ },
+
+ /**
+ * Prevents default submit action and calls form validator.
+ *
+ * @param {Event} event
+ * @return {Boolean}
+ */
+ onSubmit: function (event) {
+ if (!this.options.validationURL) {
+ return true;
+ }
+
+ if (this.isValid()) {
+ event.preventDefault();
+ this.validateItems(this.options.validationURL, this.element.serialize());
+ }
+
+ return false;
+ },
+
+ /**
+ * Validates requested form.
+ *
+ * @return {Boolean}
+ */
+ isValid: function () {
+ return this.element.validation() && this.element.validation('isValid');
+ },
+
+ /**
+ * Validates updated shopping cart data.
+ *
+ * @param {String} url - request url
+ * @param {Object} data - post data for ajax call
+ */
+ validateItems: function (url, data) {
+ $.extend(data, {
+ 'form_key': $.mage.cookies.get('form_key')
+ });
+
+ $.ajax({
+ url: url,
+ data: data,
+ type: 'post',
+ dataType: 'json',
+ context: this,
+
+ /** @inheritdoc */
+ beforeSend: function () {
+ $(document.body).trigger('processStart');
+ },
+
+ /** @inheritdoc */
+ complete: function () {
+ $(document.body).trigger('processStop');
+ }
+ })
+ .done(function (response) {
+ if (response.success) {
+ this.onSuccess();
+ } else {
+ this.onError(response);
+ }
+ })
+ .fail(function () {
+ this.submitForm();
+ });
+ },
+
+ /**
+ * Form validation succeed.
+ */
+ onSuccess: function () {
+ $(document).trigger('ajax:' + this.options.eventName);
+ this.submitForm();
+ },
+
+ /**
+ * Form validation failed.
+ */
+ onError: function (response) {
+ if (response['error_message']) {
+ alert({
+ content: response['error_message']
+ });
+ } else {
+ this.submitForm();
+ }
+ },
+
+ /**
+ * Real submit of validated form.
+ */
+ submitForm: function () {
+ this.element
+ .off('submit', this.onSubmit)
+ .submit();
+ }
+ });
+
+ return $.mage.updateShoppingCart;
+});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
index 9c0ef8bb58c07..22b37b2da0b2f 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
@@ -259,6 +259,29 @@ define([
obj.inputFieldEmailValue = email;
saveData(obj);
+ },
+
+ /**
+ * Pulling the checked email value from persistence storage
+ *
+ * @return {*}
+ */
+ getCheckedEmailValue: function () {
+ var obj = getData();
+
+ return obj.checkedEmailValue ? obj.checkedEmailValue : '';
+ },
+
+ /**
+ * Setting the checked email value pulled from persistence storage
+ *
+ * @param {String} email
+ */
+ setCheckedEmailValue: function (email) {
+ var obj = getData();
+
+ obj.checkedEmailValue = email;
+ saveData(obj);
}
};
});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js
index 6e689091d091f..a1aacf6e80320 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js
@@ -72,20 +72,20 @@ define([
output = {},
streetObject;
+ $.each(addrs, function (key) {
+ if (addrs.hasOwnProperty(key) && !$.isFunction(addrs[key])) {
+ output[self.toUnderscore(key)] = addrs[key];
+ }
+ });
+
if ($.isArray(addrs.street)) {
streetObject = {};
addrs.street.forEach(function (value, index) {
streetObject[index] = value;
});
- addrs.street = streetObject;
+ output.street = streetObject;
}
- $.each(addrs, function (key) {
- if (addrs.hasOwnProperty(key) && !$.isFunction(addrs[key])) {
- output[self.toUnderscore(key)] = addrs[key];
- }
- });
-
return output;
},
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js
index 76e3d911e7d3f..54e496131972e 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js
@@ -14,55 +14,71 @@ define([
'use strict';
var rateProcessors = [],
- totalsProcessors = [];
+ totalsProcessors = [],
- quote.shippingAddress.subscribe(function () {
- var type = quote.shippingAddress().getType();
+ /**
+ * Estimate totals for shipping address and update shipping rates.
+ */
+ estimateTotalsAndUpdateRates = function () {
+ var type = quote.shippingAddress().getType();
- if (
- quote.isVirtual() ||
- window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0
- ) {
- // update totals block when estimated address was set
- totalsProcessors['default'] = totalsDefaultProvider;
- totalsProcessors[type] ?
- totalsProcessors[type].estimateTotals(quote.shippingAddress()) :
- totalsProcessors['default'].estimateTotals(quote.shippingAddress());
- } else {
- // check if user data not changed -> load rates from cache
- if (!cartCache.isChanged('address', quote.shippingAddress()) &&
- !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) &&
- cartCache.get('rates')
+ if (
+ quote.isVirtual() ||
+ window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0
) {
- shippingService.setShippingRates(cartCache.get('rates'));
+ // update totals block when estimated address was set
+ totalsProcessors['default'] = totalsDefaultProvider;
+ totalsProcessors[type] ?
+ totalsProcessors[type].estimateTotals(quote.shippingAddress()) :
+ totalsProcessors['default'].estimateTotals(quote.shippingAddress());
+ } else {
+ // check if user data not changed -> load rates from cache
+ if (!cartCache.isChanged('address', quote.shippingAddress()) &&
+ !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) &&
+ cartCache.get('rates')
+ ) {
+ shippingService.setShippingRates(cartCache.get('rates'));
- return;
+ return;
+ }
+
+ // update rates list when estimated address was set
+ rateProcessors['default'] = defaultProcessor;
+ rateProcessors[type] ?
+ rateProcessors[type].getRates(quote.shippingAddress()) :
+ rateProcessors['default'].getRates(quote.shippingAddress());
+
+ // save rates to cache after load
+ shippingService.getShippingRates().subscribe(function (rates) {
+ cartCache.set('rates', rates);
+ });
}
+ },
- // update rates list when estimated address was set
- rateProcessors['default'] = defaultProcessor;
- rateProcessors[type] ?
- rateProcessors[type].getRates(quote.shippingAddress()) :
- rateProcessors['default'].getRates(quote.shippingAddress());
+ /**
+ * Estimate totals for shipping address.
+ */
+ estimateTotalsShipping = function () {
+ totalsDefaultProvider.estimateTotals(quote.shippingAddress());
+ },
- // save rates to cache after load
- shippingService.getShippingRates().subscribe(function (rates) {
- cartCache.set('rates', rates);
- });
- }
- });
- quote.shippingMethod.subscribe(function () {
- totalsDefaultProvider.estimateTotals(quote.shippingAddress());
- });
- quote.billingAddress.subscribe(function () {
- var type = quote.billingAddress().getType();
+ /**
+ * Estimate totals for billing address.
+ */
+ estimateTotalsBilling = function () {
+ var type = quote.billingAddress().getType();
+
+ if (quote.isVirtual()) {
+ // update totals block when estimated address was set
+ totalsProcessors['default'] = totalsDefaultProvider;
+ totalsProcessors[type] ?
+ totalsProcessors[type].estimateTotals(quote.billingAddress()) :
+ totalsProcessors['default'].estimateTotals(quote.billingAddress());
+ }
+ };
- if (quote.isVirtual()) {
- // update totals block when estimated address was set
- totalsProcessors['default'] = totalsDefaultProvider;
- totalsProcessors[type] ?
- totalsProcessors[type].estimateTotals(quote.billingAddress()) :
- totalsProcessors['default'].estimateTotals(quote.billingAddress());
- }
- });
+ quote.shippingAddress.subscribe(estimateTotalsAndUpdateRates);
+ quote.shippingMethod.subscribe(estimateTotalsShipping);
+ quote.billingAddress.subscribe(estimateTotalsBilling);
+ customerData.get('cart').subscribe(estimateTotalsShipping);
});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js
index e269462047748..0e94232786c65 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js
@@ -38,7 +38,7 @@ define([
payload.addressInformation['shipping_carrier_code'] = quote.shippingMethod()['carrier_code'];
}
- storage.post(
+ return storage.post(
serviceUrl, JSON.stringify(payload), false
).done(function (result) {
var data = {
@@ -96,7 +96,7 @@ define([
) {
quote.setTotals(cartCache.get('totals'));
} else {
- loadFromServer(address);
+ return loadFromServer(address);
}
}
};
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
index 73f4df567903c..28e04699f8daf 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js
@@ -218,16 +218,31 @@ define([
* Apply resolved billing address to quote
*/
applyBillingAddress: function () {
- var shippingAddress;
+ var shippingAddress,
+ isBillingAddressInitialized;
if (quote.billingAddress()) {
selectBillingAddress(quote.billingAddress());
return;
}
+
+ if (quote.isVirtual()) {
+ isBillingAddressInitialized = addressList.some(function (addrs) {
+ if (addrs.isDefaultBilling()) {
+ selectBillingAddress(addrs);
+
+ return true;
+ }
+
+ return false;
+ });
+ }
+
shippingAddress = quote.shippingAddress();
- if (shippingAddress &&
+ if (!isBillingAddressInitialized &&
+ shippingAddress &&
shippingAddress.canUseForBilling() &&
(shippingAddress.isDefaultShipping() || !quote.isVirtual())
) {
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js b/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js
index 848a7daf71e1b..16fc459729bbc 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js
@@ -8,8 +8,9 @@
*/
define([
'mage/url',
- 'Magento_Ui/js/model/messageList'
-], function (url, globalMessageList) {
+ 'Magento_Ui/js/model/messageList',
+ 'consoleLogger'
+], function (url, globalMessageList, consoleLogger) {
'use strict';
return {
@@ -25,8 +26,12 @@ define([
if (response.status == 401) { //eslint-disable-line eqeqeq
window.location.replace(url.build('customer/account/login/'));
} else {
- error = JSON.parse(response.responseText);
- messageContainer.addErrorMessage(error);
+ try {
+ error = JSON.parse(response.responseText);
+ messageContainer.addErrorMessage(error);
+ } catch (e) {
+ consoleLogger.error(e);
+ }
}
}
};
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js
index 3bc5911946fda..7b858c92bee34 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js
@@ -17,18 +17,28 @@ define([
*/
return function (addressData) {
var identifier = Date.now(),
+ countryId,
regionId;
- if (addressData.region && addressData.region['region_id']) {
- regionId = addressData.region['region_id'];
- } else if (addressData['country_id'] && addressData['country_id'] == window.checkoutConfig.defaultCountryId) { //eslint-disable-line
- regionId = window.checkoutConfig.defaultRegionId || undefined;
+ countryId = addressData['country_id'] || addressData.countryId;
+
+ if (countryId) {
+ if (addressData.region && addressData.region['region_id']) {
+ regionId = addressData.region['region_id'];
+ } else if (!addressData['region_id']) {
+ regionId = undefined;
+ } else if (countryId === window.checkoutConfig.defaultCountryId) {
+ regionId = window.checkoutConfig.defaultRegionId;
+ }
+ } else {
+ countryId = window.checkoutConfig.defaultCountryId;
+ regionId = window.checkoutConfig.defaultRegionId;
}
return {
email: addressData.email,
- countryId: addressData['country_id'] || addressData.countryId || window.checkoutConfig.defaultCountryId,
- regionId: regionId || addressData.regionId,
+ countryId: countryId,
+ regionId: regionId,
regionCode: addressData.region ? addressData.region['region_code'] : null,
region: addressData.region ? addressData.region.region : null,
customerId: addressData['customer_id'] || addressData.customerId,
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/resource-url-manager.js b/app/code/Magento/Checkout/view/frontend/web/js/model/resource-url-manager.js
index 2d27d4a032b24..410f5b4927374 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/resource-url-manager.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/resource-url-manager.js
@@ -75,8 +75,8 @@ define([
quoteId: quoteId
} : {},
urls = {
- 'guest': '/guest-carts/' + quoteId + '/coupons/' + couponCode,
- 'customer': '/carts/mine/coupons/' + couponCode
+ 'guest': '/guest-carts/' + quoteId + '/coupons/' + encodeURIComponent(couponCode),
+ 'customer': '/carts/mine/coupons/' + encodeURIComponent(couponCode)
};
return this.getUrl(urls, params);
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
index d31c0dca38116..fde88ebadb393 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
@@ -35,7 +35,7 @@ define([
var checkoutConfig = window.checkoutConfig,
validators = [],
observedElements = [],
- postcodeElement = null,
+ postcodeElements = [],
postcodeElementName = 'postcode';
validators.push(defaultValidator);
@@ -101,7 +101,7 @@ define([
if (element.index === postcodeElementName) {
this.bindHandler(element, delay);
- postcodeElement = element;
+ postcodeElements.push(element);
}
},
@@ -136,7 +136,13 @@ define([
if (!formPopUpState.isVisible()) {
clearTimeout(self.validateAddressTimeout);
self.validateAddressTimeout = setTimeout(function () {
- self.postcodeValidation();
+ if (element.index === postcodeElementName) {
+ self.postcodeValidation(element);
+ } else {
+ $.each(postcodeElements, function (index, elem) {
+ self.postcodeValidation(elem);
+ });
+ }
self.validateFields();
}, delay);
}
@@ -148,8 +154,8 @@ define([
/**
* @return {*}
*/
- postcodeValidation: function () {
- var countryId = $('select[name="country_id"]').val(),
+ postcodeValidation: function (postcodeElement) {
+ var countryId = $('select[name="country_id"]:visible').val(),
validationResult,
warnMessage;
@@ -178,8 +184,8 @@ define([
*/
validateFields: function () {
var addressFlat = addressConverter.formDataProviderToFlatData(
- this.collectObservedData(),
- 'shippingAddress'
+ this.collectObservedData(),
+ 'shippingAddress'
),
address;
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js
index b2a273513181d..447d626b339bd 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js
@@ -12,7 +12,8 @@ define([
'Magento_Checkout/js/model/payment/method-converter',
'Magento_Checkout/js/model/error-processor',
'Magento_Checkout/js/model/full-screen-loader',
- 'Magento_Checkout/js/action/select-billing-address'
+ 'Magento_Checkout/js/action/select-billing-address',
+ 'Magento_Checkout/js/model/shipping-save-processor/payload-extender'
], function (
ko,
quote,
@@ -22,7 +23,8 @@ define([
methodConverter,
errorProcessor,
fullScreenLoader,
- selectBillingAddressAction
+ selectBillingAddressAction,
+ payloadExtender
) {
'use strict';
@@ -33,9 +35,8 @@ define([
saveShippingInformation: function () {
var payload;
- if (!quote.billingAddress()) {
- selectBillingAddressAction(quote.shippingAddress());
- }
+ /* Assign selected address every time buyer selects address*/
+ selectBillingAddressAction(quote.shippingAddress());
payload = {
addressInformation: {
@@ -46,6 +47,8 @@ define([
}
};
+ payloadExtender(payload);
+
fullScreenLoader.startLoader();
return storage.post(
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/payload-extender.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/payload-extender.js
new file mode 100644
index 0000000000000..dcd4340774af4
--- /dev/null
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/payload-extender.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+define(function () {
+ 'use strict';
+
+ return function (payload) {
+ payload.addressInformation['extension_attributes'] = {};
+
+ return payload;
+ };
+});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js
index 1063284ecf0e2..64e751d0d56d0 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js
@@ -47,7 +47,7 @@ define([
steps.sort(this.sortItems).forEach(function (element) {
if (element.code == hashString || element.alias == hashString) { //eslint-disable-line eqeqeq
- element.navigate();
+ element.navigate(element);
} else {
element.isVisible(false);
}
@@ -66,7 +66,7 @@ define([
* @param {*} sortOrder
*/
registerStep: function (code, alias, title, isVisible, navigate, sortOrder) {
- var hash;
+ var hash, active;
if ($.inArray(code, this.validCodes) !== -1) {
throw new DOMException('Step code [' + code + '] already registered in step navigator');
@@ -87,6 +87,12 @@ define([
navigate: navigate,
sortOrder: sortOrder
});
+ active = this.getActiveItemIndex();
+ steps.each(function (elem, index) {
+ if (active !== index) {
+ elem.isVisible(false);
+ }
+ });
this.stepCodes.push(code);
hash = window.location.hash.replace('#', '');
@@ -111,10 +117,14 @@ define([
getActiveItemIndex: function () {
var activeIndex = 0;
- steps.sort(this.sortItems).forEach(function (element, index) {
+ steps.sort(this.sortItems).some(function (element, index) {
if (element.isVisible()) {
activeIndex = index;
+
+ return true;
}
+
+ return false;
});
return activeIndex;
@@ -172,6 +182,15 @@ define([
});
},
+ /**
+ * Sets window location hash.
+ *
+ * @param {String} hash
+ */
+ setHash: function (hash) {
+ window.location.hash = hash;
+ },
+
/**
* Next step.
*/
@@ -189,7 +208,7 @@ define([
if (steps().length > activeIndex + 1) {
code = steps()[activeIndex + 1].code;
steps()[activeIndex + 1].isVisible(true);
- window.location = window.checkoutConfig.checkoutUrl + '#' + code;
+ this.setHash(code);
document.body.scrollTop = document.documentElement.scrollTop = 0;
}
}
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/proceed-to-checkout.js b/app/code/Magento/Checkout/view/frontend/web/js/proceed-to-checkout.js
index f0679c657ab90..0bb0a53ce0a6b 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/proceed-to-checkout.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/proceed-to-checkout.js
@@ -22,6 +22,7 @@ define([
return false;
}
+ $(element).attr('disabled', true);
location.href = config.checkoutUrl;
});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js
index 82033590b1135..cf2a59cdba427 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js
@@ -188,6 +188,8 @@ define([
if (!this.options.optionalRegionAllowed) { //eslint-disable-line max-depth
regionList.attr('disabled', 'disabled');
+ } else {
+ regionList.removeAttr('disabled');
}
}
@@ -195,6 +197,8 @@ define([
regionInput.hide();
label.attr('for', regionList.attr('id'));
} else {
+ this._removeSelectOptions(regionList);
+
if (this.options.isRegionRequired) {
regionInput.addClass('required-entry').removeAttr('disabled');
requiredLabel.addClass('required');
@@ -206,7 +210,7 @@ define([
regionInput.removeClass('required-entry');
}
- regionList.removeClass('required-entry').hide();
+ regionList.removeClass('required-entry').prop('disabled', 'disabled').hide();
regionInput.show();
label.attr('for', regionInput.attr('id'));
}
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
index 13a2b524e5186..dde1ad72ba15e 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
@@ -9,11 +9,12 @@ define([
'Magento_Customer/js/customer-data',
'Magento_Ui/js/modal/alert',
'Magento_Ui/js/modal/confirm',
+ 'underscore',
'jquery/ui',
'mage/decorate',
'mage/collapsible',
'mage/cookies'
-], function ($, authenticationPopup, customerData, alert, confirm) {
+], function ($, authenticationPopup, customerData, alert, confirm, _) {
'use strict';
$.widget('mage.sidebar', {
@@ -60,13 +61,15 @@ define([
};
events['click ' + this.options.button.checkout] = $.proxy(function () {
var cart = customerData.get('cart'),
- customer = customerData.get('customer');
+ customer = customerData.get('customer'),
+ element = $(this.options.button.checkout);
if (!customer().firstname && cart().isGuestCheckoutAllowed === false) {
// set URL for redirect on successful login/registration. It's postprocessed on backend.
$.cookie('login_redirect', this.options.url.checkout);
if (this.options.url.isRedirectRequired) {
+ element.prop('disabled', true);
location.href = this.options.url.loginUrl;
} else {
authenticationPopup.showModal();
@@ -74,6 +77,7 @@ define([
return false;
}
+ element.prop('disabled', true);
location.href = this.options.url.checkout;
}, this);
@@ -105,6 +109,13 @@ define([
self._showItemButton($(event.target));
};
+ /**
+ * @param {jQuery.Event} event
+ */
+ events['change ' + this.options.item.qty] = function (event) {
+ self._showItemButton($(event.target));
+ };
+
/**
* @param {jQuery.Event} event
*/
@@ -212,6 +223,11 @@ define([
* @param {HTMLElement} elem
*/
_updateItemQtyAfter: function (elem) {
+ var productData = this._getProductById(Number(elem.data('cart-item')));
+
+ if (!_.isUndefined(productData)) {
+ $(document).trigger('ajax:updateCartItemQty');
+ }
this._hideItemButton(elem);
},
@@ -234,11 +250,26 @@ define([
* @private
*/
_removeItemAfter: function (elem) {
- var productData = customerData.get('cart')().items.find(function (item) {
- return Number(elem.data('cart-item')) === Number(item['item_id']);
- });
+ var productData = this._getProductById(Number(elem.data('cart-item')));
- $(document).trigger('ajax:removeFromCart', productData['product_sku']);
+ if (!_.isUndefined(productData)) {
+ $(document).trigger('ajax:removeFromCart', {
+ productIds: [productData['product_id']]
+ });
+ }
+ },
+
+ /**
+ * Retrieves product data by Id.
+ *
+ * @param {Number} productId - product Id
+ * @returns {Object|undefined}
+ * @private
+ */
+ _getProductById: function (productId) {
+ return _.find(customerData.get('cart')().items, function (item) {
+ return productId === Number(item['item_id']);
+ });
},
/**
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/authentication.js b/app/code/Magento/Checkout/view/frontend/web/js/view/authentication.js
index e7480b25aa791..796b502cab54d 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/authentication.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/authentication.js
@@ -18,7 +18,6 @@ define([
return Component.extend({
isGuestCheckoutAllowed: checkoutConfig.isGuestCheckoutAllowed,
- isCustomerLoginRequired: checkoutConfig.isCustomerLoginRequired,
registerUrl: checkoutConfig.registerUrl,
forgotPasswordUrl: checkoutConfig.forgotPasswordUrl,
autocomplete: checkoutConfig.autocomplete,
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
index 6b5d08c2641cc..6f9a1a46826da 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js
@@ -17,7 +17,8 @@ define([
'Magento_Customer/js/customer-data',
'Magento_Checkout/js/action/set-billing-address',
'Magento_Ui/js/model/messageList',
- 'mage/translate'
+ 'mage/translate',
+ 'Magento_Checkout/js/model/shipping-rates-validator'
],
function (
ko,
@@ -33,7 +34,8 @@ function (
customerData,
setBillingAddressAction,
globalMessageList,
- $t
+ $t,
+ shippingRatesValidator
) {
'use strict';
@@ -71,6 +73,7 @@ function (
quote.paymentMethod.subscribe(function () {
checkoutDataResolver.resolveBillingAddress();
}, this);
+ shippingRatesValidator.initFields(this.get('name') + '.form-fields');
},
/**
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/cart/shipping-estimation.js b/app/code/Magento/Checkout/view/frontend/web/js/view/cart/shipping-estimation.js
index 6cc5d3f6679a9..a709ff0aacee1 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/cart/shipping-estimation.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/cart/shipping-estimation.js
@@ -47,6 +47,12 @@ define(
checkoutDataResolver.resolveEstimationAddress();
address = quote.isVirtual() ? quote.billingAddress() : quote.shippingAddress();
+ if (!address && quote.isVirtual()) {
+ address = addressConverter.formAddressDataToQuoteAddress(
+ checkoutData.getSelectedBillingAddress()
+ );
+ }
+
if (address) {
estimatedAddress = address.isEditable() ?
addressConverter.quoteAddressToFormAddressData(address) :
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js b/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js
index a612b5e2dc6b7..0e2fc6bbfc8fe 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/configure/product-customer-data.js
@@ -1,15 +1,19 @@
require([
'jquery',
- 'Magento_Customer/js/customer-data'
-], function ($, customerData) {
+ 'Magento_Customer/js/customer-data',
+ 'underscore',
+ 'domReady!'
+], function ($, customerData, _) {
'use strict';
var selectors = {
qtySelector: '#product_addtocart_form [name="qty"]',
- productIdSelector: '#product_addtocart_form [name="product"]'
+ productIdSelector: '#product_addtocart_form [name="product"]',
+ itemIdSelector: '#product_addtocart_form [name="item"]'
},
cartData = customerData.get('cart'),
productId = $(selectors.productIdSelector).val(),
+ itemId = $(selectors.itemIdSelector).val(),
productQty,
productQtyInput,
@@ -38,9 +42,11 @@ require([
if (!(data && data.items && data.items.length && productId)) {
return;
}
- product = data.items.find(function (item) {
- return item['product_id'] === productId ||
- item['item_id'] === productId;
+ product = _.find(data.items, function (item) {
+ if (item['item_id'] === itemId) {
+ return item['product_id'] === productId ||
+ item['item_id'] === productId;
+ }
});
if (!product) {
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js
index b9c4aeb27ad32..90ad07da0ae37 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js
@@ -54,6 +54,15 @@ define([
return this;
},
+ /** @inheritdoc */
+ initConfig: function () {
+ this._super();
+
+ this.isPasswordVisible = this.resolveInitialPasswordVisibility();
+
+ return this;
+ },
+
/**
* Callback on changing email property
*/
@@ -81,20 +90,19 @@ define([
* Check email existing.
*/
checkEmailAvailability: function () {
- var self = this;
-
this.validateRequest();
this.isEmailCheckComplete = $.Deferred();
this.isLoading(true);
this.checkRequest = checkEmailAvailability(this.isEmailCheckComplete, this.email());
$.when(this.isEmailCheckComplete).done(function () {
- self.isPasswordVisible(false);
- }).fail(function () {
- self.isPasswordVisible(true);
- }).always(function () {
- self.isLoading(false);
- });
+ this.isPasswordVisible(false);
+ }.bind(this)).fail(function () {
+ this.isPasswordVisible(true);
+ checkoutData.setCheckedEmailValue(this.email());
+ }.bind(this)).always(function () {
+ this.isLoading(false);
+ }.bind(this));
},
/**
@@ -153,6 +161,19 @@ define([
fullScreenLoader.stopLoader();
});
}
+ },
+
+ /**
+ * Resolves an initial sate of a login form.
+ *
+ * @returns {Boolean} - initial visibility state.
+ */
+ resolveInitialPasswordVisibility: function () {
+ if (checkoutData.getInputFieldEmailValue() !== '') {
+ return checkoutData.getInputFieldEmailValue() === checkoutData.getCheckedEmailValue();
+ }
+
+ return false;
}
});
});
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
index 5ac062a12180c..a2f8c8c56ff33 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js
@@ -10,7 +10,8 @@ define([
'ko',
'underscore',
'sidebar',
- 'mage/translate'
+ 'mage/translate',
+ 'mage/dropdown'
], function (Component, customerData, $, ko, _) {
'use strict';
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js b/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js
index 0cbc16ef72bc3..db49683129f2b 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js
@@ -24,8 +24,19 @@ define([
/** @inheritdoc */
initialize: function () {
+ var stepsValue;
+
this._super();
$(window).hashchange(_.bind(stepNavigator.handleHash, stepNavigator));
+
+ if (!window.location.hash) {
+ stepsValue = stepNavigator.steps();
+
+ if (stepsValue.length) {
+ stepNavigator.setHash(stepsValue.sort(stepNavigator.sortItems)[0].code);
+ }
+ }
+
stepNavigator.handleHash();
},
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js b/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js
index c715b5c4d45ce..a7b3e18c06088 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js
@@ -38,7 +38,16 @@ define([
},
/**
- * Create new user account
+ * @return String
+ */
+ getUrl: function () {
+ return this.registrationUrl;
+ },
+
+ /**
+ * Create new user account.
+ *
+ * @deprecated
*/
createAccount: function () {
this.creationStarted(true);
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
index 8a8c32042c5e8..df50a5ae94ae9 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
@@ -127,10 +127,12 @@ define([
},
/**
- * Load data from server for shipping step
+ * Navigator change hash handler.
+ *
+ * @param {Object} step - navigation step
*/
- navigate: function () {
- //load data from server for shipping step
+ navigate: function (step) {
+ step && step.isVisible(true);
},
/**
@@ -245,6 +247,7 @@ define([
*/
setShippingInformation: function () {
if (this.validateShippingInformation()) {
+ checkoutDataResolver.resolveBillingAddress();
setShippingInformationAction().done(
function () {
stepNavigator.next();
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js
index 3fda260339254..ae4388f282c95 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js
@@ -21,14 +21,19 @@ define([
* @return {*}
*/
getShippingMethodTitle: function () {
- var shippingMethod;
+ var shippingMethod,
+ shippingMethodTitle = '';
if (!this.isCalculated()) {
return '';
}
shippingMethod = quote.shippingMethod();
- return shippingMethod ? shippingMethod['carrier_title'] + ' - ' + shippingMethod['method_title'] : '';
+ if (typeof shippingMethod['method_title'] !== 'undefined') {
+ shippingMethodTitle = ' - ' + shippingMethod['method_title'];
+ }
+
+ return shippingMethod ? shippingMethod['carrier_title'] + shippingMethodTitle : '';
},
/**
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html
index f2baf5d50030e..ea521b3a8afd4 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html
@@ -4,28 +4,37 @@
* See COPYING.txt for license details.
*/
-->
-
-
-
-
- ,
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ click="editAddress">
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html
index 1be754934042b..54fe9a1f59394 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html
@@ -9,14 +9,14 @@
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
index 8d32adb75308f..357b0e550af0f 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
@@ -24,7 +24,7 @@
-
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/registration.html b/app/code/Magento/Checkout/view/frontend/web/template/registration.html
index 256fc1968abfc..ea94726e5443e 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/registration.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/registration.html
@@ -11,11 +11,8 @@
:
-
-
-
-
-
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html
index 2e268461d1eea..2a5dc27328a43 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html
@@ -4,33 +4,38 @@
* See COPYING.txt for license details.
*/
-->
-
-
-
-
- ,
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ click="editAddress">
+
-
-
-
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html
index ec41cae0bdc5e..541413955cb47 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html
@@ -4,23 +4,29 @@
* See COPYING.txt for license details.
*/
-->
-
-
-
-
- ,
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html
index daa37cbe2dc89..dd59bd78416c6 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html
@@ -12,7 +12,7 @@
-
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html
index 981541e7251e7..eb218bbee9941 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details/thumbnail.html
@@ -8,6 +8,6 @@
data-bind="attr: {'style': 'height: ' + getHeight($parents[1]) + 'px; width: ' + getWidth($parents[1]) + 'px;' }">
+ data-bind="attr: {'src': getSrc($parents[1]), 'width': getWidth($parents[1]), 'height': getHeight($parents[1]), 'alt': getAlt($parents[1]), 'title': getAlt($parents[1]) }"/>
diff --git a/app/code/Magento/CheckoutAgreements/Block/Adminhtml/Agreement/Grid.php b/app/code/Magento/CheckoutAgreements/Block/Adminhtml/Agreement/Grid.php
index ed9ecc642e16b..4a35a58a41ff9 100644
--- a/app/code/Magento/CheckoutAgreements/Block/Adminhtml/Agreement/Grid.php
+++ b/app/code/Magento/CheckoutAgreements/Block/Adminhtml/Agreement/Grid.php
@@ -5,27 +5,42 @@
*/
namespace Magento\CheckoutAgreements\Block\Adminhtml\Agreement;
+use Magento\Framework\App\ObjectManager;
+use Magento\CheckoutAgreements\Model\ResourceModel\Agreement\Grid\CollectionFactory as GridCollectionFactory;
+
class Grid extends \Magento\Backend\Block\Widget\Grid\Extended
{
/**
* @var \Magento\CheckoutAgreements\Model\ResourceModel\Agreement\CollectionFactory
+ * @deprecated
*/
protected $_collectionFactory;
+ /**
+ * @param GridCollectionFactory
+ */
+ private $gridCollectionFactory;
+
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Backend\Helper\Data $backendHelper
* @param \Magento\CheckoutAgreements\Model\ResourceModel\Agreement\CollectionFactory $collectionFactory
* @param array $data
+ * @param GridCollectionFactory $gridColFactory
* @codeCoverageIgnore
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Backend\Helper\Data $backendHelper,
\Magento\CheckoutAgreements\Model\ResourceModel\Agreement\CollectionFactory $collectionFactory,
- array $data = []
+ array $data = [],
+ GridCollectionFactory $gridColFactory = null
) {
+
$this->_collectionFactory = $collectionFactory;
+ $this->gridCollectionFactory = $gridColFactory
+ ? : ObjectManager::getInstance()->get(GridCollectionFactory::class);
+
parent::__construct($context, $backendHelper, $data);
}
@@ -47,7 +62,7 @@ protected function _construct()
*/
protected function _prepareCollection()
{
- $this->setCollection($this->_collectionFactory->create());
+ $this->setCollection($this->gridCollectionFactory->create());
return parent::_prepareCollection();
}
diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php
index 13130a4491eb2..aa6f461fc5ee2 100644
--- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php
+++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php
@@ -5,7 +5,11 @@
*/
namespace Magento\CheckoutAgreements\Controller\Adminhtml;
-abstract class Agreement extends \Magento\Backend\App\Action
+use Magento\Backend\App\Action;
+use Magento\Backend\App\Action\Context;
+use Magento\Framework\Registry;
+
+abstract class Agreement extends Action
{
/**
* Authorization level of a basic admin session
@@ -22,12 +26,14 @@ abstract class Agreement extends \Magento\Backend\App\Action
protected $_coreRegistry = null;
/**
- * @param \Magento\Backend\App\Action\Context $context
- * @param \Magento\Framework\Registry $coreRegistry
+ * @param Context $context
+ * @param Registry $coreRegistry
* @codeCoverageIgnore
*/
- public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry)
- {
+ public function __construct(
+ Context $context,
+ Registry $coreRegistry
+ ) {
$this->_coreRegistry = $coreRegistry;
parent::__construct($context);
}
diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php
index 65aca6205caa4..f7b178df99624 100644
--- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php
+++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php
@@ -6,27 +6,53 @@
*/
namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
-class Delete extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement
+use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface;
+use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
+use Magento\Backend\App\Action\Context;
+use Magento\Framework\Registry;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Exception\LocalizedException;
+
+class Delete extends Agreement
{
+ /**
+ * @var CheckoutAgreementsRepositoryInterface
+ */
+ private $agreementRepository;
+
+ /**
+ * @param Context $context
+ * @param Registry $coreRegistry
+ * @param CheckoutAgreementsRepositoryInterface $agreementRepository
+ */
+ public function __construct(
+ Context $context,
+ Registry $coreRegistry,
+ CheckoutAgreementsRepositoryInterface $agreementRepository = null
+ ) {
+ $this->agreementRepository = $agreementRepository ?:
+ ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class);
+ parent::__construct($context, $coreRegistry);
+ }
/**
* @return void
*/
public function execute()
{
$id = (int)$this->getRequest()->getParam('id');
- $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class)->load($id);
- if (!$model->getId()) {
+ $agreement = $this->agreementRepository->get($id);
+ if (!$agreement->getAgreementId()) {
$this->messageManager->addError(__('This condition no longer exists.'));
$this->_redirect('checkout/*/');
return;
}
try {
- $model->delete();
+ $this->agreementRepository->delete($agreement);
$this->messageManager->addSuccess(__('You deleted the condition.'));
$this->_redirect('checkout/*/');
return;
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ } catch (LocalizedException $e) {
$this->messageManager->addError($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addError(__('Something went wrong while deleting this condition.'));
diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php
index 73ac129bc993c..8bec3b581cd54 100644
--- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php
+++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php
@@ -6,8 +6,34 @@
*/
namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
-class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement
+use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
+use Magento\CheckoutAgreements\Model\AgreementFactory;
+use Magento\Backend\App\Action\Context;
+use Magento\Framework\Registry;
+use Magento\Framework\App\ObjectManager;
+use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit as BlockEdit;
+
+class Edit extends Agreement
{
+ /**
+ * @var AgreementFactory
+ */
+ private $agreementFactory;
+
+ /**
+ * @param Context $context
+ * @param Registry $coreRegistry
+ * @param AgreementFactory $agreementFactory
+ */
+ public function __construct(
+ Context $context,
+ Registry $coreRegistry,
+ AgreementFactory $agreementFactory = null
+ ) {
+ $this->agreementFactory = $agreementFactory ?:
+ ObjectManager::getInstance()->get(AgreementFactory::class);
+ parent::__construct($context, $coreRegistry);
+ }
/**
* @return void
* @SuppressWarnings(PHPMD.NPathComplexity)
@@ -15,7 +41,7 @@ class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement
public function execute()
{
$id = $this->getRequest()->getParam('id');
- $agreementModel = $this->_objectManager->create(\Magento\CheckoutAgreements\Model\Agreement::class);
+ $agreementModel = $this->agreementFactory->create();
if ($id) {
$agreementModel->load($id);
@@ -26,7 +52,7 @@ public function execute()
}
}
- $data = $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getAgreementData(true);
+ $data = $this->_session->getAgreementData(true);
if (!empty($data)) {
$agreementModel->setData($data);
}
@@ -38,7 +64,7 @@ public function execute()
$id ? __('Edit Condition') : __('New Condition')
)->_addContent(
$this->_view->getLayout()->createBlock(
- \Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit::class
+ BlockEdit::class
)->setData(
'action',
$this->getUrl('checkout/*/save')
diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php
index 25c034203620b..05a16d3dd4264 100644
--- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php
+++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php
@@ -6,8 +6,35 @@
*/
namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
-class Save extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement
+use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement;
+use Magento\CheckoutAgreements\Model\AgreementFactory;
+use Magento\Backend\App\Action\Context;
+use Magento\Framework\Registry;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\DataObject;
+use Magento\Framework\Exception\LocalizedException;
+
+class Save extends Agreement
{
+ /**
+ * @var AgreementFactory
+ */
+ private $agreementFactory;
+
+ /**
+ * @param Context $context
+ * @param Registry $coreRegistry
+ * @param AgreementFactory $agreementFactory
+ */
+ public function __construct(
+ Context $context,
+ Registry $coreRegistry,
+ AgreementFactory $agreementFactory = null
+ ) {
+ $this->agreementFactory = $agreementFactory ?:
+ ObjectManager::getInstance()->get(AgreementFactory::class);
+ parent::__construct($context, $coreRegistry);
+ }
/**
* @return void
*/
@@ -15,11 +42,11 @@ public function execute()
{
$postData = $this->getRequest()->getPostValue();
if ($postData) {
- $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class);
+ $model = $this->agreementFactory->create();
$model->setData($postData);
try {
- $validationResult = $model->validateData(new \Magento\Framework\DataObject($postData));
+ $validationResult = $model->validateData(new DataObject($postData));
if ($validationResult !== true) {
foreach ($validationResult as $message) {
$this->messageManager->addError($message);
@@ -30,13 +57,13 @@ public function execute()
$this->_redirect('checkout/*/');
return;
}
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ } catch (LocalizedException $e) {
$this->messageManager->addError($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addError(__('Something went wrong while saving this condition.'));
}
- $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setAgreementData($postData);
+ $this->_session->setAgreementData($postData);
$this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*')));
}
}
diff --git a/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php
new file mode 100644
index 0000000000000..fe600d64a7c48
--- /dev/null
+++ b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php
@@ -0,0 +1,77 @@
+isLoaded()) {
+ return $this;
+ }
+
+ parent::load($printQuery, $logQuery);
+
+ $this->addStoresToResult();
+
+ return $this;
+ }
+
+ /**
+ * @return void
+ */
+ private function addStoresToResult()
+ {
+ $stores = $this->getStoresForAgreements();
+
+ if (!empty($stores)) {
+ $storesByAgreementId = [];
+
+ foreach ($stores as $storeData) {
+ $storesByAgreementId[$storeData['agreement_id']][] = $storeData['store_id'];
+ }
+
+ foreach ($this as $item) {
+ $agreementId = $item->getData('agreement_id');
+
+ if (!isset($storesByAgreementId[$agreementId])) {
+ continue;
+ }
+
+ $item->setData('stores', $storesByAgreementId[$agreementId]);
+ }
+ }
+ }
+
+ /**
+ * @return array
+ */
+ private function getStoresForAgreements()
+ {
+ $agreementId = $this->getColumnValues('agreement_id');
+
+ if (!empty($agreementId)) {
+ $select = $this->getConnection()->select()->from(
+ ['agreement_store' => 'checkout_agreement_store']
+ )->where(
+ 'agreement_store.agreement_id IN (?)',
+ $agreementId
+ );
+
+ return $this->getConnection()->fetchAll($select);
+ }
+
+ return [];
+ }
+}
diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE.txt b/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE.txt
new file mode 100644
index 0000000000000..49525fd99da9c
--- /dev/null
+++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE.txt
@@ -0,0 +1,48 @@
+
+Open Software License ("OSL") v. 3.0
+
+This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Open Software License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under
" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE_AFL.txt
new file mode 100644
index 0000000000000..f39d641b18a19
--- /dev/null
+++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/LICENSE_AFL.txt
@@ -0,0 +1,48 @@
+
+Academic Free License ("AFL") v. 3.0
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/README.md b/app/code/Magento/CheckoutAgreements/Test/Mftf/README.md
new file mode 100644
index 0000000000000..593e89f08b5b5
--- /dev/null
+++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/README.md
@@ -0,0 +1,3 @@
+# Checkout Agreements Functional Tests
+
+The Functional Test Module for **Magento Checkout Agreements** module.
diff --git a/app/code/Magento/CheckoutAgreements/composer.json b/app/code/Magento/CheckoutAgreements/composer.json
index 9c8232b8534f3..a53558981e2f8 100644
--- a/app/code/Magento/CheckoutAgreements/composer.json
+++ b/app/code/Magento/CheckoutAgreements/composer.json
@@ -2,15 +2,15 @@
"name": "magento/module-checkout-agreements",
"description": "N/A",
"require": {
- "php": "7.0.2|7.0.4|~7.0.6|~7.1.0",
+ "php": "~7.0.13|~7.1.0",
"magento/module-checkout": "100.2.*",
- "magento/module-quote": "100.2.*",
+ "magento/module-quote": "101.0.*",
"magento/module-store": "100.2.*",
"magento/module-backend": "100.2.*",
- "magento/framework": "100.2.*"
+ "magento/framework": "101.0.*"
},
"type": "magento2-module",
- "version": "100.2.0-dev",
+ "version": "100.2.2",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml b/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml
index 1249ea44b991e..5a708f49a7034 100644
--- a/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml
+++ b/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml
@@ -7,7 +7,7 @@
-->
-
+
diff --git a/app/code/Magento/Cms/Api/GetUtilityPageIdentifiersInterface.php b/app/code/Magento/Cms/Api/GetUtilityPageIdentifiersInterface.php
new file mode 100644
index 0000000000000..c6bf4c8404701
--- /dev/null
+++ b/app/code/Magento/Cms/Api/GetUtilityPageIdentifiersInterface.php
@@ -0,0 +1,20 @@
+load($id);
$model->delete();
// display success message
- $this->messageManager->addSuccess(__('You deleted the block.'));
+ $this->messageManager->addSuccessMessage(__('You deleted the block.'));
// go to grid
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
// display error message
- $this->messageManager->addError($e->getMessage());
+ $this->messageManager->addErrorMessage($e->getMessage());
// go back to edit form
return $resultRedirect->setPath('*/*/edit', ['block_id' => $id]);
}
}
// display error message
- $this->messageManager->addError(__('We can\'t find a block to delete.'));
+ $this->messageManager->addErrorMessage(__('We can\'t find a block to delete.'));
// go to grid
return $resultRedirect->setPath('*/*/');
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php
index d4c0517621144..8756089063237 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/Edit.php
@@ -42,7 +42,7 @@ public function execute()
if ($id) {
$model->load($id);
if (!$model->getId()) {
- $this->messageManager->addError(__('This block no longer exists.'));
+ $this->messageManager->addErrorMessage(__('This block no longer exists.'));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php
index e267c568fa9e5..3a7e73fbe5eaa 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/InlineEdit.php
@@ -46,6 +46,7 @@ public function __construct(
/**
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php
index cff9c8a39b746..92bc7ad71f590 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php
@@ -60,7 +60,7 @@ public function execute()
$block->delete();
}
- $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
+ $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/Save.php
index 3eb790c83ad69..4332e2edd7057 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Block/Save.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/Save.php
@@ -7,10 +7,12 @@
namespace Magento\Cms\Controller\Adminhtml\Block;
use Magento\Backend\App\Action\Context;
+use Magento\Cms\Api\BlockRepositoryInterface;
use Magento\Cms\Model\Block;
+use Magento\Cms\Model\BlockFactory;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Exception\LocalizedException;
-use Magento\TestFramework\Inspection\Exception;
+use Magento\Framework\Registry;
class Save extends \Magento\Cms\Controller\Adminhtml\Block
{
@@ -19,17 +21,35 @@ class Save extends \Magento\Cms\Controller\Adminhtml\Block
*/
protected $dataPersistor;
+ /**
+ * @var BlockFactory
+ */
+ private $blockFactory;
+
+ /**
+ * @var BlockRepositoryInterface
+ */
+ private $blockRepository;
+
/**
* @param Context $context
- * @param \Magento\Framework\Registry $coreRegistry
+ * @param Registry $coreRegistry
* @param DataPersistorInterface $dataPersistor
+ * @param BlockFactory|null $blockFactory
+ * @param BlockRepositoryInterface|null $blockRepository
*/
public function __construct(
Context $context,
- \Magento\Framework\Registry $coreRegistry,
- DataPersistorInterface $dataPersistor
+ Registry $coreRegistry,
+ DataPersistorInterface $dataPersistor,
+ BlockFactory $blockFactory = null,
+ BlockRepositoryInterface $blockRepository = null
) {
$this->dataPersistor = $dataPersistor;
+ $this->blockFactory = $blockFactory
+ ?: \Magento\Framework\App\ObjectManager::getInstance()->get(BlockFactory::class);
+ $this->blockRepository = $blockRepository
+ ?: \Magento\Framework\App\ObjectManager::getInstance()->get(BlockRepositoryInterface::class);
parent::__construct($context, $coreRegistry);
}
@@ -45,8 +65,6 @@ public function execute()
$resultRedirect = $this->resultRedirectFactory->create();
$data = $this->getRequest()->getPostValue();
if ($data) {
- $id = $this->getRequest()->getParam('block_id');
-
if (isset($data['is_active']) && $data['is_active'] === 'true') {
$data['is_active'] = Block::STATUS_ENABLED;
}
@@ -55,27 +73,32 @@ public function execute()
}
/** @var \Magento\Cms\Model\Block $model */
- $model = $this->_objectManager->create(\Magento\Cms\Model\Block::class)->load($id);
- if (!$model->getId() && $id) {
- $this->messageManager->addError(__('This block no longer exists.'));
- return $resultRedirect->setPath('*/*/');
+ $model = $this->blockFactory->create();
+
+ $id = $this->getRequest()->getParam('block_id');
+ if ($id) {
+ try {
+ $model = $this->blockRepository->getById($id);
+ } catch (LocalizedException $e) {
+ $this->messageManager->addErrorMessage(__('This block no longer exists.'));
+ return $resultRedirect->setPath('*/*/');
+ }
}
$model->setData($data);
try {
- $model->save();
- $this->messageManager->addSuccess(__('You saved the block.'));
+ $this->blockRepository->save($model);
+ $this->messageManager->addSuccessMessage(__('You saved the block.'));
$this->dataPersistor->clear('cms_block');
-
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath('*/*/edit', ['block_id' => $model->getId()]);
}
return $resultRedirect->setPath('*/*/');
} catch (LocalizedException $e) {
- $this->messageManager->addError($e->getMessage());
+ $this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
- $this->messageManager->addException($e, __('Something went wrong while saving the block.'));
+ $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the block.'));
}
$this->dataPersistor->set('cms_block', $data);
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php
index c604e683f9aee..16c99e9857c33 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php
@@ -26,21 +26,26 @@ public function execute()
$id = $this->getRequest()->getParam('page_id');
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
+
if ($id) {
$title = "";
try {
// init model and delete
$model = $this->_objectManager->create(\Magento\Cms\Model\Page::class);
$model->load($id);
+
$title = $model->getTitle();
$model->delete();
+
// display success message
- $this->messageManager->addSuccess(__('The page has been deleted.'));
+ $this->messageManager->addSuccessMessage(__('The page has been deleted.'));
+
// go to grid
- $this->_eventManager->dispatch(
- 'adminhtml_cmspage_on_delete',
- ['title' => $title, 'status' => 'success']
- );
+ $this->_eventManager->dispatch('adminhtml_cmspage_on_delete', [
+ 'title' => $title,
+ 'status' => 'success'
+ ]);
+
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
$this->_eventManager->dispatch(
@@ -48,13 +53,15 @@ public function execute()
['title' => $title, 'status' => 'fail']
);
// display error message
- $this->messageManager->addError($e->getMessage());
+ $this->messageManager->addErrorMessage($e->getMessage());
// go back to edit form
return $resultRedirect->setPath('*/*/edit', ['page_id' => $id]);
}
}
+
// display error message
- $this->messageManager->addError(__('We can\'t find a page to delete.'));
+ $this->messageManager->addErrorMessage(__('We can\'t find a page to delete.'));
+
// go to grid
return $resultRedirect->setPath('*/*/');
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php
index ca50a935ce915..6d51c28b6aca7 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Edit.php
@@ -76,7 +76,7 @@ public function execute()
if ($id) {
$model->load($id);
if (!$model->getId()) {
- $this->messageManager->addError(__('This page no longer exists.'));
+ $this->messageManager->addErrorMessage(__('This page no longer exists.'));
/** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php
index 6d75f490d42dc..8774d7e69adfe 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php
@@ -57,6 +57,7 @@ public function __construct(
/**
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php
index a711f20d65639..a1d32aa97a382 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php
@@ -59,10 +59,11 @@ public function execute()
$page->delete();
}
- $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
+ $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
+
return $resultRedirect->setPath('*/*/');
}
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php
index e39c2115f961e..a85b8ecd5e5a1 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php
@@ -59,7 +59,9 @@ public function execute()
$item->save();
}
- $this->messageManager->addSuccess(__('A total of %1 record(s) have been disabled.', $collection->getSize()));
+ $this->messageManager->addSuccessMessage(
+ __('A total of %1 record(s) have been disabled.', $collection->getSize())
+ );
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php
index 8278c28a1e696..3f26769e4c9e9 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php
@@ -59,7 +59,9 @@ public function execute()
$item->save();
}
- $this->messageManager->addSuccess(__('A total of %1 record(s) have been enabled.', $collection->getSize()));
+ $this->messageManager->addSuccessMessage(
+ __('A total of %1 record(s) have been enabled.', $collection->getSize())
+ );
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php
index 7fd1ee6020937..9b8933c8dba2e 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php
@@ -6,6 +6,15 @@
*/
namespace Magento\Cms\Controller\Adminhtml\Page;
+use Magento\Cms\Model\Page\DomValidationState;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Config\Dom\ValidationException;
+use Magento\Framework\Config\Dom\ValidationSchemaException;
+
+/**
+ * Class PostDataProcessor
+ * @package Magento\Cms\Controller\Adminhtml\Page
+ */
class PostDataProcessor
{
/**
@@ -23,19 +32,28 @@ class PostDataProcessor
*/
protected $messageManager;
+ /**
+ * @var DomValidationState
+ */
+ private $validationState;
+
/**
* @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter
* @param \Magento\Framework\Message\ManagerInterface $messageManager
* @param \Magento\Framework\View\Model\Layout\Update\ValidatorFactory $validatorFactory
+ * @param DomValidationState $validationState
*/
public function __construct(
\Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter,
\Magento\Framework\Message\ManagerInterface $messageManager,
- \Magento\Framework\View\Model\Layout\Update\ValidatorFactory $validatorFactory
+ \Magento\Framework\View\Model\Layout\Update\ValidatorFactory $validatorFactory,
+ DomValidationState $validationState = null
) {
$this->dateFilter = $dateFilter;
$this->messageManager = $messageManager;
$this->validatorFactory = $validatorFactory;
+ $this->validationState = $validationState
+ ?: ObjectManager::getInstance()->get(DomValidationState::class);
}
/**
@@ -61,27 +79,27 @@ public function filter($data)
* Validate post data
*
* @param array $data
- * @return bool Return FALSE if someone item is invalid
+ * @return bool Return FALSE if some item is invalid
*/
public function validate($data)
{
- $errorNo = true;
if (!empty($data['layout_update_xml']) || !empty($data['custom_layout_update_xml'])) {
- /** @var $validatorCustomLayout \Magento\Framework\View\Model\Layout\Update\Validator */
- $validatorCustomLayout = $this->validatorFactory->create();
- if (!empty($data['layout_update_xml']) && !$validatorCustomLayout->isValid($data['layout_update_xml'])) {
- $errorNo = false;
- }
- if (!empty($data['custom_layout_update_xml'])
- && !$validatorCustomLayout->isValid($data['custom_layout_update_xml'])
- ) {
- $errorNo = false;
- }
- foreach ($validatorCustomLayout->getMessages() as $message) {
- $this->messageManager->addError($message);
+ /** @var $layoutXmlValidator \Magento\Framework\View\Model\Layout\Update\Validator */
+ $layoutXmlValidator = $this->validatorFactory->create(
+ [
+ 'validationState' => $this->validationState,
+ ]
+ );
+
+ if (!$this->validateData($data, $layoutXmlValidator)) {
+ $validatorMessages = $layoutXmlValidator->getMessages();
+ foreach ($validatorMessages as $message) {
+ $this->messageManager->addErrorMessage($message);
+ }
+ return false;
}
}
- return $errorNo;
+ return true;
}
/**
@@ -101,11 +119,42 @@ public function validateRequireEntry(array $data)
foreach ($data as $field => $value) {
if (in_array($field, array_keys($requiredFields)) && $value == '') {
$errorNo = false;
- $this->messageManager->addError(
+ $this->messageManager->addErrorMessage(
__('To apply changes you should fill in hidden required "%1" field', $requiredFields[$field])
);
}
}
return $errorNo;
}
+
+ /**
+ * Validate data, avoid cyclomatic complexity
+ *
+ * @param array $data
+ * @param \Magento\Framework\View\Model\Layout\Update\Validator $layoutXmlValidator
+ * @return bool
+ */
+ private function validateData($data, $layoutXmlValidator)
+ {
+ try {
+ if (!empty($data['layout_update_xml']) && !$layoutXmlValidator->isValid($data['layout_update_xml'])) {
+ return false;
+ }
+
+ if (!empty($data['custom_layout_update_xml']) &&
+ !$layoutXmlValidator->isValid($data['custom_layout_update_xml'])
+ ) {
+ return false;
+ }
+ } catch (ValidationException $e) {
+ return false;
+ } catch (ValidationSchemaException $e) {
+ return false;
+ } catch (\Exception $e) {
+ $this->messageManager->addExceptionMessage($e);
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
index 5644e25dd4c4a..1364e61816796 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php
@@ -46,7 +46,6 @@ class Save extends \Magento\Backend\App\Action
* @param DataPersistorInterface $dataPersistor
* @param \Magento\Cms\Model\PageFactory $pageFactory
* @param \Magento\Cms\Api\PageRepositoryInterface $pageRepository
- *
*/
public function __construct(
Action\Context $context,
@@ -90,11 +89,10 @@ public function execute()
$id = $this->getRequest()->getParam('page_id');
if ($id) {
- $model->load($id);
- if (!$model->getId()) {
+ try {
+ $model = $this->pageRepository->getById($id);
+ } catch (LocalizedException $e) {
$this->messageManager->addErrorMessage(__('This page no longer exists.'));
- /** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
- $resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php
index 19dc989620b89..890c9bf5eae52 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php
@@ -7,6 +7,9 @@
use Magento\Framework\App\Filesystem\DirectoryList;
+/**
+ * Delete image files.
+ */
class DeleteFiles extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
{
/**
@@ -19,6 +22,11 @@ class DeleteFiles extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
*/
protected $resultRawFactory;
+ /**
+ * @var \Magento\Framework\App\Filesystem\DirectoryResolver
+ */
+ private $directoryResolver;
+
/**
* Constructor
*
@@ -26,22 +34,28 @@ class DeleteFiles extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
* @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
+ * @param \Magento\Framework\App\Filesystem\DirectoryResolver|null $directoryResolver
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
- \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
+ \Magento\Framework\Controller\Result\RawFactory $resultRawFactory,
+ \Magento\Framework\App\Filesystem\DirectoryResolver $directoryResolver = null
) {
+ parent::__construct($context, $coreRegistry);
+
$this->resultRawFactory = $resultRawFactory;
$this->resultJsonFactory = $resultJsonFactory;
- parent::__construct($context, $coreRegistry);
+ $this->directoryResolver = $directoryResolver
+ ?: $this->_objectManager->get(\Magento\Framework\App\Filesystem\DirectoryResolver::class);
}
/**
- * Delete file from media storage
+ * Delete file from media storage.
*
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
@@ -54,6 +68,11 @@ public function execute()
/** @var $helper \Magento\Cms\Helper\Wysiwyg\Images */
$helper = $this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
$path = $this->getStorage()->getSession()->getCurrentPath();
+ if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Directory %1 is not under storage root path.', $path)
+ );
+ }
foreach ($files as $file) {
$file = $helper->idDecode($file);
/** @var \Magento\Framework\Filesystem $filesystem */
@@ -64,11 +83,13 @@ public function execute()
$this->getStorage()->deleteFile($filePath);
}
}
+
return $this->resultRawFactory->create();
} catch (\Exception $e) {
$result = ['error' => true, 'message' => $e->getMessage()];
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->resultJsonFactory->create();
+
return $resultJson->setData($result);
}
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php
index 8a89de87a6f85..a1de11c3c462e 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php
@@ -6,6 +6,11 @@
*/
namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images;
+use Magento\Framework\App\Filesystem\DirectoryList;
+
+/**
+ * Delete image folder.
+ */
class DeleteFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
{
/**
@@ -18,38 +23,55 @@ class DeleteFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
*/
protected $resultRawFactory;
+ /**
+ * @var \Magento\Framework\App\Filesystem\DirectoryResolver
+ */
+ private $directoryResolver;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
* @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
+ * @param \Magento\Framework\App\Filesystem\DirectoryResolver|null $directoryResolver
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
- \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
+ \Magento\Framework\Controller\Result\RawFactory $resultRawFactory,
+ \Magento\Framework\App\Filesystem\DirectoryResolver $directoryResolver = null
) {
+ parent::__construct($context, $coreRegistry);
$this->resultRawFactory = $resultRawFactory;
$this->resultJsonFactory = $resultJsonFactory;
- parent::__construct($context, $coreRegistry);
+ $this->directoryResolver = $directoryResolver
+ ?: $this->_objectManager->get(\Magento\Framework\App\Filesystem\DirectoryResolver::class);
}
/**
- * Delete folder action
+ * Delete folder action.
*
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
try {
$path = $this->getStorage()->getCmsWysiwygImages()->getCurrentPath();
+ if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Directory %1 is not under storage root path.', $path)
+ );
+ }
$this->getStorage()->deleteDirectory($path);
+
return $this->resultRawFactory->create();
} catch (\Exception $e) {
$result = ['error' => true, 'message' => $e->getMessage()];
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->resultJsonFactory->create();
+
return $resultJson->setData($result);
}
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php
index 525fd31052db8..13765e9faca04 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Index.php
@@ -39,7 +39,7 @@ public function execute()
try {
$this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class)->getCurrentPath();
} catch (\Exception $e) {
- $this->messageManager->addError($e->getMessage());
+ $this->messageManager->addErrorMessage($e->getMessage());
}
$this->_initAction();
/** @var \Magento\Framework\View\Result\Layout $resultLayout */
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php
index 2124bdabe6009..7816e29405f27 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php
@@ -6,6 +6,11 @@
*/
namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images;
+use Magento\Framework\App\Filesystem\DirectoryList;
+
+/**
+ * Creates new folder.
+ */
class NewFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
{
/**
@@ -13,24 +18,35 @@ class NewFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
*/
protected $resultJsonFactory;
+ /**
+ * @var \Magento\Framework\App\Filesystem\DirectoryResolver
+ */
+ private $directoryResolver;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
+ * @param \Magento\Framework\App\Filesystem\DirectoryResolver|null $directoryResolver
+ * @throws \RuntimeException
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
- \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
+ \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
+ \Magento\Framework\App\Filesystem\DirectoryResolver $directoryResolver = null
) {
- $this->resultJsonFactory = $resultJsonFactory;
parent::__construct($context, $coreRegistry);
+ $this->resultJsonFactory = $resultJsonFactory;
+ $this->directoryResolver = $directoryResolver
+ ?: $this->_objectManager->get(\Magento\Framework\App\Filesystem\DirectoryResolver::class);
}
/**
- * New folder action
+ * New folder action.
*
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
@@ -38,12 +54,18 @@ public function execute()
$this->_initAction();
$name = $this->getRequest()->getPost('name');
$path = $this->getStorage()->getSession()->getCurrentPath();
+ if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Directory %1 is not under storage root path.', $path)
+ );
+ }
$result = $this->getStorage()->createDirectory($name, $path);
} catch (\Exception $e) {
$result = ['error' => true, 'message' => $e->getMessage()];
}
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->resultJsonFactory->create();
+
return $resultJson->setData($result);
}
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php
index 2daaf39d58d14..dda3940cd9ba5 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php
@@ -35,7 +35,7 @@ public function __construct(
public function execute()
{
$helper = $this->_objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
- $storeId = $this->getRequest()->getParam('store');
+ $storeId = (int)$this->getRequest()->getParam('store');
$filename = $this->getRequest()->getParam('filename');
$filename = $helper->idDecode($filename);
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php
index 7a94c4ab6aa12..5c9aa2243bc6d 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php
@@ -6,6 +6,11 @@
*/
namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images;
+use Magento\Framework\App\Filesystem\DirectoryList;
+
+/**
+ * Upload image.
+ */
class Upload extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
{
/**
@@ -13,36 +18,52 @@ class Upload extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images
*/
protected $resultJsonFactory;
+ /**
+ * @var \Magento\Framework\App\Filesystem\DirectoryResolver
+ */
+ private $directoryResolver;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
+ * @param \Magento\Framework\App\Filesystem\DirectoryResolver|null $directoryResolver
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
- \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
+ \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
+ \Magento\Framework\App\Filesystem\DirectoryResolver $directoryResolver = null
) {
- $this->resultJsonFactory = $resultJsonFactory;
parent::__construct($context, $coreRegistry);
+ $this->resultJsonFactory = $resultJsonFactory;
+ $this->directoryResolver = $directoryResolver
+ ?: $this->_objectManager->get(\Magento\Framework\App\Filesystem\DirectoryResolver::class);
}
/**
- * Files upload processing
+ * Files upload processing.
*
* @return \Magento\Framework\Controller\ResultInterface
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function execute()
{
try {
$this->_initAction();
- $targetPath = $this->getStorage()->getSession()->getCurrentPath();
- $result = $this->getStorage()->uploadFile($targetPath, $this->getRequest()->getParam('type'));
+ $path = $this->getStorage()->getSession()->getCurrentPath();
+ if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Directory %1 is not under storage root path.', $path)
+ );
+ }
+ $result = $this->getStorage()->uploadFile($path, $this->getRequest()->getParam('type'));
} catch (\Exception $e) {
$result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
}
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->resultJsonFactory->create();
+
return $resultJson->setData($result);
}
}
diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php
index 8e20feb0f058f..c027bd1a2b717 100644
--- a/app/code/Magento/Cms/Controller/Index/Index.php
+++ b/app/code/Magento/Cms/Controller/Index/Index.php
@@ -1,27 +1,55 @@
resultForwardFactory = $resultForwardFactory;
+ $this->scopeConfig = $scopeConfig ? : ObjectManager::getInstance()->get(ScopeConfigInterface::class);
+ $this->page = $page ? : ObjectManager::getInstance()->get(Page::class);
parent::__construct($context);
}
@@ -29,20 +57,17 @@ public function __construct(
* Renders CMS Home page
*
* @param string|null $coreRoute
- * @return \Magento\Framework\Controller\Result\Forward
+ *
+ * @return bool|ResponseInterface|Forward|ResultInterface|ResultPage
+ *
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute($coreRoute = null)
{
- $pageId = $this->_objectManager->get(
- \Magento\Framework\App\Config\ScopeConfigInterface::class
- )->getValue(
- \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE
- );
- $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId);
+ $pageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE);
+ $resultPage = $this->page->prepareResultPage($this, $pageId);
if (!$resultPage) {
- /** @var \Magento\Framework\Controller\Result\Forward $resultForward */
+ /** @var Forward $resultForward */
$resultForward = $this->resultForwardFactory->create();
$resultForward->forward('defaultIndex');
return $resultForward;
diff --git a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php
index f830910ca1e4f..18c7d14cc8fcf 100644
--- a/app/code/Magento/Cms/Helper/Wysiwyg/Images.php
+++ b/app/code/Magento/Cms/Helper/Wysiwyg/Images.php
@@ -8,7 +8,9 @@
use Magento\Framework\App\Filesystem\DirectoryList;
/**
- * Wysiwyg Images Helper
+ * Wysiwyg Images Helper.
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Images extends \Magento\Framework\App\Helper\AbstractHelper
{
@@ -127,17 +129,23 @@ public function convertPathToId($path)
}
/**
- * Decode HTML element id
+ * Decode HTML element id.
*
* @param string $id
* @return string
+ * @throws \InvalidArgumentException When path contains restricted symbols.
*/
public function convertIdToPath($id)
{
if ($id === \Magento\Theme\Helper\Storage::NODE_ROOT) {
return $this->getStorageRoot();
} else {
- return $this->getStorageRoot() . $this->idDecode($id);
+ $path = $this->getStorageRoot() . $this->idDecode($id);
+ if (strpos($path, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
+ throw new \InvalidArgumentException('Path is invalid');
+ }
+
+ return $path;
}
}
@@ -148,7 +156,7 @@ public function convertIdToPath($id)
*/
public function isUsingStaticUrlsAllowed()
{
- $checkResult = new \StdClass();
+ $checkResult = new \stdClass();
$checkResult->isAllowed = false;
$this->_eventManager->dispatch(
'cms_wysiwyg_images_static_urls_allowed',
@@ -177,7 +185,14 @@ public function getImageHtmlDeclaration($filename, $renderAsTag = false)
$html = $fileurl; // $mediaPath;
} else {
$directive = $this->urlEncoder->encode($directive);
- $html = $this->_backendData->getUrl('cms/wysiwyg/directive', ['___directive' => $directive]);
+
+ $html = $this->_backendData->getUrl(
+ 'cms/wysiwyg/directive',
+ [
+ '___directive' => $directive,
+ '_escape_params' => false,
+ ]
+ );
}
}
return $html;
diff --git a/app/code/Magento/Cms/Model/Block.php b/app/code/Magento/Cms/Model/Block.php
index fc369971054d7..3795409a95d2b 100644
--- a/app/code/Magento/Cms/Model/Block.php
+++ b/app/code/Magento/Cms/Model/Block.php
@@ -6,7 +6,6 @@
namespace Magento\Cms\Model;
use Magento\Cms\Api\Data\BlockInterface;
-use Magento\Cms\Model\ResourceModel\Block as ResourceCmsBlock;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Model\AbstractModel;
@@ -58,6 +57,11 @@ protected function _construct()
public function beforeSave()
{
$needle = 'block_id="' . $this->getId() . '"';
+
+ if ($this->hasDataChanges()) {
+ $this->setUpdateTime(null);
+ }
+
if (false == strstr($this->getContent(), $needle)) {
return parent::beforeSave();
}
diff --git a/app/code/Magento/Cms/Model/Config/Source/Block.php b/app/code/Magento/Cms/Model/Config/Source/Block.php
new file mode 100644
index 0000000000000..e89daf381d94a
--- /dev/null
+++ b/app/code/Magento/Cms/Model/Config/Source/Block.php
@@ -0,0 +1,46 @@
+collectionFactory = $collectionFactory;
+ }
+
+ /**
+ * To option array
+ *
+ * @return array
+ */
+ public function toOptionArray()
+ {
+ if (!$this->options) {
+ $this->options = $this->collectionFactory->create()->toOptionIdArray();
+ }
+ return $this->options;
+ }
+}
diff --git a/app/code/Magento/Cms/Model/GetUtilityPageIdentifiers.php b/app/code/Magento/Cms/Model/GetUtilityPageIdentifiers.php
new file mode 100644
index 0000000000000..09c68ee9cf82d
--- /dev/null
+++ b/app/code/Magento/Cms/Model/GetUtilityPageIdentifiers.php
@@ -0,0 +1,54 @@
+scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Get List Page Identifiers
+ * @return array
+ */
+ public function execute()
+ {
+ $homePageIdentifier = $this->scopeConfig->getValue(
+ 'web/default/cms_home_page',
+ ScopeInterface::SCOPE_STORE
+ );
+ $noRouteIdentifier = $this->scopeConfig->getValue(
+ 'web/default/cms_no_route',
+ ScopeInterface::SCOPE_STORE
+ );
+
+ $noCookieIdentifier = $this->scopeConfig->getValue(
+ 'web/default/cms_no_cookies',
+ ScopeInterface::SCOPE_STORE
+ );
+
+ return [$homePageIdentifier, $noRouteIdentifier, $noCookieIdentifier];
+ }
+}
diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php
index 591f8d93fcdc6..d950f484cd1d9 100644
--- a/app/code/Magento/Cms/Model/Page.php
+++ b/app/code/Magento/Cms/Model/Page.php
@@ -6,12 +6,11 @@
namespace Magento\Cms\Model;
use Magento\Cms\Api\Data\PageInterface;
-use Magento\Cms\Model\ResourceModel\Page as ResourceCmsPage;
+use Magento\Cms\Helper\Page as PageHelper;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Model\AbstractModel;
-use Magento\Cms\Helper\Page as PageHelper;
/**
* Cms Page Model
@@ -547,6 +546,10 @@ public function beforeSave()
$originalIdentifier = $this->getOrigData('identifier');
$currentIdentifier = $this->getIdentifier();
+ if ($this->hasDataChanges()) {
+ $this->setUpdateTime(null);
+ }
+
if (!$this->getId() || $originalIdentifier === $currentIdentifier) {
return parent::beforeSave();
}
diff --git a/app/code/Magento/Cms/Model/Page/DomValidationState.php b/app/code/Magento/Cms/Model/Page/DomValidationState.php
new file mode 100644
index 0000000000000..b08ab6342fc5d
--- /dev/null
+++ b/app/code/Magento/Cms/Model/Page/DomValidationState.php
@@ -0,0 +1,26 @@
+getStoreId())) {
+ if ($page->getStoreId() === null) {
$storeId = $this->storeManager->getStore()->getId();
$page->setStoreId($storeId);
}
diff --git a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php
index cd5945c2f47cb..2a67378614445 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php
@@ -176,4 +176,32 @@ public function getSelectCountSql()
return $countSelect;
}
+
+ /**
+ * Returns pairs identifier - title for unique identifiers
+ * and pairs identifier|entity_id - title for non-unique after first
+ *
+ * @return array
+ */
+ public function toOptionIdArray()
+ {
+ $res = [];
+ $existingIdentifiers = [];
+ foreach ($this as $item) {
+ $identifier = $item->getData('identifier');
+
+ $data['value'] = $identifier;
+ $data['label'] = $item->getData('title');
+
+ if (in_array($identifier, $existingIdentifiers)) {
+ $data['value'] .= '|' . $item->getData($this->getIdFieldName());
+ } else {
+ $existingIdentifiers[] = $identifier;
+ }
+
+ $res[] = $data;
+ }
+
+ return $res;
+ }
}
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php
index c301d8440ecd5..9aab54b02bc14 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Block.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php
@@ -7,10 +7,10 @@
use Magento\Cms\Api\Data\BlockInterface;
use Magento\Framework\DB\Select;
+use Magento\Framework\EntityManager\EntityManager;
+use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Model\AbstractModel;
-use Magento\Framework\EntityManager\MetadataPool;
-use Magento\Framework\EntityManager\EntityManager;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Magento\Framework\Model\ResourceModel\Db\Context;
use Magento\Store\Model\Store;
@@ -183,10 +183,10 @@ public function getIsUniqueBlockToStores(AbstractModel $object)
$entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class);
$linkField = $entityMetadata->getLinkField();
- if ($this->_storeManager->hasSingleStore()) {
+ if ($this->_storeManager->isSingleStoreMode()) {
$stores = [Store::DEFAULT_STORE_ID];
} else {
- $stores = (array)$object->getData('stores');
+ $stores = (array)$object->getData('store_id');
}
$select = $this->getConnection()->select()
@@ -230,7 +230,7 @@ public function lookupStoreIds($id)
'cbs.' . $linkField . ' = cb.' . $linkField,
[]
)
- ->where('cb.' . $entityMetadata->getIdentifierField() . ' = :block_id');
+ ->where('cb.' . $entityMetadata->getIdentifierField() . ' = :block_id');
return $connection->fetchCol($select, ['block_id' => (int)$id]);
}
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php
index 3c586a56d90cc..f22367393030a 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php
@@ -18,6 +18,20 @@ class Collection extends AbstractCollection
*/
protected $_idFieldName = 'block_id';
+ /**
+ * Event prefix
+ *
+ * @var string
+ */
+ protected $_eventPrefix = 'cms_block_collection';
+
+ /**
+ * Event object
+ *
+ * @var string
+ */
+ protected $_eventObject = 'block_collection';
+
/**
* Perform operations after collection load
*
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php
index f45d9ee223106..60e87afc61884 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php
@@ -6,7 +6,7 @@
namespace Magento\Cms\Model\ResourceModel\Block\Grid;
use Magento\Framework\Api\Search\SearchResultInterface;
-use Magento\Framework\Search\AggregationInterface;
+use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Cms\Model\ResourceModel\Block\Collection as BlockCollection;
/**
@@ -82,6 +82,7 @@ public function getAggregations()
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
+ return $this;
}
/**
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page.php b/app/code/Magento/Cms/Model/ResourceModel/Page.php
index 8e26c8b67fa4b..b836cf199632d 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Page.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Page.php
@@ -6,18 +6,18 @@
namespace Magento\Cms\Model\ResourceModel;
+use Magento\Cms\Api\Data\PageInterface;
use Magento\Cms\Model\Page as CmsPage;
use Magento\Framework\DB\Select;
+use Magento\Framework\EntityManager\EntityManager;
+use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Model\AbstractModel;
-use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Magento\Framework\Model\ResourceModel\Db\Context;
use Magento\Framework\Stdlib\DateTime;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
-use Magento\Framework\EntityManager\EntityManager;
-use Magento\Cms\Api\Data\PageInterface;
/**
* Cms page mysql resource
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php
index 7250eeaa47c0e..4ccc2c8f6e77d 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php
@@ -25,6 +25,20 @@ class Collection extends AbstractCollection
*/
protected $_previewFlag;
+ /**
+ * Event prefix
+ *
+ * @var string
+ */
+ protected $_eventPrefix = 'cms_page_collection';
+
+ /**
+ * Event object
+ *
+ * @var string
+ */
+ protected $_eventObject = 'page_collection';
+
/**
* Define resource model
*
@@ -37,34 +51,6 @@ protected function _construct()
$this->_map['fields']['store'] = 'store_table.store_id';
}
- /**
- * Returns pairs identifier - title for unique identifiers
- * and pairs identifier|page_id - title for non-unique after first
- *
- * @return array
- */
- public function toOptionIdArray()
- {
- $res = [];
- $existingIdentifiers = [];
- foreach ($this as $item) {
- $identifier = $item->getData('identifier');
-
- $data['value'] = $identifier;
- $data['label'] = $item->getData('title');
-
- if (in_array($identifier, $existingIdentifiers)) {
- $data['value'] .= '|' . $item->getData('page_id');
- } else {
- $existingIdentifiers[] = $identifier;
- }
-
- $res[] = $data;
- }
-
- return $res;
- }
-
/**
* Set first store flag
*
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php
index c5c43c3120dcc..19f945e5b4637 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php
@@ -83,6 +83,7 @@ public function getAggregations()
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
+ return $this;
}
/**
diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
index 0688534dc4ad9..2cd1647a1bf22 100644
--- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
+++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Cms\Model\Wysiwyg\Images;
use Magento\Cms\Helper\Wysiwyg\Images;
@@ -474,6 +476,7 @@ public function deleteFile($target)
* @param string $type Type of storage, e.g. image, media etc.
* @return array File info Array
* @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Exception
*/
public function uploadFile($targetPath, $type = null)
{
@@ -485,6 +488,9 @@ public function uploadFile($targetPath, $type = null)
}
$uploader->setAllowRenameFiles(true);
$uploader->setFilesDispersion(false);
+ if (!$uploader->checkMimeType($this->getAllowedMimeTypes($type))) {
+ throw new \Magento\Framework\Exception\LocalizedException(__('File validation failed.'));
+ }
$result = $uploader->save($targetPath);
if (!$result) {
@@ -494,14 +500,6 @@ public function uploadFile($targetPath, $type = null)
// create thumbnail
$this->resizeFile($targetPath . '/' . $uploader->getUploadedFileName(), true);
- $result['cookie'] = [
- 'name' => $this->getSession()->getName(),
- 'value' => $this->getSession()->getSessionId(),
- 'lifetime' => $this->getSession()->getCookieLifetime(),
- 'path' => $this->getSession()->getCookiePath(),
- 'domain' => $this->getSession()->getCookieDomain(),
- ];
-
return $result;
}
@@ -560,10 +558,10 @@ public function getThumbnailUrl($filePath, $checkFile = false)
* Create thumbnail for image and save it to thumbnails directory
*
* @param string $source Image path to be resized
- * @param bool $keepRation Keep aspect ratio or not
+ * @param bool $keepRatio Keep aspect ratio or not
* @return bool|string Resized filepath or false if errors were occurred
*/
- public function resizeFile($source, $keepRation = true)
+ public function resizeFile($source, $keepRatio = true)
{
$realPath = $this->_directory->getRelativePath($source);
if (!$this->_directory->isFile($realPath) || !$this->_directory->isExist($realPath)) {
@@ -580,7 +578,7 @@ public function resizeFile($source, $keepRation = true)
}
$image = $this->_imageFactory->create();
$image->open($source);
- $image->keepAspectRatio($keepRation);
+ $image->keepAspectRatio($keepRatio);
$image->resize($this->_resizeParameters['width'], $this->_resizeParameters['height']);
$dest = $targetDir . '/' . pathinfo($source, PATHINFO_BASENAME);
$image->save($dest);
@@ -641,11 +639,7 @@ public function getSession()
*/
public function getAllowedExtensions($type = null)
{
- if (is_string($type) && array_key_exists("{$type}_allowed", $this->_extensions)) {
- $allowed = $this->_extensions["{$type}_allowed"];
- } else {
- $allowed = $this->_extensions['allowed'];
- }
+ $allowed = $this->getExtensionsList($type);
return array_keys(array_filter($allowed));
}
@@ -735,7 +729,7 @@ protected function _validatePath($path)
*/
protected function _sanitizePath($path)
{
- return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPath($path)), '/');
+ return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPathSafety($path)), '/');
}
/**
@@ -751,4 +745,33 @@ protected function _getRelativePathToRoot($path)
strlen($this->_sanitizePath($this->_cmsWysiwygImages->getStorageRoot()))
);
}
+
+ /**
+ * Prepare mime types config settings
+ *
+ * @param string|null $type Type of storage, e.g. image, media etc.
+ * @return array Array of allowed file extensions
+ */
+ private function getAllowedMimeTypes($type = null): array
+ {
+ $allowed = $this->getExtensionsList($type);
+
+ return array_values(array_filter($allowed));
+ }
+
+ /**
+ * Get list of allowed file extensions with mime type in values
+ *
+ * @param string|null $type
+ * @return array
+ */
+ private function getExtensionsList($type = null): array
+ {
+ if (is_string($type) && array_key_exists("{$type}_allowed", $this->_extensions)) {
+ $allowed = $this->_extensions["{$type}_allowed"];
+ } else {
+ $allowed = $this->_extensions['allowed'];
+ }
+ return $allowed;
+ }
}
diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml
new file mode 100644
index 0000000000000..48b10d0ee48be
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml
new file mode 100644
index 0000000000000..05e61ac86e166
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml
new file mode 100644
index 0000000000000..cb019006f7a0a
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsPageData.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+ Test CMS Page
+ Test Content Heading
+ Sample page content. Yada yada yada.
+ test-page-
+
+
+ Test CMS Page
+ Test Content Heading
+ Sample page content. Yada yada yada.
+ test-page-
+
+
+ Image1
+ 1.00
+ Upload File
+ Yes
+ magento.jpg
+ Image content. Yeah.
+ 1000
+
+
+ Image1
+ 1.00
+ Upload File
+ Yes
+ magento2.jpg
+ Image content. Yeah.
+ 1000
+
+
+ Test
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/LICENSE.txt b/app/code/Magento/Cms/Test/Mftf/LICENSE.txt
new file mode 100644
index 0000000000000..49525fd99da9c
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/LICENSE.txt
@@ -0,0 +1,48 @@
+
+Open Software License ("OSL") v. 3.0
+
+This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Open Software License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/Cms/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/Cms/Test/Mftf/LICENSE_AFL.txt
new file mode 100644
index 0000000000000..f39d641b18a19
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/LICENSE_AFL.txt
@@ -0,0 +1,48 @@
+
+Academic Free License ("AFL") v. 3.0
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml
new file mode 100644
index 0000000000000..bb8dc9cf9159c
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml
new file mode 100644
index 0000000000000..9dcb3d608d04e
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsPagesPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml
new file mode 100644
index 0000000000000..571eb702bacfc
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/README.md b/app/code/Magento/Cms/Test/Mftf/README.md
new file mode 100644
index 0000000000000..5e223390c07cd
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/README.md
@@ -0,0 +1,3 @@
+# Cms Functional Tests
+
+The Functional Test Module for **Magento Cms** module.
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml
new file mode 100644
index 0000000000000..ce22bae4e2b93
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml
new file mode 100644
index 0000000000000..f60fced7d05d4
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml
new file mode 100644
index 0000000000000..cf07d1003b7c2
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageBasicFieldsSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml
new file mode 100644
index 0000000000000..3440805d3814b
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageContentSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml
new file mode 100644
index 0000000000000..0fe9c01d36fcb
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageSeoSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml
new file mode 100644
index 0000000000000..63069c2ccf264
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml
new file mode 100644
index 0000000000000..39face57e5c86
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
index 97988a5676842..a27110ca96b6d 100644
--- a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
@@ -233,6 +233,9 @@ public function testPrepareElementHtml($elementValue, $modelBlockId)
$this->assertEquals($this->elementMock, $this->this->prepareElementHtml($this->elementMock));
}
+ /**
+ * @return array
+ */
public function prepareElementHtmlDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php
index 174e3a68b7c66..7b91d54ec3aa1 100644
--- a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php
@@ -236,6 +236,9 @@ public function testPrepareElementHtml($elementValue, $cmsPageId)
$this->assertEquals($this->elementMock, $this->this->prepareElementHtml($this->elementMock));
}
+ /**
+ * @return array
+ */
public function prepareElementHtmlDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php
index ff1ed408eb131..55e8382d9ca23 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php
@@ -134,10 +134,10 @@ public function testDeleteAction()
->with($this->blockId);
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('You deleted the block.'));
$this->messageManagerMock->expects($this->never())
- ->method('addError');
+ ->method('addErrorMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
@@ -154,10 +154,10 @@ public function testDeleteActionNoId()
->willReturn(null);
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('We can\'t find a block to delete.'));
$this->messageManagerMock->expects($this->never())
- ->method('addSuccess');
+ ->method('addSuccessMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
@@ -181,10 +181,10 @@ public function testDeleteActionThrowsException()
->willThrowException(new \Exception(__($errorMsg)));
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with($errorMsg);
$this->messageManagerMock->expects($this->never())
- ->method('addSuccess');
+ ->method('addSuccessMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php
index 875dde9fb226b..a28a1b793d943 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/EditTest.php
@@ -139,7 +139,7 @@ public function testEditActionBlockNoExists()
->willReturn(null);
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('This block no longer exists.'));
$this->resultRedirectFactoryMock->expects($this->atLeastOnce())
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php
index 2dc14154c85e5..39a7d0d74e4d8 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php
@@ -68,9 +68,9 @@ public function testMassDeleteAction()
->willReturn(new \ArrayIterator($collection));
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('A total of %1 record(s) have been deleted.', $deletedBlocksCount));
- $this->messageManagerMock->expects($this->never())->method('addError');
+ $this->messageManagerMock->expects($this->never())->method('addErrorMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php
index f6b709a5c96c9..40ed379e9d7e2 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php
@@ -65,6 +65,16 @@ class SaveTest extends \PHPUnit\Framework\TestCase
*/
protected $saveController;
+ /**
+ * @var \Magento\Cms\Model\BlockFactory|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $blockFactory;
+
+ /**
+ * @var \Magento\Cms\Api\BlockRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $blockRepository;
+
/**
* @var int
*/
@@ -129,11 +139,22 @@ protected function setUp()
->method('getResultRedirectFactory')
->willReturn($this->resultRedirectFactory);
+ $this->blockFactory = $this->getMockBuilder(\Magento\Cms\Model\BlockFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->blockRepository = $this->getMockBuilder(\Magento\Cms\Api\BlockRepositoryInterface::class)
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
$this->saveController = $this->objectManager->getObject(
\Magento\Cms\Controller\Adminhtml\Block\Save::class,
[
'context' => $this->contextMock,
'dataPersistor' => $this->dataPersistorMock,
+ 'blockFactory' => $this->blockFactory,
+ 'blockRepository' => $this->blockRepository,
]
);
}
@@ -158,26 +179,24 @@ public function testSaveAction()
]
);
- $this->objectManagerMock->expects($this->atLeastOnce())
+ $this->blockFactory->expects($this->atLeastOnce())
->method('create')
- ->with($this->equalTo(\Magento\Cms\Model\Block::class))
->willReturn($this->blockMock);
- $this->blockMock->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $this->blockMock->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->blockRepository->expects($this->once())
+ ->method('getById')
+ ->with($this->blockId)
+ ->willReturn($this->blockMock);
+
$this->blockMock->expects($this->once())->method('setData');
- $this->blockMock->expects($this->once())->method('save');
+ $this->blockRepository->expects($this->once())->method('save')->with($this->blockMock);
$this->dataPersistorMock->expects($this->any())
->method('clear')
->with('cms_block');
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('You saved the block.'));
$this->resultRedirect->expects($this->atLeastOnce())->method('setPath')->with('*/*/') ->willReturnSelf();
@@ -204,20 +223,17 @@ public function testSaveActionNoId()
]
);
- $this->objectManagerMock->expects($this->atLeastOnce())
+ $this->blockFactory->expects($this->atLeastOnce())
->method('create')
- ->with($this->equalTo(\Magento\Cms\Model\Block::class))
->willReturn($this->blockMock);
- $this->blockMock->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $this->blockMock->expects($this->any())
- ->method('getId')
- ->willReturn(false);
+ $this->blockRepository->expects($this->once())
+ ->method('getById')
+ ->with($this->blockId)
+ ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('Error message')));
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('This block no longer exists.'));
$this->resultRedirect->expects($this->atLeastOnce())->method('setPath')->with('*/*/') ->willReturnSelf();
@@ -237,22 +253,20 @@ public function testSaveAndContinue()
]
);
- $this->objectManagerMock->expects($this->atLeastOnce())
+ $this->blockFactory->expects($this->atLeastOnce())
->method('create')
- ->with($this->equalTo(\Magento\Cms\Model\Block::class))
->willReturn($this->blockMock);
- $this->blockMock->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $this->blockMock->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->blockRepository->expects($this->once())
+ ->method('getById')
+ ->with($this->blockId)
+ ->willReturn($this->blockMock);
+
$this->blockMock->expects($this->once())->method('setData');
- $this->blockMock->expects($this->once())->method('save');
+ $this->blockRepository->expects($this->once())->method('save')->with($this->blockMock);
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('You saved the block.'));
$this->dataPersistorMock->expects($this->any())
@@ -279,24 +293,24 @@ public function testSaveActionThrowsException()
]
);
- $this->objectManagerMock->expects($this->atLeastOnce())
+ $this->blockFactory->expects($this->atLeastOnce())
->method('create')
- ->with($this->equalTo(\Magento\Cms\Model\Block::class))
->willReturn($this->blockMock);
- $this->blockMock->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $this->blockMock->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->blockRepository->expects($this->once())
+ ->method('getById')
+ ->with($this->blockId)
+ ->willReturn($this->blockMock);
+
$this->blockMock->expects($this->once())->method('setData');
- $this->blockMock->expects($this->once())->method('save')->willThrowException(new \Exception('Error message.'));
+ $this->blockRepository->expects($this->once())->method('save')
+ ->with($this->blockMock)
+ ->willThrowException(new \Exception('Error message.'));
$this->messageManagerMock->expects($this->never())
- ->method('addSuccess');
+ ->method('addSuccessMessage');
$this->messageManagerMock->expects($this->once())
- ->method('addException');
+ ->method('addExceptionMessage');
$this->dataPersistorMock->expects($this->any())
->method('set')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php
index 7f994bf5b3df5..09b36bc41d405 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php
@@ -124,10 +124,10 @@ public function testDeleteAction()
->method('delete');
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('The page has been deleted.'));
$this->messageManagerMock->expects($this->never())
- ->method('addError');
+ ->method('addErrorMessage');
$this->eventManagerMock->expects($this->once())
->method('dispatch')
@@ -151,10 +151,10 @@ public function testDeleteActionNoId()
->willReturn(null);
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('We can\'t find a page to delete.'));
$this->messageManagerMock->expects($this->never())
- ->method('addSuccess');
+ ->method('addSuccessMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
@@ -195,10 +195,10 @@ public function testDeleteActionThrowsException()
);
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with($errorMsg);
$this->messageManagerMock->expects($this->never())
- ->method('addSuccess');
+ ->method('addSuccessMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php
index 335abb837523a..5ea5ce5a9fdbb 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/EditTest.php
@@ -139,7 +139,7 @@ public function testEditActionPageNoExists()
->willReturn(null);
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('This page no longer exists.'));
$this->resultRedirectFactoryMock->expects($this->atLeastOnce())
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php
index 8f1a651b0a7e1..f51ab152ba2a4 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php
@@ -68,9 +68,9 @@ public function testMassDeleteAction()
->willReturn(new \ArrayIterator($collection));
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('A total of %1 record(s) have been deleted.', $deletedPagesCount));
- $this->messageManagerMock->expects($this->never())->method('addError');
+ $this->messageManagerMock->expects($this->never())->method('addErrorMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php
index 0185654434be1..5b80dd1873d5c 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php
@@ -67,9 +67,9 @@ public function testMassDisableAction()
->willReturn(new \ArrayIterator($collection));
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('A total of %1 record(s) have been disabled.', $disabledPagesCount));
- $this->messageManagerMock->expects($this->never())->method('addError');
+ $this->messageManagerMock->expects($this->never())->method('addErrorMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php
index b5907e7b3ffed..16b3dfe4ee638 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php
@@ -67,9 +67,9 @@ public function testMassEnableAction()
->willReturn(new \ArrayIterator($collection));
$this->messageManagerMock->expects($this->once())
- ->method('addSuccess')
+ ->method('addSuccessMessage')
->with(__('A total of %1 record(s) have been enabled.', $enabledPagesCount));
- $this->messageManagerMock->expects($this->never())->method('addError');
+ $this->messageManagerMock->expects($this->never())->method('addErrorMessage');
$this->resultRedirectMock->expects($this->once())
->method('setPath')
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
index 03a8fc0969064..a98dd392f6263 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php
@@ -153,12 +153,7 @@ public function testSaveAction()
->method('create')
->willReturn($page);
- $page->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $page->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page);
$page->expects($this->once())->method('setData');
$this->pageRepository->expects($this->once())->method('save')->with($page);
@@ -182,6 +177,36 @@ public function testSaveActionWithoutData()
$this->assertSame($this->resultRedirect, $this->saveController->execute());
}
+ public function testSaveActionNoId()
+ {
+ $this->requestMock->expects($this->any())->method('getPostValue')->willReturn(['page_id' => 1]);
+ $this->requestMock->expects($this->atLeastOnce())
+ ->method('getParam')
+ ->willReturnMap(
+ [
+ ['page_id', null, 1],
+ ['back', null, false],
+ ]
+ );
+
+ $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->pageFactory->expects($this->atLeastOnce())
+ ->method('create')
+ ->willReturn($page);
+ $this->pageRepository->expects($this->once())
+ ->method('getById')
+ ->with($this->pageId)
+ ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('Error message')));
+ $this->messageManagerMock->expects($this->once())
+ ->method('addErrorMessage')
+ ->with(__('This page no longer exists.'));
+ $this->resultRedirect->expects($this->atLeastOnce())->method('setPath')->with('*/*/') ->willReturnSelf();
+ $this->assertSame($this->resultRedirect, $this->saveController->execute());
+ }
+
public function testSaveAndContinue()
{
$this->requestMock->expects($this->any())->method('getPostValue')->willReturn(['page_id' => $this->pageId]);
@@ -204,12 +229,7 @@ public function testSaveAndContinue()
->method('create')
->willReturn($page);
- $page->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $page->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page);
$page->expects($this->once())->method('setData');
$this->pageRepository->expects($this->once())->method('save')->with($page);
@@ -251,12 +271,7 @@ public function testSaveActionThrowsException()
->method('create')
->willReturn($page);
- $page->expects($this->any())
- ->method('load')
- ->willReturnSelf();
- $page->expects($this->any())
- ->method('getId')
- ->willReturn(true);
+ $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page);
$page->expects($this->once())->method('setData');
$this->pageRepository->expects($this->once())->method('save')->with($page)
->willThrowException(new \Exception('Error message.'));
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php
index 8ff206e8a80fc..d8453bc6ecbce 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php
@@ -84,7 +84,9 @@ protected function setUp()
'response' => $responseMock,
'objectManager' => $objectManagerMock,
'request' => $this->requestMock,
- 'resultForwardFactory' => $this->forwardFactoryMock
+ 'resultForwardFactory' => $this->forwardFactoryMock,
+ 'scopeConfig' => $scopeConfigMock,
+ 'page' => $this->cmsHelperMock
]
);
}
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php
index 31d99df5f6289..d13dfc628201d 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Page/PostDataProcessorTest.php
@@ -65,7 +65,7 @@ public function testValidateRequireEntry()
'title' => ''
];
$this->messageManagerMock->expects($this->once())
- ->method('addError')
+ ->method('addErrorMessage')
->with(__('To apply changes you should fill in hidden required "%1" field', 'Page Title'));
$this->assertFalse($this->postDataProcessor->validateRequireEntry($postData));
diff --git a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php
index 8b41f0e3ac0d4..c50f33caa6bc2 100644
--- a/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Helper/PageTest.php
@@ -367,6 +367,9 @@ public function testPrepareResultPage(
);
}
+ /**
+ * @return array
+ */
public function renderPageExtendedDataProvider()
{
return [
@@ -467,6 +470,9 @@ public function testGetPageUrl(
$this->assertEquals($expectedResult, $this->object->getPageUrl($pageId));
}
+ /**
+ * @return array
+ */
public function getPageUrlDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php b/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php
index 1fcc10609870a..ba32f41c62f24 100644
--- a/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Helper/Wysiwyg/ImagesTest.php
@@ -230,6 +230,15 @@ public function testConvertIdToPathNodeRoot()
$this->assertEquals($this->imagesHelper->getStorageRoot(), $this->imagesHelper->convertIdToPath($pathId));
}
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Path is invalid
+ */
+ public function testConvertIdToPathInvalid()
+ {
+ $this->imagesHelper->convertIdToPath('Ly4uLy4uLy4uLy4uLy4uL3dvcms-');
+ }
+
/**
* @param string $fileName
* @param int $maxLength
@@ -293,7 +302,7 @@ protected function generalSettingsIsUsingStaticUrlsAllowed($allowedValue)
{
$storeId = 1;
$this->imagesHelper->setStoreId($storeId);
- $checkResult = new \StdClass();
+ $checkResult = new \stdClass();
$checkResult->isAllowed = false;
$this->eventManagerMock->expects($this->any())
->method('dispatch')
@@ -375,6 +384,9 @@ public function testGetCurrentPathThrowException()
$this->fail('An expected exception has not been raised.');
}
+ /**
+ * @return array
+ */
public function providerGetCurrentPath()
{
return [
@@ -422,6 +434,9 @@ public function testGetImageHtmlDeclarationRenderingAsTag($baseUrl, $fileName, $
$this->assertEquals($expectedHtml, $this->imagesHelper->getImageHtmlDeclaration($fileName, true));
}
+ /**
+ * @return array
+ */
public function providerGetImageHtmlDeclarationRenderingAsTag()
{
return [
@@ -450,12 +465,15 @@ public function testGetImageHtmlDeclaration($baseUrl, $fileName, $isUsingStaticU
$this->backendDataMock->expects($this->any())
->method('getUrl')
- ->with('cms/wysiwyg/directive', ['___directive' => $directive])
+ ->with('cms/wysiwyg/directive', ['___directive' => $directive, '_escape_params' => false])
->willReturn($directive);
$this->assertEquals($expectedHtml, $this->imagesHelper->getImageHtmlDeclaration($fileName));
}
+ /**
+ * @return array
+ */
public function providerGetImageHtmlDeclaration()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php b/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php
new file mode 100644
index 0000000000000..abfaa8701458c
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Unit/Model/BlockTest.php
@@ -0,0 +1,339 @@
+resourceMock = $this->createMock(BlockResource::class);
+ $this->eventManagerMock = $this->createMock(ManagerInterface::class);
+ $this->contextMock = $this->createMock(Context::class);
+ $this->contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($this->eventManagerMock);
+ $this->objectManager = new ObjectManager($this);
+ $this->blockModel = $this->objectManager->getObject(
+ Block::class,
+ [
+ 'context' => $this->contextMock,
+ 'resource' => $this->resourceMock,
+ ]
+ );
+ }
+
+ /**
+ * Test beforeSave method
+ *
+ * @return void
+ *
+ * @throws LocalizedException
+ */
+ public function testBeforeSave()
+ {
+ $blockId = 7;
+ $this->blockModel->setData(Block::BLOCK_ID, $blockId);
+ $this->blockModel->setData(Block::CONTENT, 'test');
+ $this->objectManager->setBackwardCompatibleProperty($this->blockModel, '_hasDataChanges', true);
+ $this->eventManagerMock->expects($this->atLeastOnce())->method('dispatch');
+
+ $expected = $this->blockModel;
+ $actual = $this->blockModel->beforeSave();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test beforeSave method
+ *
+ * @return void
+ *
+ * @throws LocalizedException
+ */
+ public function testBeforeSaveWithException()
+ {
+ $blockId = 10;
+ $this->blockModel->setData(Block::BLOCK_ID, $blockId);
+ $this->blockModel->setData(Block::CONTENT, 'Test block_id="' . $blockId . '".');
+ $this->objectManager->setBackwardCompatibleProperty($this->blockModel, '_hasDataChanges', false);
+ $this->eventManagerMock->expects($this->never())->method('dispatch');
+ $this->expectException(LocalizedException::class);
+ $this->blockModel->beforeSave();
+ }
+
+ /**
+ * Test getIdentities method
+ *
+ * @return void
+ */
+ public function testGetIdentities()
+ {
+ $result = $this->blockModel->getIdentities();
+ self::assertInternalType('array', $result);
+ }
+
+ /**
+ * Test getId method
+ *
+ * @return void
+ */
+ public function testGetId()
+ {
+ $blockId = 12;
+ $this->blockModel->setData(Block::BLOCK_ID, $blockId);
+ $expected = $blockId;
+ $actual = $this->blockModel->getId();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getIdentifier method
+ *
+ * @return void
+ */
+ public function testGetIdentifier()
+ {
+ $identifier = 'test01';
+ $this->blockModel->setData(Block::IDENTIFIER, $identifier);
+
+ $expected = $identifier;
+ $actual = $this->blockModel->getIdentifier();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getTitle method
+ *
+ * @return void
+ */
+ public function testGetTitle()
+ {
+ $title = 'test02';
+ $this->blockModel->setData(Block::TITLE, $title);
+ $expected = $title;
+ $actual = $this->blockModel->getTitle();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getContent method
+ *
+ * @return void
+ */
+ public function testGetContent()
+ {
+ $content = 'test03';
+ $this->blockModel->setData(Block::CONTENT, $content);
+ $expected = $content;
+ $actual = $this->blockModel->getContent();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getCreationTime method
+ *
+ * @return void
+ */
+ public function testGetCreationTime()
+ {
+ $creationTime = 'test04';
+ $this->blockModel->setData(Block::CREATION_TIME, $creationTime);
+ $expected = $creationTime;
+ $actual = $this->blockModel->getCreationTime();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getUpdateTime method
+ *
+ * @return void
+ */
+ public function testGetUpdateTime()
+ {
+ $updateTime = 'test05';
+ $this->blockModel->setData(Block::UPDATE_TIME, $updateTime);
+ $expected = $updateTime;
+ $actual = $this->blockModel->getUpdateTime();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test isActive method
+ *
+ * @return void
+ */
+ public function testIsActive()
+ {
+ $isActive = true;
+ $this->blockModel->setData(Block::IS_ACTIVE, $isActive);
+ $result = $this->blockModel->isActive();
+ self::assertTrue($result);
+ }
+
+ /**
+ * Test setId method
+ *
+ * @return void
+ */
+ public function testSetId()
+ {
+ $blockId = 15;
+ $this->blockModel->setId($blockId);
+ $expected = $blockId;
+ $actual = $this->blockModel->getData(Block::BLOCK_ID);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setIdentifier method
+ *
+ * @return void
+ */
+ public function testSetIdentifier()
+ {
+ $identifier = 'test06';
+ $this->blockModel->setIdentifier($identifier);
+ $expected = $identifier;
+ $actual = $this->blockModel->getData(Block::IDENTIFIER);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setTitle method
+ *
+ * @return void
+ */
+ public function testSetTitle()
+ {
+ $title = 'test07';
+ $this->blockModel->setTitle($title);
+ $expected = $title;
+ $actual = $this->blockModel->getData(Block::TITLE);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setContent method
+ *
+ * @return void
+ */
+ public function testSetContent()
+ {
+ $content = 'test08';
+ $this->blockModel->setContent($content);
+ $expected = $content;
+ $actual = $this->blockModel->getData(Block::CONTENT);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setCreationTime method
+ *
+ * @return void
+ */
+ public function testSetCreationTime()
+ {
+ $creationTime = 'test09';
+ $this->blockModel->setCreationTime($creationTime);
+ $expected = $creationTime;
+ $actual = $this->blockModel->getData(Block::CREATION_TIME);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setUpdateTime method
+ *
+ * @return void
+ */
+ public function testSetUpdateTime()
+ {
+ $updateTime = 'test10';
+ $this->blockModel->setUpdateTime($updateTime);
+ $expected = $updateTime;
+ $actual = $this->blockModel->getData(Block::UPDATE_TIME);
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test setIsActive method
+ *
+ * @return void
+ */
+ public function testSetIsActive()
+ {
+ $this->blockModel->setIsActive(false);
+ $result = $this->blockModel->getData(Block::IS_ACTIVE);
+ self::assertFalse($result);
+ }
+
+ /**
+ * Test getStores method
+ *
+ * @return void
+ */
+ public function testGetStores()
+ {
+ $stores = [1, 4, 9];
+ $this->blockModel->setData('stores', $stores);
+ $expected = $stores;
+ $actual = $this->blockModel->getStores();
+ self::assertEquals($expected, $actual);
+ }
+
+ /**
+ * Test getAvailableStatuses method
+ *
+ * @return void
+ */
+ public function testGetAvailableStatuses()
+ {
+ $result = $this->blockModel->getAvailableStatuses();
+ self::assertInternalType('array', $result);
+ }
+}
diff --git a/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php
new file mode 100644
index 0000000000000..7d9162bd56776
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Unit/Model/Config/Source/BlockTest.php
@@ -0,0 +1,64 @@
+collectionFactory = $this->createPartialMock(
+ \Magento\Cms\Model\ResourceModel\Block\CollectionFactory::class,
+ ['create']
+ );
+
+ $this->block = $objectManager->getObject(
+ \Magento\Cms\Model\Config\Source\Block::class,
+ [
+ 'collectionFactory' => $this->collectionFactory,
+ ]
+ );
+ }
+
+ /**
+ * Run test toOptionArray method
+ *
+ * @return void
+ */
+ public function testToOptionArray()
+ {
+ $blockCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Block\Collection::class);
+
+ $this->collectionFactory->expects($this->once())
+ ->method('create')
+ ->will($this->returnValue($blockCollectionMock));
+
+ $blockCollectionMock->expects($this->once())
+ ->method('toOptionIdArray')
+ ->will($this->returnValue('return-value'));
+
+ $this->assertEquals('return-value', $this->block->toOptionArray());
+ }
+}
diff --git a/app/code/Magento/Cms/Test/Unit/Model/GetUtilityPageIdentifiersTest.php b/app/code/Magento/Cms/Test/Unit/Model/GetUtilityPageIdentifiersTest.php
new file mode 100644
index 0000000000000..0cf923345c083
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Unit/Model/GetUtilityPageIdentifiersTest.php
@@ -0,0 +1,63 @@
+scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+ $this->getUtilityPageIdentifiers = new GetUtilityPageIdentifiers($this->scopeConfigMock);
+ }
+
+ /**
+ * Test execute method
+ *
+ * @return void
+ */
+ public function testExecute()
+ {
+ $homePageIdentifier = 'home';
+ $noRouteIdentifier = 'no_route';
+ $noCookieIdentifier = 'no_cookie';
+
+ $this->scopeConfigMock->expects($this->exactly(3))->method('getValue')->willReturnMap([
+ ['web/default/cms_home_page', ScopeInterface::SCOPE_STORE, null, $homePageIdentifier],
+ ['web/default/cms_no_route', ScopeInterface::SCOPE_STORE, null, $noRouteIdentifier],
+ ['web/default/cms_no_cookies', ScopeInterface::SCOPE_STORE, null, $noCookieIdentifier],
+ ]);
+
+ $expected = [$homePageIdentifier, $noRouteIdentifier, $noCookieIdentifier];
+ $actual = $this->getUtilityPageIdentifiers->execute();
+ self::assertEquals($expected, $actual);
+ }
+}
diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/CollectionTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/CollectionTest.php
index b9b0d6f772c62..26b5d74ffb961 100644
--- a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/CollectionTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/CollectionTest.php
@@ -119,6 +119,9 @@ public function testAfterLoad($item, $storesData)
$this->assertEquals($expectedResult[$item->getId()], $item->getStoreId());
}
+ /**
+ * @return array
+ */
public function getItemsDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/CollectionTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/CollectionTest.php
index dd31650cb3a3a..6d45e7bf6ab1d 100644
--- a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/CollectionTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/CollectionTest.php
@@ -119,6 +119,9 @@ public function testAfterLoad($item, $storesData)
$this->assertEquals($expectedResult[$item->getId()], $item->getStoreId());
}
+ /**
+ * @return array
+ */
public function getItemsDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/ConfigTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/ConfigTest.php
index d633c5a21fe32..7a5a6fe7b0c8c 100644
--- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/ConfigTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/ConfigTest.php
@@ -187,6 +187,9 @@ public function testGetConfig($data, $isAuthorizationAllowed, $expectedResults)
$this->assertEquals('localhost/pub/static/', $config->getData('baseStaticDefaultUrl'));
}
+ /**
+ * @return array
+ */
public function getConfigDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
index a2178489e1298..906a7d4fbc605 100644
--- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
@@ -107,6 +107,13 @@ class StorageTest extends \PHPUnit\Framework\TestCase
*/
protected $objectManagerHelper;
+ private $allowedImageExtensions = [
+ 'jpg' => 'image/jpg',
+ 'jpeg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'gif' => 'image/png'
+ ];
+
/**
* @return void
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -114,20 +121,13 @@ class StorageTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
$this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class);
- $this->driverMock = $this->getMockForAbstractClass(
- \Magento\Framework\Filesystem\DriverInterface::class,
- [],
- '',
- false,
- false,
- true,
- ['getRealPath']
- );
- $this->driverMock->expects($this->any())->method('getRealPath')->will($this->returnArgument(0));
+ $this->driverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class)
+ ->setMethods(['getRealPathSafety'])
+ ->getMockForAbstractClass();
$this->directoryMock = $this->createPartialMock(
\Magento\Framework\Filesystem\Directory\Write::class,
- ['delete', 'getDriver', 'create']
+ ['delete', 'getDriver', 'create', 'getRelativePath', 'isExist', 'isFile']
);
$this->directoryMock->expects(
$this->any()
@@ -151,7 +151,7 @@ protected function setUp()
$this->adapterFactoryMock = $this->createMock(\Magento\Framework\Image\AdapterFactory::class);
$this->imageHelperMock = $this->createPartialMock(
\Magento\Cms\Helper\Wysiwyg\Images::class,
- ['getStorageRoot']
+ ['getStorageRoot', 'getCurrentPath']
);
$this->imageHelperMock->expects(
$this->any()
@@ -182,12 +182,21 @@ protected function setUp()
$this->uploaderFactoryMock = $this->getMockBuilder(\Magento\MediaStorage\Model\File\UploaderFactory::class)
->disableOriginalConstructor()
->getMock();
- $this->sessionMock = $this->createMock(\Magento\Backend\Model\Session::class);
+ $this->sessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class)
+ ->setMethods(
+ ['getCurrentPath', 'getName', 'getSessionId', 'getCookieLifetime', 'getCookiePath', 'getCookieDomain']
+ )
+ ->disableOriginalConstructor()
+ ->getMock();
$this->backendUrlMock = $this->createMock(\Magento\Backend\Model\Url::class);
$this->coreFileStorageMock = $this->getMockBuilder(\Magento\MediaStorage\Helper\File\Storage\Database::class)
->disableOriginalConstructor()
->getMock();
+ $allowedExtensions = [
+ 'allowed' => $this->allowedImageExtensions,
+ 'image_allowed' => $this->allowedImageExtensions
+ ];
$this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
@@ -210,7 +219,8 @@ protected function setUp()
'dirs' => [
'exclude' => [],
'include' => []
- ]
+ ],
+ 'extensions' => $allowedExtensions
]
);
}
@@ -240,6 +250,7 @@ public function testDeleteDirectoryOverRoot()
\Magento\Framework\Exception\LocalizedException::class,
sprintf('Directory %s is not under storage root path.', self::INVALID_DIRECTORY_OVER_ROOT)
);
+ $this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0));
$this->imagesStorage->deleteDirectory(self::INVALID_DIRECTORY_OVER_ROOT);
}
@@ -252,6 +263,7 @@ public function testDeleteRootDirectory()
\Magento\Framework\Exception\LocalizedException::class,
sprintf('We can\'t delete root directory %s right now.', self::STORAGE_ROOT_DIR)
);
+ $this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0));
$this->imagesStorage->deleteDirectory(self::STORAGE_ROOT_DIR);
}
@@ -415,4 +427,76 @@ protected function generalTestGetDirsCollection($path, $collectionArray = [], $e
$this->imagesStorage->getDirsCollection($path);
}
+
+ public function testUploadFile()
+ {
+ $targetPath = '/target/path';
+ $fileName = 'image.gif';
+ $realPath = $targetPath . '/' . $fileName;
+ $thumbnailTargetPath = self::STORAGE_ROOT_DIR . '/.thumbs';
+ $thumbnailDestination = $thumbnailTargetPath . '/' . $fileName;
+ $type = 'image';
+ $result = [
+ 'result'
+ ];
+ $uploader = $this->getMockBuilder(\Magento\MediaStorage\Model\File\Uploader::class)
+ ->disableOriginalConstructor()
+ ->setMethods(
+ [
+ 'setAllowedExtensions',
+ 'setAllowRenameFiles',
+ 'setFilesDispersion',
+ 'checkMimeType',
+ 'save',
+ 'getUploadedFileName'
+ ]
+ )
+ ->getMock();
+ $this->uploaderFactoryMock->expects($this->atLeastOnce())->method('create')->with(['fileId' => 'image'])
+ ->willReturn($uploader);
+ $uploader->expects($this->atLeastOnce())->method('setAllowedExtensions')
+ ->with(array_keys($this->allowedImageExtensions))->willReturnSelf();
+ $uploader->expects($this->atLeastOnce())->method('setAllowRenameFiles')->with(true)->willReturnSelf();
+ $uploader->expects($this->atLeastOnce())->method('setFilesDispersion')->with(false)
+ ->willReturnSelf();
+ $uploader->expects($this->atLeastOnce())->method('checkMimeType')
+ ->with(array_values($this->allowedImageExtensions))->willReturnSelf();
+ $uploader->expects($this->atLeastOnce())->method('save')->with($targetPath)->willReturn($result);
+ $uploader->expects($this->atLeastOnce())->method('getUploadedFileName')->willReturn($fileName);
+
+ $this->directoryMock->expects($this->atLeastOnce())->method('getRelativePath')->willReturnMap(
+ [
+ [$realPath, $realPath],
+ [$thumbnailTargetPath, $thumbnailTargetPath],
+ [$thumbnailDestination, $thumbnailDestination]
+ ]
+ );
+ $this->directoryMock->expects($this->atLeastOnce())->method('isFile')
+ ->willReturnMap(
+ [
+ [$realPath, true],
+ [$thumbnailDestination, true]
+ ]
+ );
+ $this->directoryMock->expects($this->atLeastOnce())->method('isExist')
+ ->willReturnMap(
+ [
+ [$realPath, true],
+ [$thumbnailTargetPath, true]
+ ]
+ );
+
+ $image = $this->getMockBuilder(\Magento\Catalog\Model\Product\Image::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['open', 'keepAspectRatio', 'resize', 'save'])
+ ->getMock();
+ $image->expects($this->atLeastOnce())->method('open')->with($realPath);
+ $image->expects($this->atLeastOnce())->method('keepAspectRatio')->with(true);
+ $image->expects($this->atLeastOnce())->method('resize')->with(100, 50);
+ $image->expects($this->atLeastOnce())->method('save')->with($thumbnailDestination);
+
+ $this->adapterFactoryMock->expects($this->atLeastOnce())->method('create')->willReturn($image);
+
+ $this->assertEquals($result, $this->imagesStorage->uploadFile($targetPath, $type));
+ }
}
diff --git a/app/code/Magento/Cms/Test/Unit/Observer/NoCookiesObserverTest.php b/app/code/Magento/Cms/Test/Unit/Observer/NoCookiesObserverTest.php
index 8c09d42ec556e..cbb13c6f254eb 100644
--- a/app/code/Magento/Cms/Test/Unit/Observer/NoCookiesObserverTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Observer/NoCookiesObserverTest.php
@@ -139,6 +139,9 @@ public function testNoCookies($pageUrl)
$this->assertEquals($this->noCookiesObserver, $this->noCookiesObserver->execute($this->observerMock));
}
+ /**
+ * @return array
+ */
public function noCookiesDataProvider()
{
return [
diff --git a/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php b/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php
new file mode 100644
index 0000000000000..406b40fbc1647
--- /dev/null
+++ b/app/code/Magento/Cms/Ui/Component/AddFilterInterface.php
@@ -0,0 +1,26 @@
+meta = array_replace_recursive($meta, $this->prepareMetadata());
+ $this->additionalFilterPool = $additionalFilterPool;
}
/**
@@ -95,4 +105,16 @@ public function prepareMetadata()
return $metadata;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function addFilter(Filter $filter)
+ {
+ if (!empty($this->additionalFilterPool[$filter->getField()])) {
+ $this->additionalFilterPool[$filter->getField()]->addFilter($this->searchCriteriaBuilder, $filter);
+ } else {
+ parent::addFilter($filter);
+ }
+ }
}
diff --git a/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php b/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php
new file mode 100644
index 0000000000000..9b0c69a4f10c4
--- /dev/null
+++ b/app/code/Magento/Cms/Ui/Component/Page/FulltextFilter.php
@@ -0,0 +1,44 @@
+filterBuilder = $filterBuilder;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function addFilter(SearchCriteriaBuilder $searchCriteriaBuilder, Filter $filter)
+ {
+ $titleFilter = $this->filterBuilder->setField('title')
+ ->setValue(sprintf('%%%s%%', $filter->getValue()))
+ ->setConditionType('like')
+ ->create();
+ $searchCriteriaBuilder->addFilter($titleFilter);
+ }
+}
diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json
index 7a8651e7948bb..64e97e0a38e18 100644
--- a/app/code/Magento/Cms/composer.json
+++ b/app/code/Magento/Cms/composer.json
@@ -2,23 +2,23 @@
"name": "magento/module-cms",
"description": "N/A",
"require": {
- "php": "7.0.2|7.0.4|~7.0.6|~7.1.0",
+ "php": "~7.0.13|~7.1.0",
"magento/module-store": "100.2.*",
"magento/module-theme": "100.2.*",
- "magento/module-widget": "100.2.*",
+ "magento/module-widget": "101.0.*",
"magento/module-backend": "100.2.*",
- "magento/module-catalog": "101.1.*",
+ "magento/module-catalog": "102.0.*",
"magento/module-email": "100.2.*",
- "magento/module-ui": "100.2.*",
+ "magento/module-ui": "101.0.*",
"magento/module-variable": "100.2.*",
"magento/module-media-storage": "100.2.*",
- "magento/framework": "100.2.*"
+ "magento/framework": "101.0.*"
},
"suggest": {
"magento/module-cms-sample-data": "Sample Data version:100.2.*"
},
"type": "magento2-module",
- "version": "101.1.0-dev",
+ "version": "102.0.6",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml
index 8309e3b5b6150..e11f7bf5933e6 100644
--- a/app/code/Magento/Cms/etc/di.xml
+++ b/app/code/Magento/Cms/etc/di.xml
@@ -14,6 +14,7 @@
+
@@ -30,24 +31,24 @@
-
-
- 1
- - 1
- - 1
- - 1
+ - image/jpg
+ - image/jpeg
+ - image/png
+ - image/gif
-
-
- 1
- - 1
- - 1
- - 1
+ - image/jpg
+ - image/jpeg
+ - image/png
+ - image/gif
-
-
- 1
- - 1
- - 1
- - 1
- - 1
- - 1
+ - video/x-flv
+ - application/x-shockwave-flash
+ - video/x-msvideo
+ - video/x-sgi-movie
+ - application/vnd.rn-realmedia
+ - video/x-ms-wmv
@@ -188,4 +189,12 @@
Magento\Cms\Model\Api\SearchCriteria\BlockCollectionProcessor
+
+
+
+
+ - Magento\Cms\Ui\Component\Page\FulltextFilter
+
+
+
diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv
index 983493bb7732a..e4989777593f8 100644
--- a/app/code/Magento/Cms/i18n/en_US.csv
+++ b/app/code/Magento/Cms/i18n/en_US.csv
@@ -133,7 +133,7 @@ Block,Block
"Enable Page","Enable Page"
Content,Content
"Content Heading","Content Heading"
-"Search Engine Optimisation","Search Engine Optimisation"
+"Search Engine Optimization","Search Engine Optimization"
"Meta Title","Meta Title"
"Meta Keywords","Meta Keywords"
"Meta Description","Meta Description"
diff --git a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml
index 1bc8828ef6c8e..5bf66ef302f04 100644
--- a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml
+++ b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml
@@ -9,7 +9,11 @@
-
+
+
+ Magento\Backend\Block\DataProviders\UploadConfig
+
+
diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml
index 3b3b6dd4a6ca8..b26188f25f3ef 100644
--- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml
+++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml
@@ -7,6 +7,14 @@
// @codingStandardsIgnoreFile
/** @var $block \Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader */
+
+$resizeConfig = $block->getImageUploadConfigData()->getIsResizeEnabled()
+ ? "{action: 'resize', maxWidth: "
+ . $block->getImageUploadMaxWidth()
+ . ", maxHeight: "
+ . $block->getImageUploadMaxHeight()
+ . "}"
+ : "{action: 'resize'}";
?>
@@ -99,11 +107,9 @@ require([
action: 'load',
fileTypes: /^image\/(gif|jpeg|png)$/,
maxFileSize: = (int) $block->getFileSizeService()->getMaxFileSize() ?> * 10
- }, {
- action: 'resize',
- maxWidth: = (float) \Magento\Framework\File\Uploader::MAX_IMAGE_WIDTH ?> ,
- maxHeight: = (float) \Magento\Framework\File\Uploader::MAX_IMAGE_HEIGHT ?>
- }, {
+ },
+ = /* @noEscape */ $resizeConfig ?>,
+ {
action: 'save'
}]
});
diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
index f8c4f07bd4336..5441481f6cea2 100644
--- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
+++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml
@@ -141,7 +141,7 @@
true
- Search Engine Optimisation
+ Search Engine Optimization
diff --git a/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE.txt b/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE.txt
new file mode 100644
index 0000000000000..49525fd99da9c
--- /dev/null
+++ b/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE.txt
@@ -0,0 +1,48 @@
+
+Open Software License ("OSL") v. 3.0
+
+This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Open Software License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE_AFL.txt
new file mode 100644
index 0000000000000..f39d641b18a19
--- /dev/null
+++ b/app/code/Magento/CmsUrlRewrite/Test/Mftf/LICENSE_AFL.txt
@@ -0,0 +1,48 @@
+
+Academic Free License ("AFL") v. 3.0
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/app/code/Magento/CmsUrlRewrite/Test/Mftf/README.md b/app/code/Magento/CmsUrlRewrite/Test/Mftf/README.md
new file mode 100644
index 0000000000000..4b377286964b1
--- /dev/null
+++ b/app/code/Magento/CmsUrlRewrite/Test/Mftf/README.md
@@ -0,0 +1,3 @@
+# Cms Url Rewrite Functional Tests
+
+The Functional Test Module for **Magento Cms Url Rewrite** module.
diff --git a/app/code/Magento/CmsUrlRewrite/composer.json b/app/code/Magento/CmsUrlRewrite/composer.json
index a19d7b262f8c2..414dcdf54921d 100644
--- a/app/code/Magento/CmsUrlRewrite/composer.json
+++ b/app/code/Magento/CmsUrlRewrite/composer.json
@@ -2,14 +2,14 @@
"name": "magento/module-cms-url-rewrite",
"description": "N/A",
"require": {
- "php": "7.0.2|7.0.4|~7.0.6|~7.1.0",
+ "php": "~7.0.13|~7.1.0",
"magento/module-store": "100.2.*",
- "magento/module-cms": "101.1.*",
- "magento/module-url-rewrite": "100.2.*",
- "magento/framework": "100.2.*"
+ "magento/module-cms": "102.0.*",
+ "magento/module-url-rewrite": "101.0.*",
+ "magento/framework": "101.0.*"
},
"type": "magento2-module",
- "version": "100.2.0-dev",
+ "version": "100.2.2",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/CmsUrlRewrite/etc/adminhtml/di.xml b/app/code/Magento/CmsUrlRewrite/etc/di.xml
similarity index 100%
rename from app/code/Magento/CmsUrlRewrite/etc/adminhtml/di.xml
rename to app/code/Magento/CmsUrlRewrite/etc/di.xml
diff --git a/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php b/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php
index b33c944c73477..7f2f771a8d0a6 100644
--- a/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php
+++ b/app/code/Magento/Config/App/Config/Source/RuntimeConfigSource.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Config\App\Config\Source;
use Magento\Framework\App\Config\ConfigSourceInterface;
@@ -88,12 +89,12 @@ private function loadConfig()
}
}
- foreach ($config as $scope => &$item) {
+ foreach ($config as $scope => $item) {
if ($scope === ScopeConfigInterface::SCOPE_TYPE_DEFAULT) {
- $item = $this->converter->convert($item);
+ $config[$scope] = $this->converter->convert($item);
} else {
- foreach ($item as &$scopeItems) {
- $scopeItems = $this->converter->convert($scopeItems);
+ foreach ($item as $scopeCode => $scopeItems) {
+ $config[$scope][$scopeCode] = $this->converter->convert($scopeItems);
}
}
}
diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php
index ed645bb63febc..83c61f90f789a 100644
--- a/app/code/Magento/Config/App/Config/Type/System.php
+++ b/app/code/Magento/Config/App/Config/Type/System.php
@@ -5,58 +5,48 @@
*/
namespace Magento\Config\App\Config\Type;
+use Magento\Framework\App\Config\ConfigSourceInterface;
use Magento\Framework\App\Config\ConfigTypeInterface;
+use Magento\Framework\App\Config\Spi\PostProcessorInterface;
+use Magento\Framework\App\Config\Spi\PreProcessorInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Config\App\Config\Type\System\Reader;
+use Magento\Framework\Serialize\Serializer\Sensitive as SensitiveSerializer;
+use Magento\Framework\Serialize\Serializer\SensitiveFactory as SensitiveSerializerFactory;
+use Magento\Framework\App\ScopeInterface;
+use Magento\Framework\Cache\FrontendInterface;
+use Magento\Framework\Serialize\SerializerInterface;
+use Magento\Store\Model\Config\Processor\Fallback;
+use Magento\Store\Model\ScopeInterface as StoreScope;
/**
* System configuration type
+ *
* @api
* @since 100.1.2
*/
class System implements ConfigTypeInterface
{
const CACHE_TAG = 'config_scopes';
-
const CONFIG_TYPE = 'system';
- /**
- * @var \Magento\Framework\App\Config\ConfigSourceInterface
- */
- private $source;
-
/**
* @var array
*/
private $data = [];
/**
- * @var \Magento\Framework\App\Config\Spi\PostProcessorInterface
+ * @var PostProcessorInterface
*/
private $postProcessor;
/**
- * @var \Magento\Framework\App\Config\Spi\PreProcessorInterface
- */
- private $preProcessor;
-
- /**
- * @var \Magento\Framework\Cache\FrontendInterface
+ * @var FrontendInterface
*/
private $cache;
/**
- * @var int
- */
- private $cachingNestedLevel;
-
- /**
- * @var \Magento\Store\Model\Config\Processor\Fallback
- */
- private $fallback;
-
- /**
- * @var \Magento\Framework\Serialize\SerializerInterface
+ * @var SensitiveSerializer
*/
private $serializer;
@@ -79,39 +69,47 @@ class System implements ConfigTypeInterface
*
* @var array
*/
- private $availableDataScopes = null;
+ private $availableDataScopes;
/**
- * @param \Magento\Framework\App\Config\ConfigSourceInterface $source
- * @param \Magento\Framework\App\Config\Spi\PostProcessorInterface $postProcessor
- * @param \Magento\Store\Model\Config\Processor\Fallback $fallback
- * @param \Magento\Framework\Cache\FrontendInterface $cache
- * @param \Magento\Framework\Serialize\SerializerInterface $serializer
- * @param \Magento\Framework\App\Config\Spi\PreProcessorInterface $preProcessor
+ * @param ConfigSourceInterface $source
+ * @param PostProcessorInterface $postProcessor
+ * @param Fallback $fallback
+ * @param FrontendInterface $cache
+ * @param SerializerInterface $serializer
+ * @param PreProcessorInterface $preProcessor
* @param int $cachingNestedLevel
* @param string $configType
* @param Reader $reader
+ * @param SensitiveSerializerFactory|null $sensitiveFactory
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
- \Magento\Framework\App\Config\ConfigSourceInterface $source,
- \Magento\Framework\App\Config\Spi\PostProcessorInterface $postProcessor,
- \Magento\Store\Model\Config\Processor\Fallback $fallback,
- \Magento\Framework\Cache\FrontendInterface $cache,
- \Magento\Framework\Serialize\SerializerInterface $serializer,
- \Magento\Framework\App\Config\Spi\PreProcessorInterface $preProcessor,
+ ConfigSourceInterface $source,
+ PostProcessorInterface $postProcessor,
+ Fallback $fallback,
+ FrontendInterface $cache,
+ SerializerInterface $serializer,
+ PreProcessorInterface $preProcessor,
$cachingNestedLevel = 1,
$configType = self::CONFIG_TYPE,
- Reader $reader = null
+ Reader $reader = null,
+ SensitiveSerializerFactory $sensitiveFactory = null
) {
- $this->source = $source;
$this->postProcessor = $postProcessor;
- $this->preProcessor = $preProcessor;
$this->cache = $cache;
- $this->cachingNestedLevel = $cachingNestedLevel;
- $this->fallback = $fallback;
- $this->serializer = $serializer;
$this->configType = $configType;
- $this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
+ $this->reader = $reader ?: ObjectManager::getInstance()
+ ->get(Reader::class);
+ $sensitiveFactory = $sensitiveFactory ?? ObjectManager::getInstance()
+ ->get(SensitiveSerializerFactory::class);
+ //Using sensitive serializer because any kind of information may
+ //be stored in configs.
+ $this->serializer = $sensitiveFactory->create(
+ ['serializer' => $serializer]
+ );
}
/**
@@ -136,27 +134,52 @@ public function get($path = '')
{
if ($path === '') {
$this->data = array_replace_recursive($this->loadAllData(), $this->data);
+
return $this->data;
}
+
+ return $this->getWithParts($path);
+ }
+
+ /**
+ * Proceed with parts extraction from path.
+ *
+ * @param string $path
+ * @return array|int|string|boolean
+ */
+ private function getWithParts($path)
+ {
$pathParts = explode('/', $path);
- if (count($pathParts) === 1 && $pathParts[0] !== 'default') {
+
+ if (count($pathParts) === 1 && $pathParts[0] !== ScopeInterface::SCOPE_DEFAULT) {
if (!isset($this->data[$pathParts[0]])) {
- $data = $this->reader->read();
+ $data = $this->readData();
$this->data = array_replace_recursive($data, $this->data);
}
+
return $this->data[$pathParts[0]];
}
+
$scopeType = array_shift($pathParts);
- if ($scopeType === 'default') {
+
+ if ($scopeType === ScopeInterface::SCOPE_DEFAULT) {
if (!isset($this->data[$scopeType])) {
$this->data = array_replace_recursive($this->loadDefaultScopeData($scopeType), $this->data);
}
+
return $this->getDataByPathParts($this->data[$scopeType], $pathParts);
}
+
$scopeId = array_shift($pathParts);
+
if (!isset($this->data[$scopeType][$scopeId])) {
- $this->data = array_replace_recursive($this->loadScopeData($scopeType, $scopeId), $this->data);
+ $scopeData = $this->loadScopeData($scopeType, $scopeId);
+
+ if (!isset($this->data[$scopeType][$scopeId])) {
+ $this->data = array_replace_recursive($scopeData, $this->data);
+ }
}
+
return isset($this->data[$scopeType][$scopeId])
? $this->getDataByPathParts($this->data[$scopeType][$scopeId], $pathParts)
: null;
@@ -170,11 +193,13 @@ public function get($path = '')
private function loadAllData()
{
$cachedData = $this->cache->load($this->configType);
+
if ($cachedData === false) {
- $data = $this->reader->read();
+ $data = $this->readData();
} else {
$data = $this->serializer->unserialize($cachedData);
}
+
return $data;
}
@@ -187,12 +212,14 @@ private function loadAllData()
private function loadDefaultScopeData($scopeType)
{
$cachedData = $this->cache->load($this->configType . '_' . $scopeType);
+
if ($cachedData === false) {
- $data = $this->reader->read();
+ $data = $this->readData();
$this->cacheData($data);
} else {
$data = [$scopeType => $this->serializer->unserialize($cachedData)];
}
+
return $data;
}
@@ -206,6 +233,7 @@ private function loadDefaultScopeData($scopeType)
private function loadScopeData($scopeType, $scopeId)
{
$cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
+
if ($cachedData === false) {
if ($this->availableDataScopes === null) {
$cachedScopeData = $this->cache->load($this->configType . '_scopes');
@@ -216,11 +244,12 @@ private function loadScopeData($scopeType, $scopeId)
if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) {
return [$scopeType => [$scopeId => []]];
}
- $data = $this->reader->read();
+ $data = $this->readData();
$this->cacheData($data);
} else {
$data = [$scopeType => [$scopeId => $this->serializer->unserialize($cachedData)]];
}
+
return $data;
}
@@ -244,8 +273,8 @@ private function cacheData(array $data)
[self::CACHE_TAG]
);
$scopes = [];
- foreach (['websites', 'stores'] as $curScopeType) {
- foreach ($data[$curScopeType] as $curScopeId => $curScopeData) {
+ foreach ([StoreScope::SCOPE_WEBSITES, StoreScope::SCOPE_STORES] as $curScopeType) {
+ foreach ($data[$curScopeType] ?? [] as $curScopeId => $curScopeData) {
$scopes[$curScopeType][$curScopeId] = 1;
$this->cache->save(
$this->serializer->serialize($curScopeData),
@@ -256,7 +285,7 @@ private function cacheData(array $data)
}
$this->cache->save(
$this->serializer->serialize($scopes),
- $this->configType . "_scopes",
+ $this->configType . '_scopes',
[self::CACHE_TAG]
);
}
@@ -279,9 +308,25 @@ private function getDataByPathParts($data, $pathParts)
return null;
}
}
+
return $data;
}
+ /**
+ * The freshly read data.
+ *
+ * @return array
+ */
+ private function readData(): array
+ {
+ $this->data = $this->reader->read();
+ $this->data = $this->postProcessor->process(
+ $this->data
+ );
+
+ return $this->data;
+ }
+
/**
* Clean cache and global variables cache
*
diff --git a/app/code/Magento/Config/App/Config/Type/System/Reader.php b/app/code/Magento/Config/App/Config/Type/System/Reader.php
index d5a0f09cdd631..9916b795a53b2 100644
--- a/app/code/Magento/Config/App/Config/Type/System/Reader.php
+++ b/app/code/Magento/Config/App/Config/Type/System/Reader.php
@@ -27,17 +27,13 @@ class Reader
*/
private $preProcessor;
- /**
- * @var \Magento\Framework\App\Config\Spi\PostProcessorInterface
- */
- private $postProcessor;
-
/**
* Reader constructor.
* @param \Magento\Framework\App\Config\ConfigSourceInterface $source
* @param \Magento\Store\Model\Config\Processor\Fallback $fallback
* @param \Magento\Framework\App\Config\Spi\PreProcessorInterface $preProcessor
* @param \Magento\Framework\App\Config\Spi\PostProcessorInterface $postProcessor
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
\Magento\Framework\App\Config\ConfigSourceInterface $source,
@@ -48,7 +44,6 @@ public function __construct(
$this->source = $source;
$this->fallback = $fallback;
$this->preProcessor = $preProcessor;
- $this->postProcessor = $postProcessor;
}
/**
@@ -60,11 +55,9 @@ public function __construct(
*/
public function read()
{
- return $this->postProcessor->process(
- $this->fallback->process(
- $this->preProcessor->process(
- $this->source->get()
- )
+ return $this->fallback->process(
+ $this->preProcessor->process(
+ $this->source->get()
)
);
}
diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php
index 0a7f8fcca01a9..81e39a83296d7 100644
--- a/app/code/Magento/Config/Block/System/Config/Form.php
+++ b/app/code/Magento/Config/Block/System/Config/Form.php
@@ -8,6 +8,7 @@
use Magento\Config\App\Config\Type\System;
use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker;
use Magento\Config\Model\Config\Structure\ElementVisibilityInterface;
+use Magento\Framework\App\Config\Data\ProcessorInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\App\ObjectManager;
@@ -425,12 +426,14 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie
if ($placeholderValue) {
$data = $placeholderValue;
}
- if ($data === null) {
- if (array_key_exists($path, $this->_configData)) {
- $data = $this->_configData[$path];
- if ($field->hasBackendModel()) {
- $backendModel = $field->getBackendModel();
+ if ($data === null) {
+ $path = $field->getConfigPath() !== null ? $field->getConfigPath() : $path;
+ $data = $this->getConfigValue($path);
+ if ($field->hasBackendModel()) {
+ $backendModel = $field->getBackendModel();
+ // Backend models which implement ProcessorInterface are processed by ScopeConfigInterface
+ if (!$backendModel instanceof ProcessorInterface) {
$backendModel->setPath($path)
->setValue($data)
->setWebsite($this->getWebsiteCode())
@@ -438,10 +441,6 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie
->afterLoad();
$data = $backendModel->getValue();
}
- } elseif ($field->getConfigPath() !== null) {
- $data = $this->getConfigValue($field->getConfigPath());
- } else {
- $data = $this->getConfigValue($path);
}
}
@@ -710,7 +709,7 @@ protected function _getAdditionalElementTypes()
}
/**
- * Temporary moved those $this->getRequest()->getParam('blabla') from the code accross this block
+ * Temporary moved those $this->getRequest()->getParam('blabla') from the code across this block
* to getBlala() methods to be later set from controller with setters
*/
diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field/Datetime.php b/app/code/Magento/Config/Block/System/Config/Form/Field/Datetime.php
index 63dbb2b80e334..acf830f363ce6 100644
--- a/app/code/Magento/Config/Block/System/Config/Form/Field/Datetime.php
+++ b/app/code/Magento/Config/Block/System/Config/Form/Field/Datetime.php
@@ -40,7 +40,7 @@ public function __construct(
protected function _getElementHtml(AbstractElement $element)
{
return $this->dateTimeFormatter->formatObject(
- $this->_localeDate->date(intval($element->getValue())),
+ $this->_localeDate->date((int) $element->getValue()),
$this->_localeDate->getDateTimeFormat(\IntlDateFormatter::MEDIUM)
);
}
diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field/Notification.php b/app/code/Magento/Config/Block/System/Config/Form/Field/Notification.php
index 7f21bf4b92bf4..40ff76ee0e885 100644
--- a/app/code/Magento/Config/Block/System/Config/Form/Field/Notification.php
+++ b/app/code/Magento/Config/Block/System/Config/Form/Field/Notification.php
@@ -44,6 +44,6 @@ protected function _getElementHtml(AbstractElement $element)
$format = $this->_localeDate->getDateTimeFormat(
\IntlDateFormatter::MEDIUM
);
- return $this->dateTimeFormatter->formatObject($this->_localeDate->date(intval($element->getValue())), $format);
+ return $this->dateTimeFormatter->formatObject($this->_localeDate->date((int)$element->getValue()), $format);
}
}
diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorFactory.php b/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorFactory.php
index e005747ea5ed5..92cd0b0a14e93 100644
--- a/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorFactory.php
+++ b/app/code/Magento/Config/Console/Command/ConfigSet/ConfigSetProcessorFactory.php
@@ -27,7 +27,14 @@ class ConfigSetProcessorFactory
* lock - save and lock configuration
*/
const TYPE_DEFAULT = 'default';
+
+ /**
+ * @deprecated
+ * @see TYPE_LOCK_ENV or TYPE_LOCK_CONFIG
+ */
const TYPE_LOCK = 'lock';
+ const TYPE_LOCK_ENV = 'lock-env';
+ const TYPE_LOCK_CONFIG = 'lock-config';
/**#@-*/
/**#@-*/
diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php
index 2f5c10037ef06..d7d513bfad423 100644
--- a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php
+++ b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php
@@ -72,7 +72,7 @@ public function process($path, $value, $scope, $scopeCode)
throw new CouldNotSaveException(
__(
'The value you set has already been locked. To change the value, use the --%1 option.',
- ConfigSetCommand::OPTION_LOCK
+ ConfigSetCommand::OPTION_LOCK_ENV
)
);
}
diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/LockProcessor.php b/app/code/Magento/Config/Console/Command/ConfigSet/LockProcessor.php
index 0bd28f0f78d96..6fe2adde3c41e 100644
--- a/app/code/Magento/Config/Console/Command/ConfigSet/LockProcessor.php
+++ b/app/code/Magento/Config/Console/Command/ConfigSet/LockProcessor.php
@@ -16,7 +16,8 @@
/**
* Processes file lock flow of config:set command.
- * This processor saves the value of configuration and lock it for editing in Admin interface.
+ * This processor saves the value of configuration into app/etc/env.php
+ * and locks it for editing in Admin interface.
*
* {@inheritdoc}
*/
@@ -49,23 +50,30 @@ class LockProcessor implements ConfigSetProcessorInterface
* @var ConfigPathResolver
*/
private $configPathResolver;
+ /**
+ * @var string
+ */
+ private $target;
/**
* @param PreparedValueFactory $preparedValueFactory The factory for prepared value
* @param DeploymentConfig\Writer $writer The deployment configuration writer
* @param ArrayManager $arrayManager An array manager for different manipulations with arrays
* @param ConfigPathResolver $configPathResolver The resolver for configuration paths according to source type
+ * @param string $target
*/
public function __construct(
PreparedValueFactory $preparedValueFactory,
DeploymentConfig\Writer $writer,
ArrayManager $arrayManager,
- ConfigPathResolver $configPathResolver
+ ConfigPathResolver $configPathResolver,
+ $target = ConfigFilePool::APP_ENV
) {
$this->preparedValueFactory = $preparedValueFactory;
$this->deploymentConfigWriter = $writer;
$this->arrayManager = $arrayManager;
$this->configPathResolver = $configPathResolver;
+ $this->target = $target;
}
/**
@@ -97,7 +105,7 @@ public function process($path, $value, $scope, $scopeCode)
* we'll write value just after all validations are triggered.
*/
$this->deploymentConfigWriter->saveConfig(
- [ConfigFilePool::APP_ENV => $this->arrayManager->set($configPath, [], $value)],
+ [$this->target => $this->arrayManager->set($configPath, [], $value)],
false
);
}
diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php
index 06a01c6686bfd..b9b73ae5887b3 100644
--- a/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php
+++ b/app/code/Magento/Config/Console/Command/ConfigSet/ProcessorFacade.php
@@ -9,6 +9,7 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Scope\ValidatorInterface;
use Magento\Config\Model\Config\PathValidator;
+use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\ConfigurationMismatchException;
use Magento\Framework\Exception\CouldNotSaveException;
@@ -98,12 +99,35 @@ public function __construct(
* @param boolean $lock The lock flag
* @return string Processor response message
* @throws ValidatorException If some validation is wrong
- * @throws CouldNotSaveException If cannot save config value
- * @throws ConfigurationMismatchException If processor can not be instantiated
* @since 100.2.0
+ * @deprecated
+ * @see processWithLockTarget()
*/
public function process($path, $value, $scope, $scopeCode, $lock)
{
+ return $this->processWithLockTarget($path, $value, $scope, $scopeCode, $lock);
+ }
+
+ /**
+ * Processes config:set command with the option to set a target file.
+ *
+ * @param string $path The configuration path in format section/group/field_name
+ * @param string $value The configuration value
+ * @param string $scope The configuration scope (default, website, or store)
+ * @param string $scopeCode The scope code
+ * @param boolean $lock The lock flag
+ * @param string $lockTarget
+ * @return string Processor response message
+ * @throws ValidatorException If some validation is wrong
+ */
+ public function processWithLockTarget(
+ $path,
+ $value,
+ $scope,
+ $scopeCode,
+ $lock,
+ $lockTarget = ConfigFilePool::APP_ENV
+ ) {
try {
$this->scopeValidator->isValid($scope, $scopeCode);
$this->pathValidator->validate($path);
@@ -111,14 +135,23 @@ public function process($path, $value, $scope, $scopeCode, $lock)
throw new ValidatorException(__($exception->getMessage()), $exception);
}
- $processor = $lock
- ? $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK)
- : $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_DEFAULT);
- $message = $lock
- ? 'Value was saved and locked.'
- : 'Value was saved.';
+ $processor =
+ $lock
+ ? ( $lockTarget == ConfigFilePool::APP_ENV
+ ? $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK_ENV)
+ : $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_LOCK_CONFIG)
+ )
+ : $this->configSetProcessorFactory->create(ConfigSetProcessorFactory::TYPE_DEFAULT);
+
+ $message =
+ $lock
+ ? ( $lockTarget == ConfigFilePool::APP_ENV
+ ? 'Value was saved in app/etc/env.php and locked.'
+ : 'Value was saved in app/etc/config.php and locked.'
+ )
+ : 'Value was saved.';
- // The processing flow depends on --lock option.
+ // The processing flow depends on --lock and --share options.
$processor->process($path, $value, $scope, $scopeCode);
$this->hash->regenerate(System::CONFIG_TYPE);
diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php
index 1df1b3c4bed14..cb79daddbf5f9 100644
--- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php
+++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Config\Console\Command;
use Magento\Config\App\Config\Type\System;
@@ -10,6 +11,7 @@
use Magento\Deploy\Model\DeploymentConfig\ChangeDetector;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Framework\Console\Cli;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -34,6 +36,8 @@ class ConfigSetCommand extends Command
const OPTION_SCOPE = 'scope';
const OPTION_SCOPE_CODE = 'scope-code';
const OPTION_LOCK = 'lock';
+ const OPTION_LOCK_ENV = 'lock-env';
+ const OPTION_LOCK_CONFIG = 'lock-config';
/**#@-*/
/**#@-*/
@@ -108,11 +112,24 @@ protected function configure()
InputArgument::OPTIONAL,
'Scope code (required only if scope is not \'default\')'
),
+ new InputOption(
+ static::OPTION_LOCK_ENV,
+ 'le',
+ InputOption::VALUE_NONE,
+ 'Lock value which prevents modification in the Admin (will be saved in app/etc/env.php)'
+ ),
+ new InputOption(
+ static::OPTION_LOCK_CONFIG,
+ 'lc',
+ InputOption::VALUE_NONE,
+ 'Lock and share value with other installations, prevents modification in the Admin '
+ . '(will be saved in app/etc/config.php)'
+ ),
new InputOption(
static::OPTION_LOCK,
'l',
InputOption::VALUE_NONE,
- 'Lock value which prevents modification in the Admin'
+ 'Deprecated, use the --' . static::OPTION_LOCK_ENV . ' option instead.'
),
]);
@@ -146,12 +163,23 @@ protected function execute(InputInterface $input, OutputInterface $output)
try {
$message = $this->emulatedAreaProcessor->process(function () use ($input) {
- return $this->processorFacadeFactory->create()->process(
+
+ $lock = $input->getOption(static::OPTION_LOCK_ENV)
+ || $input->getOption(static::OPTION_LOCK_CONFIG)
+ || $input->getOption(static::OPTION_LOCK);
+
+ $lockTargetPath = ConfigFilePool::APP_ENV;
+ if ($input->getOption(static::OPTION_LOCK_CONFIG)) {
+ $lockTargetPath = ConfigFilePool::APP_CONFIG;
+ }
+
+ return $this->processorFacadeFactory->create()->processWithLockTarget(
$input->getArgument(static::ARG_PATH),
$input->getArgument(static::ARG_VALUE),
$input->getOption(static::OPTION_SCOPE),
$input->getOption(static::OPTION_SCOPE_CODE),
- $input->getOption(static::OPTION_LOCK)
+ $lock,
+ $lockTargetPath
);
});
diff --git a/app/code/Magento/Config/Console/Command/ConfigShow/ValueProcessor.php b/app/code/Magento/Config/Console/Command/ConfigShow/ValueProcessor.php
index 582f87508089f..aeb57010e4969 100644
--- a/app/code/Magento/Config/Console/Command/ConfigShow/ValueProcessor.php
+++ b/app/code/Magento/Config/Console/Command/ConfigShow/ValueProcessor.php
@@ -97,7 +97,7 @@ public function process($scope, $scopeCode, $value, $path)
$field = $configStructure->getElementByConfigPath($path);
/** @var Value $backendModel */
- $backendModel = $field && $field->hasBackendModel()
+ $backendModel = $field instanceof Field && $field->hasBackendModel()
? $field->getBackendModel()
: $this->configValueFactory->create();
diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php
index bc1515aadb0ca..0472c5daa276f 100644
--- a/app/code/Magento/Config/Model/Config.php
+++ b/app/code/Magento/Config/Model/Config.php
@@ -5,6 +5,11 @@
*/
namespace Magento\Config\Model;
+use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker;
+use Magento\Config\Model\Config\Structure\Element\Group;
+use Magento\Config\Model\Config\Structure\Element\Field;
+use Magento\Framework\App\ObjectManager;
+
/**
* Backend config model
* Used to save configuration
@@ -77,6 +82,11 @@ class Config extends \Magento\Framework\DataObject
*/
protected $_storeManager;
+ /**
+ * @var Config\Reader\Source\Deployed\SettingChecker
+ */
+ private $settingChecker;
+
/**
* @param \Magento\Framework\App\Config\ReinitableConfigInterface $config
* @param \Magento\Framework\Event\ManagerInterface $eventManager
@@ -85,6 +95,7 @@ class Config extends \Magento\Framework\DataObject
* @param \Magento\Config\Model\Config\Loader $configLoader
* @param \Magento\Framework\App\Config\ValueFactory $configValueFactory
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
+ * @param Config\Reader\Source\Deployed\SettingChecker|null $settingChecker
* @param array $data
*/
public function __construct(
@@ -95,6 +106,7 @@ public function __construct(
\Magento\Config\Model\Config\Loader $configLoader,
\Magento\Framework\App\Config\ValueFactory $configValueFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
+ SettingChecker $settingChecker = null,
array $data = []
) {
parent::__construct($data);
@@ -105,6 +117,7 @@ public function __construct(
$this->_configLoader = $configLoader;
$this->_configValueFactory = $configValueFactory;
$this->_storeManager = $storeManager;
+ $this->settingChecker = $settingChecker ?: ObjectManager::getInstance()->get(SettingChecker::class);
}
/**
@@ -126,11 +139,12 @@ public function save()
$oldConfig = $this->_getConfig(true);
+ /** @var \Magento\Framework\DB\Transaction $deleteTransaction */
$deleteTransaction = $this->_transactionFactory->create();
- /* @var $deleteTransaction \Magento\Framework\DB\Transaction */
+ /** @var \Magento\Framework\DB\Transaction $saveTransaction */
$saveTransaction = $this->_transactionFactory->create();
- /* @var $saveTransaction \Magento\Framework\DB\Transaction */
+ $changedPaths = [];
// Extends for old config data
$extraOldGroups = [];
@@ -145,6 +159,9 @@ public function save()
$saveTransaction,
$deleteTransaction
);
+
+ $groupChangedPaths = $this->getChangedPaths($sectionId, $groupId, $groupData, $oldConfig, $extraOldGroups);
+ $changedPaths = \array_merge($changedPaths, $groupChangedPaths);
}
try {
@@ -157,7 +174,11 @@ public function save()
// website and store codes can be used in event implementation, so set them as well
$this->_eventManager->dispatch(
"admin_system_config_changed_section_{$this->getSection()}",
- ['website' => $this->getWebsite(), 'store' => $this->getStore()]
+ [
+ 'website' => $this->getWebsite(),
+ 'store' => $this->getStore(),
+ 'changed_paths' => $changedPaths,
+ ]
);
} catch (\Exception $e) {
// re-init configuration
@@ -168,6 +189,145 @@ public function save()
return $this;
}
+ /**
+ * Map field name if they were cloned
+ *
+ * @param Group $group
+ * @param string $fieldId
+ * @return string
+ */
+ private function getOriginalFieldId(Group $group, string $fieldId): string
+ {
+ if ($group->shouldCloneFields()) {
+ $cloneModel = $group->getCloneModel();
+
+ /** @var \Magento\Config\Model\Config\Structure\Element\Field $field */
+ foreach ($group->getChildren() as $field) {
+ foreach ($cloneModel->getPrefixes() as $prefix) {
+ if ($prefix['field'] . $field->getId() === $fieldId) {
+ $fieldId = $field->getId();
+ break(2);
+ }
+ }
+ }
+ }
+
+ return $fieldId;
+ }
+
+ /**
+ * Get field object
+ *
+ * @param string $sectionId
+ * @param string $groupId
+ * @param string $fieldId
+ * @return Field
+ */
+ private function getField(string $sectionId, string $groupId, string $fieldId): Field
+ {
+ /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */
+ $group = $this->_configStructure->getElement($sectionId . '/' . $groupId);
+ $fieldPath = $group->getPath() . '/' . $this->getOriginalFieldId($group, $fieldId);
+ $field = $this->_configStructure->getElement($fieldPath);
+
+ return $field;
+ }
+
+ /**
+ * Get field path
+ *
+ * @param Field $field
+ * @param string $fieldId Need for support of clone_field feature
+ * @param array &$oldConfig Need for compatibility with _processGroup()
+ * @param array &$extraOldGroups Need for compatibility with _processGroup()
+ * @return string
+ */
+ private function getFieldPath(Field $field, string $fieldId, array &$oldConfig, array &$extraOldGroups): string
+ {
+ $path = $field->getGroupPath() . '/' . $fieldId;
+
+ /**
+ * Look for custom defined field path
+ */
+ $configPath = $field->getConfigPath();
+ if ($configPath && strrpos($configPath, '/') > 0) {
+ // Extend old data with specified section group
+ $configGroupPath = substr($configPath, 0, strrpos($configPath, '/'));
+ if (!isset($extraOldGroups[$configGroupPath])) {
+ $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig);
+ $extraOldGroups[$configGroupPath] = true;
+ }
+ $path = $configPath;
+ }
+
+ return $path;
+ }
+
+ /**
+ * Check is config value changed
+ *
+ * @param array $oldConfig
+ * @param string $path
+ * @param array $fieldData
+ * @return bool
+ */
+ private function isValueChanged(array $oldConfig, string $path, array $fieldData): bool
+ {
+ if (isset($oldConfig[$path]['value'])) {
+ $result = !isset($fieldData['value']) || $oldConfig[$path]['value'] !== $fieldData['value'];
+ } else {
+ $result = empty($fieldData['inherit']);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get changed paths
+ *
+ * @param string $sectionId
+ * @param string $groupId
+ * @param array $groupData
+ * @param array &$oldConfig
+ * @param array &$extraOldGroups
+ * @return array
+ */
+ private function getChangedPaths(
+ string $sectionId,
+ string $groupId,
+ array $groupData,
+ array &$oldConfig,
+ array &$extraOldGroups
+ ): array {
+ $changedPaths = [];
+
+ if (isset($groupData['fields'])) {
+ foreach ($groupData['fields'] as $fieldId => $fieldData) {
+ $field = $this->getField($sectionId, $groupId, $fieldId);
+ $path = $this->getFieldPath($field, $fieldId, $oldConfig, $extraOldGroups);
+ if ($this->isValueChanged($oldConfig, $path, $fieldData)) {
+ $changedPaths[] = $path;
+ }
+ }
+ }
+
+ if (isset($groupData['groups'])) {
+ $subSectionId = $sectionId . '/' . $groupId;
+ foreach ($groupData['groups'] as $subGroupId => $subGroupData) {
+ $subGroupChangedPaths = $this->getChangedPaths(
+ $subSectionId,
+ $subGroupId,
+ $subGroupData,
+ $oldConfig,
+ $extraOldGroups
+ );
+ $changedPaths = \array_merge($changedPaths, $subGroupChangedPaths);
+ }
+ }
+
+ return $changedPaths;
+ }
+
/**
* Process group data
*
@@ -182,7 +342,6 @@ public function save()
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function _processGroup(
$groupId,
@@ -195,92 +354,55 @@ protected function _processGroup(
\Magento\Framework\DB\Transaction $deleteTransaction
) {
$groupPath = $sectionPath . '/' . $groupId;
- $scope = $this->getScope();
- $scopeId = $this->getScopeId();
- $scopeCode = $this->getScopeCode();
- /**
- *
- * Map field names if they were cloned
- */
- /** @var $group \Magento\Config\Model\Config\Structure\Element\Group */
- $group = $this->_configStructure->getElement($groupPath);
- // set value for group field entry by fieldname
- // use extra memory
- $fieldsetData = [];
if (isset($groupData['fields'])) {
- if ($group->shouldCloneFields()) {
- $cloneModel = $group->getCloneModel();
- $mappedFields = [];
-
- /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */
- foreach ($group->getChildren() as $field) {
- foreach ($cloneModel->getPrefixes() as $prefix) {
- $mappedFields[$prefix['field'] . $field->getId()] = $field->getId();
- }
- }
- }
+ /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */
+ $group = $this->_configStructure->getElement($groupPath);
+
+ // set value for group field entry by fieldname
+ // use extra memory
+ $fieldsetData = [];
foreach ($groupData['fields'] as $fieldId => $fieldData) {
- $fieldsetData[$fieldId] = is_array(
- $fieldData
- ) && isset(
- $fieldData['value']
- ) ? $fieldData['value'] : null;
+ $fieldsetData[$fieldId] = $fieldData['value'] ?? null;
}
foreach ($groupData['fields'] as $fieldId => $fieldData) {
- $originalFieldId = $fieldId;
- if ($group->shouldCloneFields() && isset($mappedFields[$fieldId])) {
- $originalFieldId = $mappedFields[$fieldId];
+ $isReadOnly = $this->settingChecker->isReadOnly(
+ $groupPath . '/' . $fieldId,
+ $this->getScope(),
+ $this->getScopeCode()
+ );
+
+ if ($isReadOnly) {
+ continue;
}
- /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */
- $field = $this->_configStructure->getElement($groupPath . '/' . $originalFieldId);
+ $field = $this->getField($sectionPath, $groupId, $fieldId);
/** @var \Magento\Framework\App\Config\ValueInterface $backendModel */
- $backendModel = $field->hasBackendModel() ? $field
- ->getBackendModel() : $this
- ->_configValueFactory
- ->create();
+ $backendModel = $field->hasBackendModel()
+ ? $field->getBackendModel()
+ : $this->_configValueFactory->create();
+ if (!isset($fieldData['value'])) {
+ $fieldData['value'] = null;
+ }
$data = [
'field' => $fieldId,
'groups' => $groups,
'group_id' => $group->getId(),
- 'scope' => $scope,
- 'scope_id' => $scopeId,
- 'scope_code' => $scopeCode,
+ 'scope' => $this->getScope(),
+ 'scope_id' => $this->getScopeId(),
+ 'scope_code' => $this->getScopeCode(),
'field_config' => $field->getData(),
- 'fieldset_data' => $fieldsetData
+ 'fieldset_data' => $fieldsetData,
];
$backendModel->addData($data);
-
$this->_checkSingleStoreMode($field, $backendModel);
- if (false == isset($fieldData['value'])) {
- $fieldData['value'] = null;
- }
-
- $path = $field->getGroupPath() . '/' . $fieldId;
- /**
- * Look for custom defined field path
- */
- if ($field && $field->getConfigPath()) {
- $configPath = $field->getConfigPath();
- if (!empty($configPath) && strrpos($configPath, '/') > 0) {
- // Extend old data with specified section group
- $configGroupPath = substr($configPath, 0, strrpos($configPath, '/'));
- if (!isset($extraOldGroups[$configGroupPath])) {
- $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig);
- $extraOldGroups[$configGroupPath] = true;
- }
- $path = $configPath;
- }
- }
-
- $inherit = !empty($fieldData['inherit']);
-
+ $path = $this->getFieldPath($field, $fieldId, $extraOldGroups, $oldConfig);
$backendModel->setPath($path)->setValue($fieldData['value']);
+ $inherit = !empty($fieldData['inherit']);
if (isset($oldConfig[$path])) {
$backendModel->setConfigId($oldConfig[$path]['config_id']);
diff --git a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php
index b86b86ad3bb8c..4ae66bfd9692b 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php
@@ -71,7 +71,7 @@ protected function _getCurrencyBase()
$this->getScopeId()
);
}
- return strval($value);
+ return (string)$value;
}
/**
@@ -88,7 +88,7 @@ protected function _getCurrencyDefault()
$this->getScopeId()
);
}
- return strval($value);
+ return (string)$value;
}
/**
diff --git a/app/code/Magento/Config/Model/Config/Backend/Currency/Cron.php b/app/code/Magento/Config/Model/Config/Backend/Currency/Cron.php
index 3f80e01802b8d..e0e1525fd04cc 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Currency/Cron.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Currency/Cron.php
@@ -59,14 +59,14 @@ public function afterSave()
$frequencyMonthly = \Magento\Cron\Model\Config\Source\Frequency::CRON_MONTHLY;
$cronExprArray = [
- intval($time[1]), # Minute
- intval($time[0]), # Hour
+ (int)$time[1], # Minute
+ (int)$time[0], # Hour
$frequency == $frequencyMonthly ? '1' : '*', # Day of the Month
'*', # Month of the Year
$frequency == $frequencyWeekly ? '1' : '*', # Day of the Week
];
- $cronExprString = join(' ', $cronExprArray);
+ $cronExprString = implode(' ', $cronExprArray);
try {
/** @var $configValue \Magento\Framework\App\Config\ValueInterface */
diff --git a/app/code/Magento/Config/Model/Config/Backend/File.php b/app/code/Magento/Config/Model/Config/Backend/File.php
index 03a8ff4ffe312..1d5b26cebcfa5 100644
--- a/app/code/Magento/Config/Model/Config/Backend/File.php
+++ b/app/code/Magento/Config/Model/Config/Backend/File.php
@@ -111,6 +111,8 @@ public function beforeSave()
} else {
if (is_array($value) && !empty($value['delete'])) {
$this->setValue('');
+ } elseif (is_array($value) && !empty($value['value'])) {
+ $this->setValue($value['value']);
} else {
$this->unsValue();
}
diff --git a/app/code/Magento/Config/Model/Config/Backend/Log/Cron.php b/app/code/Magento/Config/Model/Config/Backend/Log/Cron.php
index 3c36baf6f31f4..163124eae1204 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Log/Cron.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Log/Cron.php
@@ -73,13 +73,13 @@ public function afterSave()
if ($enabled) {
$cronExprArray = [
- intval($time[1]), # Minute
- intval($time[0]), # Hour
+ (int)$time[1], # Minute
+ (int)$time[0], # Hour
$frequency == $frequencyMonthly ? '1' : '*', # Day of the Month
'*', # Month of the Year
$frequency == $frequencyWeekly ? '1' : '*', # Day of the Week
];
- $cronExprString = join(' ', $cronExprArray);
+ $cronExprString = implode(' ', $cronExprArray);
} else {
$cronExprString = '';
}
diff --git a/app/code/Magento/Config/Model/Config/Importer.php b/app/code/Magento/Config/Model/Config/Importer.php
index 70ffdaec829b2..a54af2ead5048 100644
--- a/app/code/Magento/Config/Model/Config/Importer.php
+++ b/app/code/Magento/Config/Model/Config/Importer.php
@@ -124,13 +124,15 @@ public function import(array $data)
$this->scopeConfig->clean();
}
- $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData, $data) {
+ $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData) {
$this->scope->setCurrentScope(Area::AREA_ADMINHTML);
// Invoke saving of new values.
$this->saveProcessor->process($changedData);
- $this->flagManager->saveFlag(static::FLAG_CODE, $data);
});
+
+ $this->scope->setCurrentScope($currentScope);
+ $this->flagManager->saveFlag(static::FLAG_CODE, $data);
} catch (\Exception $e) {
throw new InvalidTransitionException(__('%1', $e->getMessage()), $e);
} finally {
diff --git a/app/code/Magento/Config/Model/Config/Source/Nooptreq.php b/app/code/Magento/Config/Model/Config/Source/Nooptreq.php
index 03fe5ca2abccc..1c9eb801dfec7 100644
--- a/app/code/Magento/Config/Model/Config/Source/Nooptreq.php
+++ b/app/code/Magento/Config/Model/Config/Source/Nooptreq.php
@@ -11,15 +11,19 @@
*/
class Nooptreq implements \Magento\Framework\Option\ArrayInterface
{
+ const VALUE_NO = '';
+ const VALUE_OPTIONAL = 'opt';
+ const VALUE_REQUIRED = 'req';
+
/**
* @return array
*/
public function toOptionArray()
{
return [
- ['value' => '', 'label' => __('No')],
- ['value' => 'opt', 'label' => __('Optional')],
- ['value' => 'req', 'label' => __('Required')]
+ ['value' => self::VALUE_NO, 'label' => __('No')],
+ ['value' => self::VALUE_OPTIONAL, 'label' => __('Optional')],
+ ['value' => self::VALUE_REQUIRED, 'label' => __('Required')]
];
}
}
diff --git a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
index 115a372e6150a..c60d634210339 100644
--- a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
+++ b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php
@@ -11,7 +11,8 @@
* Defines status of visibility of form elements on Stores > Settings > Configuration page
* in Admin Panel in Production mode.
* @api
- * @since 100.2.0
+ * @deprecated class location was changed
+ * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction
*/
class ConcealInProductionConfigList implements ElementVisibilityInterface
{
@@ -54,7 +55,7 @@ public function __construct(State $state, array $configs = [])
/**
* @inheritdoc
- * @since 100.2.0
+ * @deprecated
*/
public function isHidden($path)
{
@@ -66,7 +67,7 @@ public function isHidden($path)
/**
* @inheritdoc
- * @since 100.2.0
+ * @deprecated
*/
public function isDisabled($path)
{
diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php
new file mode 100755
index 0000000000000..d5ded9292864a
--- /dev/null
+++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php
@@ -0,0 +1,138 @@
+ Settings > Configuration page
+ * in Admin Panel in Production mode.
+ * @api
+ */
+class ConcealInProduction implements ElementVisibilityInterface
+{
+ /**
+ * The list of form element paths with concrete visibility status.
+ *
+ * E.g.
+ *
+ * ```php
+ * [
+ * 'general/locale/code' => ElementVisibilityInterface::DISABLED,
+ * 'general/country' => ElementVisibilityInterface::HIDDEN,
+ * ];
+ * ```
+ *
+ * It means that:
+ * - field Locale (in group Locale Options in section General) will be disabled
+ * - group Country Options (in section General) will be hidden
+ *
+ * @var array
+ */
+ private $configs = [];
+
+ /**
+ * The object that has information about the state of the system.
+ *
+ * @var State
+ */
+ private $state;
+
+ /**
+ *
+ * The list of form element paths which ignore visibility status.
+ *
+ * E.g.
+ *
+ * ```php
+ * [
+ * 'general/country/default' => '',
+ * ];
+ * ```
+ *
+ * It means that:
+ * - field 'default' in group Country Options (in section General) will be showed, even if all group(section)
+ * will be hidden.
+ *
+ * @var array
+ */
+ private $exemptions = [];
+
+ /**
+ * @param State $state The object that has information about the state of the system
+ * @param array $configs The list of form element paths with concrete visibility status.
+ * @param array $exemptions The list of form element paths which ignore visibility status.
+ */
+ public function __construct(State $state, array $configs = [], array $exemptions = [])
+ {
+ $this->state = $state;
+ $this->configs = $configs;
+ $this->exemptions = $exemptions;
+ }
+
+ /**
+ * @inheritdoc
+ * @since 100.2.0
+ */
+ public function isHidden($path)
+ {
+ $path = $this->normalizePath($path);
+ if ($this->state->getMode() === State::MODE_PRODUCTION
+ && preg_match('/(?(?.*?)\/.*?)\/.*?/', $path, $match)) {
+ $group = $match['group'];
+ $section = $match['section'];
+ $exemptions = array_keys($this->exemptions);
+ $checkedItems = [];
+ foreach ([$path, $group, $section] as $itemPath) {
+ $checkedItems[] = $itemPath;
+ if (!empty($this->configs[$itemPath])) {
+ return $this->configs[$itemPath] === static::HIDDEN
+ && empty(array_intersect($checkedItems, $exemptions));
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @inheritdoc
+ * @since 100.2.0
+ */
+ public function isDisabled($path)
+ {
+ $path = $this->normalizePath($path);
+ if ($this->state->getMode() === State::MODE_PRODUCTION) {
+ while (true) {
+ if (!empty($this->configs[$path])) {
+ return $this->configs[$path] === static::DISABLED;
+ }
+
+ $position = strripos($path, '/');
+ if ($position === false) {
+ break;
+ }
+ $path = substr($path, 0, $position);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns normalized path.
+ *
+ * @param string $path The path to be normalized
+ * @return string The normalized path
+ */
+ private function normalizePath($path)
+ {
+ return trim($path, '/');
+ }
+}
diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php
new file mode 100755
index 0000000000000..29148a244dcc6
--- /dev/null
+++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php
@@ -0,0 +1,72 @@
+ Settings > Configuration page
+ * when Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION is enabled
+ * otherwise rule from Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction is used
+ * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction
+ *
+ * @api
+ */
+class ConcealInProductionWithoutScdOnDemand implements ElementVisibilityInterface
+{
+ /**
+ * @var ConcealInProduction Element visibility rules in the Production mode
+ */
+ private $concealInProduction;
+
+ /**
+ * @var DeploymentConfig The application deployment configuration
+ */
+ private $deploymentConfig;
+
+ /**
+ * @param ConcealInProductionFactory $concealInProductionFactory
+ * @param DeploymentConfig $deploymentConfig Deployment configuration reader
+ * @param array $configs The list of form element paths with concrete visibility status.
+ * @param array $exemptions The list of form element paths which ignore visibility status.
+ */
+ public function __construct(
+ ConcealInProductionFactory $concealInProductionFactory,
+ DeploymentConfig $deploymentConfig,
+ array $configs = [],
+ array $exemptions = []
+ ) {
+ $this->concealInProduction = $concealInProductionFactory
+ ->create(['configs' => $configs, 'exemptions' => $exemptions]);
+ $this->deploymentConfig = $deploymentConfig;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function isHidden($path): bool
+ {
+ if (!$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) {
+ return $this->concealInProduction->isHidden($path);
+ }
+ return false;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function isDisabled($path): bool
+ {
+ if (!$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) {
+ return $this->concealInProduction->isDisabled($path);
+ }
+ return false;
+ }
+}
diff --git a/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php b/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php
index f6f3a0be187a3..e615678550108 100644
--- a/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php
+++ b/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php
@@ -55,17 +55,13 @@ protected function _cmp($elementA, $elementB)
{
$sortIndexA = 0;
if ($this->_hasValue('sortOrder', $elementA)) {
- $sortIndexA = floatval($elementA['sortOrder']);
+ $sortIndexA = (float)$elementA['sortOrder'];
}
$sortIndexB = 0;
if ($this->_hasValue('sortOrder', $elementB)) {
- $sortIndexB = floatval($elementB['sortOrder']);
+ $sortIndexB = (float)$elementB['sortOrder'];
}
- if ($sortIndexA == $sortIndexB) {
- return 0;
- }
-
- return $sortIndexA < $sortIndexB ? -1 : 1;
+ return $sortIndexA <=> $sortIndexB;
}
}
diff --git a/app/code/Magento/Config/Model/Config/Structure/Reader.php b/app/code/Magento/Config/Model/Config/Structure/Reader.php
index 5916649588bcb..c83c2e1ae1320 100644
--- a/app/code/Magento/Config/Model/Config/Structure/Reader.php
+++ b/app/code/Magento/Config/Model/Config/Structure/Reader.php
@@ -124,6 +124,7 @@ protected function _readFiles($fileList)
* Processing nodes of the document before merging
*
* @param string $content
+ * @throws \Magento\Framework\Config\Dom\ValidationException
* @return string
*/
protected function processingDocument($content)
@@ -131,7 +132,12 @@ protected function processingDocument($content)
$object = new DataObject();
$document = new \DOMDocument();
- $document->loadXML($content);
+ try {
+ $document->loadXML($content);
+ } catch (\Exception $e) {
+ throw new \Magento\Framework\Config\Dom\ValidationException($e->getMessage());
+ }
+
$this->compiler->compile($document->documentElement, $object, $object);
return $document->saveXML();
diff --git a/app/code/Magento/Config/Model/ResourceModel/Config.php b/app/code/Magento/Config/Model/ResourceModel/Config.php
index d8ea2410ce860..594a9df719daa 100644
--- a/app/code/Magento/Config/Model/ResourceModel/Config.php
+++ b/app/code/Magento/Config/Model/ResourceModel/Config.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Config\Model\ResourceModel;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
/**
* Core Resource Resource Model
*
@@ -34,7 +36,7 @@ protected function _construct()
* @param int $scopeId
* @return $this
*/
- public function saveConfig($path, $value, $scope, $scopeId)
+ public function saveConfig($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0)
{
$connection = $this->getConnection();
$select = $connection->select()->from(
@@ -70,7 +72,7 @@ public function saveConfig($path, $value, $scope, $scopeId)
* @param int $scopeId
* @return $this
*/
- public function deleteConfig($path, $scope, $scopeId)
+ public function deleteConfig($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0)
{
$connection = $this->getConnection();
$connection->delete(
diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml
new file mode 100644
index 0000000000000..f563cdd3e1288
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml
new file mode 100644
index 0000000000000..5d8fe5489df05
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/LICENSE.txt b/app/code/Magento/Config/Test/Mftf/LICENSE.txt
new file mode 100644
index 0000000000000..49525fd99da9c
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/LICENSE.txt
@@ -0,0 +1,48 @@
+
+Open Software License ("OSL") v. 3.0
+
+This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Open Software License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/app/code/Magento/Config/Test/Mftf/LICENSE_AFL.txt b/app/code/Magento/Config/Test/Mftf/LICENSE_AFL.txt
new file mode 100644
index 0000000000000..f39d641b18a19
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/LICENSE_AFL.txt
@@ -0,0 +1,48 @@
+
+Academic Free License ("AFL") v. 3.0
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 3.0
+
+ 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
+
+ 1. to reproduce the Original Work in copies, either alone or as part of a collective work;
+
+ 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
+
+ 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License;
+
+ 4. to perform the Original Work publicly; and
+
+ 5. to display the Original Work publicly.
+
+ 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
+
+ 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
+
+ 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
+
+ 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
+
+ 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
+
+ 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
+
+ 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
+
+ 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
+
+ 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
+
+ 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
+
+ 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
+
+ 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
+
+ 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml
new file mode 100644
index 0000000000000..ded825ea89098
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml
new file mode 100644
index 0000000000000..35903fe33d06c
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml
new file mode 100644
index 0000000000000..1a99ff6533dbb
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesConfigPage.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminSalesTaxClassPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesTaxClassPage.xml
new file mode 100644
index 0000000000000..e790d89888526
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Page/AdminSalesTaxClassPage.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/README.md b/app/code/Magento/Config/Test/Mftf/README.md
new file mode 100644
index 0000000000000..060168a5fa643
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/README.md
@@ -0,0 +1,3 @@
+# Config Functional Tests
+
+The Functional Test Module for **Magento Config** module.
diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml
new file mode 100644
index 0000000000000..4099b55157709
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml
new file mode 100644
index 0000000000000..c58e77d200bfb
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesTaxConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesTaxConfigSection.xml
new file mode 100644
index 0000000000000..0db4a8a910c27
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesTaxConfigSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml
new file mode 100644
index 0000000000000..18a124ac669ec
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml
new file mode 100644
index 0000000000000..fd47524de8620
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Section/StoreConfigSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Unit/App/Config/Type/System/ReaderTest.php b/app/code/Magento/Config/Test/Unit/App/Config/Type/System/ReaderTest.php
index 7fb126a00ff84..9ec2d9bb9a1f4 100644
--- a/app/code/Magento/Config/Test/Unit/App/Config/Type/System/ReaderTest.php
+++ b/app/code/Magento/Config/Test/Unit/App/Config/Type/System/ReaderTest.php
@@ -84,10 +84,6 @@ public function testGetCachedWithLoadDefaultScopeData()
->method('process')
->with($data)
->willReturn($data);
- $this->postProcessor->expects($this->once())
- ->method('process')
- ->with($data)
- ->willReturn($data);
$this->assertEquals($data, $this->model->read());
}
}
diff --git a/app/code/Magento/Config/Test/Unit/App/Config/Type/SystemTest.php b/app/code/Magento/Config/Test/Unit/App/Config/Type/SystemTest.php
index 74315af448226..9ca4e6138babe 100644
--- a/app/code/Magento/Config/Test/Unit/App/Config/Type/SystemTest.php
+++ b/app/code/Magento/Config/Test/Unit/App/Config/Type/SystemTest.php
@@ -11,6 +11,8 @@
use Magento\Framework\App\Config\Spi\PostProcessorInterface;
use Magento\Framework\App\Config\Spi\PreProcessorInterface;
use Magento\Framework\Cache\FrontendInterface;
+use Magento\Framework\Serialize\Serializer\Sensitive;
+use Magento\Framework\Serialize\Serializer\SensitiveFactory;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Store\Model\Config\Processor\Fallback;
use Magento\Config\App\Config\Type\System\Reader;
@@ -74,22 +76,34 @@ public function setUp()
->getMockForAbstractClass();
$this->preProcessor = $this->getMockBuilder(PreProcessorInterface::class)
->getMockForAbstractClass();
- $this->serializer = $this->getMockBuilder(SerializerInterface::class)
+ $this->serializer = $this->getMockBuilder(Sensitive::class)
+ ->disableOriginalConstructor()
->getMock();
$this->reader = $this->getMockBuilder(Reader::class)
->disableOriginalConstructor()
->getMock();
+ $sensitiveFactory = $this->getMockBuilder(SensitiveFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sensitiveFactory->expects($this->any())
+ ->method('create')
+ ->willReturn($this->serializer);
+ /** @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject $serializerMock */
+ $serializerMock = $this->getMockBuilder(SerializerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$this->configType = new System(
$this->source,
$this->postProcessor,
$this->fallback,
$this->cache,
- $this->serializer,
+ $serializerMock,
$this->preProcessor,
1,
'system',
- $this->reader
+ $this->reader,
+ $sensitiveFactory
);
}
@@ -133,44 +147,4 @@ public function testGetCachedWithLoadAllData()
->willReturn($data);
$this->assertEquals($data, $this->configType->get(''));
}
-
- public function testGetNotCached()
- {
- $path = 'stores/default/dev/unsecure/url';
- $url = 'http://magento.test/';
-
- $dataToCache = [
- 'unsecure' => [
- 'url' => $url
- ]
- ];
- $data = [
- 'default' => [],
- 'websites' => [],
- 'stores' => [
- 'default' => [
- 'dev' => [
- 'unsecure' => [
- 'url' => $url
- ]
- ]
- ]
- ]
- ];
- $this->cache->expects($this->any())
- ->method('load')
- ->willReturnOnConsecutiveCalls(false, false);
-
- $this->serializer->expects($this->atLeastOnce())
- ->method('serialize')
- ->willReturn(serialize($dataToCache));
- $this->cache->expects($this->atLeastOnce())
- ->method('save')
- ->willReturnSelf();
- $this->reader->expects($this->once())
- ->method('read')
- ->willReturn($data);
-
- $this->assertEquals($url, $this->configType->get($path));
- }
}
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/DwstreeTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/DwstreeTest.php
index d3750022d93de..1cb393b212199 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/DwstreeTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/DwstreeTest.php
@@ -121,6 +121,9 @@ public function testInitTabs($section, $website, $store)
);
}
+ /**
+ * @return array
+ */
public function initTabsDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php
index d0648ab008234..e62aa37af47dc 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php
@@ -74,7 +74,7 @@ public function testGetElementHtmlWithValue()
'showInWebsite' => '1',
'showInStore' => '1',
'label' => null,
- 'backend_model' => \Magento\BackendModelConfig\Backend\Image::class,
+ 'backend_model' => \Magento\Config\Model\Config\Backend\Image::class,
'upload_dir' => [
'config' => 'system/filesystem/media',
'scope_info' => '1',
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
index 4f53f1072e035..0b4d5f7ef15f7 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/RegexceptionsTest.php
@@ -128,7 +128,8 @@ public function testRenderCellTemplateWrongColumnName()
$this->object->addColumn($wrongColumnName, $this->cellParameters);
- $this->expectException('\Exception', 'Wrong column name specified.');
+ $this->expectException('\Exception');
+ $this->expectExceptionMessage('Wrong column name specified.');
$this->object->renderCellTemplate($columnName);
}
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php
index be3b8e2ead0c1..f5c65e848b3bf 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php
@@ -85,6 +85,9 @@ public function testGetHtmlWhenValueIsEmpty($value)
$this->assertNotEmpty($this->_object->getHtml());
}
+ /**
+ * @return array
+ */
public function getHtmlWhenValueIsEmptyDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Fieldset/Modules/DisableOutputTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Fieldset/Modules/DisableOutputTest.php
index 9d76363213d0b..bb109bcb25f06 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Fieldset/Modules/DisableOutputTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Fieldset/Modules/DisableOutputTest.php
@@ -224,6 +224,9 @@ public function testRender($expanded, $nested, $extra)
}
}
+ /**
+ * @return array
+ */
public function renderDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
index bee0d5172093c..83b7bd5fda42e 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
@@ -224,6 +224,9 @@ public function testInitForm($sectionIsVisible)
$this->assertEquals($this->_formMock, $object->getForm());
}
+ /**
+ * @return array
+ */
public function initFormDataProvider()
{
return [
@@ -337,6 +340,9 @@ public function testInitGroup($shouldCloneFields, $prefixes, $callNum)
$object->initForm();
}
+ /**
+ * @return array
+ */
public function initGroupDataProvider()
{
return [
@@ -543,8 +549,8 @@ public function initFieldsDataProvider()
return [
[
['section1/group1/field1' => 'some_value'],
- false,
- null,
+ 'some_value',
+ 'section1/group1/field1',
false,
'some_value',
null,
@@ -560,7 +566,7 @@ public function initFieldsDataProvider()
true,
'Config Value',
null,
- 0,
+ 1,
true,
false,
true
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ConfigSetProcessorFactoryTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ConfigSetProcessorFactoryTest.php
index 1fa0310ca62eb..a8f40106eb564 100644
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ConfigSetProcessorFactoryTest.php
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ConfigSetProcessorFactoryTest.php
@@ -40,7 +40,7 @@ protected function setUp()
$this->model = new ConfigSetProcessorFactory(
$this->objectManagerMock,
[
- ConfigSetProcessorFactory::TYPE_LOCK => LockProcessor::class,
+ ConfigSetProcessorFactory::TYPE_LOCK_ENV => LockProcessor::class,
ConfigSetProcessorFactory::TYPE_DEFAULT => DefaultProcessor::class,
'wrongType' => \stdClass::class,
]
@@ -58,7 +58,7 @@ public function testCreate()
$this->assertInstanceOf(
ConfigSetProcessorInterface::class,
- $this->model->create(ConfigSetProcessorFactory::TYPE_LOCK)
+ $this->model->create(ConfigSetProcessorFactory::TYPE_LOCK_ENV)
);
}
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php
index 066b0fbe84b50..984e0fe842687 100644
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php
@@ -166,7 +166,9 @@ private function configMockForProcessTest($path, $scope, $scopeCode)
/**
* @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage The value you set has already been locked. To change the value, use the --lock option.
+ * @codingStandardsIgnoreStart
+ * @expectedExceptionMessage The value you set has already been locked. To change the value, use the --lock-env option.
+ * @codingStandardsIgnoreEnd
*/
public function testProcessLockedValue()
{
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockConfigProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockConfigProcessorTest.php
new file mode 100644
index 0000000000000..c727184efb4fc
--- /dev/null
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockConfigProcessorTest.php
@@ -0,0 +1,220 @@
+preparedValueFactory = $this->getMockBuilder(PreparedValueFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->deploymentConfigWriterMock = $this->getMockBuilder(DeploymentConfig\Writer::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->arrayManagerMock = $this->getMockBuilder(ArrayManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->configPathResolver = $this->getMockBuilder(ConfigPathResolver::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->valueMock = $this->getMockBuilder(Value::class)
+ ->setMethods(['validateBeforeSave', 'beforeSave', 'setValue', 'getValue', 'afterSave'])
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->model = new LockProcessor(
+ $this->preparedValueFactory,
+ $this->deploymentConfigWriterMock,
+ $this->arrayManagerMock,
+ $this->configPathResolver,
+ ConfigFilePool::APP_CONFIG
+ );
+ }
+
+ /**
+ * Tests process of share flow.
+ *
+ * @param string $path
+ * @param string $value
+ * @param string $scope
+ * @param string|null $scopeCode
+ * @dataProvider processDataProvider
+ */
+ public function testProcess($path, $value, $scope, $scopeCode)
+ {
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->with($path, $value, $scope, $scopeCode)
+ ->willReturn($this->valueMock);
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->arrayManagerMock->expects($this->once())
+ ->method('set')
+ ->with('system/default/test/test/test', [], $value)
+ ->willReturn([
+ 'system' => [
+ 'default' => [
+ 'test' => [
+ 'test' => [
+ 'test' => $value
+ ]
+ ]
+ ]
+ ]
+ ]);
+ $this->valueMock->expects($this->once())
+ ->method('getValue')
+ ->willReturn($value);
+ $this->deploymentConfigWriterMock->expects($this->once())
+ ->method('saveConfig')
+ ->with(
+ [
+ ConfigFilePool::APP_CONFIG => [
+ 'system' => [
+ 'default' => [
+ 'test' => [
+ 'test' => [
+ 'test' => $value
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ false
+ );
+ $this->valueMock->expects($this->once())
+ ->method('validateBeforeSave');
+ $this->valueMock->expects($this->once())
+ ->method('beforeSave');
+ $this->valueMock->expects($this->once())
+ ->method('afterSave');
+
+ $this->model->process($path, $value, $scope, $scopeCode);
+ }
+
+ /**
+ * @return array
+ */
+ public function processDataProvider()
+ {
+ return [
+ ['test/test/test', 'value', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null],
+ ['test/test/test', 'value', ScopeInterface::SCOPE_WEBSITE, 'base'],
+ ['test/test/test', 'value', ScopeInterface::SCOPE_STORE, 'test'],
+ ];
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Filesystem is not writable.
+ */
+ public function testProcessNotReadableFs()
+ {
+ $path = 'test/test/test';
+ $value = 'value';
+
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($this->valueMock);
+ $this->valueMock->expects($this->once())
+ ->method('getValue')
+ ->willReturn($value);
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->arrayManagerMock->expects($this->once())
+ ->method('set')
+ ->with('system/default/test/test/test', [], $value)
+ ->willReturn(null);
+ $this->deploymentConfigWriterMock->expects($this->once())
+ ->method('saveConfig')
+ ->willThrowException(new FileSystemException(__('Filesystem is not writable.')));
+
+ $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Invalid values
+ */
+ public function testCustomException()
+ {
+ $path = 'test/test/test';
+ $value = 'value';
+
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($this->valueMock);
+ $this->arrayManagerMock->expects($this->never())
+ ->method('set');
+ $this->valueMock->expects($this->once())
+ ->method('getValue');
+ $this->valueMock->expects($this->once())
+ ->method('afterSave')
+ ->willThrowException(new \Exception('Invalid values'));
+ $this->deploymentConfigWriterMock->expects($this->never())
+ ->method('saveConfig');
+
+ $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+ }
+}
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockEnvProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockEnvProcessorTest.php
new file mode 100644
index 0000000000000..4e0248f886028
--- /dev/null
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockEnvProcessorTest.php
@@ -0,0 +1,220 @@
+preparedValueFactory = $this->getMockBuilder(PreparedValueFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->deploymentConfigWriterMock = $this->getMockBuilder(DeploymentConfig\Writer::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->arrayManagerMock = $this->getMockBuilder(ArrayManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->configPathResolver = $this->getMockBuilder(ConfigPathResolver::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->valueMock = $this->getMockBuilder(Value::class)
+ ->setMethods(['validateBeforeSave', 'beforeSave', 'setValue', 'getValue', 'afterSave'])
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->model = new LockProcessor(
+ $this->preparedValueFactory,
+ $this->deploymentConfigWriterMock,
+ $this->arrayManagerMock,
+ $this->configPathResolver,
+ ConfigFilePool::APP_ENV
+ );
+ }
+
+ /**
+ * Tests process of lock flow.
+ *
+ * @param string $path
+ * @param string $value
+ * @param string $scope
+ * @param string|null $scopeCode
+ * @dataProvider processDataProvider
+ */
+ public function testProcess($path, $value, $scope, $scopeCode)
+ {
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->with($path, $value, $scope, $scopeCode)
+ ->willReturn($this->valueMock);
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->arrayManagerMock->expects($this->once())
+ ->method('set')
+ ->with('system/default/test/test/test', [], $value)
+ ->willReturn([
+ 'system' => [
+ 'default' => [
+ 'test' => [
+ 'test' => [
+ 'test' => $value
+ ]
+ ]
+ ]
+ ]
+ ]);
+ $this->valueMock->expects($this->once())
+ ->method('getValue')
+ ->willReturn($value);
+ $this->deploymentConfigWriterMock->expects($this->once())
+ ->method('saveConfig')
+ ->with(
+ [
+ ConfigFilePool::APP_ENV => [
+ 'system' => [
+ 'default' => [
+ 'test' => [
+ 'test' => [
+ 'test' => $value
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ false
+ );
+ $this->valueMock->expects($this->once())
+ ->method('validateBeforeSave');
+ $this->valueMock->expects($this->once())
+ ->method('beforeSave');
+ $this->valueMock->expects($this->once())
+ ->method('afterSave');
+
+ $this->model->process($path, $value, $scope, $scopeCode);
+ }
+
+ /**
+ * @return array
+ */
+ public function processDataProvider()
+ {
+ return [
+ ['test/test/test', 'value', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null],
+ ['test/test/test', 'value', ScopeInterface::SCOPE_WEBSITE, 'base'],
+ ['test/test/test', 'value', ScopeInterface::SCOPE_STORE, 'test'],
+ ];
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Filesystem is not writable.
+ */
+ public function testProcessNotReadableFs()
+ {
+ $path = 'test/test/test';
+ $value = 'value';
+
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($this->valueMock);
+ $this->valueMock->expects($this->once())
+ ->method('getValue')
+ ->willReturn($value);
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->arrayManagerMock->expects($this->once())
+ ->method('set')
+ ->with('system/default/test/test/test', [], $value)
+ ->willReturn(null);
+ $this->deploymentConfigWriterMock->expects($this->once())
+ ->method('saveConfig')
+ ->willThrowException(new FileSystemException(__('Filesystem is not writable.')));
+
+ $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Invalid values
+ */
+ public function testCustomException()
+ {
+ $path = 'test/test/test';
+ $value = 'value';
+
+ $this->configPathResolver->expects($this->once())
+ ->method('resolve')
+ ->willReturn('system/default/test/test/test');
+ $this->preparedValueFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($this->valueMock);
+ $this->arrayManagerMock->expects($this->never())
+ ->method('set');
+ $this->valueMock->expects($this->once())
+ ->method('getValue');
+ $this->valueMock->expects($this->once())
+ ->method('afterSave')
+ ->willThrowException(new \Exception('Invalid values'));
+ $this->deploymentConfigWriterMock->expects($this->never())
+ ->method('saveConfig');
+
+ $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+ }
+}
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockProcessorTest.php
deleted file mode 100644
index 4535e9ad888c2..0000000000000
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/LockProcessorTest.php
+++ /dev/null
@@ -1,219 +0,0 @@
-preparedValueFactory = $this->getMockBuilder(PreparedValueFactory::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->deploymentConfigWriterMock = $this->getMockBuilder(DeploymentConfig\Writer::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->arrayManagerMock = $this->getMockBuilder(ArrayManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->configPathResolver = $this->getMockBuilder(ConfigPathResolver::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->valueMock = $this->getMockBuilder(Value::class)
- ->setMethods(['validateBeforeSave', 'beforeSave', 'setValue', 'getValue', 'afterSave'])
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->model = new LockProcessor(
- $this->preparedValueFactory,
- $this->deploymentConfigWriterMock,
- $this->arrayManagerMock,
- $this->configPathResolver
- );
- }
-
- /**
- * Tests process of lock flow.
- *
- * @param string $path
- * @param string $value
- * @param string $scope
- * @param string|null $scopeCode
- * @dataProvider processDataProvider
- */
- public function testProcess($path, $value, $scope, $scopeCode)
- {
- $this->preparedValueFactory->expects($this->once())
- ->method('create')
- ->with($path, $value, $scope, $scopeCode)
- ->willReturn($this->valueMock);
- $this->configPathResolver->expects($this->once())
- ->method('resolve')
- ->willReturn('system/default/test/test/test');
- $this->arrayManagerMock->expects($this->once())
- ->method('set')
- ->with('system/default/test/test/test', [], $value)
- ->willReturn([
- 'system' => [
- 'default' => [
- 'test' => [
- 'test' => [
- 'test' => $value
- ]
- ]
- ]
- ]
- ]);
- $this->valueMock->expects($this->once())
- ->method('getValue')
- ->willReturn($value);
- $this->deploymentConfigWriterMock->expects($this->once())
- ->method('saveConfig')
- ->with(
- [
- ConfigFilePool::APP_ENV => [
- 'system' => [
- 'default' => [
- 'test' => [
- 'test' => [
- 'test' => $value
- ]
- ]
- ]
- ]
- ]
- ],
- false
- );
- $this->valueMock->expects($this->once())
- ->method('validateBeforeSave');
- $this->valueMock->expects($this->once())
- ->method('beforeSave');
- $this->valueMock->expects($this->once())
- ->method('afterSave');
-
- $this->model->process($path, $value, $scope, $scopeCode);
- }
-
- /**
- * @return array
- */
- public function processDataProvider()
- {
- return [
- ['test/test/test', 'value', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null],
- ['test/test/test', 'value', ScopeInterface::SCOPE_WEBSITE, 'base'],
- ['test/test/test', 'value', ScopeInterface::SCOPE_STORE, 'test'],
- ];
- }
-
- /**
- * @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage Filesystem is not writable.
- */
- public function testProcessNotReadableFs()
- {
- $path = 'test/test/test';
- $value = 'value';
-
- $this->preparedValueFactory->expects($this->once())
- ->method('create')
- ->willReturn($this->valueMock);
- $this->valueMock->expects($this->once())
- ->method('getValue')
- ->willReturn($value);
- $this->configPathResolver->expects($this->once())
- ->method('resolve')
- ->willReturn('system/default/test/test/test');
- $this->arrayManagerMock->expects($this->once())
- ->method('set')
- ->with('system/default/test/test/test', [], $value)
- ->willReturn(null);
- $this->deploymentConfigWriterMock->expects($this->once())
- ->method('saveConfig')
- ->willThrowException(new FileSystemException(__('Filesystem is not writable.')));
-
- $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
- }
-
- /**
- * @expectedException \Exception
- * @expectedExceptionMessage Invalid values
- */
- public function testCustomException()
- {
- $path = 'test/test/test';
- $value = 'value';
-
- $this->configPathResolver->expects($this->once())
- ->method('resolve')
- ->willReturn('system/default/test/test/test');
- $this->preparedValueFactory->expects($this->once())
- ->method('create')
- ->willReturn($this->valueMock);
- $this->arrayManagerMock->expects($this->never())
- ->method('set');
- $this->valueMock->expects($this->once())
- ->method('getValue');
- $this->valueMock->expects($this->once())
- ->method('afterSave')
- ->willThrowException(new \Exception('Invalid values'));
- $this->deploymentConfigWriterMock->expects($this->never())
- ->method('saveConfig');
-
- $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
- }
-}
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
index 4e65ab3f4cc21..ac4dda2a98517 100644
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/ProcessorFacadeTest.php
@@ -11,6 +11,7 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Scope\ValidatorInterface;
use Magento\Config\Model\Config\PathValidator;
+use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\ValidatorException;
use Magento\Framework\Exception\CouldNotSaveException;
@@ -122,7 +123,13 @@ public function testProcess()
$this->assertSame(
'Value was saved.',
- $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, false)
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ false
+ )
);
}
@@ -132,12 +139,19 @@ public function testProcess()
*/
public function testProcessWithValidatorException(LocalizedException $exception)
{
- $this->expectException(ValidatorException::class, 'Some error');
+ $this->expectException(ValidatorException::class);
+ $this->expectExceptionMessage('Some error');
$this->scopeValidatorMock->expects($this->once())
->method('isValid')
->willThrowException($exception);
- $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, false);
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ false
+ );
}
/**
@@ -172,7 +186,13 @@ public function testProcessWithConfigurationMismatchException()
$this->configMock->expects($this->never())
->method('clean');
- $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, false);
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ false
+ );
}
/**
@@ -198,17 +218,50 @@ public function testProcessWithCouldNotSaveException()
$this->configMock->expects($this->never())
->method('clean');
- $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, false);
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ false
+ );
+ }
+
+ public function testExecuteLockEnv()
+ {
+ $this->scopeValidatorMock->expects($this->once())
+ ->method('isValid')
+ ->willReturn(true);
+ $this->configSetProcessorFactoryMock->expects($this->once())
+ ->method('create')
+ ->with(ConfigSetProcessorFactory::TYPE_LOCK_ENV)
+ ->willReturn($this->processorMock);
+ $this->processorMock->expects($this->once())
+ ->method('process')
+ ->with('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+ $this->configMock->expects($this->once())
+ ->method('clean');
+
+ $this->assertSame(
+ 'Value was saved in app/etc/env.php and locked.',
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ true
+ )
+ );
}
- public function testExecuteLock()
+ public function testExecuteLockConfig()
{
$this->scopeValidatorMock->expects($this->once())
->method('isValid')
->willReturn(true);
$this->configSetProcessorFactoryMock->expects($this->once())
->method('create')
- ->with(ConfigSetProcessorFactory::TYPE_LOCK)
+ ->with(ConfigSetProcessorFactory::TYPE_LOCK_CONFIG)
->willReturn($this->processorMock);
$this->processorMock->expects($this->once())
->method('process')
@@ -217,8 +270,15 @@ public function testExecuteLock()
->method('clean');
$this->assertSame(
- 'Value was saved and locked.',
- $this->model->process('test/test/test', 'test', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, true)
+ 'Value was saved in app/etc/config.php and locked.',
+ $this->model->processWithLockTarget(
+ 'test/test/test',
+ 'test',
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
+ null,
+ true,
+ ConfigFilePool::APP_CONFIG
+ )
);
}
}
diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php
index 39f9c47361352..4f7327486d64a 100644
--- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php
+++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSetCommandTest.php
@@ -94,7 +94,7 @@ public function testExecute()
->method('create')
->willReturn($this->processorFacadeMock);
$this->processorFacadeMock->expects($this->once())
- ->method('process')
+ ->method('processWithLockTarget')
->willReturn('Some message');
$this->emulatedAreProcessorMock->expects($this->once())
->method('process')
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/AddressTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/AddressTest.php
index bacbda537fb1d..e6b774db041c3 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/AddressTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/AddressTest.php
@@ -40,6 +40,9 @@ public function testBeforeSave($value, $expectedValue)
$this->assertEquals($expectedValue, $this->model->getValue());
}
+ /**
+ * @return array
+ */
public function beforeSaveDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/SenderTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/SenderTest.php
index 8e559ff8284ed..e38c247c3861a 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/SenderTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/Email/SenderTest.php
@@ -41,6 +41,9 @@ public function testBeforeSave($value, $expectedValue)
$this->assertEquals($expectedValue, $this->model->getValue());
}
+ /**
+ * @return array
+ */
public function beforeSaveDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php
index 493fdf9505c4c..bb1e0e0225901 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php
@@ -52,6 +52,9 @@ public function testAfterLoad($expected, $value, $numCalls, $unserializedValue =
$this->assertEquals($expected, $this->serializedConfig->getValue());
}
+ /**
+ * @return array
+ */
public function afterLoadDataProvider()
{
return [
@@ -87,6 +90,9 @@ public function testBeforeSave($expected, $value, $numCalls, $serializedValue =
$this->assertEquals($expected, $this->serializedConfig->getValue());
}
+ /**
+ * @return array
+ */
public function beforeSaveDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php
index 0fdf4532462ac..4d8eec0aa76ba 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/ImporterTest.php
@@ -156,6 +156,9 @@ public function testImport()
$this->scopeMock->expects($this->at(2))
->method('setCurrentScope')
->with('oldScope');
+ $this->scopeMock->expects($this->at(3))
+ ->method('setCurrentScope')
+ ->with('oldScope');
$this->flagManagerMock->expects($this->once())
->method('saveFlag')
->with(Importer::FLAG_CODE, $data);
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
index 51432366bb441..e602e0407feff 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
@@ -141,6 +141,9 @@ public function testIsVisibleReturnsTrueForProperScopes($settings, $scope)
$this->assertTrue($this->_model->isVisible());
}
+ /**
+ * @return array
+ */
public function isVisibleReturnsTrueForProperScopesDataProvider()
{
return [
@@ -170,6 +173,9 @@ public function testIsVisibleReturnsFalseForNonProperScopes($settings, $scope)
$this->assertFalse($this->_model->isVisible());
}
+ /**
+ * @return array
+ */
public function isVisibleReturnsFalseForNonProperScopesDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
index 5cad923264e00..ba74b93d9ad76 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php
@@ -8,6 +8,11 @@
use Magento\Config\Model\Config\Structure\ConcealInProductionConfigList;
use Magento\Framework\App\State;
+/**
+ * @deprecated Original class has changed the location
+ * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction
+ * @see \Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility\ConcealInProductionTest
+ */
class ConcealInProductionConfigListTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -43,6 +48,8 @@ protected function setUp()
* @param string $mageMode
* @param bool $expectedResult
* @dataProvider disabledDataProvider
+ *
+ * @deprecated
*/
public function testIsDisabled($path, $mageMode, $expectedResult)
{
@@ -54,6 +61,8 @@ public function testIsDisabled($path, $mageMode, $expectedResult)
/**
* @return array
+ *
+ * @deprecated
*/
public function disabledDataProvider()
{
@@ -78,6 +87,8 @@ public function disabledDataProvider()
* @param string $mageMode
* @param bool $expectedResult
* @dataProvider hiddenDataProvider
+ *
+ * @deprecated
*/
public function testIsHidden($path, $mageMode, $expectedResult)
{
@@ -89,6 +100,8 @@ public function testIsHidden($path, $mageMode, $expectedResult)
/**
* @return array
+ *
+ * @deprecated
*/
public function hiddenDataProvider()
{
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php
index 30c567fb490e6..750a829eef7ec 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php
@@ -88,6 +88,9 @@ public function testIsNegative($data, $isNegative)
$this->assertEquals($isNegative, $this->_getFieldObject($data, $isNegative)->isNegative());
}
+ /**
+ * @return array
+ */
public function dataProvider()
{
return [
@@ -110,6 +113,9 @@ public function testIsValueSatisfy($data, $isNegative, $value, $expected)
$this->assertEquals($expected, $this->_getFieldObject($data, $isNegative)->isValueSatisfy($value));
}
+ /**
+ * @return array
+ */
public function isValueSatisfyDataProvider()
{
return [
@@ -135,6 +141,9 @@ public function testGetValues($data, $isNegative, $expected)
$this->assertEquals($expected, $this->_getFieldObject($data, $isNegative)->getValues());
}
+ /**
+ * @return array
+ */
public function getValuesDataProvider()
{
$complexDataValues = [self::COMPLEX_VALUE1, self::COMPLEX_VALUE2, self::COMPLEX_VALUE3];
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/MapperTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/MapperTest.php
index 1c758bfcbefaa..c6cd03cf8f35b 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/MapperTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/MapperTest.php
@@ -98,7 +98,8 @@ public function testGetDependenciesWhenDependentIsInvisible($isValueSatisfy)
{
$expected = [];
$rowData = array_values($this->_testData);
- for ($i = 0; $i < count($this->_testData); ++$i) {
+ $count = count($this->_testData);
+ for ($i = 0; $i < $count; ++$i) {
$data = $rowData[$i];
$dependentPath = 'some path ' . $i;
$field = $this->_getField(
@@ -149,6 +150,9 @@ public function testGetDependenciesWhenDependentIsInvisible($isValueSatisfy)
$this->assertEquals($expected, $actual);
}
+ /**
+ * @return array
+ */
public function getDependenciesDataProvider()
{
return [[true], [false]];
@@ -158,7 +162,8 @@ public function testGetDependenciesIsVisible()
{
$expected = [];
$rowData = array_values($this->_testData);
- for ($i = 0; $i < count($this->_testData); ++$i) {
+ $count = count($this->_testData);
+ for ($i = 0; $i < $count; ++$i) {
$data = $rowData[$i];
$field = $this->_getField(
true,
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/IteratorTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/IteratorTest.php
index 1a0f3d03b060c..dcb7a90e55290 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/IteratorTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/IteratorTest.php
@@ -68,6 +68,9 @@ public function testIsLast($elementId, $result)
$this->assertEquals($result, $this->_model->isLast($elementMock));
}
+ /**
+ * @return array
+ */
public function isLastDataProvider()
{
return [[1, false], [2, false], [3, true]];
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php
new file mode 100644
index 0000000000000..873d447d9868c
--- /dev/null
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php
@@ -0,0 +1,107 @@
+stateMock = $this->getMockBuilder(State::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $configs = [
+ 'section1/group1/field1' => ElementVisibilityInterface::DISABLED,
+ 'section1/group1' => ElementVisibilityInterface::HIDDEN,
+ 'section1' => ElementVisibilityInterface::DISABLED,
+ 'section1/group2' => 'no',
+ 'section2/group1' => ElementVisibilityInterface::DISABLED,
+ 'section2/group2' => ElementVisibilityInterface::HIDDEN,
+ 'section3' => ElementVisibilityInterface::HIDDEN,
+ 'section3/group1/field1' => 'no',
+ ];
+ $exemptions = [
+ 'section1/group1/field3' => '',
+ 'section1/group2/field1' => '',
+ 'section2/group2/field1' => '',
+ 'section3/group2' => '',
+ ];
+
+ $this->model = new ConcealInProduction($this->stateMock, $configs, $exemptions);
+ }
+
+ /**
+ * @param string $path
+ * @param string $mageMode
+ * @param bool $isDisabled
+ * @param bool $isHidden
+ * @dataProvider disabledDataProvider
+ * @return void
+ */
+ public function testCheckVisibility(string $path, string $mageMode, bool $isHidden, bool $isDisabled)
+ {
+ $this->stateMock->expects($this->any())
+ ->method('getMode')
+ ->willReturn($mageMode);
+
+ $this->assertSame($isHidden, $this->model->isHidden($path));
+ $this->assertSame($isDisabled, $this->model->isDisabled($path));
+ }
+
+ /**
+ * @return array
+ */
+ public function disabledDataProvider(): array
+ {
+ return [
+ //visibility of field 'section1/group1/field1' should be applied
+ ['section1/group1/field1', State::MODE_PRODUCTION, false, true],
+ ['section1/group1/field1', State::MODE_DEFAULT, false, false],
+ ['section1/group1/field1', State::MODE_DEVELOPER, false, false],
+ //visibility of group 'section1/group1' should be applied
+ ['section1/group1/field2', State::MODE_PRODUCTION, true, false],
+ ['section1/group1/field2', State::MODE_DEFAULT, false, false],
+ ['section1/group1/field2', State::MODE_DEVELOPER, false, false],
+ //exemption should be applied for section1/group2/field1
+ ['section1/group2/field1', State::MODE_PRODUCTION, false, false],
+ ['section1/group2/field1', State::MODE_DEFAULT, false, false],
+ ['section1/group2/field1', State::MODE_DEVELOPER, false, false],
+ //as 'section1/group2' has neither Disable nor Hidden rule, this field should be visible
+ ['section1/group2/field2', State::MODE_PRODUCTION, false, false],
+ //exemption should be applied for section1/group1/field3
+ ['section1/group1/field3', State::MODE_PRODUCTION, false, false],
+ //visibility of group 'section2/group1' should be applied
+ ['section2/group1/field1', State::MODE_PRODUCTION, false, true],
+ //exemption should be applied for section2/group2/field1
+ ['section2/group2/field1', State::MODE_PRODUCTION, false, false],
+ //any rule should not be applied
+ ['section2/group3/field1', State::MODE_PRODUCTION, false, false],
+ //any rule should not be applied
+ ['section3/group1/field1', State::MODE_PRODUCTION, false, false],
+ //visibility of section 'section3' should be applied
+ ['section3/group1/field2', State::MODE_PRODUCTION, true, false],
+ //exception from 'section3/group2' should be applied
+ ['section3/group2/field1', State::MODE_PRODUCTION, false, false],
+
+ ];
+ }
+}
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php
new file mode 100644
index 0000000000000..ae213c19a5337
--- /dev/null
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php
@@ -0,0 +1,156 @@
+createMock(ConcealInProductionFactory::class);
+
+ $this->concealInProductionMock = $this->createMock(ConcealInProduction::class);
+
+ $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class);
+
+ $configs = [
+ 'section1/group1/field1' => ElementVisibilityInterface::DISABLED,
+ 'section1/group1' => ElementVisibilityInterface::HIDDEN,
+ 'section1' => ElementVisibilityInterface::DISABLED,
+ 'section1/group2' => 'no',
+ 'section2/group1' => ElementVisibilityInterface::DISABLED,
+ 'section2/group2' => ElementVisibilityInterface::HIDDEN,
+ 'section3' => ElementVisibilityInterface::HIDDEN,
+ 'section3/group1/field1' => 'no',
+ ];
+ $exemptions = [
+ 'section1/group1/field3' => '',
+ 'section1/group2/field1' => '',
+ 'section2/group2/field1' => '',
+ 'section3/group2' => '',
+ ];
+
+ $concealInProductionFactoryMock->expects($this->any())
+ ->method('create')
+ ->with(['configs' => $configs, 'exemptions' => $exemptions])
+ ->willReturn($this->concealInProductionMock);
+
+ $this->model = new ConcealInProductionWithoutScdOnDemand(
+ $concealInProductionFactoryMock,
+ $this->deploymentConfigMock,
+ $configs,
+ $exemptions
+ );
+ }
+
+ /**
+ * @return void
+ */
+ public function testIsHiddenScdOnDemandEnabled()
+ {
+ $path = 'section1/group1/field1';
+ $this->deploymentConfigMock->expects($this->once())
+ ->method('getConfigData')
+ ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)
+ ->willReturn(true);
+ $this->concealInProductionMock->expects($this->never())
+ ->method('isHidden');
+
+ $this->assertFalse($this->model->isHidden($path));
+ }
+
+ /**
+ * @return void
+ */
+ public function testIsDisabledScdOnDemandEnabled()
+ {
+ $path = 'section1/group1/field1';
+ $this->deploymentConfigMock->expects($this->once())
+ ->method('getConfigData')
+ ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)
+ ->willReturn(true);
+ $this->concealInProductionMock->expects($this->never())
+ ->method('isDisabled');
+
+ $this->assertFalse($this->model->isDisabled($path));
+ }
+
+ /**
+ * @param bool $isHidden
+ *
+ * @dataProvider visibilityDataProvider
+ * @return void
+ */
+ public function testIsHiddenScdOnDemandDisabled(bool $isHidden)
+ {
+ $path = 'section1/group1/field1';
+ $this->deploymentConfigMock->expects($this->once())
+ ->method('getConfigData')
+ ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)
+ ->willReturn(false);
+ $this->concealInProductionMock->expects($this->once())
+ ->method('isHidden')
+ ->with($path)
+ ->willReturn($isHidden);
+
+ $this->assertSame($isHidden, $this->model->isHidden($path));
+ }
+
+ /**
+ * @param bool $isDisabled
+ *
+ * @dataProvider visibilityDataProvider
+ * @return void
+ */
+ public function testIsDisabledScdOnDemandDisabled(bool $isDisabled)
+ {
+ $path = 'section1/group1/field1';
+ $this->deploymentConfigMock->expects($this->once())
+ ->method('getConfigData')
+ ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)
+ ->willReturn(false);
+ $this->concealInProductionMock->expects($this->once())
+ ->method('isDisabled')
+ ->with($path)
+ ->willReturn($isDisabled);
+
+ $this->assertSame($isDisabled, $this->model->isDisabled($path));
+ }
+
+ /**
+ * @return array
+ */
+ public function visibilityDataProvider(): array
+ {
+ return [
+ [true],
+ [false],
+ ];
+ }
+}
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibilityCompositeTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibilityCompositeTest.php
index a3bdb6f008f1d..b779c29c93155 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibilityCompositeTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibilityCompositeTest.php
@@ -44,7 +44,7 @@ protected function setUp()
public function testException()
{
$visibility = [
- 'stdClass' => new \StdClass()
+ 'stdClass' => new \stdClass()
];
new ElementVisibilityComposite($visibility);
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/ExtendsTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/ExtendsTest.php
index 95e8246c6a3d3..7762c8993b24b 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/ExtendsTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/ExtendsTest.php
@@ -44,6 +44,9 @@ public function testMapWithBadPath()
$this->_sut->map($sourceData);
}
+ /**
+ * @return array
+ */
public function mapDataProvider()
{
return [
@@ -55,6 +58,9 @@ public function mapDataProvider()
];
}
+ /**
+ * @return array
+ */
protected function _emptySectionsNodeData()
{
$data = ['config' => ['system' => ['sections' => 'some_non_array']]];
@@ -62,6 +68,9 @@ protected function _emptySectionsNodeData()
return [$data, $data];
}
+ /**
+ * @return array
+ */
protected function _extendFromASiblingData()
{
$source = $result = [
@@ -81,6 +90,9 @@ protected function _extendFromASiblingData()
return [$source, $result];
}
+ /**
+ * @return array
+ */
protected function _extendFromNodeOnHigherLevelData()
{
$source = $result = [
@@ -114,6 +126,9 @@ protected function _extendFromNodeOnHigherLevelData()
return [$source, $result];
}
+ /**
+ * @return array
+ */
protected function _extendWithMerge()
{
$source = $result = [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
index c671a5326c4de..dd95574ffa62d 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Mapper/Helper/RelativePathConverterTest.php
@@ -24,7 +24,8 @@ public function testConvertWithInvalidRelativePath()
$exceptionMessage = sprintf('Invalid relative path %s in %s node', $relativePath, $nodePath);
- $this->expectException('InvalidArgumentException', $exceptionMessage);
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage($exceptionMessage);
$this->_sut->convert($nodePath, $relativePath);
}
@@ -35,7 +36,8 @@ public function testConvertWithInvalidRelativePath()
*/
public function testConvertWithInvalidArguments($nodePath, $relativePath)
{
- $this->expectException('InvalidArgumentException', 'Invalid arguments');
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('Invalid arguments');
$this->_sut->convert($nodePath, $relativePath);
}
@@ -50,11 +52,17 @@ public function testConvert($nodePath, $relativePath, $result)
$this->assertEquals($result, $this->_sut->convert($nodePath, $relativePath));
}
+ /**
+ * @return array
+ */
public function convertWithInvalidArgumentsDataProvider()
{
return [['', ''], ['some/node', ''], ['', 'some/node']];
}
+ /**
+ * @return array
+ */
public function convertDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php
index e67ea6ec0fba1..6c059f4b69b70 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php
@@ -221,6 +221,9 @@ private function getElementReturnsEmptyElementIfNotExistingElementIsRequested(
return $elementMock;
}
+ /**
+ * @return array
+ */
public function emptyElementDataProvider()
{
return [
@@ -389,6 +392,9 @@ public function testGetFieldPathsByAttribute($attributeName, $attributeValue, $p
$this->assertEquals($paths, $this->_model->getFieldPathsByAttribute($attributeName, $attributeValue));
}
+ /**
+ * @return array
+ */
public function getFieldPathsByAttributeDataProvider()
{
return [
diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
index 2832e8e54e5f6..fcc1ff8b9c70c 100644
--- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
@@ -60,6 +60,11 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
*/
protected $_configStructure;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $_settingsChecker;
+
protected function setUp()
{
$this->_eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class);
@@ -79,7 +84,7 @@ protected function setUp()
$this->_transFactoryMock = $this->createPartialMock(
\Magento\Framework\DB\TransactionFactory::class,
- ['create']
+ ['create', 'addObject']
);
$this->_appConfigMock = $this->createMock(\Magento\Framework\App\Config\ReinitableConfigInterface::class);
$this->_configLoaderMock = $this->createPartialMock(
@@ -90,6 +95,9 @@ protected function setUp()
$this->_storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class);
+ $this->_settingsChecker = $this
+ ->createMock(\Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker::class);
+
$this->_model = new \Magento\Config\Model\Config(
$this->_appConfigMock,
$this->_eventManagerMock,
@@ -97,7 +105,8 @@ protected function setUp()
$this->_transFactoryMock,
$this->_configLoaderMock,
$this->_dataFactoryMock,
- $this->_storeManager
+ $this->_storeManager,
+ $this->_settingsChecker
);
}
@@ -149,55 +158,96 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent()
$this->_model->save();
}
- public function testSaveToCheckScopeDataSet()
+ public function testDoNotSaveReadOnlyFields()
{
$transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class);
-
$this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock));
+ $this->_settingsChecker->expects($this->any())->method('isReadOnly')->will($this->returnValue(true));
$this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([]));
- $this->_eventManagerMock->expects(
- $this->at(0)
- )->method(
- 'dispatch'
- )->with(
- $this->equalTo('admin_system_config_changed_section_'),
- $this->arrayHasKey('website')
- );
-
- $this->_eventManagerMock->expects(
- $this->at(0)
- )->method(
- 'dispatch'
- )->with(
- $this->equalTo('admin_system_config_changed_section_'),
- $this->arrayHasKey('store')
- );
+ $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]);
+ $this->_model->setSection('section');
$group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class);
+ $group->method('getPath')->willReturn('section/1');
$field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class);
+ $field->method('getGroupPath')->willReturn('section/1');
+ $field->method('getId')->willReturn('key');
+
+ $this->_configStructure->expects($this->at(0))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(1))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(2))
+ ->method('getElement')
+ ->with('section/1/key')
+ ->will($this->returnValue($field));
- $this->_configStructure->expects(
- $this->at(0)
- )->method(
- 'getElement'
- )->with(
- '/1'
- )->will(
- $this->returnValue($group)
+ $backendModel = $this->createPartialMock(
+ \Magento\Framework\App\Config\Value::class,
+ ['addData']
);
+ $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel));
- $this->_configStructure->expects(
- $this->at(1)
- )->method(
- 'getElement'
- )->with(
- '/1/key'
- )->will(
- $this->returnValue($field)
- );
+ $this->_transFactoryMock->expects($this->never())->method('addObject');
+ $backendModel->expects($this->never())->method('addData');
+
+ $this->_model->save();
+ }
+
+ public function testSaveToCheckScopeDataSet()
+ {
+ $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class);
+ $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock));
+
+ $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([]));
+
+ $this->_eventManagerMock->expects($this->at(0))
+ ->method('dispatch')
+ ->with(
+ $this->equalTo('admin_system_config_changed_section_section'),
+ $this->arrayHasKey('website')
+ );
+ $this->_eventManagerMock->expects($this->at(0))
+ ->method('dispatch')
+ ->with(
+ $this->equalTo('admin_system_config_changed_section_section'),
+ $this->arrayHasKey('store')
+ );
+
+ $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class);
+ $group->method('getPath')->willReturn('section/1');
+
+ $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class);
+ $field->method('getGroupPath')->willReturn('section/1');
+ $field->method('getId')->willReturn('key');
+
+ $this->_configStructure->expects($this->at(0))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(1))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(2))
+ ->method('getElement')
+ ->with('section/1/key')
+ ->will($this->returnValue($field));
+ $this->_configStructure->expects($this->at(3))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(4))
+ ->method('getElement')
+ ->with('section/1/key')
+ ->will($this->returnValue($field));
$website = $this->createMock(\Magento\Store\Model\Website::class);
$website->expects($this->any())->method('getCode')->will($this->returnValue('website_code'));
@@ -206,19 +256,16 @@ public function testSaveToCheckScopeDataSet()
$this->_storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true));
$this->_model->setWebsite('website');
-
+ $this->_model->setSection('section');
$this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]);
$backendModel = $this->createPartialMock(
\Magento\Framework\App\Config\Value::class,
['setPath', 'addData', '__sleep', '__wakeup']
);
- $backendModel->expects(
- $this->once()
- )->method(
- 'addData'
- )->with(
- [
+ $backendModel->expects($this->once())
+ ->method('addData')
+ ->with([
'field' => 'key',
'groups' => [1 => ['fields' => ['key' => ['data']]]],
'group_id' => null,
@@ -227,17 +274,11 @@ public function testSaveToCheckScopeDataSet()
'scope_code' => 'website_code',
'field_config' => null,
'fieldset_data' => ['key' => null],
- ]
- );
- $backendModel->expects(
- $this->once()
- )->method(
- 'setPath'
- )->with(
- '/key'
- )->will(
- $this->returnValue($backendModel)
- );
+ ]);
+ $backendModel->expects($this->once())
+ ->method('setPath')
+ ->with('section/1/key')
+ ->will($this->returnValue($backendModel));
$this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel));
@@ -280,7 +321,8 @@ public function testSetDataByPathEmpty()
public function testSetDataByPathWrongDepth($path, $expectedException)
{
$expectedException = 'Allowed depth of configuration is 3 (//). ' . $expectedException;
- $this->expectException('\UnexpectedValueException', $expectedException);
+ $this->expectException('\UnexpectedValueException');
+ $this->expectExceptionMessage($expectedException);
$value = 'value';
$this->_model->setDataByPath($path, $value);
}
diff --git a/app/code/Magento/Config/Test/Unit/Model/Placeholder/EnvironmentTest.php b/app/code/Magento/Config/Test/Unit/Model/Placeholder/EnvironmentTest.php
index 8217ff09c0541..e4c01e794fb0f 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Placeholder/EnvironmentTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Placeholder/EnvironmentTest.php
@@ -52,6 +52,9 @@ public function testGenerate($path, $scope, $scopeId, $expected)
);
}
+ /**
+ * @return array
+ */
public function getGenerateDataProvider()
{
return [
diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json
index 28cec4141cbf8..e43c9b0382e25 100644
--- a/app/code/Magento/Config/composer.json
+++ b/app/code/Magento/Config/composer.json
@@ -2,8 +2,8 @@
"name": "magento/module-config",
"description": "N/A",
"require": {
- "php": "7.0.2|7.0.4|~7.0.6|~7.1.0",
- "magento/framework": "100.2.*",
+ "php": "~7.0.13|~7.1.0",
+ "magento/framework": "101.0.*",
"magento/module-store": "100.2.*",
"magento/module-cron": "100.2.*",
"magento/module-email": "100.2.*",
@@ -13,7 +13,7 @@
"magento/module-deploy": "100.2.*"
},
"type": "magento2-module",
- "version": "100.2.0-dev",
+ "version": "101.0.6",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/Config/etc/adminhtml/di.xml b/app/code/Magento/Config/etc/adminhtml/di.xml
index c21c06c7f3e1f..5e54f177776ba 100644
--- a/app/code/Magento/Config/etc/adminhtml/di.xml
+++ b/app/code/Magento/Config/etc/adminhtml/di.xml
@@ -15,6 +15,8 @@
- Magento\Config\Model\Config\Structure\ConcealInProductionConfigList
+ - Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction
+ - Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand
diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml
index bcddd8ceaf27a..a5dd18097fb47 100644
--- a/app/code/Magento/Config/etc/di.xml
+++ b/app/code/Magento/Config/etc/di.xml
@@ -296,10 +296,21 @@
- Magento\Config\Console\Command\ConfigSet\DefaultProcessor
- - Magento\Config\Console\Command\ConfigSet\LockProcessor
+ - Magento\Config\Console\Command\ConfigSet\VirtualLockEnvProcessor
+ - Magento\Config\Console\Command\ConfigSet\VirtualLockConfigProcessor
+
+
+ app_env
+
+
+
+
+ app_config
+
+
diff --git a/app/code/Magento/Config/etc/module.xml b/app/code/Magento/Config/etc/module.xml
index b64cbe2b72623..cdf31ab7a5d19 100644
--- a/app/code/Magento/Config/etc/module.xml
+++ b/app/code/Magento/Config/etc/module.xml
@@ -6,5 +6,9 @@
*/
-->
-
+
+
+
+
+
diff --git a/app/code/Magento/Config/etc/system_file.xsd b/app/code/Magento/Config/etc/system_file.xsd
index 5a2b915262a9a..f1688b2e35371 100644
--- a/app/code/Magento/Config/etc/system_file.xsd
+++ b/app/code/Magento/Config/etc/system_file.xsd
@@ -474,7 +474,7 @@
-
+
diff --git a/app/code/Magento/Config/view/adminhtml/layout/adminhtml_system_config_edit.xml b/app/code/Magento/Config/view/adminhtml/layout/adminhtml_system_config_edit.xml
index 4d114d237b91b..1a58798c386c0 100644
--- a/app/code/Magento/Config/view/adminhtml/layout/adminhtml_system_config_edit.xml
+++ b/app/code/Magento/Config/view/adminhtml/layout/adminhtml_system_config_edit.xml
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/app/code/Magento/Config/view/adminhtml/templates/system/config/form/field/array.phtml b/app/code/Magento/Config/view/adminhtml/templates/system/config/form/field/array.phtml
index 93af2cfa653f8..cf235d368b9bc 100644
--- a/app/code/Magento/Config/view/adminhtml/templates/system/config/form/field/array.phtml
+++ b/app/code/Magento/Config/view/adminhtml/templates/system/config/form/field/array.phtml
@@ -21,7 +21,7 @@ $_colspan = $block->isAddAfter() ? 2 : 1;
getColumns() as $columnName => $column): ?>
= /* @escapeNotVerified */ $column['label'] ?>
- Action
+ = /* @escapeNotVerified */ __('Action') ?>