diff --git a/app/bootstrap.php b/app/bootstrap.php index ec60a1708dacc3d2238960e5b1c9e7c0da05cc92..c8e676cb69cffd5cf32de8f12f9f83987cc6619a 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -11,14 +11,14 @@ error_reporting(E_ALL); #ini_set('display_errors', 1); /* PHP version validation */ -if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID >= 50600 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) { +if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID >= 50605 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) { if (PHP_SAPI == 'cli') { - echo 'Magento supports PHP 5.6, 7.0.2, 7.0.4, and 7.0.6 or later. ' . + echo 'Magento supports PHP 5.6.5, 7.0.2, 7.0.4, and 7.0.6 or later. ' . 'Please read http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html'; } else { echo <<<HTML <div style="font:12px/1.35em arial, helvetica, sans-serif;"> - <p>Magento supports PHP 5.6, 7.0.2, 7.0.4, and 7.0.6 or later. Please read + <p>Magento supports PHP 5.6.5, 7.0.2, 7.0.4, and 7.0.6 or later. Please read <a target="_blank" href="http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html"> Magento System Requirements</a>. </div> @@ -35,6 +35,17 @@ $umaskFile = BP . '/magento_umask'; $mask = file_exists($umaskFile) ? octdec(file_get_contents($umaskFile)) : 002; umask($mask); +if (empty($_SERVER['ENABLE_IIS_REWRITES']) || ($_SERVER['ENABLE_IIS_REWRITES'] != 1)) { + /* + * Unset headers used by IIS URL rewrites. + */ + unset($_SERVER['HTTP_X_REWRITE_URL']); + unset($_SERVER['HTTP_X_ORIGINAL_URL']); + unset($_SERVER['IIS_WasUrlRewritten']); + unset($_SERVER['UNENCODED_URL']); + unset($_SERVER['ORIG_PATH_INFO']); +} + if (!empty($_SERVER['MAGE_PROFILER']) && isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false diff --git a/app/code/Magento/AdminNotification/composer.json b/app/code/Magento/AdminNotification/composer.json index 527268df36b41efe5078528138342a03fa33745d..09d8ce41bbb03607a2f8cd16831454c8565a5204 100644 --- a/app/code/Magento/AdminNotification/composer.json +++ b/app/code/Magento/AdminNotification/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-admin-notification", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-media-storage": "100.2.*", diff --git a/app/code/Magento/AdvancedPricingImportExport/composer.json b/app/code/Magento/AdvancedPricingImportExport/composer.json index 65ea7524dffff5bb778aaf5ec7658302072cfaf9..2fc465fa3c3dfd7a51abdd713f0c5c6dfd6843c6 100644 --- a/app/code/Magento/AdvancedPricingImportExport/composer.json +++ b/app/code/Magento/AdvancedPricingImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-advanced-pricing-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Authorization/composer.json b/app/code/Magento/Authorization/composer.json index 0ca367d4854dfe5c35811d15aee4d718c0a4edb7..af88e8376dc756cb9e1f6180848df41e6fd8b5f3 100644 --- a/app/code/Magento/Authorization/composer.json +++ b/app/code/Magento/Authorization/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-authorization", "description": "Authorization module provides access to Magento ACL functionality.", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index 0c9e9641b6076850310edd75a2a4ea5ed3ccd094..b93cb6688f56d826a8d981518612897aae6f1ac8 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-authorizenet", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-sales": "100.2.*", "magento/module-store": "100.2.*", "magento/module-quote": "100.2.*", diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index 7d428636a1f45e23d81879c99baaeef6744518fc..5cfe6955b46bb99f6dd8c76cd58e3ba9bb6f28ad 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-backend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-directory": "100.2.*", "magento/module-developer": "100.2.*", diff --git a/app/code/Magento/Backup/composer.json b/app/code/Magento/Backup/composer.json index 21ed6f1780a41348c5bb26556c14fcaa02c08770..ee05d6726db4c3bea7460893242d546a511fe0cc 100644 --- a/app/code/Magento/Backup/composer.json +++ b/app/code/Magento/Backup/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-backup", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-cron": "100.2.*", diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 3b77e208837a4a4341baabbae615b7e35b266500..91cacf6add2b891dc4ad759cb6b1e9a6990cb397 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-braintree", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/magento-composer-installer": "*", "magento/module-config": "100.2.*", diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index 184260c39e954d1c5a1fd6d45b34f09fcab9daa7..075a1fdaf1fc118f42832fa438762715420a585a 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -98,7 +98,6 @@ define([ quote.totals.subscribe(function () { if (self.grandTotalAmount !== quote.totals()['base_grand_total']) { self.grandTotalAmount = quote.totals()['base_grand_total']; - self.reInitPayPal(); } }); diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index 50a3caaf68b3bd39ce1458e4883b4c1dd35c0525..538c80d9b1cf27af4ba14c911b0d0a6913c403c7 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -268,15 +268,12 @@ class BundlePanel extends AbstractModifier 'arguments' => [ 'data' => [ 'config' => [ - 'componentType' => 'dynamicRows', + 'componentType' => Container::NAME, + 'component' => 'Magento_Bundle/js/components/bundle-dynamic-rows', 'template' => 'ui/dynamic-rows/templates/collapsible', - 'label' => '', 'additionalClasses' => 'admin__field-wide', - 'collapsibleHeader' => true, - 'columnsHeader' => false, - 'deleteProperty' => false, - 'addButton' => false, 'dataScope' => 'data.bundle_options', + 'bundleSelectionsName' => 'product_bundle_container.bundle_selections' ], ], ], @@ -318,14 +315,11 @@ class BundlePanel extends AbstractModifier 'arguments' => [ 'data' => [ 'config' => [ - 'componentType' => DynamicRows::NAME, - 'label' => '', + 'componentType' => Container::NAME, + 'component' => 'Magento_Bundle/js/components/bundle-dynamic-rows-grid', 'sortOrder' => 50, 'additionalClasses' => 'admin__field-wide', - 'component' => 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid', 'template' => 'ui/dynamic-rows/templates/default', - 'columnsHeader' => false, - 'columnsHeaderAfterRender' => true, 'provider' => 'product_form.product_form_data_source', 'dataProvider' => '${ $.dataScope }' . '.bundle_button_proxy', 'identificationDRProperty' => 'product_id', @@ -343,8 +337,7 @@ class BundlePanel extends AbstractModifier 'selection_qty' => '', ], 'links' => ['insertData' => '${ $.provider }:${ $.dataProvider }'], - 'source' => 'product', - 'addButton' => false, + 'source' => 'product' ], ], ], @@ -561,7 +554,7 @@ class BundlePanel extends AbstractModifier 'componentType' => Container::NAME, 'isTemplate' => true, 'component' => 'Magento_Ui/js/dynamic-rows/record', - 'is_collection' => true, + 'is_collection' => true ], ], ], diff --git a/app/code/Magento/Bundle/composer.json b/app/code/Magento/Bundle/composer.json index ab587044476db47c0a824cfdb130988f22591971..24756bdf950a7a42431e31cbc08090ca0141eb27 100644 --- a/app/code/Magento/Bundle/composer.json +++ b/app/code/Magento/Bundle/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-bundle", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-tax": "100.2.*", diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js new file mode 100644 index 0000000000000000000000000000000000000000..e9a924e1cffe696688fed502bc715347c8afb8d2 --- /dev/null +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js @@ -0,0 +1,59 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid' +], function (_, dynamicRowsGrid) { + 'use strict'; + + return dynamicRowsGrid.extend({ + defaults: { + label: '', + columnsHeader: false, + columnsHeaderAfterRender: true, + addButton: false + }, + + /** + * Initialize elements from grid + * + * @param {Array} data + * + * @returns {Object} Chainable. + */ + initElements: function (data) { + var newData = this.getNewData(data), + recordIndex; + + this.parsePagesData(data); + + if (newData.length) { + if (this.insertData().length) { + recordIndex = data.length - newData.length - 1; + + _.each(newData, function (newRecord) { + this.processingAddChild(newRecord, ++recordIndex, newRecord[this.identificationProperty]); + }, this); + } + } + + return this; + }, + + /** + * Mapping value from grid + * + * @param {Array} data + */ + mappingValue: function (data) { + if (_.isEmpty(data)) { + return; + } + + this._super(); + } + }); +}); diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js new file mode 100644 index 0000000000000000000000000000000000000000..b36d8003a399fb4af88d3e3cfab34559e2e80bea --- /dev/null +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows.js @@ -0,0 +1,98 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'mageUtils', + 'uiRegistry', + 'Magento_Ui/js/dynamic-rows/dynamic-rows' +], function (_, utils, registry, dynamicRows) { + 'use strict'; + + return dynamicRows.extend({ + defaults: { + label: '', + collapsibleHeader: true, + columnsHeader: false, + deleteProperty: false, + addButton: false + }, + + /** + * Set new data to dataSource, + * delete element + * + * @param {Array} data - record data + */ + _updateData: function (data) { + var elems = _.clone(this.elems()), + path, + dataArr, + optionBaseData; + + dataArr = this.recordData.splice(this.startIndex, this.recordData().length - this.startIndex); + dataArr.splice(0, this.pageSize); + elems = _.sortBy(this.elems(), function (elem) { + return ~~elem.index; + }); + + data.concat(dataArr).forEach(function (rec, idx) { + if (elems[idx]) { + elems[idx].recordId = rec[this.identificationProperty]; + } + + if (!rec.position) { + rec.position = this.maxPosition; + this.setMaxPosition(); + } + + path = this.dataScope + '.' + this.index + '.' + (this.startIndex + idx); + optionBaseData = _.pick(rec, function (value) { + return !_.isObject(value); + }); + this.source.set(path, optionBaseData); + this.source.set(path + '.bundle_button_proxy', []); + this.source.set(path + '.bundle_selections', []); + this.removeBundleItemsFromOption(idx); + _.each(rec['bundle_selections'], function (obj, index) { + this.source.set(path + '.bundle_button_proxy' + '.' + index, rec['bundle_button_proxy'][index]); + this.source.set(path + '.bundle_selections' + '.' + index, obj); + }, this); + }, this); + + this.elems(elems); + }, + + /** + * Removes nested dynamic-rows-grid rendered records from option + * + * @param {Number|String} index - element index + */ + removeBundleItemsFromOption: function (index) { + var bundleSelections = registry.get(this.name + '.' + index + '.' + this.bundleSelectionsName), + bundleSelectionsLength = (bundleSelections.elems() || []).length, + i; + + if (bundleSelectionsLength) { + for (i = 0; i < bundleSelectionsLength; i++) { + bundleSelections.elems()[0].destroy(); + } + } + }, + + /** + * {@inheritdoc} + */ + processingAddChild: function (ctx, index, prop) { + var recordIds = _.map(this.recordData(), function (rec) { + return parseInt(rec['record_id'], 10); + }), + maxRecordId = _.max(recordIds); + + prop = maxRecordId > -1 ? maxRecordId + 1 : prop; + this._super(ctx, index, prop); + } + }); +}); diff --git a/app/code/Magento/BundleImportExport/composer.json b/app/code/Magento/BundleImportExport/composer.json index 3d7900d98287c1eba32a5badd2d5b2fdc1857f25..2f5e3afdbff86ded9c88161ebea832c29b37f229 100644 --- a/app/code/Magento/BundleImportExport/composer.json +++ b/app/code/Magento/BundleImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-bundle-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index cafccadb41ad869ff87ee8eb154c747f569a4c79..c886e069b66e37d3b0c7a12af377a250cfc75192 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cache-invalidate", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-page-cache": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index 9d0ce3db92aa402a7c247fed21185953c4707342..45ecd32464e9cc3f647264420961d6848b0445e9 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-captcha", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 9e9f18e0113717ad6853ea5d4512661a897e926c..c913c82de1acdecbf3328c37fe7529dfbab193ba 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -22,7 +22,6 @@ use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryExtensionFactory; * @method Product setHasError(bool $value) * @method \Magento\Catalog\Model\ResourceModel\Product getResource() * @method null|bool getHasError() - * @method Product setAssociatedProductIds(array $productIds) * @method array getAssociatedProductIds() * @method Product setNewVariationsAttributeSetId(int $value) * @method int getNewVariationsAttributeSetId() @@ -2614,4 +2613,16 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements } return $this->mediaGalleryProcessor; } + + /** + * Set the associated products + * + * @param array $productIds + * @return $this + */ + public function setAssociatedProductIds(array $productIds) + { + $this->getExtensionAttributes()->setConfigurableProductLinks($productIds); + return $this; + } } diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 4385c8add37aef25dc52a888d2ff839b49e734db..27796e1cda2a167848c2a3b8fb0d096f40c172d6 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -640,6 +640,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa $collection->load(); + $collection->addCategoryIds(); $searchResult = $this->searchResultsFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index d9612f18c9111df4634e18cb8b9abf523643f2e5..432bc696ef7cecd822b5168e7da70a9ed1cd4fc5 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -197,7 +197,7 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\ )->joinLeft( ['e' => $this->getTable('catalog_product_entity')], 'e.' . $linkField .' = l.parent_id', - ['e.entity_id as parent_id'] + [] )->join( ['cs' => $this->getTable('store')], '', @@ -205,9 +205,17 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\ )->join( ['i' => $idxTable], 'l.child_id = i.entity_id AND cs.store_id = i.store_id', - ['attribute_id', 'store_id', 'value'] + [] )->group( - ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value'] + ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] + )->columns( + [ + 'parent_id' => 'e.entity_id', + 'attribute_id' => 'i.attribute_id', + 'store_id' => 'i.store_id', + 'value' => 'i.value', + 'source_id' => 'l.child_id' + ] ); if ($parentIds !== null) { $select->where('e.entity_id IN(?)', $parentIds); @@ -222,7 +230,7 @@ abstract class AbstractEav extends \Magento\Catalog\Model\ResourceModel\Product\ 'select' => $select, 'entity_field' => new \Zend_Db_Expr('l.parent_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php index e8d9889e68d59384e74cfb3fc0491a2edb8f3edb..a45d4f13a1a9a811fffb6269bf94575675c60d80 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Decimal.php @@ -85,6 +85,7 @@ class Decimal extends AbstractEav 'pdd.attribute_id', 'cs.store_id', 'value' => $productValueExpression, + 'source_id' => 'cpe.entity_id', ] ); @@ -116,7 +117,7 @@ class Decimal extends AbstractEav 'select' => $select, 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php index c4eda1c987192acb5dd7e5ce003fe8b9cff0bdc6..1d37c57aa8b251bc73edf6e4a06aed1789cdc476 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php @@ -178,6 +178,7 @@ class Source extends AbstractEav 'pid.attribute_id', 'pid.store_id', 'value' => $ifNullSql, + 'pid.entity_id', ] )->where( 'pid.attribute_id IN(?)', @@ -200,7 +201,7 @@ class Source extends AbstractEav 'select' => $select, 'entity_field' => new \Zend_Db_Expr('pid.entity_id'), 'website_field' => new \Zend_Db_Expr('pid.website_id'), - 'store_field' => new \Zend_Db_Expr('pid.store_id') + 'store_field' => new \Zend_Db_Expr('pid.store_id'), ] ); $query = $select->insertFromSelect($idxTable); @@ -221,11 +222,7 @@ class Source extends AbstractEav $connection = $this->getConnection(); // prepare multiselect attributes - if ($attributeId === null) { - $attrIds = $this->_getIndexableAttributes(true); - } else { - $attrIds = [$attributeId]; - } + $attrIds = $attributeId === null ? $this->_getIndexableAttributes(true) : [$attributeId]; if (!$attrIds) { return $this; @@ -247,20 +244,20 @@ class Source extends AbstractEav $productValueExpression = $connection->getCheckSql('pvs.value_id > 0', 'pvs.value', 'pvd.value'); $select = $connection->select()->from( ['pvd' => $this->getTable('catalog_product_entity_varchar')], - [$productIdField, 'attribute_id'] + [] )->join( ['cs' => $this->getTable('store')], '', - ['store_id'] + [] )->joinLeft( ['pvs' => $this->getTable('catalog_product_entity_varchar')], "pvs.{$productIdField} = pvd.{$productIdField} AND pvs.attribute_id = pvd.attribute_id" . ' AND pvs.store_id=cs.store_id', - ['value' => $productValueExpression] + [] )->joinLeft( ['cpe' => $this->getTable('catalog_product_entity')], "cpe.{$productIdField} = pvd.{$productIdField}", - ['entity_id'] + [] )->where( 'pvd.store_id=?', $connection->getIfNullSql('pvs.store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID) @@ -272,6 +269,14 @@ class Source extends AbstractEav $attrIds )->where( 'cpe.entity_id IS NOT NULL' + )->columns( + [ + 'entity_id' => 'cpe.entity_id', + 'attribute_id' => 'attribute_id', + 'store_id' => 'cs.store_id', + 'value' => $productValueExpression, + 'source_id' => 'cpe.entity_id', + ] ); $statusCond = $connection->quoteInto('=?', ProductStatus::STATUS_ENABLED); @@ -289,30 +294,11 @@ class Source extends AbstractEav 'select' => $select, 'entity_field' => new \Zend_Db_Expr('cpe.entity_id'), 'website_field' => new \Zend_Db_Expr('cs.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); - $i = 0; - $data = []; - $query = $select->query(); - while ($row = $query->fetch()) { - $values = explode(',', $row['value']); - foreach ($values as $valueId) { - if (isset($options[$row['attribute_id']][$valueId])) { - $data[] = [$row['entity_id'], $row['attribute_id'], $row['store_id'], $valueId]; - $i++; - if ($i % 10000 == 0) { - $this->_saveIndexData($data); - $data = []; - } - } - } - } - - $this->_saveIndexData($data); - unset($options); - unset($data); + $this->saveDataFromSelect($select, $options); return $this; } @@ -331,7 +317,7 @@ class Source extends AbstractEav $connection = $this->getConnection(); $connection->insertArray( $this->getIdxTable(), - ['entity_id', 'attribute_id', 'store_id', 'value'], + ['entity_id', 'attribute_id', 'store_id', 'value', 'source_id'], $data ); return $this; @@ -348,4 +334,31 @@ class Source extends AbstractEav { return $this->tableStrategy->getTableName('catalog_product_index_eav'); } + + /** + * @param \Magento\Framework\DB\Select $select + * @param array $options + * @return void + */ + private function saveDataFromSelect(\Magento\Framework\DB\Select $select, array $options) + { + $i = 0; + $data = []; + $query = $select->query(); + while ($row = $query->fetch()) { + $values = explode(',', $row['value']); + foreach ($values as $valueId) { + if (isset($options[$row['attribute_id']][$valueId])) { + $data[] = [$row['entity_id'], $row['attribute_id'], $row['store_id'], $valueId, $row['source_id']]; + $i++; + if ($i % 10000 == 0) { + $this->_saveIndexData($data); + $data = []; + } + } + } + } + + $this->_saveIndexData($data); + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 289445ae2daf07c4132f6d69372c84fcfb36a066..2b979ff79fe5c498215b6b144e71897b9ca2c913 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -368,7 +368,7 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface 'select' => $select, 'entity_field' => new \Zend_Db_Expr('e.entity_id'), 'website_field' => new \Zend_Db_Expr('cw.website_id'), - 'store_field' => new \Zend_Db_Expr('cs.store_id') + 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); diff --git a/app/code/Magento/Catalog/Setup/InstallSchema.php b/app/code/Magento/Catalog/Setup/InstallSchema.php index 171e96efe9b0a1e714e1331fc2ed95a175cd58fb..a2ba6aa283f6516021f0baffaafd6e6470c060f0 100644 --- a/app/code/Magento/Catalog/Setup/InstallSchema.php +++ b/app/code/Magento/Catalog/Setup/InstallSchema.php @@ -18,6 +18,7 @@ class InstallSchema implements InstallSchemaInterface /** * {@inheritdoc} * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @throws \Zend_Db_Exception */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { @@ -2429,7 +2430,6 @@ class InstallSchema implements InstallSchemaInterface 'option_id', $installer->getTable('catalog_product_option'), 'option_id', - \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE, \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE ) ->setComment( diff --git a/app/code/Magento/Catalog/Setup/UpgradeSchema.php b/app/code/Magento/Catalog/Setup/UpgradeSchema.php index cbcce1d427bb6c91fa000bff0b4a069ab02d034f..7fc2ef7d219ba917aa59c9887749a9024bda49ce 100755 --- a/app/code/Magento/Catalog/Setup/UpgradeSchema.php +++ b/app/code/Magento/Catalog/Setup/UpgradeSchema.php @@ -61,9 +61,56 @@ class UpgradeSchema implements UpgradeSchemaInterface } } + if (version_compare($context->getVersion(), '2.1.2', '<')) { + $this->addSourceEntityIdToProductEavIndex($setup); + } + $setup->endSetup(); } + /** + * Add the column 'source_id' to the Product EAV index tables. + * It allows to identify which entity was used to create value in the index. + * It is useful to identify original entity in a composite products. + * + * @param SchemaSetupInterface $setup + * @return void + */ + private function addSourceEntityIdToProductEavIndex(SchemaSetupInterface $setup) + { + $tables = [ + 'catalog_product_index_eav', + 'catalog_product_index_eav_idx', + 'catalog_product_index_eav_tmp', + 'catalog_product_index_eav_decimal', + 'catalog_product_index_eav_decimal_idx', + 'catalog_product_index_eav_decimal_tmp', + ]; + $connection = $setup->getConnection(); + foreach ($tables as $tableName) { + $tableName = $setup->getTable($tableName); + $connection->addColumn( + $tableName, + 'source_id', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + 'unsigned' => true, + 'nullable' => false, + 'default' => 0, + 'comment' => 'Original entity Id for attribute value', + ] + ); + $connection->dropIndex($tableName, $connection->getPrimaryKeyName($tableName)); + $primaryKeyFields = ['entity_id', 'attribute_id', 'store_id', 'value', 'source_id']; + $setup->getConnection()->addIndex( + $tableName, + $connection->getIndexName($tableName, $primaryKeyFields), + $primaryKeyFields, + \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_PRIMARY + ); + } + } + /** * @param SchemaSetupInterface $setup * @return void diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index e91bb469eb112d4de70cba92bff5b93ca069ca58..8c08d0c63bc5e93c7ab690f6441eaa02281b3255 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -715,6 +715,7 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase ->method('process') ->with($searchCriteriaMock, $collectionMock); $collectionMock->expects($this->once())->method('load'); + $collectionMock->expects($this->once())->method('addCategoryIds'); $collectionMock->expects($this->once())->method('getItems')->willReturn([$itemsMock]); $collectionMock->expects($this->once())->method('getSize')->willReturn(128); $searchResultsMock = $this->getMock( diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 0fbba264f7e93383f07434a7fe2ebf4586d54516..886c7a070adf93b68ec54f45813ba80911029e44 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -315,17 +315,6 @@ class General extends AbstractModifier $importsConfig = [ 'mask' => $this->locator->getStore()->getConfig('catalog/fields_masks/' . $listener), 'component' => 'Magento_Catalog/js/components/import-handler', - 'imports' => [ - 'handleNameChanges' => '${$.provider}:data.product.name', - 'handleDescriptionChanges' => '${$.provider}:data.product.description', - 'handleSkuChanges' => '${$.provider}:data.product.sku', - 'handleColorChanges' => '${$.provider}:data.product.color', - 'handleCountryChanges' => '${$.provider}:data.product.country_of_manufacture', - 'handleGenderChanges' => '${$.provider}:data.product.gender', - 'handleMaterialChanges' => '${$.provider}:data.product.material', - 'handleShortDescriptionChanges' => '${$.provider}:data.product.short_description', - 'handleSizeChanges' => '${$.provider}:data.product.size' - ], 'allowImport' => !$this->locator->getProduct()->getId(), ]; diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 64157f6bc8af74e82975668e43a62ef583227908..8c77dd77f98b74a53ee1124a16c473d843d21d60 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-cms": "101.1.*", diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml index c629bf6a180ccc0ec42e51a67352f6eaa673dd86..0c9e6bb356fe14101d467a04aa91b23fb6873f09 100644 --- a/app/code/Magento/Catalog/etc/module.xml +++ b/app/code/Magento/Catalog/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Catalog" setup_version="2.1.1"> + <module name="Magento_Catalog" setup_version="2.1.2"> <sequence> <module name="Magento_Eav"/> <module name="Magento_Cms"/> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/import-handler.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/import-handler.js index e67bde152475f62769d6eaff73548893f98ed7d0..a0aec918ccda24f1ae05486bc5d9ec59522f0ca7 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/import-handler.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/import-handler.js @@ -4,148 +4,78 @@ */ define([ + 'Magento_Ui/js/form/element/abstract', 'underscore', - 'Magento_Ui/js/form/element/textarea' -], function (_, Textarea) { + 'uiRegistry' +], function (Abstract, _, registry) { 'use strict'; - return Textarea.extend({ + return Abstract.extend({ defaults: { allowImport: true, autoImportIfEmpty: false, - values: { - 'name': '', - 'description': '', - 'sku': '', - 'color': '', - 'country_of_manufacture': '', - 'gender': '', - 'material': '', - 'short_description': '', - 'size': '' - }, - valueUpdate: 'input', - mask: '' + values: {}, + mask: '', + queryTemplate: 'ns = ${ $.ns }, index = ' }, - /** - * Handle name value changes, if it's allowed - * - * @param {String} newValue - */ - handleNameChanges: function (newValue) { - this.values.name = newValue; - this.updateValue(); - }, - - /** - * Handle description value changes, if it's allowed - * - * @param {String} newValue - */ - handleDescriptionChanges: function (newValue) { - this.values.description = newValue; - this.updateValue(); - }, + /** @inheritdoc */ + initialize: function () { + this._super(); - /** - * Handle sku value changes, if it's allowed - * - * @param {String} newValue - */ - handleSkuChanges: function (newValue) { - if (this.code !== 'sku') { - this.values.sku = newValue; - this.updateValue(); + if (this.allowImport) { + this.setHandlers(); } }, /** - * Handle color value changes, if it's allowed - * - * @param {String} newValue - */ - handleColorChanges: function (newValue) { - this.values.color = newValue; - this.updateValue(); - }, - - /** - * Handle country value changes, if it's allowed - * - * @param {String} newValue + * Split mask placeholder and attach events to placeholder fields. */ - handleCountryChanges: function (newValue) { - this.values.country = newValue; - this.updateValue(); - }, + setHandlers: function () { + var str = this.mask || '', + placeholders; - /** - * Handle gender value changes, if it's allowed - * - * @param {String} newValue - */ - handleGenderChanges: function (newValue) { - this.values.gender = newValue; - this.updateValue(); - }, + placeholders = str.match(/{{(.*?)}}/g); // Get placeholders - /** - * Handle material value changes, if it's allowed - * - * @param {String} newValue - */ - handleMaterialChanges: function (newValue) { - this.values.material = newValue; - this.updateValue(); - }, + _.each(placeholders, function (placeholder) { + placeholder = placeholder.replace(/[{{}}]/g, ''); // Remove curly braces - /** - * Handle short description value changes, if it's allowed - * - * @param {String} newValue - */ - handleShortDescriptionChanges: function (newValue) { - this.values['short_description'] = newValue; - this.updateValue(); + registry.get(this.queryTemplate + placeholder, function (component) { + this.values[placeholder] = component.getPreview(); + component.on('value', this.updateValue.bind(this, placeholder, component)); + component.valueUpdate = 'keyup'; + }.bind(this)); + }, this); }, /** - * Handle size value changes, if it's allowed + * Update field with mask value, if it's allowed. * - * @param {String} newValue + * @param {Object} placeholder + * @param {Object} component */ - handleSizeChanges: function (newValue) { - this.values.size = newValue; - this.updateValue(); - }, + updateValue: function (placeholder, component) { + var string = this.mask || '', + nonEmptyValueFlag = false; - /** - * Update field value, if it's allowed - */ - updateValue: function () { - var str = this.mask || '', - nonEmptyValueFlag = false, - tmpElement; + if (placeholder) { + this.values[placeholder] = component.getPreview() || ''; + } if (!this.allowImport) { return; } - if (str) { - _.each(this.values, function (propertyValue, propertyName) { - str = str.replace('{{' + propertyName + '}}', propertyValue); - nonEmptyValueFlag = nonEmptyValueFlag || !!propertyValue; - }); - } - - // strip tags - tmpElement = document.createElement('div'); - tmpElement.innerHTML = str; - str = tmpElement.textContent || tmpElement.innerText || ''; + _.each(this.values, function (propertyValue, propertyName) { + string = string.replace('{{' + propertyName + '}}', propertyValue); + nonEmptyValueFlag = nonEmptyValueFlag || !!propertyValue; + }); if (nonEmptyValueFlag) { - this.value(str); + string = string.replace(/(<([^>]+)>)/ig, ''); // Remove html tags + this.value(string); + } else { + this.value(''); } }, @@ -169,13 +99,20 @@ define([ * and disallow/allow import value */ userChanges: function () { + + /** + * As userChanges is called before updateValue, + * we forced to get value from component by reference + */ + var actualValue = arguments[1].currentTarget.value; + this._super(); - if (this.value() === '') { + if (actualValue === '') { this.allowImport = true; if (this.autoImportIfEmpty) { - this.updateValue(); + this.updateValue(null, null); } } else { this.allowImport = false; diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml index 4c8ae8eaae9524fe75f74ba5f0dd45ef045c2954..298cdcc29e953a74625a3834edab6b9a5eb47631 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_category_view.xml @@ -28,53 +28,6 @@ </block> <block class="Magento\Catalog\Block\Product\ProductList\Toolbar" name="product_list_toolbar" template="Magento_Catalog::product/list/toolbar.phtml"> <block class="Magento\Theme\Block\Html\Pager" name="product_list_toolbar_pager"/> - <!-- The following code shows how to set your own pager increments --> - <!-- - <action method="setDefaultListPerPage"> - <argument name="limit" xsi:type="string">4</argument> - </action> - <action method="setDefaultGridPerPage"> - <argument name="limit" xsi:type="string">3</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">list</argument> - <argument name="limit" xsi:type="string">2</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">list</argument> - <argument name="limit" xsi:type="string">4</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">list</argument> - <argument name="limit" xsi:type="string">6</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">list</argument> - <argument name="limit" xsi:type="string">8</argument> - </action> - <action method="addPagerLimit" translate="label"> - <argument name="mode" xsi:type="string">list</argument> - <argument name="limit" xsi:type="string">all</argument> - <argument name="label" xsi:type="string">All</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">grid</argument> - <argument name="limit" xsi:type="string">3</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">grid</argument> - <argument name="limit" xsi:type="string">6</argument> - </action> - <action method="addPagerLimit"> - <argument name="mode" xsi:type="string">grid</argument> - <argument name="limit" xsi:type="string">9</argument> - </action> - <action method="addPagerLimit" translate="label"> - <argument name="mode" xsi:type="string">grid</argument> - <argument name="limit" xsi:type="string">all</argument> - <argument name="label" xsi:type="string">All</argument> - </action> - --> </block> <action method="setToolbarBlockName"> <argument name="name" xsi:type="string">product_list_toolbar</argument> diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index afb2e86b6248a79523405c78acd1cf27953b90c2..54208dcdba534139c96e8fd975e0757f9ecb11e1 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2304,7 +2304,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $sku = $rowData[self::COL_SKU]; - if (isset($this->_oldSku[$sku])) { + if (isset($this->_oldSku[$sku]) && Import::BEHAVIOR_REPLACE !== $this->getBehavior()) { // can we get all necessary data from existent DB product? // check for supported type of existing product if (isset($this->_productTypeModels[$this->_oldSku[$sku]['type_id']])) { @@ -2356,7 +2356,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $rowAttributesValid = $productTypeValidator->isRowValid( $rowData, $rowNum, - !isset($this->_oldSku[$sku]) + !(isset($this->_oldSku[$sku]) && Import::BEHAVIOR_REPLACE !== $this->getBehavior()) ); if (!$rowAttributesValid && self::SCOPE_DEFAULT == $rowScope) { // mark SCOPE_DEFAULT row as invalid for future child rows if product not in DB already diff --git a/app/code/Magento/CatalogImportExport/composer.json b/app/code/Magento/CatalogImportExport/composer.json index 7dbd7f4bc5e656ff09712549886e97a95fa3f4f1..8ed5f43692df51482038e1d1f7d4b91f05fd8c23 100644 --- a/app/code/Magento/CatalogImportExport/composer.json +++ b/app/code/Magento/CatalogImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-url-rewrite": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index 254d8d2db4bf9db35b338507cdd744d1aaf0e152..684f616ecd6fcfca7088c257afec000fba6a4b1d 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-inventory", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/CatalogRule/composer.json b/app/code/Magento/CatalogRule/composer.json index 71eb5b49db1fc68eadb064968796cfdaf1081d60..004ab82a028d3aba288b71a234dead24d63a4755 100644 --- a/app/code/Magento/CatalogRule/composer.json +++ b/app/code/Magento/CatalogRule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-rule": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/CatalogRuleConfigurable/composer.json b/app/code/Magento/CatalogRuleConfigurable/composer.json index cc51269e2d9720e380612cf56c712ff2459bbe35..b930380f7bb025ea409be398c013c12773cb2f20 100644 --- a/app/code/Magento/CatalogRuleConfigurable/composer.json +++ b/app/code/Magento/CatalogRuleConfigurable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-rule-configurable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-configurable-product": "100.2.*", "magento/framework": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php index 9b75e6e6e0c325819d09f543cc445ae61eb7fb84..ddf86951068acc3c95017e54e489faf92e4b28cc 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php @@ -6,6 +6,7 @@ namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation; use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Model\Stock; use Magento\Customer\Model\Session; use Magento\Eav\Model\Config; use Magento\Framework\App\ResourceConnection; @@ -79,7 +80,13 @@ class DataProvider implements DataProviderInterface $select = $this->getSelect(); - if ($attribute->getAttributeCode() == 'price') { + $select->joinInner( + ['entities' => $entityIdsTable->getName()], + 'main_table.entity_id = entities.entity_id', + [] + ); + + if ($attribute->getAttributeCode() === 'price') { /** @var \Magento\Store\Model\Store $store */ $store = $this->scopeResolver->getScope($currentScope); if (!$store instanceof \Magento\Store\Model\Store) { @@ -94,19 +101,24 @@ class DataProvider implements DataProviderInterface $currentScopeId = $this->scopeResolver->getScope($currentScope) ->getId(); $table = $this->resource->getTableName( - 'catalog_product_index_eav' . ($attribute->getBackendType() == 'decimal' ? '_decimal' : '') + 'catalog_product_index_eav' . ($attribute->getBackendType() === 'decimal' ? '_decimal' : '') ); - $select->from(['main_table' => $table], ['value']) + $subSelect = $select; + $subSelect->from(['main_table' => $table], ['main_table.value']) + ->joinLeft( + ['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], + 'main_table.source_id = stock_index.product_id', + [] + ) ->where('main_table.attribute_id = ?', $attribute->getAttributeId()) - ->where('main_table.store_id = ? ', $currentScopeId); + ->where('main_table.store_id = ? ', $currentScopeId) + ->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK) + ->group(['main_table.entity_id', 'main_table.value']); + $parentSelect = $this->getSelect(); + $parentSelect->from(['main_table' => $subSelect], ['main_table.value']); + $select = $parentSelect; } - $select->joinInner( - ['entities' => $entityIdsTable->getName()], - 'main_table.entity_id = entities.entity_id', - [] - ); - return $select; } diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/AliasResolver.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/AliasResolver.php new file mode 100644 index 0000000000000000000000000000000000000000..7099ce2502b19e21de48ae570e2dc063be6162d1 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/AliasResolver.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Adapter\Mysql\Filter; + + +use Magento\CatalogSearch\Model\Search\RequestGenerator; + +/** + * Purpose of class is to resolve table alias for Search Request filter + */ +class AliasResolver +{ + /** + * The suffix for stock status filter that may be added to the query beside the filter query + * Used when showing of Out of Stock products is disabled. + */ + const STOCK_FILTER_SUFFIX = '_stock'; + + /** + * @param \Magento\Framework\Search\Request\FilterInterface $filter + * @return string alias of the filter in database + */ + public function getAlias(\Magento\Framework\Search\Request\FilterInterface $filter) + { + $alias = null; + $field = $filter->getField(); + switch ($field) { + case 'price': + $alias = 'price_index'; + break; + case 'category_ids': + $alias = 'category_ids_index'; + break; + default: + $alias = $field . RequestGenerator::FILTER_SUFFIX; + break; + } + return $alias; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php index 05205f04f8b99cd903f345f8c1f7dc04384e7aee..fb579c1dce29c7da26b35288ff61040363f4f510 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php @@ -8,8 +8,11 @@ namespace Magento\CatalogSearch\Model\Adapter\Mysql\Filter; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\CatalogInventory\Model\Stock; use Magento\CatalogSearch\Model\Search\TableMapper; use Magento\Eav\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\App\ScopeResolverInterface; use Magento\Framework\DB\Adapter\AdapterInterface; @@ -17,6 +20,7 @@ use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; use Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface; use Magento\Framework\Search\Request\FilterInterface; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; /** @@ -60,9 +64,14 @@ class Preprocessor implements PreprocessorInterface private $metadataPool; /** - * @var TableMapper + * @var ScopeConfigInterface */ - private $tableMapper; + private $scopeConfig; + + /** + * @var AliasResolver + */ + private $aliasResolver; /** * @param ConditionManager $conditionManager @@ -71,6 +80,9 @@ class Preprocessor implements PreprocessorInterface * @param ResourceConnection $resource * @param TableMapper $tableMapper * @param string $attributePrefix + * @param ScopeConfigInterface $scopeConfig + * @param AliasResolver $aliasResolver + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ConditionManager $conditionManager, @@ -78,7 +90,9 @@ class Preprocessor implements PreprocessorInterface Config $config, ResourceConnection $resource, TableMapper $tableMapper, - $attributePrefix + $attributePrefix, + ScopeConfigInterface $scopeConfig = null, + AliasResolver $aliasResolver = null ) { $this->conditionManager = $conditionManager; $this->scopeResolver = $scopeResolver; @@ -86,7 +100,16 @@ class Preprocessor implements PreprocessorInterface $this->resource = $resource; $this->connection = $resource->getConnection(); $this->attributePrefix = $attributePrefix; - $this->tableMapper = $tableMapper; + + if (null === $scopeConfig) { + $scopeConfig = ObjectManager::getInstance()->get(ScopeConfigInterface::class); + } + if (null === $aliasResolver) { + $aliasResolver = ObjectManager::getInstance()->get(AliasResolver::class); + } + + $this->scopeConfig = $scopeConfig; + $this->aliasResolver = $aliasResolver; } /** @@ -117,7 +140,7 @@ class Preprocessor implements PreprocessorInterface } elseif ($filter->getField() === 'category_ids') { return 'category_ids_index.category_id = ' . (int) $filter->getValue(); } elseif ($attribute->isStatic()) { - $alias = $this->tableMapper->getMappingAlias($filter); + $alias = $this->aliasResolver->getAlias($filter); $resultQuery = str_replace( $this->connection->quoteIdentifier($attribute->getAttributeCode()), $this->connection->quoteIdentifier($alias . '.' . $attribute->getAttributeCode()), @@ -208,7 +231,7 @@ class Preprocessor implements PreprocessorInterface */ private function processTermSelect(FilterInterface $filter, $isNegation) { - $alias = $this->tableMapper->getMappingAlias($filter); + $alias = $this->aliasResolver->getAlias($filter); if (is_array($filter->getValue())) { $value = sprintf( '%s IN (%s)', @@ -224,9 +247,31 @@ class Preprocessor implements PreprocessorInterface $value ); + if ($this->isAddStockFilter()) { + $resultQuery = sprintf( + '%1$s AND %2$s%3$s.stock_status = %4$s', + $resultQuery, + $alias, + AliasResolver::STOCK_FILTER_SUFFIX, + Stock::STOCK_IN_STOCK + ); + } + return $resultQuery; } + /** + * @return bool + */ + private function isAddStockFilter() + { + $isShowOutOfStock = $this->scopeConfig->isSetFlag( + 'cataloginventory/options/show_out_of_stock', + ScopeInterface::SCOPE_STORE + ); + return false === $isShowOutOfStock; + } + /** * Get product metadata pool * diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php new file mode 100644 index 0000000000000000000000000000000000000000..8626b2ea15b077fc42380afaba82b245e7387937 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/ExclusionStrategy.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; + +/** + * Strategy which processes exclusions from general rules + */ +class ExclusionStrategy implements FilterStrategyInterface +{ + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resourceConnection; + + /** + * @var AliasResolver + */ + private $aliasResolver; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @param \Magento\Framework\App\ResourceConnection $resourceConnection + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param AliasResolver $aliasResolver + */ + public function __construct( + \Magento\Framework\App\ResourceConnection $resourceConnection, + \Magento\Store\Model\StoreManagerInterface $storeManager, + AliasResolver $aliasResolver + ) { + $this->resourceConnection = $resourceConnection; + $this->storeManager = $storeManager; + $this->aliasResolver = $aliasResolver; + } + + /** + * {@inheritDoc} + */ + public function apply( + \Magento\Framework\Search\Request\FilterInterface $filter, + \Magento\Framework\DB\Select $select + ) { + $isApplied = false; + $field = $filter->getField(); + if ('price' === $field) { + $alias = $this->aliasResolver->getAlias($filter); + $tableName = $this->resourceConnection->getTableName('catalog_product_index_price'); + $select->joinInner( + [ + $alias => $tableName + ], + $this->resourceConnection->getConnection()->quoteInto( + 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ?', + $this->storeManager->getWebsite()->getId() + ), + [] + ); + $isApplied = true; + } elseif ('category_ids' === $field) { + $alias = $this->aliasResolver->getAlias($filter); + $tableName = $this->resourceConnection->getTableName('catalog_category_product_index'); + $select->joinInner( + [ + $alias => $tableName + ], + 'search_index.entity_id = category_ids_index.product_id', + [] + ); + $isApplied = true; + } + return $isApplied; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterContext.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterContext.php new file mode 100644 index 0000000000000000000000000000000000000000..d244e3d5f754893fb671a3b6c81565eb5c920ff7 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterContext.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + + +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; + +/** + * FilterContext represents a Context of the Strategy pattern + * Its responsibility is to choose appropriate strategy to apply passed filter to the Select + */ +class FilterContext implements FilterStrategyInterface +{ + /** + * @var ExclusionStrategy + */ + private $exclusionStrategy; + + /** + * @var EavConfig + */ + private $eavConfig; + + /** + * @var TermDropdownStrategy + */ + private $termDropdownStrategy; + + /** + * @var StaticAttributeStrategy + */ + private $staticAttributeStrategy; + + /** + * @var AliasResolver + */ + private $aliasResolver; + + /** + * @param EavConfig $eavConfig + * @param AliasResolver $aliasResolver + * @param ExclusionStrategy $exclusionStrategy + * @param TermDropdownStrategy $termDropdownStrategy + * @param StaticAttributeStrategy $staticAttributeStrategy + */ + public function __construct( + EavConfig $eavConfig, + AliasResolver $aliasResolver, + ExclusionStrategy $exclusionStrategy, + TermDropdownStrategy $termDropdownStrategy, + StaticAttributeStrategy $staticAttributeStrategy + ) { + $this->eavConfig = $eavConfig; + $this->aliasResolver = $aliasResolver; + $this->exclusionStrategy = $exclusionStrategy; + $this->termDropdownStrategy = $termDropdownStrategy; + $this->staticAttributeStrategy = $staticAttributeStrategy; + } + + /** + * {@inheritDoc} + */ + public function apply( + \Magento\Framework\Search\Request\FilterInterface $filter, + \Magento\Framework\DB\Select $select + ) { + $isApplied = $this->exclusionStrategy->apply($filter, $select); + + if (!$isApplied) { + $attribute = $this->getAttributeByCode($filter->getField()); + if ($attribute) { + if ($filter->getType() === \Magento\Framework\Search\Request\FilterInterface::TYPE_TERM + && in_array($attribute->getFrontendInput(), ['select', 'multiselect'], true) + ) { + $isApplied = $this->termDropdownStrategy->apply($filter, $select); + } elseif ($attribute->getBackendType() === AbstractAttribute::TYPE_STATIC) { + $isApplied = $this->staticAttributeStrategy->apply($filter, $select); + } + } + } + + return $isApplied; + } + + /** + * @param string $field + * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getAttributeByCode($field) + { + return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterStrategyInterface.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterStrategyInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..cf17f7d5132efbf8246af0de674d7c789551aa3e --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterStrategyInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +/** + * FilterStrategyInterface provides the interface to work with strategies + */ +interface FilterStrategyInterface +{ + /** + * @param \Magento\Framework\Search\Request\FilterInterface $filter + * @param \Magento\Framework\DB\Select $select + * @return bool is filter was applied + */ + public function apply( + \Magento\Framework\Search\Request\FilterInterface $filter, + \Magento\Framework\DB\Select $select + ); +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StaticAttributeStrategy.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StaticAttributeStrategy.php new file mode 100644 index 0000000000000000000000000000000000000000..eb9d61b5a7f4c352d23096050a5acd292dc80d54 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StaticAttributeStrategy.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Eav\Model\Config as EavConfig; + +/** + * This strategy handles static attributes + */ +class StaticAttributeStrategy implements FilterStrategyInterface +{ + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resourceConnection; + + /** + * @var AliasResolver + */ + private $aliasResolver; + + /** + * @var EavConfig + */ + private $eavConfig; + + /** + * @param \Magento\Framework\App\ResourceConnection $resourceConnection + * @param EavConfig $eavConfig + * @param AliasResolver $aliasResolver + */ + public function __construct( + \Magento\Framework\App\ResourceConnection $resourceConnection, + EavConfig $eavConfig, + AliasResolver $aliasResolver + ) { + $this->resourceConnection = $resourceConnection; + $this->eavConfig = $eavConfig; + $this->aliasResolver = $aliasResolver; + } + + /** + * {@inheritDoc} + */ + public function apply( + \Magento\Framework\Search\Request\FilterInterface $filter, + \Magento\Framework\DB\Select $select + ) { + $attribute = $this->getAttributeByCode($filter->getField()); + $alias = $this->aliasResolver->getAlias($filter); + $select->joinInner( + [$alias => $attribute->getBackendTable()], + 'search_index.entity_id = ' + . $this->resourceConnection->getConnection()->quoteIdentifier("$alias.entity_id"), + [] + ); + return true; + } + + /** + * @param string $field + * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getAttributeByCode($field) + { + return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/TermDropdownStrategy.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/TermDropdownStrategy.php new file mode 100644 index 0000000000000000000000000000000000000000..76828fe28f4348c8b90c9f1bf695906242c77222 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/TermDropdownStrategy.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * This strategy handles attributes which comply with two criteria: + * - The filter for dropdown or multi-select attribute + * - The filter is Term filter + * + */ +class TermDropdownStrategy implements FilterStrategyInterface +{ + /** + * @var AliasResolver + */ + private $aliasResolver; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var EavConfig + */ + private $eavConfig; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param StoreManagerInterface $storeManager + * @param ResourceConnection $resourceConnection + * @param EavConfig $eavConfig + * @param ScopeConfigInterface $scopeConfig + * @param AliasResolver $aliasResolver + */ + public function __construct( + StoreManagerInterface $storeManager, + ResourceConnection $resourceConnection, + EavConfig $eavConfig, + ScopeConfigInterface $scopeConfig, + AliasResolver $aliasResolver + ) { + $this->storeManager = $storeManager; + $this->resourceConnection = $resourceConnection; + $this->eavConfig = $eavConfig; + $this->scopeConfig = $scopeConfig; + $this->aliasResolver = $aliasResolver; + } + + /** + * {@inheritDoc} + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function apply( + \Magento\Framework\Search\Request\FilterInterface $filter, + \Magento\Framework\DB\Select $select + ) { + $alias = $this->aliasResolver->getAlias($filter); + $attribute = $this->getAttributeByCode($filter->getField()); + $joinCondition = sprintf( + 'search_index.entity_id = %1$s.entity_id AND %1$s.attribute_id = %2$d AND %1$s.store_id = %3$d', + $alias, + $attribute->getId(), + $this->storeManager->getWebsite()->getId() + ); + $select->joinLeft( + [$alias => $this->resourceConnection->getTableName('catalog_product_index_eav')], + $joinCondition, + [] + ); + if ($this->isAddStockFilter()) { + $stockAlias = $alias . AliasResolver::STOCK_FILTER_SUFFIX; + $select->joinLeft( + [ + $stockAlias => $this->resourceConnection->getTableName('cataloginventory_stock_status'), + ], + sprintf('%2$s.product_id = %1$s.source_id', $alias, $stockAlias), + [] + ); + } + + return true; + } + + /** + * @param string $field + * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getAttributeByCode($field) + { + return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field); + } + + /** + * @return bool + */ + private function isAddStockFilter() + { + $isShowOutOfStock = $this->scopeConfig->isSetFlag( + 'cataloginventory/options/show_out_of_stock', + ScopeInterface::SCOPE_STORE + ); + + return false === $isShowOutOfStock; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php index 1d30bb4a14d23a94d79a2faa5bc20823df220d8d..0e96e4de700259c9dc48c8f876983e2642316a86 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php +++ b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php @@ -99,6 +99,7 @@ class IndexBuilder implements IndexBuilderInterface * * @param RequestInterface $request * @return Select + * @throws \LogicException */ public function build(RequestInterface $request) { @@ -132,7 +133,7 @@ class IndexBuilder implements IndexBuilderInterface ), [] ); - $select->where('stock_index.stock_status = ?', Stock::DEFAULT_STOCK_ID); + $select->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK); } return $select; diff --git a/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php b/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php index ca7298e1beaac560ee66427f2b9d57db7ce21508..ac726192856a5fa555eae6757cd83b16930565ee 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php +++ b/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php @@ -6,10 +6,11 @@ namespace Magento\CatalogSearch\Model\Search; -use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\CatalogSearch\Model\Search\FilterMapper\FilterStrategyInterface; use Magento\Eav\Model\Config as EavConfig; -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection as AppResource; use Magento\Framework\DB\Select; @@ -21,12 +22,15 @@ use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Store\Model\StoreManagerInterface; /** + * Responsibility of the TableMapper is to collect all filters from the search query + * and pass them one by one for processing in the FilterContext, + * which will apply them to the Select * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TableMapper { /** - * @var Resource + * @var AppResource */ private $resource; @@ -40,22 +44,59 @@ class TableMapper */ private $eavConfig; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var FilterStrategyInterface + */ + private $filterStrategy; + + /** + * @var AliasResolver + */ + private $aliasResolver; + /** * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param AppResource $resource * @param StoreManagerInterface $storeManager * @param CollectionFactory $attributeCollectionFactory * @param EavConfig $eavConfig + * @param ScopeConfigInterface $scopeConfig + * @param FilterStrategyInterface $filterStrategy + * @param AliasResolver $aliasResolver */ public function __construct( AppResource $resource, StoreManagerInterface $storeManager, CollectionFactory $attributeCollectionFactory, - EavConfig $eavConfig = null + EavConfig $eavConfig = null, + ScopeConfigInterface $scopeConfig = null, + FilterStrategyInterface $filterStrategy = null, + AliasResolver $aliasResolver = null ) { $this->resource = $resource; $this->storeManager = $storeManager; - $this->eavConfig = $eavConfig !== null ? $eavConfig : ObjectManager::getInstance()->get(EavConfig::class); + + if (null === $eavConfig) { + $eavConfig = ObjectManager::getInstance()->get(EavConfig::class); + } + if (null === $scopeConfig) { + $scopeConfig = ObjectManager::getInstance()->get(ScopeConfigInterface::class); + } + if (null === $filterStrategy) { + $filterStrategy = ObjectManager::getInstance()->get(FilterStrategyInterface::class); + } + if (null === $aliasResolver) { + $aliasResolver = ObjectManager::getInstance()->get(AliasResolver::class); + } + $this->eavConfig = $eavConfig; + $this->scopeConfig = $scopeConfig; + $this->filterStrategy = $filterStrategy; + $this->aliasResolver = $aliasResolver; } /** @@ -66,111 +107,53 @@ class TableMapper */ public function addTables(Select $select, RequestInterface $request) { - $mappedTables = []; - $filters = $this->getFilters($request->getQuery()); + $appliedFilters = []; + $filters = $this->getFiltersFromQuery($request->getQuery()); foreach ($filters as $filter) { - list($alias, $table, $mapOn, $mappedFields, $joinType) = $this->getMappingData($filter); - if (!array_key_exists($alias, $mappedTables)) { - switch ($joinType) { - case \Magento\Framework\DB\Select::INNER_JOIN: - $select->joinInner( - [$alias => $table], - $mapOn, - $mappedFields - ); - break; - case \Magento\Framework\DB\Select::LEFT_JOIN: - $select->joinLeft( - [$alias => $table], - $mapOn, - $mappedFields - ); - break; - default: - throw new \LogicException(__('Unsupported join type: %1', $joinType)); + $alias = $this->aliasResolver->getAlias($filter); + if (!array_key_exists($alias, $appliedFilters)) { + $isApplied = $this->filterStrategy->apply($filter, $select); + if ($isApplied) { + $appliedFilters[$alias] = true; } - $mappedTables[$alias] = $table; } } return $select; } /** + * This method is deprecated. + * Please use \Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver::getAlias() instead. + * + * @deprecated + * @see AliasResolver::getAlias() + * * @param FilterInterface $filter * @return string */ public function getMappingAlias(FilterInterface $filter) { - list($alias) = $this->getMappingData($filter); - return $alias; - } - - /** - * Returns mapping data for field in format: [ - * 'table_alias', - * 'table', - * 'join_condition', - * ['fields'], - * 'joinType' - * ] - * @param FilterInterface $filter - * @return array - */ - private function getMappingData(FilterInterface $filter) - { - $alias = null; - $table = null; - $mapOn = null; - $mappedFields = null; - $field = $filter->getField(); - $joinType = \Magento\Framework\DB\Select::INNER_JOIN; - $fieldToTableMap = $this->getFieldToTableMap($field); - if ($fieldToTableMap) { - list($alias, $table, $mapOn, $mappedFields) = $fieldToTableMap; - $table = $this->resource->getTableName($table); - } elseif ($attribute = $this->getAttributeByCode($field)) { - if ($filter->getType() === FilterInterface::TYPE_TERM - && in_array($attribute->getFrontendInput(), ['select', 'multiselect'], true) - ) { - $joinType = \Magento\Framework\DB\Select::LEFT_JOIN; - $table = $this->resource->getTableName('catalog_product_index_eav'); - $alias = $field . RequestGenerator::FILTER_SUFFIX; - $mapOn = sprintf( - 'search_index.entity_id = %1$s.entity_id AND %1$s.attribute_id = %2$d AND %1$s.store_id = %3$d', - $alias, - $attribute->getId(), - $this->getStoreId() - ); - $mappedFields = []; - } elseif ($attribute->getBackendType() === AbstractAttribute::TYPE_STATIC) { - $table = $attribute->getBackendTable(); - $alias = $field . RequestGenerator::FILTER_SUFFIX; - $mapOn = 'search_index.entity_id = ' . $alias . '.entity_id'; - $mappedFields = null; - } - } - - return [$alias, $table, $mapOn, $mappedFields, $joinType]; + return $this->aliasResolver->getAlias($filter); } /** * @param RequestQueryInterface $query * @return FilterInterface[] */ - private function getFilters($query) + private function getFiltersFromQuery(RequestQueryInterface $query) { $filters = []; switch ($query->getType()) { case RequestQueryInterface::TYPE_BOOL: /** @var \Magento\Framework\Search\Request\Query\BoolExpression $query */ foreach ($query->getMust() as $subQuery) { - $filters = array_merge($filters, $this->getFilters($subQuery)); + $filters = array_merge($filters, $this->getFiltersFromQuery($subQuery)); } foreach ($query->getShould() as $subQuery) { - $filters = array_merge($filters, $this->getFilters($subQuery)); + $filters = array_merge($filters, $this->getFiltersFromQuery($subQuery)); } foreach ($query->getMustNot() as $subQuery) { - $filters = array_merge($filters, $this->getFilters($subQuery)); + $filters = array_merge($filters, $this->getFiltersFromQuery($subQuery)); } break; case RequestQueryInterface::TYPE_FILTER: @@ -219,57 +202,4 @@ class TableMapper } return $filters; } - - /** - * @return int - */ - private function getWebsiteId() - { - return $this->storeManager->getWebsite()->getId(); - } - - /** - * @return int - */ - private function getStoreId() - { - return $this->storeManager->getStore()->getId(); - } - - /** - * @param string $field - * @return array|null - */ - private function getFieldToTableMap($field) - { - $fieldToTableMap = [ - 'price' => [ - 'price_index', - 'catalog_product_index_price', - $this->resource->getConnection()->quoteInto( - 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ?', - $this->getWebsiteId() - ), - [] - ], - 'category_ids' => [ - 'category_ids_index', - 'catalog_category_product_index', - 'search_index.entity_id = category_ids_index.product_id', - [] - ] - ]; - return array_key_exists($field, $fieldToTableMap) ? $fieldToTableMap[$field] : null; - } - - /** - * @param string $field - * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function getAttributeByCode($field) - { - $attribute = $this->eavConfig->getAttribute(Product::ENTITY, $field); - return $attribute; - } } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/AliasResolverTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/AliasResolverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ab01d2553a3edcebde8a564a8ca8b8bb8cf2c7dd --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/AliasResolverTest.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Unit\Model\Adapter\Mysql\Filter; + +use Magento\CatalogSearch\Model\Search\RequestGenerator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class AliasResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver + */ + private $aliasResolver; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManagerHelper($this); + $this->aliasResolver = $objectManagerHelper->getObject( + \Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver::class, + [] + ); + } + + /** + * @param string $field + * @param string $expectedAlias + * @dataProvider aliasDataProvider + */ + public function testGetFilterAlias($field, $expectedAlias) + { + $filter = $this->getMockBuilder(\Magento\Framework\Search\Request\Filter\Term::class) + ->setMethods(['getField']) + ->disableOriginalConstructor() + ->getMock(); + $filter->expects($this->once()) + ->method('getField') + ->willReturn($field); + $this->assertSame($expectedAlias, $this->aliasResolver->getAlias($filter)); + } + + /** + * @return array + */ + public function aliasDataProvider() + { + return [ + 'general' => [ + 'field' => 'general', + 'alias' => 'general' . RequestGenerator::FILTER_SUFFIX, + ], + 'price' => [ + 'field' => 'price', + 'alias' => 'price_index', + ], + 'category_ids' => [ + 'field' => 'category_ids', + 'alias' => 'category_ids_index', + ], + ]; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php index 0949bf6469be92611fbefa4c45962d8255570892..2cd6935586bd816bcc8c1e444b242c3b3f7a1472 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php @@ -6,6 +6,7 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Adapter\Mysql\Filter; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\EntityMetadata; use Magento\Framework\Search\Request\FilterInterface; @@ -18,9 +19,9 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; class PreprocessorTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\CatalogSearch\Model\Search\TableMapper|\PHPUnit_Framework_MockObject_MockObject + * @var AliasResolver|\PHPUnit_Framework_MockObject_MockObject */ - private $tableMapper; + private $aliasResolver; /** * @var \Magento\Framework\DB\Adapter\AdapterInterface|MockObject @@ -141,7 +142,7 @@ class PreprocessorTest extends \PHPUnit_Framework_TestCase ) ); - $this->tableMapper = $this->getMockBuilder(\Magento\CatalogSearch\Model\Search\TableMapper::class) + $this->aliasResolver = $this->getMockBuilder(AliasResolver::class) ->disableOriginalConstructor() ->getMock(); $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) @@ -164,7 +165,7 @@ class PreprocessorTest extends \PHPUnit_Framework_TestCase 'resource' => $resource, 'attributePrefix' => 'attr_', 'metadataPool' => $this->metadataPoolMock, - 'tableMapper' => $this->tableMapper, + 'aliasResolver' => $this->aliasResolver, ] ); } @@ -234,7 +235,7 @@ class PreprocessorTest extends \PHPUnit_Framework_TestCase $this->attribute->method('getAttributeCode') ->willReturn('static_attribute'); - $this->tableMapper->expects($this->once())->method('getMappingAlias') + $this->aliasResolver->expects($this->once())->method('getAlias') ->willReturn('attr_table_alias'); $this->filter->expects($this->exactly(3)) ->method('getField') @@ -272,7 +273,7 @@ class PreprocessorTest extends \PHPUnit_Framework_TestCase ->method('getFrontendInput') ->willReturn($frontendInput); - $this->tableMapper->expects($this->once())->method('getMappingAlias') + $this->aliasResolver->expects($this->once())->method('getAlias') ->willReturn('termAttrAlias'); $this->filter->expects($this->exactly(3)) diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/FilterContextTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/FilterContextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9f133b763889bafb4843f7f9f868a3995a842eed --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/FilterMapper/FilterContextTest.php @@ -0,0 +1,236 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Unit\Model\Search\FilterMapper; + +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\CatalogSearch\Model\Search\FilterMapper\ExclusionStrategy; +use Magento\CatalogSearch\Model\Search\FilterMapper\FilterContext; +use Magento\CatalogSearch\Model\Search\FilterMapper\StaticAttributeStrategy; +use Magento\CatalogSearch\Model\Search\FilterMapper\TermDropdownStrategy; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\Search\Request\FilterInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class FilterContextTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FilterContext|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterContext; + + /** + * @var AliasResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $aliasResolver; + + /** + * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavConfig; + + /** + * @var ExclusionStrategy|\PHPUnit_Framework_MockObject_MockObject + */ + private $exclusionStrategy; + + /** + * @var TermDropdownStrategy|\PHPUnit_Framework_MockObject_MockObject + */ + private $termDropdownStrategy; + + /** + * @var StaticAttributeStrategy|\PHPUnit_Framework_MockObject_MockObject + */ + private $staticAttributeStrategy; + + /** + * @var \Magento\Framework\DB\Select + */ + private $select; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['getAttribute']) + ->getMock(); + $this->aliasResolver = $this->getMockBuilder( + AliasResolver::class + ) + ->disableOriginalConstructor() + ->setMethods(['getAlias']) + ->getMock(); + $this->exclusionStrategy = $this->getMockBuilder(ExclusionStrategy::class) + ->disableOriginalConstructor() + ->setMethods(['apply']) + ->getMock(); + $this->termDropdownStrategy = $this->getMockBuilder(TermDropdownStrategy::class) + ->disableOriginalConstructor() + ->setMethods(['apply']) + ->getMock(); + $this->staticAttributeStrategy = $this->getMockBuilder(StaticAttributeStrategy::class) + ->disableOriginalConstructor() + ->setMethods(['apply']) + ->getMock(); + $this->select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $objectManager = new ObjectManager($this); + $this->filterContext = $objectManager->getObject( + FilterContext::class, + [ + 'eavConfig' => $this->eavConfig, + 'aliasResolver' => $this->aliasResolver, + 'exclusionStrategy' => $this->exclusionStrategy, + 'termDropdownStrategy' => $this->termDropdownStrategy, + 'staticAttributeStrategy' => $this->staticAttributeStrategy, + ] + ); + } + + public function testApplyOnExclusionFilter() + { + $filter = $this->createFilterMock(); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(true); + $this->eavConfig->expects($this->never())->method('getAttribute'); + $this->assertTrue($this->filterContext->apply($filter, $this->select)); + } + + public function testApplyFilterWithoutAttribute() + { + $filter = $this->createFilterMock('some_field'); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(false); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(\Magento\Catalog\Model\Product::ENTITY, 'some_field') + ->willReturn(null); + $this->assertFalse($this->filterContext->apply($filter, $this->select)); + } + + public function testApplyOnTermFilterBySelect() + { + $filter = $this->createFilterMock('select_field', FilterInterface::TYPE_TERM); + $attribute = $this->createAttributeMock('select'); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(\Magento\Catalog\Model\Product::ENTITY, 'select_field') + ->willReturn($attribute); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(false); + $this->termDropdownStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(true); + $this->assertTrue($this->filterContext->apply($filter, $this->select)); + } + + public function testApplyOnTermFilterByMultiSelect() + { + $filter = $this->createFilterMock('multiselect_field', FilterInterface::TYPE_TERM); + $attribute = $this->createAttributeMock('multiselect'); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(\Magento\Catalog\Model\Product::ENTITY, 'multiselect_field') + ->willReturn($attribute); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(false); + $this->termDropdownStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(true); + $this->assertTrue($this->filterContext->apply($filter, $this->select)); + } + + public function testApplyOnTermFilterByStaticAttribute() + { + $filter = $this->createFilterMock('multiselect_field', FilterInterface::TYPE_TERM); + $attribute = $this->createAttributeMock('text', AbstractAttribute::TYPE_STATIC); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(\Magento\Catalog\Model\Product::ENTITY, 'multiselect_field') + ->willReturn($attribute); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(false); + $this->staticAttributeStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(true); + $this->assertTrue($this->filterContext->apply($filter, $this->select)); + } + + public function testApplyOnTermFilterByUnknownAttributeType() + { + $filter = $this->createFilterMock('multiselect_field', FilterInterface::TYPE_TERM); + $attribute = $this->createAttributeMock('text', 'text'); + $this->eavConfig->expects($this->once()) + ->method('getAttribute') + ->with(\Magento\Catalog\Model\Product::ENTITY, 'multiselect_field') + ->willReturn($attribute); + $this->exclusionStrategy->expects($this->once()) + ->method('apply') + ->with($filter, $this->select) + ->willReturn(false); + $this->assertFalse($this->filterContext->apply($filter, $this->select)); + } + + /** + * @param string $field + * @param string $type + * @return FilterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private function createFilterMock($field = null, $type = null) + { + $filter = $this->getMockBuilder(FilterInterface::class) + ->setMethods(['getField', 'getType']) + ->getMockForAbstractClass(); + $filter->expects($this->any()) + ->method('getField') + ->willReturn($field); + $filter->expects($this->any()) + ->method('getType') + ->willReturn($type); + + return $filter; + } + + /** + * @param string|null $frontendInput + * @param string|null $backendType + * @return Attribute|\PHPUnit_Framework_MockObject_MockObject + */ + private function createAttributeMock($frontendInput = null, $backendType = null) + { + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['getFrontendInput', 'getBackendType']) + ->getMock(); + $attribute->expects($this->any()) + ->method('getFrontendInput') + ->willReturn($frontendInput); + $attribute->expects($this->any()) + ->method('getBackendType') + ->willReturn($backendType); + return $attribute; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php index dd9a556146ab3e40e4e1182f44c959c8260d9c8d..dad4ad8095f5af27fa3ce7cd48b1803c706d80a6 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php @@ -6,6 +6,9 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Search; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; use Magento\Framework\Search\Request\FilterInterface; use Magento\Framework\Search\Request\QueryInterface; use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -16,8 +19,10 @@ use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; */ class TableMapperTest extends \PHPUnit_Framework_TestCase { - const WEBSITE_ID = 4512; - const STORE_ID = 2514; + /** + * @var AliasResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $aliasResolver; /** * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject @@ -25,7 +30,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase private $eavConfig; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|\PHPUnit_Framework_MockObject_MockObject */ private $attributeCollection; @@ -59,11 +64,6 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase */ private $resource; - /** - * @var \Magento\Store\Api\Data\StoreInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $store; - /** * @var \Magento\CatalogSearch\Model\Search\TableMapper */ @@ -76,65 +76,49 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->connection->expects($this->any()) - ->method('quoteInto') - ->willReturnCallback( - function ($query, $expression) { - return str_replace('?', $expression, $query); - } - ); + $this->connection->expects($this->never())->method('quoteInto'); $this->resource = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) ->disableOriginalConstructor() ->getMock(); - $this->resource->method('getTableName') - ->willReturnCallback( - function ($table) { - return 'prefix_' . $table; - } - ); - $this->resource->expects($this->any()) - ->method('getConnection') - ->willReturn($this->connection); + $this->resource->expects($this->never())->method('getTableName'); + $this->resource->expects($this->never())->method('getConnection'); $this->website = $this->getMockBuilder(\Magento\Store\Api\Data\WebsiteInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->website->expects($this->any()) - ->method('getId') - ->willReturn(self::WEBSITE_ID); - $this->store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->store->expects($this->any()) - ->method('getId') - ->willReturn(self::STORE_ID); + $this->website->expects($this->never())->method('getId'); + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->storeManager->expects($this->any()) - ->method('getWebsite') - ->willReturn($this->website); - $this->storeManager->expects($this->any()) - ->method('getStore') - ->willReturn($this->store); - $this->attributeCollection = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection::class - ) + $this->storeManager->expects($this->never())->method('getWebsite'); + $this->storeManager->expects($this->never())->method('getStore'); + + $this->attributeCollection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $attributeCollectionFactory = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class - ) + $attributeCollectionFactory = $this->getMockBuilder(CollectionFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); $attributeCollectionFactory->expects($this->never()) ->method('create'); + $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) ->setMethods(['getAttribute']) ->disableOriginalConstructor() ->getMock(); + + $this->aliasResolver = $this->getMockBuilder(AliasResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $this->aliasResolver->expects($this->any()) + ->method('getAlias') + ->willReturnCallback(function (FilterInterface $filter) { + return $filter->getField() . '_alias'; + }); + $this->target = $objectManager->getObject( \Magento\CatalogSearch\Model\Search\TableMapper::class, [ @@ -142,6 +126,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase 'storeManager' => $this->storeManager, 'attributeCollectionFactory' => $attributeCollectionFactory, 'eavConfig' => $this->eavConfig, + 'aliasResolver' => $this->aliasResolver, ] ); @@ -160,14 +145,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->once()) - ->method('joinInner') - ->with( - ['price_index' => 'prefix_catalog_product_index_price'], - 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ' . self::WEBSITE_ID, - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } @@ -176,18 +154,10 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase { $priceFilter = $this->createRangeFilter('static'); $query = $this->createFilterQuery($priceFilter); - $this->createAttributeMock('static', 'static', 'backend_table', 0, 'select'); $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->once()) - ->method('joinInner') - ->with( - ['static_filter' => 'backend_table'], - 'search_index.entity_id = static_filter.entity_id', - null - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } @@ -199,46 +169,25 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->once()) - ->method('joinInner') - ->with( - ['category_ids_index' => 'prefix_catalog_category_product_index'], - 'search_index.entity_id = category_ids_index.product_id', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } public function testAddTermFilter() { - $this->createAttributeMock('color', null, null, 132, 'select', 0); $categoryIdsFilter = $this->createTermFilter('color'); $query = $this->createFilterQuery($categoryIdsFilter); $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->once()) - ->method('joinLeft') - ->with( - ['color_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = color_filter.entity_id' - . ' AND color_filter.attribute_id = 132' - . ' AND color_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } public function testAddBoolQueryWithTermFiltersInside() { - $this->createAttributeMock('must1', null, null, 101, 'select', 0); - $this->createAttributeMock('should1', null, null, 102, 'select', 1); - $this->createAttributeMock('mustNot1', null, null, 103, 'select', 2); - $query = $this->createBoolQuery( [ $this->createFilterQuery($this->createTermFilter('must1')), @@ -253,45 +202,13 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->at(0)) - ->method('joinLeft') - ->with( - ['must1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = must1_filter.entity_id' - . ' AND must1_filter.attribute_id = 101' - . ' AND must1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(1)) - ->method('joinLeft') - ->with( - ['should1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = should1_filter.entity_id' - . ' AND should1_filter.attribute_id = 102' - . ' AND should1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(2)) - ->method('joinLeft') - ->with( - ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = mustNot1_filter.entity_id' - . ' AND mustNot1_filter.attribute_id = 103' - . ' AND mustNot1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } public function testAddBoolQueryWithTermAndPriceFiltersInside() { - $this->createAttributeMock('must1', null, null, 101, 'select', 0); - $this->createAttributeMock('should1', null, null, 102, 'select', 1); - $this->createAttributeMock('mustNot1', null, null, 103, 'select', 2); $query = $this->createBoolQuery( [ $this->createFilterQuery($this->createTermFilter('must1')), @@ -307,53 +224,13 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->at(0)) - ->method('joinLeft') - ->with( - ['must1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = must1_filter.entity_id' - . ' AND must1_filter.attribute_id = 101' - . ' AND must1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(1)) - ->method('joinInner') - ->with( - ['price_index' => 'prefix_catalog_product_index_price'], - 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ' . self::WEBSITE_ID, - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(2)) - ->method('joinLeft') - ->with( - ['should1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = should1_filter.entity_id' - . ' AND should1_filter.attribute_id = 102' - . ' AND should1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(3)) - ->method('joinLeft') - ->with( - ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = mustNot1_filter.entity_id' - . ' AND mustNot1_filter.attribute_id = 103' - . ' AND mustNot1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } public function testAddBoolFilterWithTermFiltersInside() { - $this->createAttributeMock('must1', null, null, 101, 'select', 0); - $this->createAttributeMock('should1', null, null, 102, 'select', 1); - $this->createAttributeMock('mustNot1', null, null, 103, 'select', 2); $query = $this->createFilterQuery( $this->createBoolFilter( [ @@ -370,45 +247,13 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->at(0)) - ->method('joinLeft') - ->with( - ['must1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = must1_filter.entity_id' - . ' AND must1_filter.attribute_id = 101' - . ' AND must1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(1)) - ->method('joinLeft') - ->with( - ['should1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = should1_filter.entity_id' - . ' AND should1_filter.attribute_id = 102' - . ' AND should1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(2)) - ->method('joinLeft') - ->with( - ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = mustNot1_filter.entity_id' - . ' AND mustNot1_filter.attribute_id = 103' - . ' AND mustNot1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } public function testAddBoolFilterWithBoolFiltersInside() { - $this->createAttributeMock('must1', null, null, 101, 'select', 0); - $this->createAttributeMock('should1', null, null, 102, 'select', 1); - $this->createAttributeMock('mustNot1', null, null, 103, 'select', 2); $query = $this->createFilterQuery( $this->createBoolFilter( [ @@ -425,36 +270,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase $this->request->expects($this->once()) ->method('getQuery') ->willReturn($query); - $this->select->expects($this->at(0)) - ->method('joinLeft') - ->with( - ['must1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = must1_filter.entity_id' - . ' AND must1_filter.attribute_id = 101' - . ' AND must1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(1)) - ->method('joinLeft') - ->with( - ['should1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = should1_filter.entity_id' - . ' AND should1_filter.attribute_id = 102' - . ' AND should1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); - $this->select->expects($this->at(2)) - ->method('joinLeft') - ->with( - ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], - 'search_index.entity_id = mustNot1_filter.entity_id' - . ' AND mustNot1_filter.attribute_id = 103' - . ' AND mustNot1_filter.store_id = 2514', - [] - ) - ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); } @@ -472,6 +288,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase ->willReturn(QueryInterface::TYPE_FILTER); $query->method('getReference') ->willReturn($filter); + return $query; } @@ -495,6 +312,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase ->willReturn($should); $query->method('getMustNot') ->willReturn($mustNot); + return $query; } @@ -518,6 +336,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase ->willReturn($should); $query->method('getMustNot') ->willReturn($mustNot); + return $query; } @@ -532,6 +351,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase FilterInterface::TYPE_RANGE, $field ); + return $filter; } @@ -546,6 +366,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase FilterInterface::TYPE_TERM, $field ); + return $filter; } @@ -564,40 +385,7 @@ class TableMapperTest extends \PHPUnit_Framework_TestCase ->willReturn($type); $filter->method('getField') ->willReturn($field); - return $filter; - } - /** - * @param string $code - * @param string $backendType - * @param string $backendTable - * @param int $attributeId - * @param string $frontendInput - * @param int $positionInCollection - */ - private function createAttributeMock( - $code, - $backendType = null, - $backendTable = null, - $attributeId = 120, - $frontendInput = 'select', - $positionInCollection = 0 - ) { - $attribute = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->setMethods(['getBackendType', 'getBackendTable', 'getId', 'getFrontendInput']) - ->disableOriginalConstructor() - ->getMock(); - $attribute->method('getId') - ->willReturn($attributeId); - $attribute->method('getBackendType') - ->willReturn($backendType); - $attribute->method('getBackendTable') - ->willReturn($backendTable); - $attribute->method('getFrontendInput') - ->willReturn($frontendInput); - $this->eavConfig->expects($this->at($positionInCollection)) - ->method('getAttribute') - ->with(\Magento\Catalog\Model\Product::ENTITY, $code) - ->willReturn($attribute); + return $filter; } } diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 49756420bd2308cd1f8326f474395b6209c7450c..313cc99881a318799c15488fa742151e26db18ec 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-search", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-search": "100.2.*", diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index f62b4e47767a25230c5c2ad8372f99acc6bda2eb..7e9451f9b83e7ada30455509b8fd686a0c251705 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -11,6 +11,7 @@ <preference for="Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface" type="Magento\CatalogSearch\Model\Adapter\Mysql\Filter\Preprocessor" /> <preference for="Magento\Framework\Search\Dynamic\DataProviderInterface" type="Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic\DataProvider" /> <preference for="Magento\Framework\Search\Adapter\OptionsInterface" type="Magento\CatalogSearch\Model\Adapter\Options" /> + <preference for="Magento\CatalogSearch\Model\Search\FilterMapper\FilterStrategyInterface" type="Magento\CatalogSearch\Model\Search\FilterMapper\FilterContext"/> <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> <arguments> <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument> diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json index 8fd54cbc8ee8319f625cd6320954a0c821bf9a52..a5f66cd09dda49086434a4dc73b073fe7f0ee9c3 100644 --- a/app/code/Magento/CatalogUrlRewrite/composer.json +++ b/app/code/Magento/CatalogUrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/CatalogWidget/composer.json b/app/code/Magento/CatalogWidget/composer.json index 330f46176286c70822a32762dccfed157a2ce358..198e54db32d888dfc32eafd33f5c6b3ca45da870 100644 --- a/app/code/Magento/CatalogWidget/composer.json +++ b/app/code/Magento/CatalogWidget/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-widget", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-widget": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Checkout/Controller/Cart/Add.php b/app/code/Magento/Checkout/Controller/Cart/Add.php index fddfa63b5983e5f508f5211c19f8980d56aafc34..bfbf0008bf5892067aaa7b0ba53ca1bbb7632339 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Add.php +++ b/app/code/Magento/Checkout/Controller/Cart/Add.php @@ -84,6 +84,7 @@ class Add extends \Magento\Checkout\Controller\Cart } $params = $this->getRequest()->getParams(); + try { if (isset($params['qty'])) { $filter = new \Zend_Filter_LocalizedToNormalized( diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php index 4ce23c9c7f7090980d48f4c3bb231d4214a7e893..236c716f572d91e4bd4b88366414480567ccabc0 100644 --- a/app/code/Magento/Checkout/Model/Cart.php +++ b/app/code/Magento/Checkout/Model/Cart.php @@ -91,6 +91,11 @@ class Cart extends DataObject implements CartInterface */ protected $productRepository; + /** + * @var \Magento\Checkout\Model\Cart\RequestInfoFilterInterface + */ + private $requestInfoFilter; + /** * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -315,6 +320,7 @@ class Cart extends DataObject implements CartInterface * * @param \Magento\Framework\DataObject|int|array $requestInfo * @return \Magento\Framework\DataObject + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _getProductRequest($requestInfo) { @@ -322,11 +328,14 @@ class Cart extends DataObject implements CartInterface $request = $requestInfo; } elseif (is_numeric($requestInfo)) { $request = new \Magento\Framework\DataObject(['qty' => $requestInfo]); - } else { + } elseif (is_array($requestInfo)) { $request = new \Magento\Framework\DataObject($requestInfo); + } else { + throw new \Magento\Framework\Exception\LocalizedException( + __('We found an invalid request for adding product to quote.') + ); } - - !$request->hasFormKey() ?: $request->unsFormKey(); + $this->getRequestInfoFilter()->filter($request); return $request; } @@ -722,4 +731,19 @@ class Cart extends DataObject implements CartInterface $this->_checkoutSession->setLastAddedProductId($productId); return $result; } + + /** + * Getter for RequestInfoFilter + * + * @deprecated + * @return \Magento\Checkout\Model\Cart\RequestInfoFilterInterface + */ + private function getRequestInfoFilter() + { + if ($this->requestInfoFilter === null) { + $this->requestInfoFilter = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Checkout\Model\Cart\RequestInfoFilterInterface::class); + } + return $this->requestInfoFilter; + } } diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..10f3b81386b8febcee8f2b49ee4acd6242d1e5e8 --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php @@ -0,0 +1,44 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Model\Cart; + +/** + * Class RequestInfoFilter used for filtering data from a request + */ +class RequestInfoFilter implements RequestInfoFilterInterface +{ + /** + * @var array $params + */ + private $filterList; + + /** + * @param array $filterList + */ + public function __construct( + array $filterList = [] + ) { + $this->filterList = $filterList; + } + + /** + * Filters the data with values from filterList + * + * @param \Magento\Framework\DataObject $params + * @return $this + */ + public function filter(\Magento\Framework\DataObject $params) + { + foreach ($this->filterList as $filterKey) { + /** @var string $filterKey */ + if ($params->hasData($filterKey)) { + $params->unsetData($filterKey); + } + } + return $this; + } +} diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php new file mode 100644 index 0000000000000000000000000000000000000000..2ef24c0a5f28a198be28cbb4fc0ba212ebbd9097 --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php @@ -0,0 +1,41 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Model\Cart; + +/** + * Class RequestInfoFilterComposite + */ +class RequestInfoFilterComposite implements RequestInfoFilterInterface +{ + /** + * @var RequestInfoFilter[] $params + */ + private $filters = []; + + /** + * @param RequestInfoFilter[] $filters + */ + public function __construct( + $filters = [] + ) { + $this->filters = $filters; + } + + /** + * Loops through all leafs of the composite and calls filter method + * + * @param \Magento\Framework\DataObject $params + * @return $this + */ + public function filter(\Magento\Framework\DataObject $params) + { + foreach ($this->filters as $filter) { + $filter->filter($params); + } + return $this; + } +} diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..4bd268f6c896e149bf042ccdbaaa85e411ec6153 --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Model\Cart; + +/** + * Interface RequestInfoFilterInterface used by composite and leafs to implement filtering + */ +interface RequestInfoFilterInterface +{ + /** + * Filters the data object by an array of parameters + * + * @param \Magento\Framework\DataObject $params + * @return RequestInfoFilterInterface + */ + public function filter(\Magento\Framework\DataObject $params); +} diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index b07e90384c1647b51d59405022bb63a0b8e108d3..f2ad294d06cbe45d0b5d77ebe8658d546d27efd3 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -9,6 +9,9 @@ namespace Magento\Checkout\Model; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Framework\Exception\CouldNotSaveException; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPaymentInformationManagementInterface { @@ -42,6 +45,11 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa */ protected $cartRepository; + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + /** * @param \Magento\Quote\Api\GuestBillingAddressManagementInterface $billingAddressManagement * @param \Magento\Quote\Api\GuestPaymentMethodManagementInterface $paymentMethodManagement @@ -79,7 +87,13 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress); try { $orderId = $this->cartManagement->placeOrder($cartId); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + throw new CouldNotSaveException( + __($e->getMessage()), + $e + ); } catch (\Exception $e) { + $this->getLogger()->critical($e); throw new CouldNotSaveException( __('An error occurred on the server. Please try to place the order again.'), $e @@ -117,4 +131,18 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); return $this->paymentInformationManagement->getPaymentInformation($quoteIdMask->getQuoteId()); } + + /** + * Get logger instance + * + * @return \Psr\Log\LoggerInterface + * @deprecated + */ + private function getLogger() + { + if (!$this->logger) { + $this->logger = \Magento\Framework\App\ObjectManager::getInstance()->get(\Psr\Log\LoggerInterface::class); + } + return $this->logger; + } } diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 140917dcdfeff22eaa73b9a1dc928d5d83f6ccd5..79e76feb4366190cb1d81389e7c54ac4f97cca9e 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -7,6 +7,9 @@ namespace Magento\Checkout\Model; use Magento\Framework\Exception\CouldNotSaveException; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInformationManagementInterface { /** @@ -34,6 +37,11 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor */ protected $cartTotalsRepository; + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + /** * @param \Magento\Quote\Api\BillingAddressManagementInterface $billingAddressManagement * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement @@ -67,7 +75,13 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor $this->savePaymentInformation($cartId, $paymentMethod, $billingAddress); try { $orderId = $this->cartManagement->placeOrder($cartId); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + throw new CouldNotSaveException( + __($e->getMessage()), + $e + ); } catch (\Exception $e) { + $this->getLogger()->critical($e); throw new CouldNotSaveException( __('An error occurred on the server. Please try to place the order again.'), $e @@ -102,4 +116,18 @@ class PaymentInformationManagement implements \Magento\Checkout\Api\PaymentInfor $paymentDetails->setTotals($this->cartTotalsRepository->get($cartId)); return $paymentDetails; } + + /** + * Get logger instance + * + * @return \Psr\Log\LoggerInterface + * @deprecated + */ + private function getLogger() + { + if (!$this->logger) { + $this->logger = \Magento\Framework\App\ObjectManager::getInstance()->get(\Psr\Log\LoggerInterface::class); + } + return $this->logger; + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6c758cf4661fdab253a58b04f70969dc845e89d1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Test\Unit\Model\Cart; + +/** + * Class RequestInfoFilterTest + */ +class RequestInfoFilterCompositeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Checkout\Model\Cart\RequestInfoFilterComposite + */ + protected $model; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * Setup the test + */ + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $requestInfoFilterMock1 = $this->getMock( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + ['filter'], + [], + '', + false + ); + $requestInfoFilterMock2 = $this->getMock( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + ['filter'], + [], + '', + false + ); + + $requestInfoFilterMock1->expects($this->atLeastOnce()) + ->method('filter'); + $requestInfoFilterMock2->expects($this->atLeastOnce()) + ->method('filter'); + + $filterList = [ $requestInfoFilterMock1, $requestInfoFilterMock2]; + + $this->model = $this->objectManager->getObject( + \Magento\Checkout\Model\Cart\RequestInfoFilterComposite::class, + [ + 'filters' => $filterList, + ] + ); + } + + /** + * Test Filter method + */ + public function testFilter() + { + /** @var \Magento\Framework\DataObject $params */ + $params = $this->objectManager->getObject( + \Magento\Framework\DataObject::class, + ['data' => ['abc' => 1, 'efg' => 1, 'xyz' => 1]] + ); + $result = $this->model->filter($params); + $this->assertEquals($this->model, $result); + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cd6ca330da1b2d7252b8911f1820d7bc217d3561 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Checkout\Test\Unit\Model\Cart; + +/** + * Class RequestInfoFilterTest + */ +class RequestInfoFilterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Checkout\Model\Cart\RequestInfoFilter + */ + protected $model; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * Setup the test + */ + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->model = $this->objectManager->getObject( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + [ + 'filterList' => ['efg', 'xyz'], + ] + ); + } + + /** + * Test Filter method + */ + public function testFilter() + { + /** @var \Magento\Framework\DataObject $params */ + $params = $this->objectManager->getObject( + \Magento\Framework\DataObject::class, + ['data' => ['abc' => 1, 'efg' => 1, 'xyz' => 1]] + ); + $result = $this->model->filter($params); + $this->assertEquals($this->model, $result); + $this->assertEquals(['abc' => 1], $params->convertToArray()); + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php b/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php index 199e6692e68d1777c382c8125391cac88326a549..9984fe12d3792e95f0b29d0d2c71ff36b37a5897 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php @@ -29,7 +29,7 @@ class CartTest extends \PHPUnit_Framework_TestCase */ protected $customerSessionMock; - /** @var \Magento\CatalogInventory\Api\StockItem|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogInventory\Api\Data\StockItemInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $stockItemMock; /** @@ -57,16 +57,39 @@ class CartTest extends \PHPUnit_Framework_TestCase */ protected $stockState; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $productRepository; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $requestInfoFilterMock; + protected function setUp() { $this->checkoutSessionMock = $this->getMock(\Magento\Checkout\Model\Session::class, [], [], '', false); $this->customerSessionMock = $this->getMock(\Magento\Customer\Model\Session::class, [], [], '', false); $this->scopeConfigMock = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); + $this->eventManagerMock = $this->getMock(\Magento\Framework\Event\ManagerInterface::class); + $this->storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->productRepository = $this->getMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); $this->stockRegistry = $this->getMockBuilder(\Magento\CatalogInventory\Model\StockRegistry::class) ->disableOriginalConstructor() ->setMethods(['getStockItem', '__wakeup']) ->getMock(); - $this->stockItemMock = $this->getMock( \Magento\CatalogInventory\Model\Stock\Item::class, ['getMinSaleQty', '__wakeup'], @@ -74,7 +97,6 @@ class CartTest extends \PHPUnit_Framework_TestCase '', false ); - $this->stockState = $this->getMock( \Magento\CatalogInventory\Model\StockState::class, ['suggestQty', '__wakeup'], @@ -82,12 +104,22 @@ class CartTest extends \PHPUnit_Framework_TestCase '', false ); + $this->storeMock = + $this->getMock(\Magento\Store\Model\Store::class, ['getWebsiteId', 'getId', '__wakeup'], [], '', false); + $this->requestInfoFilterMock = $this->getMock(\Magento\Checkout\Model\Cart\RequestInfoFilterInterface::class); $this->stockRegistry->expects($this->any()) ->method('getStockItem') ->will($this->returnValue($this->stockItemMock)); - $this->quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); - $this->eventManagerMock = $this->getMock(\Magento\Framework\Event\ManagerInterface::class); + $this->storeMock->expects($this->any()) + ->method('getWebsiteId') + ->will($this->returnValue(10)); + $this->storeMock->expects($this->any()) + ->method('getId') + ->will($this->returnValue(10)); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->cart = $this->objectManagerHelper->getObject( @@ -98,9 +130,14 @@ class CartTest extends \PHPUnit_Framework_TestCase 'stockRegistry' => $this->stockRegistry, 'stockState' => $this->stockState, 'customerSession' => $this->customerSessionMock, - 'eventManager' => $this->eventManagerMock + 'eventManager' => $this->eventManagerMock, + 'storeManager' => $this->storeManagerMock, + 'productRepository' => $this->productRepository ] ); + + $this->objectManagerHelper + ->setBackwardCompatibleProperty($this->cart, 'requestInfoFilter', $this->requestInfoFilterMock); } public function testSuggestItemsQty() @@ -169,10 +206,17 @@ class CartTest extends \PHPUnit_Framework_TestCase */ public function prepareQuoteItemMock($itemId) { - $store = $this->getMock(\Magento\Store\Model\Store::class, ['getWebsiteId', '__wakeup'], [], '', false); + $store = $this->getMock(\Magento\Store\Model\Store::class, ['getId', '__wakeup'], [], '', false); $store->expects($this->any()) ->method('getWebsiteId') ->will($this->returnValue(10)); + $store->expects($this->any()) + ->method('getId') + ->will($this->returnValue(10)); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($store)); + switch ($itemId) { case 2: $product = $this->getMock( @@ -255,4 +299,162 @@ class CartTest extends \PHPUnit_Framework_TestCase ['useQty' => false] ]; } + + /** + * Test successful scenarios for AddProduct + * + * @param int|\Magento\Catalog\Model\Product $productInfo + * @param \Magento\Framework\DataObject|int|array $requestInfo + * @dataProvider addProductDataProvider + */ + public function testAddProduct($productInfo, $requestInfo) + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getStore', 'getWebsiteIds', 'getProductUrl', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->once()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $product->expects($this->any()) + ->method('getProductUrl') + ->will($this->returnValue('url')); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + $this->quoteMock->expects($this->once()) + ->method('addProduct') + ->will($this->returnValue(1)); + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->will($this->returnValue($this->quoteMock)); + + $this->eventManagerMock->expects($this->at(0))->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + + if (!$productInfo) { + $productInfo = $product; + } + $result = $this->cart->addProduct($productInfo, $requestInfo); + $this->assertSame($this->cart, $result); + } + + /** + * Test exception on adding product for AddProduct + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testAddProductException() + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getStore', 'getWebsiteIds', 'getProductUrl', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->once()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $product->expects($this->any()) + ->method('getProductUrl') + ->will($this->returnValue('url')); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + $this->quoteMock->expects($this->once()) + ->method('addProduct') + ->will($this->returnValue('error')); + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->will($this->returnValue($this->quoteMock)); + + $this->eventManagerMock->expects($this->never())->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + $this->setExpectedException(\Magento\Framework\Exception\LocalizedException::class); + $this->cart->addProduct(4, 4); + } + + /** + * Test bad parameters on adding product for AddProduct + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testAddProductExceptionBadParams() + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getWebsiteIds', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + + $this->eventManagerMock->expects($this->never())->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + $this->setExpectedException(\Magento\Framework\Exception\LocalizedException::class); + $this->cart->addProduct(4, 'bad'); + } + + /** + * Data provider for testAddProduct + * + * @return array + */ + public function addProductDataProvider() + { + $obj = new ObjectManagerHelper($this) ; + $data = ['qty' => 5.5, 'sku' => 'prod']; + + return [ + 'prod_int_info_int' => [4, 4], + 'prod_int_info_array' => [ 4, $data], + 'prod_int_info_object' => [ + 4, + $obj->getObject( + \Magento\Framework\DataObject::class, + ['data' => $data] + ) + ], + 'prod_obj_info_int' => [null, 4], + 'prod_obj_info_array' => [ null, $data], + 'prod_obj_info_object' => [ + null, + $obj->getObject( + \Magento\Framework\DataObject::class, + ['data' => $data] + ) + ] + ]; + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index 093bbf4a5accc66bdd5c00fa4f30660409e09f79..76cbafb48ebd47e4fd83d01ef1361ef29df2f3be 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -42,6 +42,11 @@ class GuestPaymentInformationManagementTest extends \PHPUnit_Framework_TestCase */ protected $model; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -61,6 +66,7 @@ class GuestPaymentInformationManagementTest extends \PHPUnit_Framework_TestCase '', false ); + $this->loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); $this->model = $objectManager->getObject( \Magento\Checkout\Model\GuestPaymentInformationManagement::class, [ @@ -71,6 +77,7 @@ class GuestPaymentInformationManagementTest extends \PHPUnit_Framework_TestCase 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock ] ); + $objectManager->setBackwardCompatibleProperty($this->model, 'logger', $this->loggerMock); } public function testSavePaymentInformationAndPlaceOrder() @@ -112,7 +119,7 @@ class GuestPaymentInformationManagementTest extends \PHPUnit_Framework_TestCase ->method('assign') ->with($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); - $exception = new CouldNotSaveException(__('DB exception')); + $exception = new \Exception(__('DB exception')); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); @@ -161,4 +168,29 @@ class GuestPaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $billingAddressMock->expects($this->once())->method('setEmail')->with($email); $this->assertTrue($this->model->savePaymentInformation($cartId, $email, $paymentMock)); } + + /** + * @expectedExceptionMessage DB exception + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + */ + public function testSavePaymentInformationAndPlaceOrderWithLocolizedException() + { + $cartId = 100; + $email = 'email@magento.com'; + $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); + $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); + + $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); + + $this->billingAddressManagementMock->expects($this->once()) + ->method('assign') + ->with($cartId, $billingAddressMock); + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); + $phrase = new \Magento\Framework\Phrase(__('DB exception')); + $exception = new \Magento\Framework\Exception\LocalizedException($phrase); + $this->loggerMock->expects($this->never())->method('critical'); + $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); + + $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index 496054b2c7b1e0a0b9e60c119a8c25d322832a26..8da67a1fcb71512dd218a2c62d51b7922bb36c25 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -29,6 +29,11 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase */ protected $model; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -40,6 +45,8 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase ); $this->cartManagementMock = $this->getMock(\Magento\Quote\Api\CartManagementInterface::class); + $this->loggerMock = $this->getMock(\Psr\Log\LoggerInterface::class); + $this->model = $objectManager->getObject( \Magento\Checkout\Model\PaymentInformationManagement::class, [ @@ -48,6 +55,7 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase 'cartManagement' => $this->cartManagementMock ] ); + $objectManager->setBackwardCompatibleProperty($this->model, 'logger', $this->loggerMock); } public function testSavePaymentInformationAndPlaceOrder() @@ -83,7 +91,8 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase ->method('assign') ->with($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); - $exception = new CouldNotSaveException(__('DB exception')); + $exception = new \Exception(__('DB exception')); + $this->loggerMock->expects($this->once())->method('critical'); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); @@ -129,4 +138,26 @@ class PaymentInformationManagementTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->model->savePaymentInformation($cartId, $paymentMock)); } + + /** + * @expectedExceptionMessage DB exception + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + */ + public function testSavePaymentInformationAndPlaceOrderWithLocolizedException() + { + $cartId = 100; + $paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class); + $billingAddressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class); + + $this->billingAddressManagementMock->expects($this->once()) + ->method('assign') + ->with($cartId, $billingAddressMock); + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); + $phrase = new \Magento\Framework\Phrase(__('DB exception')); + $exception = new \Magento\Framework\Exception\LocalizedException($phrase); + $this->loggerMock->expects($this->never())->method('critical'); + $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); + + $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); + } } diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index 5545e409b65abb3fb381e593c4ec34298dba6b80..60919d9fa2ca949817641c43328ad3be88edc8be 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-checkout", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml index c55598fdff5e792dd7404967d12f9fcc5e78e828..a2243b33a04edc0af4dc71ed79b802eac68f4cc2 100644 --- a/app/code/Magento/Checkout/etc/di.xml +++ b/app/code/Magento/Checkout/etc/di.xml @@ -26,4 +26,20 @@ <preference for="Magento\Checkout\Api\GuestTotalsInformationManagementInterface" type="Magento\Checkout\Model\GuestTotalsInformationManagement" /> <preference for="Magento\Checkout\Api\TotalsInformationManagementInterface" type="Magento\Checkout\Model\TotalsInformationManagement" /> <preference for="Magento\Checkout\Api\AgreementsValidatorInterface" type="Magento\Checkout\Model\AgreementsValidator" /> + <preference for="Magento\Checkout\Model\Cart\RequestInfoFilterInterface" + type="Magento\Checkout\Model\Cart\RequestInfoFilterComposite"/> + <type name="Magento\Checkout\Model\Cart\RequestInfoFilter"> + <arguments> + <argument name="filterList" xsi:type="array"> + <item name="form_key" xsi:type="string">form_key</item> + </argument> + </arguments> + </type> + <type name="Magento\Checkout\Model\Cart\RequestInfoFilterComposite"> + <arguments> + <argument name="filters" xsi:type="array"> + <item name="filter" xsi:type="object">Magento\Checkout\Model\Cart\RequestInfoFilter</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index bccf81bcb6ee81c97bd6bef98d3c5f08ec33b9cc..6fb9058c3b7681c8905a40a5eace5d88d8566b6f 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -72,4 +72,12 @@ </argument> </arguments> </type> + <type name="Magento\Checkout\Model\Cart\RequestInfoFilter"> + <arguments> + <argument name="filterList" xsi:type="array"> + <item name="form_key" xsi:type="string">form_key</item> + <item name="custom_price" xsi:type="string">custom_price</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js index ca9d6493a16740f1a43555d84459df4403bd3c88..9005015d2460e12c5462551aecce97f1bd31c003 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js @@ -24,7 +24,7 @@ define([ /** * Stop full page loader action * - * @param {Boolean} forceStop + * @param {Boolean} [forceStop] */ stopLoader: function (forceStop) { var $elem = $(containerId), diff --git a/app/code/Magento/CheckoutAgreements/composer.json b/app/code/Magento/CheckoutAgreements/composer.json index afb706a39a25ec97915abfe0bc6bfcb99c95f6d4..56a2b567745c2dd81fa3ca691309c4a76fe05aed 100644 --- a/app/code/Magento/CheckoutAgreements/composer.json +++ b/app/code/Magento/CheckoutAgreements/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-checkout-agreements", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-checkout": "100.2.*", "magento/module-quote": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js index 3d030a62eb425cdb0365331c8fac5325a5dbc91d..2fa3c2cddd3d5af870f02db0e189cae8456c51aa 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js @@ -12,9 +12,8 @@ define( function ($) { 'use strict'; var checkoutConfig = window.checkoutConfig, - agreementsConfig = checkoutConfig ? checkoutConfig.checkoutAgreements : {}; - - var agreementsInputPath = '.payment-method._active div.checkout-agreements input'; + agreementsConfig = checkoutConfig ? checkoutConfig.checkoutAgreements : {}, + agreementsInputPath = '.payment-method._active div.checkout-agreements input'; return { /** @@ -23,26 +22,11 @@ define( * @returns {boolean} */ validate: function() { - if (!agreementsConfig.isEnabled) { - return true; - } - - if ($(agreementsInputPath).length == 0) { + if (!agreementsConfig.isEnabled || $(agreementsInputPath).length == 0) { return true; } - return $('#co-payment-form').validate({ - errorClass: 'mage-error', - errorElement: 'div', - meta: 'validate', - errorPlacement: function (error, element) { - var errorPlacement = element; - if (element.is(':checkbox') || element.is(':radio')) { - errorPlacement = element.siblings('label').last(); - } - errorPlacement.after(error); - } - }).element(agreementsInputPath); + return $.validator.validateSingleElement(agreementsInputPath, {errorElement: 'div'}); } } } diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index bb7d5de636ce5fd98b5f98943500910603d56267..7d8ef43084f3264c09bb65d2939f6660fa343f7f 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -8,14 +8,13 @@ <div class="checkout-agreements" data-bind="visible: isVisible"> <!-- ko foreach: agreements --> <!-- ko if: ($parent.isAgreementRequired($data)) --> - <div class="checkout-agreement"> - <input type="checkbox" + <div class="checkout-agreement required"> + <input type="checkbox" class="required-entry" data-bind="attr: { 'id': 'agreement_' + agreementId, 'name': 'agreement[' + agreementId + ']', 'value': agreementId - }" - data-validate="{required:true}" /> + }"/> <label data-bind="attr: {'for': 'agreement_' + agreementId}"> <button type="button" class="action action-show" diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php index ec5a438247e5cddc205d2f49c8d49ea746943e34..1bd96eff4dceef562040df73978679cc236a00bb 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php @@ -41,6 +41,7 @@ class Collection extends AbstractCollection { $this->_init(\Magento\Cms\Model\Block::class, \Magento\Cms\Model\ResourceModel\Block::class); $this->_map['fields']['store'] = 'store_table.store_id'; + $this->_map['fields']['block_id'] = 'main_table.block_id'; } /** diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4e1de289e5ef0e004a34d70dd793c1715a8bf292 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Cms\Test\Unit\Ui\Component\Listing\Column; + +use Magento\Cms\Ui\Component\Listing\Column\BlockActions; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponent\Processor; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * BlockActionsTest contains unit tests for \Magento\Cms\Ui\Component\Listing\Column\BlockActions class + */ +class BlockActionsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var BlockActions + */ + private $blockActions; + + /** + * @var Escaper|MockObject + */ + private $escaper; + + /** + * @var UrlInterface|MockObject + */ + private $urlBuilder; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $context = $this->getMock(ContextInterface::class); + + $processor = $this->getMockBuilder(Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $context->expects(static::once()) + ->method('getProcessor') + ->willReturn($processor); + + $this->urlBuilder = $this->getMock(UrlInterface::class); + + $this->escaper = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); + + $this->blockActions = $objectManager->getObject(BlockActions::class, [ + 'context' => $context, + 'urlBuilder' => $this->urlBuilder + ]); + + $objectManager->setBackwardCompatibleProperty($this->blockActions, 'escaper', $this->escaper); + } + + /** + * @covers \Magento\Cms\Ui\Component\Listing\Column\BlockActions::prepareDataSource + */ + public function testPrepareDataSource() + { + $blockId = 1; + $title = 'block title'; + $items = [ + 'data' => [ + 'items' => [ + [ + 'block_id' => $blockId, + 'title' => $title + ] + ] + ] + ]; + $name = 'item_name'; + $expectedItems = [ + [ + 'block_id' => $blockId, + 'title' => $title, + $name => [ + 'edit' => [ + 'href' => 'test/url/edit', + 'label' => __('Edit'), + ], + 'delete' => [ + 'href' => 'test/url/delete', + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete %1', $title), + 'message' => __('Are you sure you wan\'t to delete a %1 record?', $title) + ], + ] + ], + ] + ]; + + $this->escaper->expects(static::once()) + ->method('escapeHtml') + ->with($title) + ->willReturn($title); + + $this->urlBuilder->expects(static::exactly(2)) + ->method('getUrl') + ->willReturnMap( + [ + [ + BlockActions::URL_PATH_EDIT, + [ + 'block_id' => $blockId + ], + 'test/url/edit', + ], + [ + BlockActions::URL_PATH_DELETE, + [ + 'block_id' => $blockId + ], + 'test/url/delete', + ], + ] + ); + + $this->blockActions->setData('name', $name); + + $actual = $this->blockActions->prepareDataSource($items); + static::assertEquals($expectedItems, $actual['data']['items']); + } +} diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php index 4b6ce1ae2ff42d39bf53a9e57bd3b9f6aa639f89..731c08fbc64dd59a967b4d74ea52ba188336d0a9 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php @@ -6,6 +6,7 @@ namespace Magento\Cms\Test\Unit\Ui\Component\Listing\Column; use Magento\Cms\Ui\Component\Listing\Column\PageActions; +use Magento\Framework\Escaper; class PageActionsTest extends \PHPUnit_Framework_TestCase { @@ -34,12 +35,20 @@ class PageActionsTest extends \PHPUnit_Framework_TestCase ] ); + $escaper = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); + $objectManager->setBackwardCompatibleProperty($model, 'escaper', $escaper); + // Define test input and expectations + $title = 'page title'; $items = [ 'data' => [ 'items' => [ [ - 'page_id' => $pageId + 'page_id' => $pageId, + 'title' => $title ] ] ] @@ -48,6 +57,7 @@ class PageActionsTest extends \PHPUnit_Framework_TestCase $expectedItems = [ [ 'page_id' => $pageId, + 'title' => $title, $name => [ 'edit' => [ 'href' => 'test/url/edit', @@ -57,14 +67,19 @@ class PageActionsTest extends \PHPUnit_Framework_TestCase 'href' => 'test/url/delete', 'label' => __('Delete'), 'confirm' => [ - 'title' => __('Delete ${ $.$data.title }'), - 'message' => __('Are you sure you wan\'t to delete a ${ $.$data.title } record?') + 'title' => __('Delete %1', $title), + 'message' => __('Are you sure you wan\'t to delete a %1 record?', $title) ], ] ], ] ]; + $escaper->expects(static::once()) + ->method('escapeHtml') + ->with($title) + ->willReturn($title); + // Configure mocks and object data $urlBuilderMock->expects($this->any()) ->method('getUrl') diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php index e71352c465333396312cc0bd0242cbe45f62bd17..f0a1fe7981f8c3007055973eb4e296c23afe5ab8 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php @@ -9,6 +9,8 @@ use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Escaper; /** * Class BlockActions @@ -27,6 +29,11 @@ class BlockActions extends Column */ protected $urlBuilder; + /** + * @var Escaper + */ + private $escaper; + /** * Constructor * @@ -47,10 +54,6 @@ class BlockActions extends Column parent::__construct($context, $uiComponentFactory, $components, $data); } - /** - * @param array $items - * @return array - */ /** * Prepare Data Source * @@ -62,6 +65,7 @@ class BlockActions extends Column if (isset($dataSource['data']['items'])) { foreach ($dataSource['data']['items'] as & $item) { if (isset($item['block_id'])) { + $title = $this->getEscaper()->escapeHtml($item['title']); $item[$this->getData('name')] = [ 'edit' => [ 'href' => $this->urlBuilder->getUrl( @@ -81,8 +85,8 @@ class BlockActions extends Column ), 'label' => __('Delete'), 'confirm' => [ - 'title' => __('Delete "${ $.$data.title }"'), - 'message' => __('Are you sure you wan\'t to delete a "${ $.$data.title }" record?') + 'title' => __('Delete %1', $title), + 'message' => __('Are you sure you wan\'t to delete a %1 record?', $title) ] ] ]; @@ -92,4 +96,17 @@ class BlockActions extends Column return $dataSource; } + + /** + * Get instance of escaper + * @return Escaper + * @deprecated + */ + private function getEscaper() + { + if (!$this->escaper) { + $this->escaper = ObjectManager::getInstance()->get(Escaper::class); + } + return $this->escaper; + } } diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php index fe85fd0ef314070c5c6cdb6f34435e72af3e152b..f23afbffa792e6b5b92166b5268e3942735bd21f 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php @@ -5,11 +5,13 @@ */ namespace Magento\Cms\Ui\Component\Listing\Column; +use Magento\Cms\Block\Adminhtml\Page\Grid\Renderer\Action\UrlBuilder; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Escaper; +use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; -use Magento\Cms\Block\Adminhtml\Page\Grid\Renderer\Action\UrlBuilder; -use Magento\Framework\UrlInterface; /** * Class PageActions @@ -31,6 +33,11 @@ class PageActions extends Column */ private $editUrl; + /** + * @var Escaper + */ + private $escaper; + /** * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory @@ -71,12 +78,13 @@ class PageActions extends Column 'href' => $this->urlBuilder->getUrl($this->editUrl, ['page_id' => $item['page_id']]), 'label' => __('Edit') ]; + $title = $this->getEscaper()->escapeHtml($item['title']); $item[$name]['delete'] = [ 'href' => $this->urlBuilder->getUrl(self::CMS_URL_PATH_DELETE, ['page_id' => $item['page_id']]), 'label' => __('Delete'), 'confirm' => [ - 'title' => __('Delete ${ $.$data.title }'), - 'message' => __('Are you sure you wan\'t to delete a ${ $.$data.title } record?') + 'title' => __('Delete %1', $title), + 'message' => __('Are you sure you wan\'t to delete a %1 record?', $title) ] ]; } @@ -95,4 +103,17 @@ class PageActions extends Column return $dataSource; } + + /** + * Get instance of escaper + * @return Escaper + * @deprecated + */ + private function getEscaper() + { + if (!$this->escaper) { + $this->escaper = ObjectManager::getInstance()->get(Escaper::class); + } + return $this->escaper; + } } diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index 66f6da042e6c9a77e25eb5c9e8f1595c16e3e5f6..7f2ed4d7a7dcdc59916c34c796f7f3fc05ec6cc9 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cms", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-theme": "100.2.*", "magento/module-widget": "100.2.*", diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv index 6f3a2955a5c65b5cf1760519741fd60d49133123..9a9f6ac4c6888e386d44c679c94a87533844342d 100644 --- a/app/code/Magento/Cms/i18n/en_US.csv +++ b/app/code/Magento/Cms/i18n/en_US.csv @@ -84,10 +84,8 @@ Exception,Exception "All Store Views","All Store Views" Edit,Edit Delete,Delete -"Delete ${ $.$data.title }","Delete ${ $.$data.title }" -"Are you sure you wan\'t to delete a ${ $.$data.title } record?","Are you sure you wan\'t to delete a ${ $.$data.title } record?" -"Delete ""${ $.$data.title }""","Delete ""${ $.$data.title }""" -"Are you sure you wan\'t to delete a ""${ $.$data.title }"" record?","Are you sure you wan\'t to delete a ""${ $.$data.title }"" record?" +"Delete %1","Delete %1" +"Are you sure you wan\'t to delete a %1 record?","Are you sure you wan\'t to delete a %1 record?" View,View px.,px. "No files found","No files found" diff --git a/app/code/Magento/CmsUrlRewrite/composer.json b/app/code/Magento/CmsUrlRewrite/composer.json index 1ae1efd9350a760846e76fcac8939ef574194361..1d2e70c74eb4f9fd0876f92c37b30b5e9387d2cd 100644 --- a/app/code/Magento/CmsUrlRewrite/composer.json +++ b/app/code/Magento/CmsUrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cms-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-cms": "101.1.*", "magento/module-url-rewrite": "100.2.*", diff --git a/app/code/Magento/Config/Model/Config/Backend/Baseurl.php b/app/code/Magento/Config/Model/Config/Backend/Baseurl.php index 09fd99773813117a71b43240eaafc5b96bf967ca..f08c2f8e04434c7a26645669ff3a4c1c8d1840c7 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Baseurl.php +++ b/app/code/Magento/Config/Model/Config/Backend/Baseurl.php @@ -5,6 +5,9 @@ */ namespace Magento\Config\Model\Config\Backend; +use Magento\Framework\Validator\Url as UrlValidator; +use Magento\Framework\App\ObjectManager; + class Baseurl extends \Magento\Framework\App\Config\Value { /** @@ -12,6 +15,11 @@ class Baseurl extends \Magento\Framework\App\Config\Value */ protected $_mergeService; + /** + * @var UrlValidator + */ + private $urlValidator; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -193,8 +201,7 @@ class Baseurl extends \Magento\Framework\App\Config\Value */ private function _isFullyQualifiedUrl($value) { - $url = parse_url($value); - return isset($url['scheme']) && isset($url['host']) && preg_match('/\/$/', $value); + return preg_match('/\/$/', $value) && $this->getUrlValidator()->isValid($value, ['http', 'https']); } /** @@ -216,4 +223,18 @@ class Baseurl extends \Magento\Framework\App\Config\Value } return parent::afterSave(); } + + /** + * Get URL Validator + * + * @deprecated + * @return UrlValidator + */ + private function getUrlValidator() + { + if (!$this->urlValidator) { + $this->urlValidator = ObjectManager::getInstance()->get(UrlValidator::class); + } + return $this->urlValidator; + } } diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 54f25860572d07ab371458b5bafcc1e4e358aab0..ab9c95e1166536ebb71adf36a1b5f1b9aea06b68 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-config", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-store": "100.2.*", "magento/module-cron": "100.2.*", diff --git a/app/code/Magento/ConfigurableImportExport/composer.json b/app/code/Magento/ConfigurableImportExport/composer.json index 6c249134d28626b4c8846fd7491cf25b2e278638..d292e73198435431015dff6bcfe1a69d516debc9 100644 --- a/app/code/Magento/ConfigurableImportExport/composer.json +++ b/app/code/Magento/ConfigurableImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-configurable-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-import-export": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php index 0fdfbd3bfa172ee597414ed3ab4c46bbac2a9549..5575d6f7ef56fa4f7f2fed433299c01eaa4ba98d 100644 --- a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php +++ b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php @@ -32,6 +32,16 @@ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementI */ private $dataObjectHelper; + /** + * @var \Magento\ConfigurableProduct\Helper\Product\Options\Factory; + */ + private $optionsFactory; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory + */ + private $attributeFactory; + /** * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository * @param \Magento\Catalog\Api\Data\ProductInterfaceFactory $productFactory @@ -102,9 +112,28 @@ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementI throw new StateException(__('Product has been already attached')); } + $configurableProductOptions = $product->getExtensionAttributes()->getConfigurableProductOptions(); + if (empty($configurableProductOptions)) { + throw new StateException(__('Parent product does not have configurable product options')); + } + + $attributeIds = []; + foreach ($configurableProductOptions as $configurableProductOption) { + $attributeCode = $configurableProductOption->getProductAttribute()->getAttributeCode(); + if (!$child->getData($attributeCode)) { + throw new StateException(__('Child product does not have attribute value %1', $attributeCode)); + } + $attributeIds[] = $configurableProductOption->getAttributeId(); + } + $configurableOptionData = $this->getConfigurableAttributesData($attributeIds); + + /** @var \Magento\ConfigurableProduct\Helper\Product\Options\Factory $optionFactory */ + $optionFactory = $this->getOptionsFactory(); + $options = $optionFactory->create($configurableOptionData); $childrenIds[] = $child->getId(); + $product->getExtensionAttributes()->setConfigurableProductOptions($options); $product->getExtensionAttributes()->setConfigurableProductLinks($childrenIds); - $product->save(); + $this->productRepository->save($product); return true; } @@ -133,7 +162,75 @@ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementI throw new NoSuchEntityException(__('Requested option doesn\'t exist')); } $product->getExtensionAttributes()->setConfigurableProductLinks($ids); - $product->save(); + $this->productRepository->save($product); return true; } + + /** + * Get Options Factory + * + * @return \Magento\ConfigurableProduct\Helper\Product\Options\Factory + * + * @deprecated + */ + private function getOptionsFactory() + { + if (!$this->optionsFactory) { + $this->optionsFactory = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class); + } + return $this->optionsFactory; + } + + /** + * Get Attribute Factory + * + * @return \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory + * + * @deprecated + */ + private function getAttributeFactory() + { + if (!$this->attributeFactory) { + $this->attributeFactory = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory::class); + } + return $this->attributeFactory; + } + + /** + * Get Configurable Attribute Data + * + * @param int[] $attributeIds + * @return array + */ + private function getConfigurableAttributesData($attributeIds) + { + $configurableAttributesData = []; + $attributeValues = []; + $attributes = $this->getAttributeFactory()->create() + ->getCollection() + ->addFieldToFilter('attribute_id', $attributeIds) + ->getItems(); + foreach ($attributes as $attribute) { + foreach ($attribute->getOptions() as $option) { + if ($option->getValue()) { + $attributeValues[] = [ + 'label' => $option->getLabel(), + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + } + } + $configurableAttributesData[] = + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'values' => $attributeValues, + ]; + } + + return $configurableAttributesData; + } } diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php index 4e6fa8ae5210d77726b393047a7433ca03d69c7d..fbb69798ff94fbdf8caafc7486e564da662cf655 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php @@ -8,6 +8,7 @@ namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice { @@ -166,6 +167,9 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ $this->_prepareConfigurableOptionAggregateTable(); $this->_prepareConfigurableOptionPriceTable(); + $statusAttribute = $this->_getAttribute(ProductInterface::STATUS); + $linkField = $metadata->getLinkField(); + $select = $connection->select()->from( ['i' => $this->_getDefaultFinalPriceTable()], [] @@ -175,7 +179,7 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ ['parent_id' => 'e.entity_id'] )->join( ['l' => $this->getTable('catalog_product_super_link')], - 'l.parent_id = e.' . $metadata->getLinkField(), + 'l.parent_id = e.' . $linkField, ['product_id'] )->columns( ['customer_group_id', 'website_id'], @@ -186,11 +190,21 @@ class Configurable extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\ [] )->where( 'le.required_options=0' + )->join( + ['product_status' => $this->getTable($statusAttribute->getBackend()->getTable())], + sprintf( + 'le.%1$s = product_status.%1$s AND product_status.attribute_id = %2$s', + $linkField, + $statusAttribute->getAttributeId() + ), + [] + )->where( + 'product_status.value=' . ProductStatus::STATUS_ENABLED )->group( - ['parent_id', 'i.customer_group_id', 'i.website_id', 'l.product_id'] + ['e.entity_id', 'i.customer_group_id', 'i.website_id', 'l.product_id'] ); - $priceColumn = $this->_addAttributeToSelect($select, 'price', 'l.product_id', 0, null, true); - $tierPriceColumn = $connection->getCheckSql("MIN(i.tier_price) IS NOT NULL", "i.tier_price", 'NULL'); + $priceColumn = $this->_addAttributeToSelect($select, 'price', 'le.' . $linkField, 0, null, true); + $tierPriceColumn = $connection->getIfNullSql('MIN(i.tier_price)', 'NULL'); $select->columns( ['price' => $priceColumn, 'tier_price' => $tierPriceColumn] diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/LinkManagementTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/LinkManagementTest.php index 6b69888e6dd0924cf6e090bdb1eb0af2daddaab1..5a7886ea3ea640c2f276ffb7eedeaa243ff9e790 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/LinkManagementTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/LinkManagementTest.php @@ -6,8 +6,8 @@ namespace Magento\ConfigurableProduct\Test\Unit\Model; +use Magento\ConfigurableProduct\Model\LinkManagement; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; -use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -146,15 +146,59 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase $configurable = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() + ->setMethods(['getId', 'getExtensionAttributes']) + ->getMock(); + $simple = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['getId', 'getData']) ->getMock(); - $configurable->expects($this->any())->method('getId')->will($this->returnValue(666)); + $extensionAttributesMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductExtension::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getConfigurableProductOptions', 'setConfigurableProductOptions', 'setConfigurableProductLinks' + ]) + ->getMock(); + $optionMock = $this->getMockBuilder(\Magento\ConfigurableProduct\Api\Data\Option::class) + ->disableOriginalConstructor() + ->setMethods(['getProductAttribute', 'getAttributeId']) + ->getMock(); + $productAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeCode']) + ->getMock(); + $optionsFactoryMock = $this->getMockBuilder(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $reflectionClass = new \ReflectionClass(\Magento\ConfigurableProduct\Model\LinkManagement::class); + $optionsFactoryReflectionProperty = $reflectionClass->getProperty('optionsFactory'); + $optionsFactoryReflectionProperty->setAccessible(true); + $optionsFactoryReflectionProperty->setValue($this->object, $optionsFactoryMock); - $simple = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $attributeFactoryMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory::class) ->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); + $attributeFactoryReflectionProperty = $reflectionClass->getProperty('attributeFactory'); + $attributeFactoryReflectionProperty->setAccessible(true); + $attributeFactoryReflectionProperty->setValue($this->object, $attributeFactoryMock); - $simple->expects($this->any())->method('getId')->will($this->returnValue(999)); + $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->setMethods(['getCollection', 'getOptions', 'getId', 'getAttributeCode', 'getStoreLabel']) + ->getMock(); + $attributeOptionMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Option::class) + ->disableOriginalConstructor() + ->setMethods(['getValue', 'getLabel']) + ->getMock(); + + $attributeCollectionMock = $this->getMockBuilder( + \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class + ) + ->disableOriginalConstructor() + ->setMethods(['addFieldToFilter', 'getItems']) + ->getMock(); $this->productRepository->expects($this->at(0))->method('get')->with($productSku)->willReturn($configurable); $this->productRepository->expects($this->at(1))->method('get')->with($childSku)->willReturn($simple); @@ -164,15 +208,30 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase $this->returnValue([0 => [1, 2, 3]]) ); - $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) - ->setMethods(['setConfigurableProductLinks']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); + $configurable->expects($this->any())->method('getId')->will($this->returnValue(666)); + $simple->expects($this->any())->method('getId')->will($this->returnValue(999)); + + $configurable->expects($this->any())->method('getExtensionAttributes')->willReturn($extensionAttributesMock); + $extensionAttributesMock->expects($this->any()) + ->method('getConfigurableProductOptions') + ->willReturn([$optionMock]); + $optionMock->expects($this->any())->method('getProductAttribute')->willReturn($productAttributeMock); + $productAttributeMock->expects($this->any())->method('getAttributeCode')->willReturn('color'); + $simple->expects($this->any())->method('getData')->willReturn('color'); + $optionMock->expects($this->any())->method('getAttributeId')->willReturn('1'); - $configurable->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributes); - $extensionAttributes->expects($this->once())->method('setConfigurableProductLinks')->willReturnSelf(); + $optionsFactoryMock->expects($this->any())->method('create')->willReturn([$optionMock]); + $attributeFactoryMock->expects($this->any())->method('create')->willReturn($attributeMock); + $attributeMock->expects($this->any())->method('getCollection')->willReturn($attributeCollectionMock); + $attributeCollectionMock->expects($this->any())->method('addFieldToFilter')->willReturnSelf(); + $attributeCollectionMock->expects($this->any())->method('getItems')->willReturn([$attributeMock]); - $configurable->expects($this->once())->method('save'); + $attributeMock->expects($this->any())->method('getOptions')->willReturn([$attributeOptionMock]); + + $extensionAttributesMock->expects($this->any())->method('setConfigurableProductOptions'); + $extensionAttributesMock->expects($this->any())->method('setConfigurableProductLinks'); + + $this->productRepository->expects($this->once())->method('save'); $this->assertTrue(true, $this->object->addChild($productSku, $childSku)); } @@ -243,15 +302,13 @@ class LinkManagementTest extends \PHPUnit_Framework_TestCase $productType->expects($this->once())->method('getUsedProducts') ->will($this->returnValue([$option])); - $extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class) + $extensionAttributesMock = $this->getMockBuilder(\Magento\Framework\Api\ExtensionAttributesInterface::class) ->setMethods(['setConfigurableProductLinks']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $product->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributes); - $extensionAttributes->expects($this->once())->method('setConfigurableProductLinks')->willReturnSelf(); - - $product->expects($this->once())->method('save'); + $product->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributesMock); + $this->productRepository->expects($this->once())->method('save'); $this->assertTrue($this->object->removeChild($productSku, $childSku)); } diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 8dda841250ff126d0fed90a03dd4272169266392..755b94a600a099a0ada188d435f3682cf6736e69 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-configurable-product", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", diff --git a/app/code/Magento/Contact/composer.json b/app/code/Magento/Contact/composer.json index b541f1e7a0ed3afe8f8bfb8c25b7f27282bc9372..377e46256c0886c5479cbe256451310bac14b896 100644 --- a/app/code/Magento/Contact/composer.json +++ b/app/code/Magento/Contact/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-contact", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Cookie/composer.json b/app/code/Magento/Cookie/composer.json index 5222975723385a48adf043f695c08e319f11d57b..96a050f2ad20dfa94958fd11f7a24cfb73010642 100644 --- a/app/code/Magento/Cookie/composer.json +++ b/app/code/Magento/Cookie/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cookie", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Cron/composer.json b/app/code/Magento/Cron/composer.json index b1155c028f91971b5508269a06ebef675032e5b5..75a7ce27a2aa110d55398a0588ae4f4907c724c2 100644 --- a/app/code/Magento/Cron/composer.json +++ b/app/code/Magento/Cron/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cron", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/CurrencySymbol/composer.json b/app/code/Magento/CurrencySymbol/composer.json index 2cdfedadf7a38c7c792a27de4498772cc59c692b..29f0bd7435eef99279f54273163f17041d64a351 100644 --- a/app/code/Magento/CurrencySymbol/composer.json +++ b/app/code/Magento/CurrencySymbol/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-currency-symbol", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-page-cache": "100.2.*", diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 77bf7af2a4031ac71488b7644225a6c13dc520f2..59e12d72ded6c6cd4de5aaf323acd5f1ffbbd122 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -84,7 +84,7 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index $result = $metadataForm->compactData($formData); // Re-initialize additional attributes - $formData = array_replace($formData, $result); + $formData = array_replace($result, $formData); // Unset unused attributes $formAttributes = $metadataForm->getAttributes(); diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index 19b361b2c830d763681184b5055f0d477c4641ae..c1ec593e4a7aeec51a53323052aeae62cb7036ff 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -5,10 +5,10 @@ */ namespace Magento\Customer\Controller\Section; +use Magento\Customer\CustomerData\Section\Identifier; use Magento\Customer\CustomerData\SectionPoolInterface; use Magento\Framework\App\Action\Context; use Magento\Framework\Controller\Result\JsonFactory; -use Magento\Framework\Exception\LocalizedException; /** * Customer section controller @@ -22,6 +22,7 @@ class Load extends \Magento\Framework\App\Action\Action /** * @var Identifier + * @deprecated */ protected $sectionIdentifier; @@ -30,26 +31,34 @@ class Load extends \Magento\Framework\App\Action\Action */ protected $sectionPool; + /** + * @var \Magento\Framework\Escaper + */ + private $escaper; + /** * @param Context $context * @param JsonFactory $resultJsonFactory - * @param \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier + * @param Identifier $sectionIdentifier * @param SectionPoolInterface $sectionPool + * @param Escaper $escaper */ public function __construct( Context $context, JsonFactory $resultJsonFactory, - \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier, - SectionPoolInterface $sectionPool + Identifier $sectionIdentifier, + SectionPoolInterface $sectionPool, + \Magento\Framework\Escaper $escaper = null ) { parent::__construct($context); $this->resultJsonFactory = $resultJsonFactory; $this->sectionIdentifier = $sectionIdentifier; $this->sectionPool = $sectionPool; + $this->escaper = $escaper ?: $this->_objectManager->get(\Magento\Framework\Escaper::class); } /** - * @return \Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\Result\Redirect + * @return \Magento\Framework\Controller\Result\Json */ public function execute() { @@ -60,7 +69,7 @@ class Load extends \Magento\Framework\App\Action\Action $sectionNames = $sectionNames ? array_unique(\explode(',', $sectionNames)) : null; $updateSectionId = $this->getRequest()->getParam('update_section_id'); - if ('false' == $updateSectionId) { + if ('false' === $updateSectionId) { $updateSectionId = false; } $response = $this->sectionPool->getSectionsData($sectionNames, (bool)$updateSectionId); @@ -70,7 +79,7 @@ class Load extends \Magento\Framework\App\Action\Action \Zend\Http\AbstractMessage::VERSION_11, 'Bad Request' ); - $response = ['message' => $e->getMessage()]; + $response = ['message' => $this->escaper->escapeHtml($e->getMessage())]; } return $resultJson->setData($response); diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json index 74f0ebc2b5e0026acbdc5061949c10ad04aa8d9c..53e1a270fb3b04a791a368a375ce7e8b1ef62d8d 100644 --- a/app/code/Magento/Customer/composer.json +++ b/app/code/Magento/Customer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-customer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Customer/view/frontend/web/template/authentication-popup.html b/app/code/Magento/Customer/view/frontend/web/template/authentication-popup.html index 1ff64fffe20369a5675afcc573c99b9f926ba30a..9ad2cf5265043b4fd1850514258d99276d500ff3 100644 --- a/app/code/Magento/Customer/view/frontend/web/template/authentication-popup.html +++ b/app/code/Magento/Customer/view/frontend/web/template/authentication-popup.html @@ -78,7 +78,6 @@ <!-- ko foreach: getRegion('additional-login-form-fields') --> <!-- ko template: getTemplate() --><!-- /ko --> <!-- /ko --> - </div> <div class="actions-toolbar"> <input name="context" type="hidden" value="checkout" /> <div class="primary"> diff --git a/app/code/Magento/CustomerImportExport/composer.json b/app/code/Magento/CustomerImportExport/composer.json index 533f40c7688e4aa8269bedfe84f5331a772b0b2a..cd02f6dbfb38b46bda32ebdcb87720124eab37e4 100644 --- a/app/code/Magento/CustomerImportExport/composer.json +++ b/app/code/Magento/CustomerImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-customer-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Deploy/composer.json b/app/code/Magento/Deploy/composer.json index 856e0d8b3e542a37cdd7fcf7554b9e4e158de245..ed0b8520ca645afa8af2506616f9dccde6965f08 100644 --- a/app/code/Magento/Deploy/composer.json +++ b/app/code/Magento/Deploy/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-deploy", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-store": "100.2.*", "magento/module-require-js": "100.2.*", diff --git a/app/code/Magento/Developer/composer.json b/app/code/Magento/Developer/composer.json index 23f6d20c2aab74dc0f23c03f5727aa99379fbf54..73fd1c17750c6749bd0f89cd9fe55939fbe2d419 100644 --- a/app/code/Magento/Developer/composer.json +++ b/app/code/Magento/Developer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-developer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*", "magento/module-config": "100.2.*" diff --git a/app/code/Magento/Dhl/composer.json b/app/code/Magento/Dhl/composer.json index e88cd535952d628628bbc984137430d5370a6ecd..15ca7882331a145ba1993ded371bd084bc8c83b3 100644 --- a/app/code/Magento/Dhl/composer.json +++ b/app/code/Magento/Dhl/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-dhl", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", diff --git a/app/code/Magento/Directory/Model/Country/Postcode/Config/Reader.php b/app/code/Magento/Directory/Model/Country/Postcode/Config/Reader.php index 9ea0babbfd9982c3e9c5657e218668c384b47501..ed36ad3ace22b232c8071bbb195184066a2ebd50 100644 --- a/app/code/Magento/Directory/Model/Country/Postcode/Config/Reader.php +++ b/app/code/Magento/Directory/Model/Country/Postcode/Config/Reader.php @@ -12,7 +12,10 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem * * @var array */ - protected $_idAttributes = ['/config/zip' => 'countryCode']; + protected $_idAttributes = [ + '/config/zip' => 'countryCode', + '/config/zip/codes/code' => 'id', + ]; /** * Construct the FileSystem Reader Class diff --git a/app/code/Magento/Directory/composer.json b/app/code/Magento/Directory/composer.json index 9b3b253c26a35ed14c4de51c77624a10750ce5f2..82d8bca34da840e57511bb3912c9b531297d9e86 100644 --- a/app/code/Magento/Directory/composer.json +++ b/app/code/Magento/Directory/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-directory", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Downloadable/composer.json b/app/code/Magento/Downloadable/composer.json index 8d8814740ccd13248b1e7ab903052ca429567ead..ea8eef6436ef9174e9643c742684f29d1129799f 100644 --- a/app/code/Magento/Downloadable/composer.json +++ b/app/code/Magento/Downloadable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-downloadable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/DownloadableImportExport/composer.json b/app/code/Magento/DownloadableImportExport/composer.json index c99a0d6d05b822e792e24a27a69f68f240f8b002..4f95711b279d0f907934fc4cf2bc4b8056ff4817 100644 --- a/app/code/Magento/DownloadableImportExport/composer.json +++ b/app/code/Magento/DownloadableImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-downloadable-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/AttributeTest.php index bcad34654b98aa2d6d3844d1a780d585713c8d86..c18af6380a3b71ca7adfd6af5146f853926b419d 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/Entity/AttributeTest.php @@ -29,7 +29,7 @@ class AttributeTest extends \PHPUnit_Framework_TestCase { $this->contextMock = $this->getMock( \Magento\Framework\Model\Context::class, - ['getCacheManager', 'getEventDispatcher', 'getLogger', 'getAppState', 'getActionValidator'], + [], [], '', false diff --git a/app/code/Magento/Eav/composer.json b/app/code/Magento/Eav/composer.json index 16e62a6434a26aa358a3491d697ce7cd3bb54ad7..9863a89fff5d3d64ccbf8eaa4b3f5691168e0155 100644 --- a/app/code/Magento/Eav/composer.json +++ b/app/code/Magento/Eav/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-eav", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 33d04b921278d3c57ce0107792c6deef458c73ab..648c984de0bbb6e93d16f84110075dc36909cfda 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-email", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-theme": "100.2.*", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/EncryptionKey/composer.json b/app/code/Magento/EncryptionKey/composer.json index 6d7ff5558734ab1952b59b2e02db4cc5093f9984..11b1c034a76a792cd782be06db484e494764553f 100644 --- a/app/code/Magento/EncryptionKey/composer.json +++ b/app/code/Magento/EncryptionKey/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-encryption-key", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/Fedex/composer.json b/app/code/Magento/Fedex/composer.json index d27d5c1c60d407ce4d1d5b3781b0209c5955a0d4..8d487485c0d210824949657ab7444ba937ef9996 100644 --- a/app/code/Magento/Fedex/composer.json +++ b/app/code/Magento/Fedex/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-fedex", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/GiftMessage/composer.json b/app/code/Magento/GiftMessage/composer.json index 04c620dc36db5829f9c8c6aa8a6aec620d211e95..067bf16017b231977183491ff6a4e8cdf91dfe68 100644 --- a/app/code/Magento/GiftMessage/composer.json +++ b/app/code/Magento/GiftMessage/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-gift-message", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/GoogleAdwords/composer.json b/app/code/Magento/GoogleAdwords/composer.json index ad3df5c0b8b2621f395acbfe81bfde3c6b7bae27..cacbbe2088c778ff4a3777f129aea263cf9dee79 100644 --- a/app/code/Magento/GoogleAdwords/composer.json +++ b/app/code/Magento/GoogleAdwords/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-adwords", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/GoogleAnalytics/composer.json b/app/code/Magento/GoogleAnalytics/composer.json index 4cccc7bd0cbff69369ae852138045ed06a0dbf4a..8653e93d7a98338160895c65011cdc2f7471270f 100644 --- a/app/code/Magento/GoogleAnalytics/composer.json +++ b/app/code/Magento/GoogleAnalytics/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-analytics", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/GoogleOptimizer/composer.json b/app/code/Magento/GoogleOptimizer/composer.json index 20a48fc78060b53fb144aa5da405b343ce696063..8c61e9f068ea122b74a7dea02ed5c110d26629b6 100644 --- a/app/code/Magento/GoogleOptimizer/composer.json +++ b/app/code/Magento/GoogleOptimizer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-optimizer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-google-analytics": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/GroupedImportExport/composer.json b/app/code/Magento/GroupedImportExport/composer.json index b036a8d5d528bd3959ea66b294d7b51736f7ba2c..50c061e5b56da30c1faf2bfab9439460be6908a3 100644 --- a/app/code/Magento/GroupedImportExport/composer.json +++ b/app/code/Magento/GroupedImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-grouped-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/GroupedProduct/composer.json b/app/code/Magento/GroupedProduct/composer.json index 69aed214284454722708703068de37e3134393f0..0f207eb414789becda0c4e57846f0a48ced13d7a 100644 --- a/app/code/Magento/GroupedProduct/composer.json +++ b/app/code/Magento/GroupedProduct/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-grouped-product", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 35a088cbd9c91907c3f9f2c434f16cfb86ed57a8..5e403d2180652a6f3565bf03b573abf75310f19f 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Indexer/composer.json b/app/code/Magento/Indexer/composer.json index cbaab12f6ee285ac1207f8c01d5ed5ff5af42c07..e80d8a9f7bc90d63dfbbced39431571d73fdc937 100644 --- a/app/code/Magento/Indexer/composer.json +++ b/app/code/Magento/Indexer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-indexer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Integration/composer.json b/app/code/Magento/Integration/composer.json index 028e56472268abc0e0fcc612107a5bd8072da806..f76d60c957c87d828cc773e96a5edf10266293c9 100644 --- a/app/code/Magento/Integration/composer.json +++ b/app/code/Magento/Integration/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-integration", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/LayeredNavigation/composer.json b/app/code/Magento/LayeredNavigation/composer.json index 99299c3e4a32cccd465af4131bc549b033142545..d1d82781bb5c881cceaaf0b8ccdf85fa02c54e86 100644 --- a/app/code/Magento/LayeredNavigation/composer.json +++ b/app/code/Magento/LayeredNavigation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-layered-navigation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-catalog": "101.1.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/Marketplace/composer.json b/app/code/Magento/Marketplace/composer.json index 2916ff94544bcc46cd59a9e48e9385e89ea6283b..ed79a80bc0c35d81efa3f27867b33846e3a19007 100644 --- a/app/code/Magento/Marketplace/composer.json +++ b/app/code/Magento/Marketplace/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-marketplace", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-backend": "100.2.*" }, diff --git a/app/code/Magento/MediaStorage/composer.json b/app/code/Magento/MediaStorage/composer.json index fa7858d5b0aff32de7d37f2756aca1891cb15cf7..141e1c19c532cc67f8152459de7e12d50fb0b9d8 100644 --- a/app/code/Magento/MediaStorage/composer.json +++ b/app/code/Magento/MediaStorage/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-media-storage", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-config": "100.2.*", diff --git a/app/code/Magento/Msrp/composer.json b/app/code/Magento/Msrp/composer.json index b86374e6be2779d14fd3d2b3e8a4d5b46b1b881f..422aac99869919aa08c31e7ac4edd39d9e0a6f2e 100644 --- a/app/code/Magento/Msrp/composer.json +++ b/app/code/Magento/Msrp/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-msrp", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-downloadable": "100.2.*", diff --git a/app/code/Magento/Multishipping/composer.json b/app/code/Magento/Multishipping/composer.json index 8d9ac91cd05a07b78c19588cbcd34ce3a110cc9f..970c7ea0569a28d226f8f50c419b3d5ee998a17f 100644 --- a/app/code/Magento/Multishipping/composer.json +++ b/app/code/Magento/Multishipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-multishipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/NewRelicReporting/composer.json b/app/code/Magento/NewRelicReporting/composer.json index 9faa17d454e5072f7dc9ec5473057bfad1533dce..097de6bea926c60644bedefec56adde409f5aa04 100644 --- a/app/code/Magento/NewRelicReporting/composer.json +++ b/app/code/Magento/NewRelicReporting/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-new-relic-reporting", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Newsletter/composer.json b/app/code/Magento/Newsletter/composer.json index 06a278aef6d48692c856f56daf213e4bab162db8..ab5cdd1510a35285d42698ee999737a74b699d54 100644 --- a/app/code/Magento/Newsletter/composer.json +++ b/app/code/Magento/Newsletter/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-newsletter", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-widget": "100.2.*", diff --git a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php index 84b6e554d4cfb193e1418f3d9460f9fcc44f6a7d..8616c186555e3e71db0a13a13c648b5c20ff3e0c 100644 --- a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php +++ b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php @@ -49,20 +49,13 @@ class Checkmo extends \Magento\Payment\Block\Info } /** - * Enter description here... - * + * @deprecated * @return $this */ protected function _convertAdditionalData() { - $details = @unserialize($this->getInfo()->getAdditionalData()); - if (is_array($details)) { - $this->_payableTo = isset($details['payable_to']) ? (string)$details['payable_to'] : ''; - $this->_mailingAddress = isset($details['mailing_address']) ? (string)$details['mailing_address'] : ''; - } else { - $this->_payableTo = ''; - $this->_mailingAddress = ''; - } + $this->_payableTo = $this->getInfo()->getAdditionalInformation('payable_to'); + $this->_mailingAddress = $this->getInfo()->getAdditionalInformation('mailing_address'); return $this; } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index 848cfbae52b25a0b4b202b296f43ae4b79c5c19a..27209fc8d3538a1465e985929ef6adb52408de93 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -5,81 +5,118 @@ */ namespace Magento\OfflinePayments\Test\Unit\Block\Info; +use Magento\Framework\View\Element\Template\Context; +use Magento\OfflinePayments\Block\Info\Checkmo; +use Magento\Payment\Model\Info; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * CheckmoTest contains list of test for block methods testing + */ class CheckmoTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\OfflinePayments\Block\Info\Checkmo + * @var Info|MockObject + */ + private $info; + + /** + * @var Checkmo */ - protected $_model; + private $block; + /** + * @inheritdoc + */ protected function setUp() { - $context = $this->getMock(\Magento\Framework\View\Element\Template\Context::class, [], [], '', false); - $this->_model = new \Magento\OfflinePayments\Block\Info\Checkmo($context); + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->info = $this->getMockBuilder(Info::class) + ->disableOriginalConstructor() + ->setMethods(['getAdditionalInformation']) + ->getMock(); + + $this->block = new Checkmo($context); } /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo + * @param array $details + * @param string|null $expected * @dataProvider getPayableToDataProvider */ public function testGetPayableTo($details, $expected) { - $info = $this->getMock(\Magento\Payment\Model\Info::class, ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize($details)); - $this->_model->setData('info', $info); + $this->info->expects(static::at(0)) + ->method('getAdditionalInformation') + ->with('payable_to') + ->willReturn($details); + $this->block->setData('info', $this->info); - $this->assertEquals($expected, $this->_model->getPayableTo()); + static::assertEquals($expected, $this->block->getPayableTo()); } /** + * Get list of variations for payable configuration option testing * @return array */ public function getPayableToDataProvider() { return [ - [['payable_to' => 'payable'], 'payable'], - ['', ''] + ['payable_to' => 'payable', 'payable'], + ['', null] ]; } /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + * @param array $details + * @param string|null $expected * @dataProvider getMailingAddressDataProvider */ public function testGetMailingAddress($details, $expected) { - $info = $this->getMock(\Magento\Payment\Model\Info::class, ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize($details)); - $this->_model->setData('info', $info); + $this->info->expects(static::at(1)) + ->method('getAdditionalInformation') + ->with('mailing_address') + ->willReturn($details); + $this->block->setData('info', $this->info); - $this->assertEquals($expected, $this->_model->getMailingAddress()); + static::assertEquals($expected, $this->block->getMailingAddress()); } /** + * Get list of variations for mailing address testing * @return array */ public function getMailingAddressDataProvider() { return [ - [['mailing_address' => 'blah@blah.com'], 'blah@blah.com'], - ['', ''] + ['mailing_address' => 'blah@blah.com', 'blah@blah.com'], + ['mailing_address' => '', null] ]; } + /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + */ public function testConvertAdditionalDataIsNeverCalled() { - $info = $this->getMock(\Magento\Payment\Model\Info::class, ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize(['mailing_address' => 'blah@blah.com'])); - $this->_model->setData('info', $info); + $mailingAddress = 'blah@blah.com'; + $this->info->expects(static::at(1)) + ->method('getAdditionalInformation') + ->with('mailing_address') + ->willReturn($mailingAddress); + $this->block->setData('info', $this->info); // First we set the property $this->_mailingAddress - $this->_model->getMailingAddress(); + $this->block->getMailingAddress(); // And now we get already setted property $this->_mailingAddress - $this->assertEquals('blah@blah.com', $this->_model->getMailingAddress()); + static::assertEquals($mailingAddress, $this->block->getMailingAddress()); } } diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json index 52deb08ba3d904a824faa762391ddf67ae29b026..cf728aaa1c5f6a7ea7c36163b021cb378d7df7f0 100644 --- a/app/code/Magento/OfflinePayments/composer.json +++ b/app/code/Magento/OfflinePayments/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-offline-payments", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-checkout": "100.2.*", "magento/module-payment": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml index d5bff77e002d17d5ad8a80349ea646aee0135fa4..8c5cfd50cc110f2f8c61414c14ed2d48965929d6 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml @@ -10,7 +10,7 @@ */ ?> <?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?> -<?php if ($block->getInfo()->getAdditionalData()): ?> +<?php if ($block->getInfo()->getAdditionalInformation()): ?> <?php if ($block->getPayableTo()): ?> <br /><?php echo $block->escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> <?php endif; ?> diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml index 6195cdbd776548c229a6d1dfca03c8f4c1b2a61f..5587ac239d37e84a21348cfd994d9d0b73f0ba97 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml @@ -11,7 +11,7 @@ ?> <?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?> {{pdf_row_separator}} -<?php if ($block->getInfo()->getAdditionalData()): ?> +<?php if ($block->getInfo()->getAdditionalInformation()): ?> {{pdf_row_separator}} <?php if ($block->getPayableTo()): ?> <?php echo $block->escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml index f0dbff1add32b2fd7d49e3b6ca5f14c4038fc2ca..3c0b6bb23086a67b48de1134cdb245daffeab045 100644 --- a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml @@ -11,7 +11,7 @@ ?> <dl class="payment-method checkmemo"> <dt class="title"><?php echo $block->escapeHtml($block->getMethod()->getTitle()) ?></dt> - <?php if ($block->getInfo()->getAdditionalData()): ?> + <?php if ($block->getInfo()->getAdditionalInformation()): ?> <?php if ($block->getPayableTo()): ?> <dd class="content"> <strong><?php echo $block->escapeHtml(__('Make Check payable to')) ?></strong> diff --git a/app/code/Magento/OfflineShipping/composer.json b/app/code/Magento/OfflineShipping/composer.json index 3c959b4dec766fe09cf81df46571051b714ee4e1..b429376c6cd7adc0a70b133a633b4edf068d684e 100644 --- a/app/code/Magento/OfflineShipping/composer.json +++ b/app/code/Magento/OfflineShipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-offline-shipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index 50ed4cdc9417833683a04e3a715ff828f04b9674..4a4f550588f5366668f4ceee0ff895964246172b 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -52,6 +52,7 @@ <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <frontend_class>shipping-skip-hide</frontend_class> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> @@ -146,6 +147,7 @@ <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <frontend_class>shipping-skip-hide</frontend_class> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> diff --git a/app/code/Magento/PageCache/composer.json b/app/code/Magento/PageCache/composer.json index 5c29e4fc3c97081626ae4a77243ccd6ac9843d38..f18eb7bbe94edd31a48c4796dc41b21cdef38a8b 100644 --- a/app/code/Magento/PageCache/composer.json +++ b/app/code/Magento/PageCache/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-page-cache", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Payment/composer.json b/app/code/Magento/Payment/composer.json index 35ff326d916bd43f8ae2366d9b50f5b77fe46dc8..c8d7254ce9e002ced1ecfb1067d821c63dbeb5be 100644 --- a/app/code/Magento/Payment/composer.json +++ b/app/code/Magento/Payment/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-payment", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/Paypal/composer.json b/app/code/Magento/Paypal/composer.json index bddc5d91a94587f4d26532246d8c83f8de6ba7f7..7503db18f008ca76252f8687df6d52f88dc0e02d 100644 --- a/app/code/Magento/Paypal/composer.json +++ b/app/code/Magento/Paypal/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-paypal", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml index 1876f07830ba7b55df25230bc256b829f65b2fc3..90cb60186e34e3730ae6d068b34e4002add00a6c 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml @@ -23,7 +23,8 @@ <label>Express Checkout</label> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> <field id="business_account" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="5"> - <label>Email Associated with PayPal Merchant Account</label> + <label>Email Associated with PayPal Merchant Account (Optional)</label> + <frontend_class>not-required</frontend_class> <comment> <![CDATA[<a href="http://www.magentocommerce.com/paypal">Start accepting payments via PayPal!</a>]]> </comment> diff --git a/app/code/Magento/Persistent/composer.json b/app/code/Magento/Persistent/composer.json index 25cc9d76acd768fe970363628abad60945e418d9..61f77a104c016d23f3734433efd8646bd21b42f4 100644 --- a/app/code/Magento/Persistent/composer.json +++ b/app/code/Magento/Persistent/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-persistent", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/ProductAlert/composer.json b/app/code/Magento/ProductAlert/composer.json index a43a52209740318e0f5adcc8bea03a8860c04037..9c63c6958a465852ea3148cab659102880b07630 100644 --- a/app/code/Magento/ProductAlert/composer.json +++ b/app/code/Magento/ProductAlert/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-product-alert", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php index f0edccbaf812acbf8495d97817515395635af409..7a087f769877160a316635baf9017b8b37ebbd38 100644 --- a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php +++ b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php @@ -6,6 +6,7 @@ namespace Magento\ProductVideo\Controller\Adminhtml\Product\Gallery; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\File\Uploader; /** @@ -43,6 +44,13 @@ class RetrieveImage extends \Magento\Backend\App\Action */ protected $fileUtility; + /** + * URI validator + * + * @var \Magento\Framework\Validator\ValidatorInterface + */ + private $protocolValidator; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory @@ -51,6 +59,7 @@ class RetrieveImage extends \Magento\Backend\App\Action * @param \Magento\Framework\Image\AdapterFactory $imageAdapterFactory * @param \Magento\Framework\HTTP\Adapter\Curl $curl * @param \Magento\MediaStorage\Model\ResourceModel\File\Storage\File $fileUtility + * @param \Magento\Framework\Validator\ValidatorInterface $protocolValidator */ public function __construct( \Magento\Backend\App\Action\Context $context, @@ -59,7 +68,8 @@ class RetrieveImage extends \Magento\Backend\App\Action \Magento\Framework\Filesystem $fileSystem, \Magento\Framework\Image\AdapterFactory $imageAdapterFactory, \Magento\Framework\HTTP\Adapter\Curl $curl, - \Magento\MediaStorage\Model\ResourceModel\File\Storage\File $fileUtility + \Magento\MediaStorage\Model\ResourceModel\File\Storage\File $fileUtility, + \Magento\Framework\Validator\ValidatorInterface $protocolValidator = null ) { parent::__construct($context); $this->resultRawFactory = $resultRawFactory; @@ -68,6 +78,10 @@ class RetrieveImage extends \Magento\Backend\App\Action $this->imageAdapter = $imageAdapterFactory->create(); $this->curl = $curl; $this->fileUtility = $fileUtility; + + $this->protocolValidator = $protocolValidator ?: + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Validator\ValidatorInterface::class); } /** @@ -78,15 +92,15 @@ class RetrieveImage extends \Magento\Backend\App\Action $baseTmpMediaPath = $this->mediaConfig->getBaseTmpMediaPath(); try { $remoteFileUrl = $this->getRequest()->getParam('remote_image'); - $originalFileName = basename($remoteFileUrl); - $localFileName = Uploader::getCorrectFileName($originalFileName); + $this->validateRemoteFile($remoteFileUrl); + $localFileName = Uploader::getCorrectFileName(basename($remoteFileUrl)); $localTmpFileName = Uploader::getDispretionPath($localFileName) . DIRECTORY_SEPARATOR . $localFileName; - $localFileMediaPath = $baseTmpMediaPath . ($localTmpFileName); - $localUniqueFileMediaPath = $this->appendNewFileName($localFileMediaPath); - $this->retrieveRemoteImage($remoteFileUrl, $localUniqueFileMediaPath); - $localFileFullPath = $this->appendAbsoluteFileSystemPath($localUniqueFileMediaPath); + $localFilePath = $baseTmpMediaPath . ($localTmpFileName); + $localUniqFilePath = $this->appendNewFileName($localFilePath); + $this->retrieveRemoteImage($remoteFileUrl, $localUniqFilePath); + $localFileFullPath = $this->appendAbsoluteFileSystemPath($localUniqFilePath); $this->imageAdapter->validateUploadFile($localFileFullPath); - $result = $this->appendResultSaveRemoteImage($localUniqueFileMediaPath); + $result = $this->appendResultSaveRemoteImage($localUniqFilePath); } catch (\Exception $e) { $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()]; } @@ -98,6 +112,25 @@ class RetrieveImage extends \Magento\Backend\App\Action return $response; } + /** + * Validate remote file + * + * @param string $remoteFileUrl + * @throws LocalizedException + * + * @return $this + */ + private function validateRemoteFile($remoteFileUrl) + { + if (!$this->protocolValidator->isValid($remoteFileUrl)) { + throw new LocalizedException( + __("Protocol isn't allowed") + ); + } + + return $this; + } + /** * @param string $fileName * @return mixed @@ -116,6 +149,8 @@ class RetrieveImage extends \Magento\Backend\App\Action } /** + * Trying to get remote image to save it locally + * * @param string $fileUrl * @param string $localFilePath * @return void @@ -127,7 +162,7 @@ class RetrieveImage extends \Magento\Backend\App\Action $this->curl->write('GET', $fileUrl); $image = $this->curl->read(); if (empty($image)) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('Could not get preview image information. Please check your connection and try again.') ); } diff --git a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php index 79d83f38421bba1a2e582efc3bc90f261c8b79dc..64149d529e5ca8f993e300594279a7e0842f9b8b 100644 --- a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php +++ b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php @@ -73,17 +73,11 @@ class RetrieveImageTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->contextMock = $this->getMock(\Magento\Backend\App\Action\Context::class, [], [], '', false); $this->rawFactoryMock = $this->getMock(\Magento\Framework\Controller\Result\RawFactory::class, ['create'], [], '', false); - $response = - $this->getMock( - \Magento\Framework\Controller\Result\Raw::class, - [], - [], - '', - false - ); + $response = new \Magento\Framework\DataObject(); $this->rawFactoryMock->expects($this->once())->method('create')->willReturn($response); $this->configMock = $this->getMock(\Magento\Catalog\Model\Product\Media\Config::class, [], [], '', false); $this->filesystemMock = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); @@ -101,11 +95,15 @@ class RetrieveImageTest extends \PHPUnit_Framework_TestCase $this->adapterFactoryMock->expects($this->once())->method('create')->willReturn($this->abstractAdapter); $this->curlMock = $this->getMock(\Magento\Framework\HTTP\Adapter\Curl::class, [], [], '', false); $this->storageFileMock = - $this->getMock(\Magento\MediaStorage\Model\ResourceModel\File\Storage\File::class, [], [], '', false); + $this->getMock(\Magento\MediaStorage\Model\ResourceModel\File\Storage\File::class, [], [], '', false); $this->request = $this->getMock(\Magento\Framework\App\RequestInterface::class); $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); - - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $managerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMockForAbstractClass(); + $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); + $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($managerMock); $this->image = $objectManager->getObject( \Magento\ProductVideo\Controller\Adminhtml\Product\Gallery\RetrieveImage::class, @@ -117,6 +115,7 @@ class RetrieveImageTest extends \PHPUnit_Framework_TestCase 'imageAdapterFactory' => $this->adapterFactoryMock, 'curl' => $this->curlMock, 'fileUtility' => $this->storageFileMock, + 'protocolValidator' => new \Magento\Framework\Validator\AllowedProtocols(), ] ); } diff --git a/app/code/Magento/ProductVideo/composer.json b/app/code/Magento/ProductVideo/composer.json index 362fd006d2690c9f9b60b7be3abb71d5ce0b4da7..08d21e4c2abbfb167aed2ca24c0ba012d6b2e8c8 100644 --- a/app/code/Magento/ProductVideo/composer.json +++ b/app/code/Magento/ProductVideo/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-product-video", "description": "Add Video to Products", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-backend": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/ProductVideo/etc/di.xml b/app/code/Magento/ProductVideo/etc/di.xml index 09c15d8fccecc64d3dd183e7940aa26b6819d6df..7242a9d48ce1e3c0da66a3f4d5953cda705132cd 100644 --- a/app/code/Magento/ProductVideo/etc/di.xml +++ b/app/code/Magento/ProductVideo/etc/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Framework\Api\Data\VideoContentInterface" type="Magento\ProductVideo\Model\Product\Attribute\Media\VideoEntry" /> + <preference for="Magento\Framework\Validator\ValidatorInterface" type="Magento\Framework\Validator\AllowedProtocols" /> <type name="Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool"> <arguments> <argument name="mediaGalleryEntryConvertersCollection" xsi:type="array"> diff --git a/app/code/Magento/ProductVideo/i18n/en_US.csv b/app/code/Magento/ProductVideo/i18n/en_US.csv index 4cfabe1592dd2d0ce5c8072f0bdaafbc847689e6..64a08075c19548bc092f3918e7dabf606dd2052e 100644 --- a/app/code/Magento/ProductVideo/i18n/en_US.csv +++ b/app/code/Magento/ProductVideo/i18n/en_US.csv @@ -40,3 +40,4 @@ Delete,Delete "Show related video","Show related video" "Auto restart video","Auto restart video" "Images And Videos","Images And Videos" +"Protocol isn't allowed", "Protocol isn't allowed" diff --git a/app/code/Magento/Quote/composer.json b/app/code/Magento/Quote/composer.json index 79c3a1aaa9d907cea1c505ece12a22e4ee3986f2..76063b177d46c2eb93626dea77a346aaaeb5c21d 100644 --- a/app/code/Magento/Quote/composer.json +++ b/app/code/Magento/Quote/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-quote", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Reports/composer.json b/app/code/Magento/Reports/composer.json index 0bb989cb54ac8cb84e3da943a231634b6707f256..9c76abd07b9b35276f1f923138c9dd47031c289d 100644 --- a/app/code/Magento/Reports/composer.json +++ b/app/code/Magento/Reports/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-reports", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/RequireJs/composer.json b/app/code/Magento/RequireJs/composer.json index 8c9fed33ab730dd2431b222b2edd04aa2252e12f..68428caa867bf7108fc466ebfd1fa2829a2b75fa 100644 --- a/app/code/Magento/RequireJs/composer.json +++ b/app/code/Magento/RequireJs/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-require-js", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Review/composer.json b/app/code/Magento/Review/composer.json index 09f5529feabdd97512839be2dacd00f791083c3c..3ec82943476088c3aa307de1b5a934a3cac8ca0d 100644 --- a/app/code/Magento/Review/composer.json +++ b/app/code/Magento/Review/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-review", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Review/view/frontend/templates/form.phtml b/app/code/Magento/Review/view/frontend/templates/form.phtml index 7c89fd8740c5607ec83f0d0a4dda761de50d97ed..bcac1dbca059833749faf9db4d759f8a06a6f24a 100644 --- a/app/code/Magento/Review/view/frontend/templates/form.phtml +++ b/app/code/Magento/Review/view/frontend/templates/form.phtml @@ -20,7 +20,7 @@ <?php if ($block->getRatings() && $block->getRatings()->getSize()): ?> <span id="input-message-box"></span> <fieldset class="field required review-field-ratings"> - <legend class="label"><span><?php echo $block->escapeHtml(__('Your Rating')) ?><span></legend><br/> + <legend class="label"><span><?php echo $block->escapeHtml(__('Your Rating')) ?></span></legend><br/> <div class="control"> <div class="nested" id="product-review-table"> <?php foreach ($block->getRatings() as $_rating): ?> diff --git a/app/code/Magento/Rss/composer.json b/app/code/Magento/Rss/composer.json index d35f0ee6ebecbf75af87566d776f172ab3d22c0d..fedd0ffeaacfe9577ea6e5971411540d4925fe4c 100644 --- a/app/code/Magento/Rss/composer.json +++ b/app/code/Magento/Rss/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-rss", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/Rule/composer.json b/app/code/Magento/Rule/composer.json index 95b8e6ffec2a682895b3459ca7bdcc7b4f001669..31ee3254d99b98a10ce9973e3a8a4adb1c5818da 100644 --- a/app/code/Magento/Rule/composer.json +++ b/app/code/Magento/Rule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index 09d1a9d6b7dd41ea95a09b26c0f72276327fe245..6b5638455a1c5986fb487f7f9c948d9ddf19aad3 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml index 775c7214c2f104771c79d5fd299229187595b167..20cfcdeadf22a0f18d81b262134058640c04877f 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml @@ -38,10 +38,10 @@ $orderStoreDate = $block->formatDate( <div class="admin__page-section-item-title"> <span class="title"> <?php if ($block->getNoUseOrderLink()): ?> - <?php /* @escapeNotVerified */ echo __('Order # %1', $_order->getRealOrderId()) ?> (<span><?php /* @escapeNotVerified */ echo $_email ?></span>) + <?php /* @escapeNotVerified */ echo __('Order # %1', $_order->getRealOrderId()) ?> (<span><?php echo $block->escapeHtml($_email) ?></span>) <?php else: ?> <a href="<?php /* @escapeNotVerified */ echo $block->getViewUrl($_order->getId()) ?>"><?php /* @escapeNotVerified */ echo __('Order # %1', $_order->getRealOrderId()) ?></a> - <span>(<?php /* @escapeNotVerified */ echo $_email ?>)</span> + <span>(<?php echo $block->escapeHtml($_email) ?>)</span> <?php endif; ?> </span> </div> @@ -49,7 +49,7 @@ $orderStoreDate = $block->formatDate( <table class="admin__table-secondary order-information-table"> <tr> <th><?php /* @escapeNotVerified */ echo __('Order Date') ?></th> - <td><?php /* @escapeNotVerified */ echo $orderAdminDate ?></td> + <td><?php echo $block->escapeHtml($orderAdminDate) ?></td> </tr> <?php if ($orderAdminDate != $orderStoreDate):?> <tr> @@ -57,12 +57,12 @@ $orderStoreDate = $block->formatDate( 'Order Date (%1)', $block->getTimezoneForStore($_order->getStore()) ) ?></th> - <td><?php /* @escapeNotVerified */ echo $orderStoreDate ?></td> + <td><?php echo $block->escapeHtml($orderStoreDate) ?></td> </tr> <?php endif;?> <tr> <th><?php /* @escapeNotVerified */ echo __('Order Status') ?></th> - <td><span id="order_status"><?php /* @escapeNotVerified */ echo $_order->getStatusLabel() ?></span></td> + <td><span id="order_status"><?php echo $block->escapeHtml($_order->getStatusLabel()) ?></span></td> </tr> <?php echo $block->getChildHtml(); ?> <?php if ($block->isSingleStoreMode() == false):?> @@ -136,13 +136,13 @@ $orderStoreDate = $block->formatDate( <?php if ($_groupName = $block->getCustomerGroupName()) : ?> <tr> <th><?php /* @escapeNotVerified */ echo __('Customer Group') ?></th> - <td><?php /* @escapeNotVerified */ echo $_groupName ?></td> + <td><?php echo $block->escapeHtml($_groupName) ?></td> </tr> <?php endif; ?> <?php foreach ($block->getCustomerAccountData() as $data):?> <tr> - <th><?php /* @escapeNotVerified */ echo $data['label'] ?></th> - <td><?php /* @escapeNotVerified */ echo $data['value'] ?></td> + <th><?php echo $block->escapeHtml($data['label']) ?></th> + <td><?php echo $block->escapeHtml($data['value']) ?></td> </tr> <?php endforeach;?> </table> diff --git a/app/code/Magento/SalesInventory/composer.json b/app/code/Magento/SalesInventory/composer.json index ff72ce7f0022690682b356235c506512e65497b9..d7f9075cdd310141bab6b25869f33d6229f513dc 100644 --- a/app/code/Magento/SalesInventory/composer.json +++ b/app/code/Magento/SalesInventory/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales-inventory", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog-inventory": "100.2.*", "magento/module-sales": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php index 57477b5ce0623f57bfc048839cc8bdaee8d40593..efa45512acc8320cd878c97b92c5912c7713f882 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php @@ -26,8 +26,13 @@ class Save extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); + + $filterValues = ['from_date' => $this->_dateFilter]; + if ($this->getRequest()->getParam('to_date')) { + $filterValues['to_date'] = $this->_dateFilter; + } $inputFilter = new \Zend_Filter_Input( - ['from_date' => $this->_dateFilter, 'to_date' => $this->_dateFilter], + $filterValues, [], $data ); diff --git a/app/code/Magento/SalesRule/composer.json b/app/code/Magento/SalesRule/composer.json index fabef581762a84cac8844bf7e7099b0bb8b6b6c0..8479acad47246a1f91bc6b098d88cfa6f6027f76 100644 --- a/app/code/Magento/SalesRule/composer.json +++ b/app/code/Magento/SalesRule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-rule": "100.2.*", diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js index ed5744feb6ce84df31a5a59b42f520bb97316792..d1e294c9a8f0c1dc4edf82e52cbe509b09da8fbe 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js @@ -17,16 +17,20 @@ define( 'mage/storage', 'Magento_Checkout/js/action/get-payment-information', 'Magento_Checkout/js/model/totals', - 'mage/translate' + 'mage/translate', + 'Magento_Checkout/js/model/full-screen-loader' ], - function ($, quote, urlManager, errorProcessor, messageContainer, storage, getPaymentInformationAction, totals, $t) { + function ($, quote, urlManager, errorProcessor, messageContainer, storage, getPaymentInformationAction, totals, $t, + fullScreenLoader) { 'use strict'; - return function (isApplied, isLoading) { + return function (isApplied) { var quoteId = quote.getQuoteId(), url = urlManager.getCancelCouponUrl(quoteId), message = $t('Your coupon was successfully removed.'); + messageContainer.clear(); + fullScreenLoader.startLoader(); return storage.delete( url, @@ -39,6 +43,7 @@ define( $.when(deferred).done(function () { isApplied(false); totals.isLoading(false); + fullScreenLoader.stopLoader(); }); messageContainer.addSuccessMessage({ 'message': message @@ -47,12 +52,9 @@ define( ).fail( function (response) { totals.isLoading(false); + fullScreenLoader.stopLoader(); errorProcessor.process(response, messageContainer); } - ).always( - function () { - isLoading(false); - } ); }; } diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js index 606fe4013ea762c5460c49202907463d7c7952f2..a2b7ff19f2139ffa7e23b37eda039d5b9ccc32a8 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js @@ -18,25 +18,22 @@ define( 'mage/storage', 'mage/translate', 'Magento_Checkout/js/action/get-payment-information', - 'Magento_Checkout/js/model/totals' + 'Magento_Checkout/js/model/totals', + 'Magento_Checkout/js/model/full-screen-loader' ], function ( - ko, - $, - quote, - urlManager, - errorProcessor, - messageContainer, - storage, - $t, - getPaymentInformationAction, - totals + ko, $, quote, urlManager, errorProcessor, messageContainer, storage, $t, getPaymentInformationAction, totals, + fullScreenLoader ) { 'use strict'; - return function (couponCode, isApplied, isLoading) { - var quoteId = quote.getQuoteId(); - var url = urlManager.getApplyCouponUrl(couponCode, quoteId); - var message = $t('Your coupon was successfully applied.'); + + return function (couponCode, isApplied) { + var quoteId = quote.getQuoteId(), + url = urlManager.getApplyCouponUrl(couponCode, quoteId), + message = $t('Your coupon was successfully applied.'); + + fullScreenLoader.startLoader(); + return storage.put( url, {}, @@ -45,19 +42,22 @@ define( function (response) { if (response) { var deferred = $.Deferred(); - isLoading(false); + isApplied(true); totals.isLoading(true); getPaymentInformationAction(deferred); $.when(deferred).done(function () { + fullScreenLoader.stopLoader(); totals.isLoading(false); }); - messageContainer.addSuccessMessage({'message': message}); + messageContainer.addSuccessMessage({ + 'message': message + }); } } ).fail( function (response) { - isLoading(false); + fullScreenLoader.stopLoader(); totals.isLoading(false); errorProcessor.process(response, messageContainer); } diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js b/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js index 8e8c0798ff3b59ce0ffa0a803242abe1d02fcee7..6dc973a7a1e4298469251f71f6e26f09a95cc5d0 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/view/payment/discount.js @@ -13,49 +13,53 @@ define( ], function ($, ko, Component, quote, setCouponCodeAction, cancelCouponAction) { 'use strict'; - var totals = quote.getTotals(); - var couponCode = ko.observable(null); + + var totals = quote.getTotals(), + couponCode = ko.observable(null), + isApplied = ko.observable(couponCode() != null); + if (totals()) { couponCode(totals()['coupon_code']); } - var isApplied = ko.observable(couponCode() != null); - var isLoading = ko.observable(false); + return Component.extend({ defaults: { template: 'Magento_SalesRule/payment/discount' }, couponCode: couponCode, + /** * Applied flag */ isApplied: isApplied, - isLoading: isLoading, + /** * Coupon code application procedure */ apply: function() { if (this.validate()) { - isLoading(true); - setCouponCodeAction(couponCode(), isApplied, isLoading); + setCouponCodeAction(couponCode(), isApplied); } }, + /** * Cancel using coupon */ cancel: function() { if (this.validate()) { - isLoading(true); couponCode(''); - cancelCouponAction(isApplied, isLoading); + cancelCouponAction(isApplied); } }, + /** * Coupon form validation * - * @returns {boolean} + * @returns {Boolean} */ - validate: function() { + validate: function () { var form = '#discount-form'; + return $(form).validation() && $(form).validation('isValid'); } }); diff --git a/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html b/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html index d6bc4c764d5714382d017adce856deaf84c8b3ff..4ba38906fe9c1e98c8d248574585cc01ef815914 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html +++ b/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html @@ -15,7 +15,7 @@ <!-- ko foreach: getRegion('messages') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> - <form class="form form-discount" id="discount-form" data-bind="blockLoader: isLoading"> + <form class="form form-discount" id="discount-form"> <div class="payment-option-inner"> <div class="field"> <label class="label" for="discount-code"> diff --git a/app/code/Magento/SalesSequence/composer.json b/app/code/Magento/SalesSequence/composer.json index eeda2be05e68f86cb5552e6f48ca502dd96cb9be..5b9efe61f72724e28d23c1af0dc197fede8e117f 100644 --- a/app/code/Magento/SalesSequence/composer.json +++ b/app/code/Magento/SalesSequence/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales-sequence", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/SampleData/composer.json b/app/code/Magento/SampleData/composer.json index 936bbf0d6be3a7399babc2e835282e7c71bea4c2..4c98a5abd3a16ab72b5ea04e0606955986924ebd 100644 --- a/app/code/Magento/SampleData/composer.json +++ b/app/code/Magento/SampleData/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sample-data", "description": "Sample Data fixtures", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "suggest": { diff --git a/app/code/Magento/Search/composer.json b/app/code/Magento/Search/composer.json index cc95e6784526f33bb65246b599a4147e8a1afa1d..8f809fb3b8af3533c4715aea1e13854650e0cbac 100644 --- a/app/code/Magento/Search/composer.json +++ b/app/code/Magento/Search/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-search", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-catalog-search": "100.2.*", diff --git a/app/code/Magento/Security/composer.json b/app/code/Magento/Security/composer.json index f64c0f0b29d282b0feaefb24ebb3963086f0e083..4048b63bf607030d27ae7fa84dbec6a0da838957 100644 --- a/app/code/Magento/Security/composer.json +++ b/app/code/Magento/Security/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-security", "description": "Security management module", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/SendFriend/composer.json b/app/code/Magento/SendFriend/composer.json index de9dbf7a84576022d8f6b1a38324f902565870df..1095dedc0ba6606107660d65373096d8c82b204c 100644 --- a/app/code/Magento/SendFriend/composer.json +++ b/app/code/Magento/SendFriend/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-send-friend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Shipping/composer.json b/app/code/Magento/Shipping/composer.json index dc9ea808c0f7b19e396a7c73f2b416221729a34b..03f51bbd95e0ff5f65b88e565cfbe77102a44760 100644 --- a/app/code/Magento/Shipping/composer.json +++ b/app/code/Magento/Shipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-shipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/Sitemap/composer.json b/app/code/Magento/Sitemap/composer.json index be553a4da194f4dbf64fb4237fd92bb27e4e17ff..9f556178fc2cccfd070c272a89164e438f1d33e8 100644 --- a/app/code/Magento/Sitemap/composer.json +++ b/app/code/Magento/Sitemap/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sitemap", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index a03aa6fee9e2d84134aa0b3bd63ded9c1e7f8bd3..1c74c46cdf89f3aaa0bf5efc3143556d610e518b 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-store", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-directory": "100.2.*", "magento/module-ui": "100.2.*", diff --git a/app/code/Magento/Swagger/composer.json b/app/code/Magento/Swagger/composer.json index dc2d1cbee003de647cf72f7312ad7f7fcc222e3b..47e347426c83fdb1b969c5a03bd46d1776e89950 100644 --- a/app/code/Magento/Swagger/composer.json +++ b/app/code/Magento/Swagger/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swagger", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index f87fdb635cb73386b40fec6ac932eaee3f9cd398..b61ff41d8c955890aae0c22baedcdb8abd22e004 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swatches", "description": "Add Swatches to Products", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-configurable-product": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/SwatchesLayeredNavigation/composer.json b/app/code/Magento/SwatchesLayeredNavigation/composer.json index 9b658d0a42e61e0b556f1aa5aed6e12ed8cfdbe7..67c6d90c51e7f81de645451da6553bcdb750fddd 100644 --- a/app/code/Magento/SwatchesLayeredNavigation/composer.json +++ b/app/code/Magento/SwatchesLayeredNavigation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swatches-layered-navigation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/magento-composer-installer": "*" }, diff --git a/app/code/Magento/Tax/composer.json b/app/code/Magento/Tax/composer.json index 4553fdbb383323732b667df9be3e140aca011744..3dfacaaecd9635dec8229175de2876da6c957a77 100644 --- a/app/code/Magento/Tax/composer.json +++ b/app/code/Magento/Tax/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-tax", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/TaxImportExport/composer.json b/app/code/Magento/TaxImportExport/composer.json index c3d09e4926e02d67a98c0e15c050d67997ae1ecd..9d9cac5dec59c94e8013bc5ab3275fee84c73b6b 100644 --- a/app/code/Magento/TaxImportExport/composer.json +++ b/app/code/Magento/TaxImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-tax-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-tax": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Theme/composer.json b/app/code/Magento/Theme/composer.json index 49382f9642fa5d2b88d7d2f4cea79c07d5306cb1..7158ac314076d38aae4766bbea1a917dc869b5dd 100644 --- a/app/code/Magento/Theme/composer.json +++ b/app/code/Magento/Theme/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-theme", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Translation/composer.json b/app/code/Magento/Translation/composer.json index 6359d5925149630694e5bf598a99ce09cfd08fed..6c6c5fad3e7b9abcab6211e1c2326f9a31054088 100644 --- a/app/code/Magento/Translation/composer.json +++ b/app/code/Magento/Translation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-translation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-developer": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Ui/README.md b/app/code/Magento/Ui/README.md index 1337383ea3fb83bf3e2927589859587aad5a48f7..b7dd1a858e4a8161f836b3ea73d1483ae56ba2dd 100644 --- a/app/code/Magento/Ui/README.md +++ b/app/code/Magento/Ui/README.md @@ -1,7 +1,7 @@ # Overview ## Purpose of module -The Magento\Ui module introduces a set of common UI components, which could be easily used and configured via layout XML files. +The Magento\Ui module introduces a set of common UI components, which could be used and configured via layout XML files. # Deployment ## System requirements diff --git a/app/code/Magento/Ui/composer.json b/app/code/Magento/Ui/composer.json index 43c7b7fa3011fd7c6386fcde17eb45b6c5e02695..a3de6c6dfbdde7860dbb07503e0e6fe25e8b3dcf 100644 --- a/app/code/Magento/Ui/composer.json +++ b/app/code/Magento/Ui/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-ui", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js index de10c065d123bf773f9fba66135b2308a2abe3e9..518f09fa73ba6c6771654c7c9fc3f3ce07be79be 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js @@ -52,13 +52,15 @@ define([ obj; if (this.recordData().length && !this.update) { - this.recordData.each(function (recordData) { + _.each(this.recordData(), function (recordData) { obj = {}; obj[this.map[this.identificationProperty]] = recordData[this.identificationProperty]; insertData.push(obj); }, this); - this.source.set(this.dataProvider, insertData); + if (insertData.length) { + this.source.set(this.dataProvider, insertData); + } } }, @@ -178,7 +180,7 @@ define([ tmpObj = {}; if (data.length !== this.relatedData.length) { - data.forEach(function (obj) { + _.each(data, function (obj) { tmpObj[this.identificationDRProperty] = obj[this.identificationDRProperty]; if (!_.findWhere(this.relatedData, tmpObj)) { @@ -193,7 +195,7 @@ define([ /** * Processing insert data * - * @param {Array} data + * @param {Object} data */ processingInsertData: function (data) { var changes, diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js index 16c102b8367f4b8762030057c4d249adb132693b..77d3a069ccef76bbcacad28459820b3359b025e7 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js @@ -51,7 +51,12 @@ define([ observable() && $(el).datepicker( 'setDate', - moment(observable(), utils.normalizeDate(config.options.dateFormat)).toDate() + moment( + observable(), + utils.normalizeDate( + options.dateFormat + (options.showsTime ? ' ' + options.timeFormat : '') + ) + ).toDate() ); $(el).blur(); diff --git a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/collapsible.html b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/collapsible.html index f3319a05525f2f55bc4ba43ec5ce9bc1e4e2c300..d1ec1d26df6c56dff099b09aa0eb83232687573e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/collapsible.html +++ b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/collapsible.html @@ -43,6 +43,7 @@ </div> <button class="action-delete" + data-index="delete_button" type="button" title="'Delete'" click="function(){ diff --git a/app/code/Magento/Ups/composer.json b/app/code/Magento/Ups/composer.json index ade4e738a68efe0499041830892bb6b61f5a3156..c20dd28ba88876a70c04ea49038b94f82d3d9b06 100644 --- a/app/code/Magento/Ups/composer.json +++ b/app/code/Magento/Ups/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-ups", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/UrlRewrite/composer.json b/app/code/Magento/UrlRewrite/composer.json index ac75ecdf61b8fd0531f2b14f76d2aec7d72581be..290ffc6731d89ce142e94c58616ca1a74c82c54b 100644 --- a/app/code/Magento/UrlRewrite/composer.json +++ b/app/code/Magento/UrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-store": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/User/composer.json b/app/code/Magento/User/composer.json index 755c47ac1147612e1e55b589e72afa705b39b0ce..4632e32bc17f90549f7271cadc5e1ef7ba08869e 100644 --- a/app/code/Magento/User/composer.json +++ b/app/code/Magento/User/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-user", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-authorization": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Usps/composer.json b/app/code/Magento/Usps/composer.json index 5baccddf4884307f4ebdeed9199fedb00debf4bc..15e93d0bea4fb6d0e89f21f180b2c91486c8f5aa 100644 --- a/app/code/Magento/Usps/composer.json +++ b/app/code/Magento/Usps/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-usps", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Variable/composer.json b/app/code/Magento/Variable/composer.json index 21f3ec1dd467f240c609d5931ffa5cbde8dc3e15..e4b06d0edb34ab25d4d1279fb463a4f630ee77e9 100644 --- a/app/code/Magento/Variable/composer.json +++ b/app/code/Magento/Variable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-variable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-email": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Vault/composer.json b/app/code/Magento/Vault/composer.json index 74dd568e3b45ee4e3e7f9e235d3389191b7a93e4..b2edf040674d83f6335d407089201b46fe16ecce 100644 --- a/app/code/Magento/Vault/composer.json +++ b/app/code/Magento/Vault/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-vault", "description": "", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-sales": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Version/composer.json b/app/code/Magento/Version/composer.json index 3a972432101154fa2c4f167473fc6f4d6aa113a0..4396f1fb2b3bf4e114dd2ab4dfbd37eb7c2ffd7e 100644 --- a/app/code/Magento/Version/composer.json +++ b/app/code/Magento/Version/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-version", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Webapi/composer.json b/app/code/Magento/Webapi/composer.json index f2796df70a8fae522a34d0c94e079afd9f525f9c..6fc712722adddf10c16e4add70913a62c2ef2bc7 100644 --- a/app/code/Magento/Webapi/composer.json +++ b/app/code/Magento/Webapi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-webapi", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-authorization": "100.2.*", "magento/module-integration": "100.2.*", diff --git a/app/code/Magento/WebapiSecurity/composer.json b/app/code/Magento/WebapiSecurity/composer.json index 506975f1aa188d4f0d90f8d2f0484153998f2c39..0f4ef7b3dc8839518bcc2982e9975c53d99a6c1b 100644 --- a/app/code/Magento/WebapiSecurity/composer.json +++ b/app/code/Magento/WebapiSecurity/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-webapi-security", "description": "WebapiSecurity module provides option to loosen security on some webapi resources.", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-webapi": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Weee/composer.json b/app/code/Magento/Weee/composer.json index 035d66e5a96b416f1f5e3140ef9e45e299c1b52a..70fd4a25d0310d94d4254a8079755eab01ad610f 100644 --- a/app/code/Magento/Weee/composer.json +++ b/app/code/Magento/Weee/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-weee", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-tax": "100.2.*", diff --git a/app/code/Magento/Widget/composer.json b/app/code/Magento/Widget/composer.json index f59c4180eb2bb56522ae20f3615da92dd0c60370..b5a5e9e5569e3c62641cfc2d3cbc34030229020b 100644 --- a/app/code/Magento/Widget/composer.json +++ b/app/code/Magento/Widget/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-widget", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-cms": "101.1.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Wishlist/Pricing/ConfiguredPrice/ConfigurableProduct.php b/app/code/Magento/Wishlist/Pricing/ConfiguredPrice/ConfigurableProduct.php index 6d8e4bcd2fd1c41844cd4398043b61dec4ff36ff..be995258794cc05f1fca4a2124cac04e8c0e1c87 100644 --- a/app/code/Magento/Wishlist/Pricing/ConfiguredPrice/ConfigurableProduct.php +++ b/app/code/Magento/Wishlist/Pricing/ConfiguredPrice/ConfigurableProduct.php @@ -21,15 +21,12 @@ class ConfigurableProduct extends FinalPrice implements ConfiguredPriceInterface */ public function getValue() { - $result = 0.; /** @var \Magento\Wishlist\Model\Item\Option $customOption */ $customOption = $this->getProduct()->getCustomOption('simple_product'); - if ($customOption) { - /** @var \Magento\Framework\Pricing\PriceInfoInterface $priceInfo */ - $priceInfo = $customOption->getProduct()->getPriceInfo(); - $result = $priceInfo->getPrice(self::PRICE_CODE)->getValue(); - } - return max(0, $result); + $product = $customOption ? $customOption->getProduct() : $this->getProduct(); + $price = $product->getPriceInfo()->getPrice(self::PRICE_CODE)->getValue(); + + return max(0, $price); } /** diff --git a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php index 9e05a578080efbd7ec91f49babfbbafc447e330d..ee4841ef79f2e6e31545f12e44fe49073166a8e3 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Pricing/ConfiguredPrice/ConfigurableProductTest.php @@ -5,36 +5,30 @@ */ namespace Magento\Wishlist\Test\Unit\Pricing\ConfiguredPrice; -use Magento\Framework\Pricing\Adjustment\CalculatorInterface; -use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Framework\Pricing\PriceInfoInterface; -use Magento\Framework\Pricing\SaleableInterface; -use Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct; - class ConfigurableProductTest extends \PHPUnit_Framework_TestCase { /** - * @var SaleableInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\SaleableInterface|\PHPUnit_Framework_MockObject_MockObject */ private $saleableItem; /** - * @var CalculatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\Adjustment\CalculatorInterface|\PHPUnit_Framework_MockObject_MockObject */ private $calculator; /** - * @var PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject */ private $priceCurrency; /** - * @var ConfigurableProduct + * @var \Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct */ private $model; /** - * @var PriceInfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceInfoInterface|\PHPUnit_Framework_MockObject_MockObject */ private $priceInfoMock; @@ -49,9 +43,6 @@ class ConfigurableProductTest extends \PHPUnit_Framework_TestCase 'getCustomOption', ]) ->getMockForAbstractClass(); - $this->saleableItem->expects($this->once()) - ->method('getPriceInfo') - ->willReturn($this->priceInfoMock); $this->calculator = $this->getMockBuilder(\Magento\Framework\Pricing\Adjustment\CalculatorInterface::class) ->getMockForAbstractClass(); @@ -59,7 +50,7 @@ class ConfigurableProductTest extends \PHPUnit_Framework_TestCase $this->priceCurrency = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) ->getMockForAbstractClass(); - $this->model = new ConfigurableProduct( + $this->model = new \Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct( $this->saleableItem, null, $this->calculator, @@ -82,7 +73,7 @@ class ConfigurableProductTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->priceInfoMock->expects($this->once()) ->method('getPrice') - ->with(ConfigurableProduct::PRICE_CODE) + ->with(\Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct::PRICE_CODE) ->willReturn($priceMock); $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) @@ -109,11 +100,28 @@ class ConfigurableProductTest extends \PHPUnit_Framework_TestCase public function testGetValueWithNoCustomOption() { + $priceValue = 100; + + $priceMock = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) + ->getMockForAbstractClass(); + $priceMock->expects($this->once()) + ->method('getValue') + ->willReturn($priceValue); + $this->saleableItem->expects($this->once()) ->method('getCustomOption') ->with('simple_product') ->willReturn(null); - $this->assertEquals(0, $this->model->getValue()); + $this->saleableItem->expects($this->once()) + ->method('getPriceInfo') + ->willReturn($this->priceInfoMock); + + $this->priceInfoMock->expects($this->once()) + ->method('getPrice') + ->with(\Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct::PRICE_CODE) + ->willReturn($priceMock); + + $this->assertEquals(100, $this->model->getValue()); } } diff --git a/app/code/Magento/Wishlist/composer.json b/app/code/Magento/Wishlist/composer.json index 3faee8912493c96092f70d7fc3ca0e77164b39bc..b341edde760b0761872aecc488216faa37392c22 100644 --- a/app/code/Magento/Wishlist/composer.json +++ b/app/code/Magento/Wishlist/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-wishlist", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/design/adminhtml/Magento/backend/composer.json b/app/design/adminhtml/Magento/backend/composer.json index 29569db622d2ccc9fad9e9b87b3ad4f7ae4757d2..ff0eda055bba6a9e9899d66839216c14bbdbe02e 100644 --- a/app/design/adminhtml/Magento/backend/composer.json +++ b/app/design/adminhtml/Magento/backend/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-adminhtml-backend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-theme", diff --git a/app/design/frontend/Magento/blank/composer.json b/app/design/frontend/Magento/blank/composer.json index 27ed40860dd5b065dc51e82f487722eb519ba215..7c03167ebc23278eaab5a72cddd381de7cd5afcc 100644 --- a/app/design/frontend/Magento/blank/composer.json +++ b/app/design/frontend/Magento/blank/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-frontend-blank", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-theme", diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml index 3a92ad0a6741040e0adb3fc25e00948520cfa565..6c53a8613f09ca1f91c9e024b8e2cec723f0b309 100644 --- a/app/design/frontend/Magento/blank/etc/view.xml +++ b/app/design/frontend/Magento/blank/etc/view.xml @@ -227,7 +227,7 @@ </var> <var name="options"> <var name="options"> - <var name="navigation">dots</var> + <var name="nav">dots</var> </var> </var> </var> diff --git a/app/design/frontend/Magento/luma/composer.json b/app/design/frontend/Magento/luma/composer.json index 98fe0ebf8061c23fa37d30a9551f05d5c41bce1b..97eb48f3c7e9efe028eae034757a6a17c4d98bd0 100644 --- a/app/design/frontend/Magento/luma/composer.json +++ b/app/design/frontend/Magento/luma/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-frontend-luma", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/theme-frontend-blank": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/composer.json b/composer.json index fed4465140724cef3c24cd7facdd2ec5ceeba5b6..f9e49415d6ad3aff6976144df442861909155d2d 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "AFL-3.0" ], "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "zendframework/zend-stdlib": "~2.4.6", "zendframework/zend-code": "~2.4.6", "zendframework/zend-server": "~2.4.6", diff --git a/composer.lock b/composer.lock index 7feb568d766d012e009c59a50226f3ea8bd7b74a..02f0e31a325638840641667cac1c6783cf381e87 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c23e80be1cc71ab108ce5ac19b3fe509", - "content-hash": "5b9734c1bdbda68cf20507525cafa0f2", + "hash": "1f34dce6d48c9e4e694c27e001414000", + "content-hash": "600aca1692cf3fe5c2ea1cbf66de09ab", "packages": [ { "name": "braintree/braintree_php", @@ -56,7 +56,7 @@ }, { "name": "colinmollenhour/cache-backend-file", - "version": "1.4", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_File.git", @@ -818,16 +818,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.2", + "version": "v2.0.3", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf" + "reference": "c0125896dbb151380ab47e96c621741e79623beb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf", - "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c0125896dbb151380ab47e96c621741e79623beb", + "reference": "c0125896dbb151380ab47e96c621741e79623beb", "shasum": "" }, "require": { @@ -862,7 +862,7 @@ "pseudorandom", "random" ], - "time": "2016-04-03 06:00:07" + "time": "2016-10-17 15:23:22" }, { "name": "pelago/emogrifier", @@ -922,16 +922,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "41f85e9c2582b3f6d1b7d20395fb40c687ad5370" + "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/41f85e9c2582b3f6d1b7d20395fb40c687ad5370", - "reference": "41f85e9c2582b3f6d1b7d20395fb40c687ad5370", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf", + "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf", "shasum": "" }, "require": { @@ -1010,20 +1010,20 @@ "x.509", "x509" ], - "time": "2016-08-18 18:49:14" + "time": "2016-10-04 00:57:04" }, { "name": "psr/log", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "5277094ed527a1c4477177d102fe4c53551953e0" + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/5277094ed527a1c4477177d102fe4c53551953e0", - "reference": "5277094ed527a1c4477177d102fe4c53551953e0", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, "require": { @@ -1057,7 +1057,7 @@ "psr", "psr-3" ], - "time": "2016-09-19 16:02:08" + "time": "2016-10-10 12:19:37" }, { "name": "ramsey/uuid", @@ -1390,7 +1390,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1450,7 +1450,7 @@ }, { "name": "symfony/filesystem", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -1499,16 +1499,16 @@ }, { "name": "symfony/finder", - "version": "v3.1.4", + "version": "v3.1.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577" + "reference": "205b5ffbb518a98ba2ae60a52656c4a31ab00c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e568ef1784f447a0e54dcb6f6de30b9747b0f577", - "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577", + "url": "https://api.github.com/repos/symfony/finder/zipball/205b5ffbb518a98ba2ae60a52656c4a31ab00c6f", + "reference": "205b5ffbb518a98ba2ae60a52656c4a31ab00c6f", "shasum": "" }, "require": { @@ -1544,20 +1544,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-08-26 12:04:02" + "time": "2016-09-28 00:11:12" }, { "name": "symfony/process", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "05a03ed27073638658cab9405d99a67dd1014987" + "reference": "024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/05a03ed27073638658cab9405d99a67dd1014987", - "reference": "05a03ed27073638658cab9405d99a67dd1014987", + "url": "https://api.github.com/repos/symfony/process/zipball/024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f", + "reference": "024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f", "shasum": "" }, "require": { @@ -1593,7 +1593,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-09-06 10:55:00" + "time": "2016-09-29 14:03:54" }, { "name": "tedivm/jshrink", @@ -4329,16 +4329,16 @@ }, { "name": "symfony/config", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84" + "reference": "f8b1922bbda9d2ac86aecd649399040bce849fde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/005bf10c156335ede2e89fb9a9ee10a0b742bc84", - "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84", + "url": "https://api.github.com/repos/symfony/config/zipball/f8b1922bbda9d2ac86aecd649399040bce849fde", + "reference": "f8b1922bbda9d2ac86aecd649399040bce849fde", "shasum": "" }, "require": { @@ -4378,20 +4378,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2016-08-16 14:56:08" + "time": "2016-09-14 20:31:12" }, { "name": "symfony/dependency-injection", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "0a732a9cafc30e54077967da4d019e1d618a8cb9" + "reference": "ee9ec9ac2b046462d341e9de7c4346142d335e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0a732a9cafc30e54077967da4d019e1d618a8cb9", - "reference": "0a732a9cafc30e54077967da4d019e1d618a8cb9", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ee9ec9ac2b046462d341e9de7c4346142d335e75", + "reference": "ee9ec9ac2b046462d341e9de7c4346142d335e75", "shasum": "" }, "require": { @@ -4441,11 +4441,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2016-09-06 23:19:39" + "time": "2016-09-24 09:47:20" }, { "name": "symfony/stopwatch", - "version": "v3.1.4", + "version": "v3.1.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -4494,7 +4494,7 @@ }, { "name": "symfony/yaml", - "version": "v2.8.11", + "version": "v2.8.12", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", @@ -4590,7 +4590,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "lib-libxml": "*", "ext-ctype": "*", "ext-gd": "*", diff --git a/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json b/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json index 629afd442a46e7549206c1520b1b6c75f024dcb1..00888c7baf74990c6ba4d98ba6569c210ba08c7e 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json +++ b/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-test-module-integration-from-config", "description": "test integration create from config", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.42.0-beta8", "magento/module-integration": "0.42.0-beta8" }, @@ -16,4 +16,4 @@ ] ] } -} \ No newline at end of file +} diff --git a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json index 1e2d9c0fa13dd80fcfe69374a7e02700f6b570ea..a9aa5a2ede89ce314a1f2257bc70c6b20b3bb2a0 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json +++ b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-test-join-directives", "description": "test integration for join directives", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.42.0-beta8", "magento/module-sales": "0.42.0-beta8" }, diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 4191ec1faa959aa1203ee45850c298f1d43bb988..cd02c6d4d7500ef9adf7d17ac9f866bfc8ef0711 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -713,6 +713,18 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract $this->assertNotNull($response['items'][0]['sku']); $this->assertEquals('simple', $response['items'][0]['sku']); + + $index = null; + foreach ($response['items'][0]['custom_attributes'] as $key => $customAttribute) { + if ($customAttribute['attribute_code'] == 'category_ids') { + $index = $key; + break; + } + } + $this->assertNotNull($index, 'Category information wasn\'t set'); + + $expectedResult = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ? ['string' => '2'] : ['2']; + $this->assertEquals($expectedResult, $response['items'][0]['custom_attributes'][$index]['value']); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php index e4129a0739912c05dbb8546beab7f028945ad5f6..1a06be978d34c897f10aadd32e7f3e1720b1983b 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/LinkManagementTest.php @@ -6,12 +6,33 @@ */ namespace Magento\ConfigurableProduct\Api; +use Magento\Eav\Model\AttributeRepository; + class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract { const SERVICE_NAME = 'configurableProductLinkManagementV1'; const SERVICE_VERSION = 'V1'; const RESOURCE_PATH = '/V1/configurable-products'; + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var AttributeRepository + */ + protected $attributeRepository; + + /** + * Execute per test initialization + */ + public function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->attributeRepository = $this->objectManager->get(\Magento\Eav\Model\AttributeRepository::class); + } + /** * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ @@ -40,14 +61,76 @@ class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract } /** - * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_simple_77.php * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_simple_77.php * @magentoApiDataFixture Magento/ConfigurableProduct/_files/delete_association.php */ public function testAddChild() { $productSku = 'configurable'; $childSku = 'simple_77'; + $res = $this->addChild($productSku, $childSku); + $this->assertTrue($res); + } + + /** + * Test the full flow of creating a configurable product and adding a child via REST + * + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + */ + public function testAddChildFullRestCreation() + { + $productSku = 'configurable-product-sku'; + $childSku = 'simple-product-sku'; + + $this->createConfigurableProduct($productSku); + $attribute = $this->attributeRepository->get('catalog_product', 'test_configurable'); + $attributeValue = $attribute->getOptions()[1]->getValue(); + $this->addOptionToConfigurableProduct($productSku, $attribute->getAttributeId(), $attributeValue); + $this->createSimpleProduct($childSku, $attributeValue); + $res = $this->addChild($productSku, $childSku); + $this->assertTrue($res); + + // confirm that the simple product was added + $children = $this->getChildren($productSku); + $added = false; + foreach ($children as $child) { + if ($child['sku'] == $childSku) { + $added = true; + break; + } + } + $this->assertTrue($added); + + // clean up products + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/' . $productSku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + ], + 'soap' => [ + 'service' => 'catalogProductRepositoryV1', + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => 'catalogProductRepositoryV1DeleteById', + ], + ]; + $this->_webApiCall($serviceInfo, ['sku' => $productSku]); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/' . $childSku, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE + ], + 'soap' => [ + 'service' => 'catalogProductRepositoryV1', + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => 'catalogProductRepositoryV1DeleteById', + ], + ]; + $this->_webApiCall($serviceInfo, ['sku' => $childSku]); + } + + private function addChild($productSku, $childSku) + { $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/' . $productSku . '/child', @@ -59,8 +142,90 @@ class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract 'operation' => self::SERVICE_NAME . 'AddChild' ] ]; - $res = $this->_webApiCall($serviceInfo, ['sku' => $productSku, 'childSku' => $childSku]); - $this->assertTrue($res); + return $this->_webApiCall($serviceInfo, ['sku' => $productSku, 'childSku' => $childSku]); + } + + protected function createConfigurableProduct($productSku) + { + $requestData = [ + 'product' => [ + 'sku' => $productSku, + 'name' => 'configurable-product-' . $productSku, + 'type_id' => 'configurable', + 'price' => 50, + 'attribute_set_id' => 4 + ] + ]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => 'catalogProductRepositoryV1', + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => 'catalogProductRepositoryV1Save', + ], + ]; + return $this->_webApiCall($serviceInfo, $requestData); + } + + protected function addOptionToConfigurableProduct($productSku, $attributeId, $attributeValue) + { + $requestData = [ + 'sku' => $productSku, + 'option' => [ + 'attribute_id' => $attributeId, + 'label' => 'test_configurable', + 'position' => 0, + 'is_use_default' => true, + 'values' => [ + ['value_index' => $attributeValue], + ] + ] + ]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/configurable-products/'. $productSku .'/options', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => 'configurableProductOptionRepositoryV1', + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => 'configurableProductOptionRepositoryV1Save', + ], + ]; + return $this->_webApiCall($serviceInfo, $requestData); + } + + protected function createSimpleProduct($sku, $attributeValue) + { + $requestData = [ + 'product' => [ + 'sku' => $sku, + 'name' => 'simple-product-' . $sku, + 'type_id' => 'simple', + 'attribute_set_id' => 4, + 'price' => 3.62, + 'status' => 1, + 'visibility' => 4, + 'custom_attributes' => [ + ['attribute_code' => 'test_configurable', 'value' => $attributeValue], + ] + ] + ]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => 'catalogProductRepositoryV1', + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => 'catalogProductRepositoryV1Save', + ], + ]; + return $this->_webApiCall($serviceInfo, $requestData); } /** @@ -93,7 +258,7 @@ class LinkManagementTest extends \Magento\TestFramework\TestCase\WebapiAbstract /** * @param string $productSku - * @return string + * @return string[] */ protected function getChildren($productSku) { diff --git a/dev/tests/functional/composer.json b/dev/tests/functional/composer.json index 512c5bc8d5df666ced655806af0da64952d0f4ef..cef145167860c8ebad9695d870b6cc73580e47f3 100644 --- a/dev/tests/functional/composer.json +++ b/dev/tests/functional/composer.json @@ -1,7 +1,7 @@ { "require": { "magento/mtf": "1.0.0-rc48", - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.5|7.0.2|~7.0.6", "phpunit/phpunit": "~4.8.0|~5.5.0", "phpunit/phpunit-selenium": ">=1.2" }, diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Template.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Template.php index 29361ced38bcc6b4ba344e2191f3c963eb92576d..fb22754faf483aa32b97d3e1672aeb17d58bec26 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Template.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Template.php @@ -19,7 +19,7 @@ class Template extends Block * * @var string */ - protected $spinner = '[data-role="spinner"]'; + protected $spinner = '#container [data-role="spinner"]'; /** * Magento loader. diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWithDiscountTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWithDiscountTest.xml index 23fc729edb99f9645f7587d8e350464c81083fcd..f8ffa51982f1fae4d34a6290e1bebce28ba85712 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWithDiscountTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWithDiscountTest.xml @@ -30,5 +30,25 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> </variation> + <variation name="OnePageCheckoutWithDiscountTestVariation2" summary="Checkout with 100% discount and free shipping if Braintree through PayPal is enabled" ticketId="MAGETWO-59940"> + <data name="description" xsi:type="string">Use saved for Braintree credit card on checkout</data> + <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> + <data name="customer/dataset" xsi:type="string">default</data> + <data name="salesRule" xsi:type="string">active_sales_rule_with_fixed_price_discount_coupon</data> + <data name="shippingAddress/dataset" xsi:type="string">US_address_1_without_email</data> + <data name="checkoutMethod" xsi:type="string">login</data> + <data name="shipping/shipping_service" xsi:type="string">Free Shipping</data> + <data name="shipping/shipping_method" xsi:type="string">Free</data> + <data name="payment/method" xsi:type="string">free</data> + <data name="prices" xsi:type="array"> + <item name="grandTotal" xsi:type="string">0.00</item> + </data> + <data name="configData" xsi:type="string">braintree, braintree_use_vault, freeshipping</data> + <data name="status" xsi:type="string">Pending</data> + <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S1</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertOrderSuccessPlacedMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Bundle.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Bundle.php index e94ec38d010f3d519e8ab2e06c5fdd21dccd544c..5cf84cf6764270708bd30327f2596253f510370d 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Bundle.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Bundle.php @@ -8,7 +8,6 @@ namespace Magento\Bundle\Test\Block\Adminhtml\Catalog\Product\Edit\Section; use Magento\Mtf\Client\Element\SimpleElement; use Magento\Bundle\Test\Block\Adminhtml\Catalog\Product\Edit\Section\Bundle\Option; -use Magento\Mtf\Client\Element; use Magento\Mtf\Client\ElementInterface; use Magento\Mtf\Client\Locator; use Magento\Ui\Test\Block\Adminhtml\Section; @@ -53,6 +52,13 @@ class Bundle extends Section */ protected $bundleOptionRow = './tr[%d]'; + /** + * Selector for trash can button in bundle option row. + * + * @var string + */ + protected $deleteOption = './tr[%d]//*[@data-index="delete_button"]'; + /** * Get bundle options block. * @@ -83,17 +89,49 @@ class Bundle extends Section if (!isset($fields['bundle_selections'])) { return $this; } + + $context = $this->_rootElement->find($this->bundleOptions, Locator::SELECTOR_XPATH); + + if (isset($fields['bundle_selections']['value']['bundle_options'])) { + foreach ($fields['bundle_selections']['value']['bundle_options'] as $key => $bundleOption) { + $count = $key + 1; + $itemOption = $context->find(sprintf($this->openOption, $count), Locator::SELECTOR_XPATH); + $isContent = $context->find(sprintf($this->optionContent, $count), Locator::SELECTOR_XPATH) + ->isVisible(); + if ($itemOption->isVisible() && !$isContent) { + $itemOption->click(); + } elseif (!$itemOption->isVisible()) { + $this->_rootElement->find($this->addNewOption)->click(); + } + $this->getBundleOptionBlock($count, $context)->fillOption($bundleOption); + } + } + + if (isset($fields['bundle_selections']['value']['bundle_options_delete'])) { + $this->deleteFieldsData($fields['bundle_selections']['value']['bundle_options_delete']); + } + + return $this; + } + + /** + * Delete some bundle options. + * + * @param array $fields + * @return $this + */ + public function deleteFieldsData(array $fields) + { $context = $this->_rootElement->find($this->bundleOptions, Locator::SELECTOR_XPATH); - foreach ($fields['bundle_selections']['value']['bundle_options'] as $key => $bundleOption) { - $count = $key + 1; - $itemOption = $context->find(sprintf($this->openOption, $count), Locator::SELECTOR_XPATH); - $isContent = $context->find(sprintf($this->optionContent, $count), Locator::SELECTOR_XPATH)->isVisible(); - if ($itemOption->isVisible() && !$isContent) { - $itemOption->click(); - } elseif (!$itemOption->isVisible()) { - $this->_rootElement->find($this->addNewOption)->click(); + foreach (array_keys($fields) as $key) { + $bundleOptionIndex = $key + 1; + $deleteOption = $context->find( + sprintf($this->deleteOption, $bundleOptionIndex), + Locator::SELECTOR_XPATH + ); + if ($deleteOption->isVisible()) { + $deleteOption->click(); } - $this->getBundleOptionBlock($count, $context)->fillOption($bundleOption); } return $this; } diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionsDeleted.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionsDeleted.php new file mode 100644 index 0000000000000000000000000000000000000000..69e694abd261ab4ca4e4102f542b72f612552d5b --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionsDeleted.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Test\Constraint; + +use Magento\Bundle\Test\Fixture\BundleProduct; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Mtf\Fixture\FixtureInterface; + +/** + * Assert bundle product form. + */ +class AssertBundleOptionsDeleted extends AbstractConstraint +{ + /** + * Assert that displayed price view for bundle product on product page equals passed from fixture. + * @param BundleProduct $product + * @param BundleProduct $originalProduct + * @param CatalogProductIndex $productGrid + * @param CatalogProductEdit $productPage + * @return void + */ + public function processAssert( + BundleProduct $product, + BundleProduct $originalProduct, + CatalogProductIndex $productGrid, + CatalogProductEdit $productPage + ) { + $filter = ['sku' => $product->getSku()]; + $productGrid->open(); + $productGrid->getProductGrid()->searchAndOpen($filter); + + $productData = $product->getData()['bundle_selections']['bundle_options']; + $originalProductData = $originalProduct->getData()['bundle_selections']['bundle_options']; + $formData = $productPage->getProductForm()->getData($product)['bundle_selections']; + + $productDataLength = count($productData); + $formDataLength = count($productData); + \PHPUnit_Framework_Assert::assertEquals($productDataLength, $formDataLength); + + foreach ($productData as $index => $option) { + $productAssociatedDataLength = count($option['assigned_products']); + $formAssociatedDataLength = count($formData[$index]['assigned_products']); + \PHPUnit_Framework_Assert::assertEquals($productAssociatedDataLength, $formAssociatedDataLength); + + foreach ($option['assigned_products'] as $productIndex => $associatedProduct) { + $associatedProduct['data']['getProductName'] = + $originalProductData[$index+1]['assigned_products'][$productIndex]['search_data']['name']; + $associatedProduct = $associatedProduct['data']; + $errorAssociatedProducts = array_diff( + $associatedProduct, + $formData[$index]['assigned_products'][$productIndex] + ); + \PHPUnit_Framework_Assert::assertCount(0, $errorAssociatedProducts); + } + + unset($option['assigned_products']); + unset($formData[$index]['assigned_products']); + $errorFields = array_diff($option, $formData[$index]); + \PHPUnit_Framework_Assert::assertCount(0, $errorFields); + } + } + + /** + * Returns a string representation of the object + * + * @return string + */ + public function toString() + { + return 'Bundle options were not deleted correctly. There is difference with expected options'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml index 2b5e9a437b32bdec615fd9e2a928b3784ba68fb4..6a507a7a99b437a793b4096f8f10d9e33ccb825a 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml @@ -225,5 +225,27 @@ <item name="dataset" xsi:type="string">bundle_fixed_100_dollar</item> </field> </dataset> + + <dataset name="with_3_bundle_options"> + <field name="name" xsi:type="string">Bundle with 3 options %isolation%</field> + <field name="url_key" xsi:type="string">with_3_bundle_options-%isolation%</field> + <field name="sku" xsi:type="string">sku_with_3_bundle_options_%isolation%</field> + <field name="sku_type" xsi:type="string">No</field> + <field name="price_type" xsi:type="string">No</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">100</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="bundle_selections" xsi:type="array"> + <item name="dataset" xsi:type="string">with_3_options</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml index 3e9b055c48ca5a1bfa5df3410ec48ef708a71fa7..7131aab3cffcbb69ca84f1078540a2dbada2ae28 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml @@ -665,5 +665,170 @@ </item> </field> </dataset> + + <dataset name="with_3_options"> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Option 1</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">No</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">1</item> + <item name="selection_price_value" xsi:type="string">1</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">Option 2</item> + <item name="type" xsi:type="string">Radio Buttons</item> + <item name="required" xsi:type="string">No</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">20</item> + <item name="selection_price_value" xsi:type="string">20</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">21</item> + <item name="selection_price_value" xsi:type="string">21</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + <item name="2" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">22</item> + <item name="selection_price_value" xsi:type="string">22</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + </item> + </item> + <item name="2" xsi:type="array"> + <item name="title" xsi:type="string">Option 3</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">No</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">3</item> + <item name="selection_price_value" xsi:type="string">3</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + </item> + <item name="1" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + <item name="1" xsi:type="string">catalogProductSimple::product_100_dollar</item> + <item name="2" xsi:type="string">catalogProductSimple::product_without_category</item> + </item> + <item name="2" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + </item> + </field> + </dataset> + + <dataset name="with_3_options_delete"> + <field name="bundle_options_delete" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Option 1</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">No</item> + </item> + </field> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Option 2</item> + <item name="type" xsi:type="string">Radio Buttons</item> + <item name="required" xsi:type="string">No</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">20</item> + <item name="selection_price_value" xsi:type="string">20</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">21</item> + <item name="selection_price_value" xsi:type="string">21</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + <item name="2" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">22</item> + <item name="selection_price_value" xsi:type="string">22</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">Option 3</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">No</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">3</item> + <item name="selection_price_value" xsi:type="string">3</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + <item name="1" xsi:type="string">catalogProductSimple::product_100_dollar</item> + <item name="2" xsi:type="string">catalogProductSimple::product_without_category</item> + </item> + <item name="1" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3c17360200f44f2fe8875590578c6960ea7757d0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Test\TestCase; + +use Magento\Bundle\Test\Fixture\BundleProduct; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Mtf\TestCase\Injectable; + +/** + * Test Flow: + * 1. Login as admin + * 2. Navigate to the Products>Inventory>Catalog + * 3. Click on Bundle product in the grid to edit it + * 4. Fill in some Bundle Options data according to data set + * 5. Delete some Bundle Options data according to data set + * 6. Save product + * 7. Verify Bundle Options in the updated product + * + * @group Bundle_Product + * @ZephyrId MAGETWO-26195 + */ +class UpdateBundleOptionsTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Page product on backend + * + * @var CatalogProductIndex + */ + protected $catalogProductIndex; + + /** + * Edit page on backend + * + * @var CatalogProductEdit + */ + protected $catalogProductEdit; + + /** + * Injection data + * + * @param CatalogProductIndex $catalogProductIndexNewPage + * @param CatalogProductEdit $catalogProductEditPage + * @return void + */ + public function __inject( + CatalogProductIndex $catalogProductIndexNewPage, + CatalogProductEdit $catalogProductEditPage + ) { + $this->catalogProductIndex = $catalogProductIndexNewPage; + $this->catalogProductEdit = $catalogProductEditPage; + } + + /** + * Test update bundle product + * + * @param BundleProduct $product + * @param BundleProduct $originalProduct + * @return void + */ + public function test(BundleProduct $product, BundleProduct $originalProduct) + { + // Preconditions + $originalProduct->persist(); + + // Steps + $filter = ['sku' => $originalProduct->getSku()]; + + $this->catalogProductIndex->open(); + $this->catalogProductIndex->getProductGrid()->searchAndOpen($filter); + + $form = $this->catalogProductEdit->getProductForm(); + $form->openSection('bundle'); + $container = $form->getSection('bundle'); + $containerFields = $product->getData()['bundle_selections']['bundle_options_delete']; + $container->deleteFieldsData($containerFields); + + $form->openSection('product-details'); + $container = $form->getSection('product-details'); + $containerFields = $product->getData(); + unset($containerFields['bundle_selections']); + $container->setFieldsData($containerFields); + + $this->catalogProductEdit->getFormPageActions()->save(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..795665d66d2bf7d34438381d3390e94c8458d3b7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleOptionsTest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Bundle\Test\TestCase\UpdateBundleOptionsTest" summary="Update Bundle Product Options" ticketId="MAGETWO-26195"> + <variation name="UpdateBundleOptionsTestVariation1"> + <data name="description" xsi:type="string">Update bundle product options</data> + <data name="originalProduct/dataset" xsi:type="string">with_3_bundle_options</data> + <data name="product/data/name" xsi:type="string">bundle_3_options_%isolation%</data> + <data name="product/data/sku" xsi:type="string">bundle_3_options_%isolation%</data> + <data name="product/data/bundle_selections/dataset" xsi:type="string">with_3_options_delete</data> + <constraint name="Magento\Bundle\Test\Constraint\AssertBundleOptionsDeleted" /> + </variation> + </testCase> +</config> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml index 9ae1e2ebfacca405f8505496f546cf2182e2dd3f..c32999a6ebce7e0a4c44c1a10bc8fe4010a795f7 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml @@ -34,5 +34,16 @@ <item name="inherit" xsi:type="number">1</item> </field> </dataset> + <dataset name="attribute_product_mask_sku"> + <field name="catalog/fields_masks/sku" xsi:type="array"> + <item name="value" xsi:type="string">{{name}} {{country_of_manufacture}}</item> + </field> + </dataset> + <dataset name="attribute_product_mask_sku_rollback"> + <field name="catalog/fields_masks/sku" xsi:type="array"> + <item name="value" xsi:type="string">{{name}}</item> + <item name="inherit" xsi:type="number">1</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0884e93387b2ac60c0be00e56791ae2b03443d38 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\TestCase\Product; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductNew; +use Magento\Mtf\TestCase\Injectable; + +/** + * Steps: + * 1. Login to the backend. + * 2. Navigate to Products > Catalog. + * 3. Start to create simple product. + * 4. Fill in data according to data set. + * 5. Save Product. + * 6. Perform appropriate assertions. + * + * @group Products + * @ZephyrId MAGETWO-59861 + */ +class CreateSimpleProductEntityByAttributeMaskSkuTest extends Injectable +{ + /** + * Configuration setting. + * + * @var string + */ + protected $configData; + + /** + * Should cache be flushed + * + * @var bool + */ + private $flushCache; + + /** + * @var \Magento\Mtf\Fixture\FixtureFactory + */ + private $fixtureFactory; + + /** + * Run create product simple entity by attribute mask SKU test. + * + * @param CatalogProductSimple $product + * @param CatalogProductIndex $productGrid + * @param CatalogProductNew $newProductPage + * @param string $configData + * @param bool $flushCache + * @return array + */ + public function testCreate( + CatalogProductSimple $product, + CatalogProductIndex $productGrid, + CatalogProductNew $newProductPage, + \Magento\Mtf\Fixture\FixtureFactory $fixtureFactory, + $flushCache = false, + $configData = null + ) { + $this->configData = $configData; + $this->flushCache = $flushCache; + $this->fixtureFactory = $fixtureFactory; + + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'flushCache' => $this->flushCache] + )->run(); + + // Steps + $productGrid->open(); + $productGrid->getGridPageActionBlock()->addProduct('simple'); + $newProductPage->getProductForm()->fill($product); + $newProductPage->getFormPageActions()->save(); + + $skuMask = $this->prepareSkuByMask($product); + + $productSimple = $fixtureFactory->createByCode( + 'catalogProductSimple', + ['data' => array_merge($product->getData(), ['sku' => $skuMask])] + ); + + return ['product' => $productSimple]; + } + + /** + * Obtains product sku based on attributes define in Stores > Configuration->Catalog > Catalog > Mask for SKU + * + * @param CatalogProductSimple $product + * @return string + */ + private function prepareSkuByMask(CatalogProductSimple $product) + { + $productData = $product->getData(); + $skuMask = ''; + $config = $this->fixtureFactory->createByCode('configData', ['dataset' => $this->configData]); + $section = $config->getData('section'); + if (is_array($section) && array_key_exists('catalog/fields_masks/sku', $section)) { + $skuMask = $section['catalog/fields_masks/sku']['value']; + } + + $attributesInPattern = []; + $count = preg_match_all('/{{(\w+)}}/', $skuMask, $matches); + if ($count > 0 && is_array($matches[0])) { + foreach ($matches[1] as $attributeName) { + if (array_key_exists($attributeName, $productData)) { + $attributesInPattern[$attributeName] = $productData[$attributeName]; + } + } + } + foreach ($attributesInPattern as $attributeName => $attributeValue) { + $skuMask = str_replace('{{' . $attributeName . '}}', $attributeValue, $skuMask); + } + return $skuMask; + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true, 'flushCache' => $this->flushCache] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..338f34a57ab5d8745a9cf53665d34e39a0369694 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityByAttributeMaskSkuTest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Catalog\Test\TestCase\Product\CreateSimpleProductEntityByAttributeMaskSkuTest" summary="Create Simple Product with attribute sku mask" ticketId="MAGETWO-59861"> + <variation name="CreateSimpleProductEntityByAttributeMaskSkuTest1"> + <data name="configData" xsi:type="string">attribute_product_mask_sku</data> + <data name="description" xsi:type="string">Create product with country of manufacture attribute sku mask</data> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/price/value" xsi:type="string">10000</data> + <data name="product/data/weight" xsi:type="string">50</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">657</data> + <data name="product/data/country_of_manufacture" xsi:type="string">Ukraine</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductInGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php index f1776d2bf933e17d63006948795418309348ffe3..cad6dda90459845718bc3ab1e3ac2952cca16640 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php @@ -85,6 +85,7 @@ class Payment extends Block $paymentLabelSelector = sprintf($this->paymentMethodLabel, $payment['method']); try { + $this->waitForElementNotVisible($this->waitElement); $this->waitForElementVisible($paymentLabelSelector); } catch (\Exception $exception) { throw new \Exception('Such payment method is absent.'); diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalOrderReview.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalOrderReview.php index 3bf6bda89ad939ad4ae100006996cf4077e1aeb9..dc1433506f2623d2122f293b063a0707017edc13 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalOrderReview.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalOrderReview.php @@ -14,6 +14,13 @@ use Magento\Mtf\Constraint\AbstractConstraint; */ class AssertGrandTotalOrderReview extends AbstractConstraint { + /** + * Wait element. + * + * @var string + */ + protected $waitElement = '.loading-mask'; + /** * Assert that Order Grand Total is correct on checkoutOnePage * @@ -23,6 +30,7 @@ class AssertGrandTotalOrderReview extends AbstractConstraint */ public function processAssert(CheckoutOnepage $checkoutOnepage, $grandTotal) { + $checkoutOnepage->getReviewBlock()->waitForElementNotVisible($this->waitElement); $checkoutReviewGrandTotal = $checkoutOnepage->getReviewBlock()->getGrandTotal(); \PHPUnit_Framework_Assert::assertEquals( diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductPage.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductPage.php index 62d6c72efe8c6aa8d0a8ed4aff4594316318c9e3..ad9807b76d6e532fd00e9f22fac7bf4007a80502 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductPage.php +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductPage.php @@ -125,15 +125,23 @@ class AssertConfigurableProductPage extends AssertProductPage protected function getLowestConfigurablePrice() { $price = null; - $configurableOptions = $this->product->getConfigurableAttributesData(); - - foreach ($configurableOptions['matrix'] as $option) { - $price = $price === null ? $option['price'] : $price; - if ($price > $option['price']) { - $price = $option['price']; + $priceDataConfig = $this->product->getDataFieldConfig('price'); + if (isset($priceDataConfig['source'])) { + $priceData = $priceDataConfig['source']->getPriceData(); + if (isset($priceData['price_from'])) { + $price = $priceData['price_from']; } } + if (null === $price) { + $configurableOptions = $this->product->getConfigurableAttributesData(); + foreach ($configurableOptions['matrix'] as $option) { + $price = $price === null ? $option['price'] : $price; + if ($price > $option['price']) { + $price = $option['price']; + } + } + } return $price; } } diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml index 2e9d708dd42d82ca79d0507b6a698c0c5c897d3b..1b6282b9432c39fd444ca2089f1b8c8aa6bfb876 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml @@ -17,5 +17,8 @@ <dataset name="MAGETWO-12620"> <field name="category_price" xsi:type="string">11</field> </dataset> + <dataset name="from-9"> + <field name="price_from" xsi:type="string">9</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml index 58435b066261ed28cc7f33ff01fc250940b6eb17..187283da4602fd56c6fb6f2441d11dc5c1ad05bd 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateConfigurableProductEntityTest.xml @@ -58,6 +58,7 @@ <data name="product/data/checkout_data/dataset" xsi:type="string">configurable_two_new_options_with_special_price</data> <data name="product/data/name" xsi:type="string">Configurable Product %isolation%</data> <data name="product/data/sku" xsi:type="string">configurable_sku_%isolation%</data> + <data name="product/data/price/dataset" xsi:type="string">from-9</data> <data name="product/data/price/value" xsi:type="string">100</data> <data name="product/data/special_price" xsi:type="string">9</data> <data name="product/data/short_description" xsi:type="string">Configurable short description</data> diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/Block/WebConfiguration.php b/dev/tests/functional/tests/app/Magento/Install/Test/Block/WebConfiguration.php index 03dbfee05ca6973d90da4a4205eb1b64f9f2edca..bf4886a9f208d4a9bf308ee8f5f8b26daf9c6d6e 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/Block/WebConfiguration.php +++ b/dev/tests/functional/tests/app/Magento/Install/Test/Block/WebConfiguration.php @@ -21,7 +21,7 @@ class WebConfiguration extends Form * * @var string */ - protected $next = "[ng-click*='next']"; + protected $next = "[ng-click*='validateUrl']"; /** * 'Advanced Options' locator. diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/ExpressCheckout.php b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/ExpressCheckout.php index d83ae0c53ec0a575ba11280b9a8731bbe23a6f8a..6e4849669568851cbe4787766c6cf8b2f630e9e6 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/ExpressCheckout.php +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/ExpressCheckout.php @@ -20,8 +20,6 @@ class ExpressCheckout extends Block * @var array */ private $fields = [ - 'Email Associated with PayPal Merchant Account' => '#payment_us_paypal_alternative_payment_methods_express_' . - 'checkout_us_express_checkout_required_express_checkout_required_express_checkout_business_account', 'API Username' => '#payment_us_paypal_alternative_payment_methods_express_checkout_us_express_checkout_' . 'required_express_checkout_required_express_checkout_api_username', 'API Password' => '#payment_us_paypal_alternative_payment_methods_express_checkout_us_express_checkout_' . @@ -72,8 +70,6 @@ class ExpressCheckout extends Block */ public function specifyCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account']) - ->setValue('test@test.com'); $this->_rootElement->find($this->fields['API Username'])->setValue('1'); $this->_rootElement->find($this->fields['API Password'])->setValue('1'); $this->_rootElement->find($this->fields['API Signature'])->setValue('1'); @@ -86,7 +82,6 @@ class ExpressCheckout extends Block */ public function clearCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account'])->setValue(''); $this->_rootElement->find($this->fields['API Username'])->setValue(''); $this->_rootElement->find($this->fields['API Password'])->setValue(''); $this->_rootElement->find($this->fields['API Signature'])->setValue(''); diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowLink.php b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowLink.php index de6eb107c61ce5e6b63451abdea0473351ddde43..f1af6acb6174e1b462651b7887d80159d6910952 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowLink.php +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowLink.php @@ -20,8 +20,6 @@ class PayflowLink extends Block * @var array */ private $fields = [ - 'Email Associated with PayPal Merchant Account' => '#payment_us_paypal_payment_gateways_payflow_link_us_' . - 'payflow_link_required_payflow_link_payflow_link_business_account', 'Partner' => '#payment_us_paypal_payment_gateways_payflow_link_us_payflow_link_required_payflow_link_payflow_' . 'link_partner', 'Vendor' => '#payment_us_paypal_payment_gateways_payflow_link_us_payflow_link_required_payflow_link_payflow_' . @@ -60,8 +58,6 @@ class PayflowLink extends Block */ public function specifyCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account']) - ->setValue('test@test.com'); $this->_rootElement->find($this->fields['Partner'])->setValue('1'); $this->_rootElement->find($this->fields['Vendor'])->setValue('1'); $this->_rootElement->find($this->fields['User'])->setValue('1'); @@ -75,7 +71,6 @@ class PayflowLink extends Block */ public function clearCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account'])->setValue(''); $this->_rootElement->find($this->fields['Partner'])->setValue(''); $this->_rootElement->find($this->fields['Vendor'])->setValue(''); $this->_rootElement->find($this->fields['User'])->setValue(''); diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowPro.php b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowPro.php index e3b01aa3af9a0dfa4517ad3fe4b527eec9d30ad0..1db07ac435a37ed9c9d9d237f62b7debed956861 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowPro.php +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PayflowPro.php @@ -20,8 +20,6 @@ class PayflowPro extends Block * @var array */ private $fields = [ - 'Email Associated with PayPal Merchant Account' => '#payment_us_paypal_payment_gateways_paypal_payflowpro_' . - 'with_express_checkout_paypal_payflow_required_paypal_payflow_api_settings_business_account', 'Partner' => '#payment_us_paypal_payment_gateways_paypal_payflowpro_with_express_checkout_paypal_payflow_' . 'required_paypal_payflow_api_settings_partner', 'Vendor' => '#payment_us_paypal_payment_gateways_paypal_payflowpro_with_express_checkout_paypal_payflow_' . @@ -60,8 +58,6 @@ class PayflowPro extends Block */ public function specifyCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account']) - ->setValue('test@test.com'); $this->_rootElement->find($this->fields['Partner'])->setValue('1'); $this->_rootElement->find($this->fields['Vendor'])->setValue('1'); $this->_rootElement->find($this->fields['User'])->setValue('1'); @@ -75,7 +71,6 @@ class PayflowPro extends Block */ public function clearCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account'])->setValue(''); $this->_rootElement->find($this->fields['Partner'])->setValue(''); $this->_rootElement->find($this->fields['Vendor'])->setValue(''); $this->_rootElement->find($this->fields['User'])->setValue(''); diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsAdvanced.php b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsAdvanced.php index cd6d1a5b1b38a016382ffa2e68ab73bd566c4116..87adb729a1646e6eb5b61e8e167d2eed20111e4e 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsAdvanced.php +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsAdvanced.php @@ -20,8 +20,6 @@ class PaymentsAdvanced extends Block * @var array */ private $fields = [ - 'Email Associated with PayPal Merchant Account' => '#payment_us_paypal_group_all_in_one_payflow_advanced_' . - 'required_settings_payments_advanced_business_account', 'Partner' => '#payment_us_paypal_group_all_in_one_payflow_advanced_required_settings_payments_advanced_partner', 'Vendor' => '#payment_us_paypal_group_all_in_one_payflow_advanced_required_settings_payments_advanced_vendor', 'User' => '#payment_us_paypal_group_all_in_one_payflow_advanced_required_settings_payments_advanced_user', @@ -54,8 +52,6 @@ class PaymentsAdvanced extends Block */ public function specifyCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account']) - ->setValue('test@test.com'); $this->_rootElement->find($this->fields['Partner'])->setValue('1'); $this->_rootElement->find($this->fields['Vendor'])->setValue('1'); $this->_rootElement->find($this->fields['User'])->setValue('1'); @@ -69,7 +65,6 @@ class PaymentsAdvanced extends Block */ public function clearCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account'])->setValue(''); $this->_rootElement->find($this->fields['Partner'])->setValue(''); $this->_rootElement->find($this->fields['Vendor'])->setValue(''); $this->_rootElement->find($this->fields['User'])->setValue(''); diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsPro.php b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsPro.php index cd146aa8f809cf5de0cbddcb02be83a19e59928f..55fd7444ba50ad8dbf9662b758b10d7a70ee6c6d 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsPro.php +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/Block/System/Config/PaymentsPro.php @@ -20,8 +20,6 @@ class PaymentsPro extends Block * @var array */ private $fields = [ - 'Email Associated with PayPal Merchant Account' => '#payment_us_paypal_group_all_in_one_wpp_usuk_paypal_' . - 'payflow_required_paypal_payflow_api_settings_business_account', 'Partner' => '#payment_us_paypal_group_all_in_one_wpp_usuk_paypal_payflow_required_paypal_payflow_api_' . 'settings_partner', 'Vendor' => '#payment_us_paypal_group_all_in_one_wpp_usuk_paypal_payflow_required_paypal_payflow_api_' . @@ -60,8 +58,6 @@ class PaymentsPro extends Block */ public function specifyCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account']) - ->setValue('test@test.com'); $this->_rootElement->find($this->fields['Partner'])->setValue('1'); $this->_rootElement->find($this->fields['Vendor'])->setValue('1'); $this->_rootElement->find($this->fields['User'])->setValue('1'); @@ -75,7 +71,6 @@ class PaymentsPro extends Block */ public function clearCredentials() { - $this->_rootElement->find($this->fields['Email Associated with PayPal Merchant Account'])->setValue(''); $this->_rootElement->find($this->fields['Partner'])->setValue(''); $this->_rootElement->find($this->fields['Vendor'])->setValue(''); $this->_rootElement->find($this->fields['User'])->setValue(''); diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleForm.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleForm.php index bfe0bc9eabddfb0cc0f746f79efdad5b6ad0a43b..1f3f0e5a300b46ac9a193666796e52c584b8c944 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleForm.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleForm.php @@ -25,8 +25,6 @@ class AssertCartPriceRuleForm extends AbstractConstraint protected $skippedFields = [ 'conditions_serialized', 'actions_serialized', - 'from_date', - 'to_date', 'rule_id' ]; diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml index 13b68aca6c6eb7867286019b3a9609b3b93db2a8..3a35215d841d50780d212084d76d014af8708aa4 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml @@ -114,10 +114,10 @@ <field name="uses_per_coupon" xsi:type="string">13</field> <field name="uses_per_customer" xsi:type="string">63</field> <field name="from_date" xsi:type="array"> - <item name="pattern" xsi:type="string">3/25/2014</item> + <item name="pattern" xsi:type="string">03/25/2014</item> </field> <field name="to_date" xsi:type="array"> - <item name="pattern" xsi:type="string">6/29/2024</item> + <item name="pattern" xsi:type="string">-</item> </field> <field name="sort_order" xsi:type="string">1</field> <field name="is_rss" xsi:type="string">Yes</field> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4b42430c0b8a81987b78d907f8b4a4c56d6db834 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\TestCase; + + +use Magento\User\Test\Page\Adminhtml\UserEdit; +use Magento\User\Test\Page\Adminhtml\UserIndex; +use Magento\Mtf\TestCase\Injectable; +use Magento\User\Test\Fixture\User; +use Magento\Backend\Test\Page\AdminAuthLogin; + +/** + * Preconditions: + * 1. Create admin user. + * 2. Configure 'Maximum Login Failures to Lockout Account'. + * + * Steps: + * 1. Log in to backend as admin user. + * 2. Navigate to System > All Users. + * 3. Click on Add New User. + * 4. Fill in all data according to data set (password is incorrect). + * 5. Perform action 4 specified number of times. + * 6. "You have entered an invalid password for current user." appears after each attempt. + * 7. Perform all assertions. + * + * @ZephyrId MAGETWO-49034 + */ +class LockAdminUserWhenCreatingNewUserTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S2'; + /* end tags */ + + /** + * User grid page + * + * @var UserIndex + */ + protected $userIndexPage; + + /** + * User new/edit page + * + * @var UserEdit + */ + protected $userEditPage; + + /** + * Configuration setting. + * + * @var string + */ + protected $configData; + + /** + * @var AdminAuthLogin page + */ + protected $adminAuthLogin; + + /** + * Setup data for test. + * @param UserIndex $userIndex + * @param UserEdit $userEdit + * @param AdminAuthLogin $adminAuthLogin + */ + public function __inject( + UserIndex $userIndex, + UserEdit $userEdit, + AdminAuthLogin $adminAuthLogin + ) { + $this->userIndexPage = $userIndex; + $this->userEditPage = $userEdit; + $this->adminAuthLogin = $adminAuthLogin; + } + + /** + * Runs Lock admin user when creating new user test. + * + * @param int $attempts + * @param User $customAdmin, + * @param User $user, + * @param string $configData + * @return void + */ + public function test( + $attempts, + User $customAdmin, + User $user, + $configData + ) { + $this->configData = $configData; + + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + $customAdmin->persist(); + + // Steps + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + $this->userIndexPage->open(); + $this->userIndexPage->getPageActions()->addNew(); + for ($i = 0; $i < $attempts; $i++) { + $this->userEditPage->getUserForm()->fill($user); + $this->userEditPage->getPageActions()->save(); + } + + // Reload + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..e36f8b4625dd66eeb5a41f15c74c4bdf6e7eeeea --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Security\Test\TestCase\LockAdminUserWhenCreatingNewUserTest" summary="Lock admin user after entering incorrect password while creating new User"> + <variation name="LockAdminUserWhenCreatingNewUserTestVariation1"> + <data name="configData" xsi:type="string">user_lockout_failures</data> + <data name="tag" xsi:type="string">severity:S2</data> + <data name="customAdmin/dataset" xsi:type="string">custom_admin_with_default_role</data> + <data name="user/data/username" xsi:type="string">AdminUser%isolation%</data> + <data name="user/data/firstname" xsi:type="string">FirstName%isolation%</data> + <data name="user/data/lastname" xsi:type="string">LastName%isolation%</data> + <data name="user/data/email" xsi:type="string">email%isolation%@example.com</data> + <data name="user/data/password" xsi:type="string">123123q</data> + <data name="user/data/password_confirmation" xsi:type="string">123123q</data> + <data name="user/data/current_password" xsi:type="string">incorrect password</data> + <data name="attempts" xsi:type="string">4</data> + <constraint name="Magento\Security\Test\Constraint\AssertUserIsLocked" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4a1c1d0507c91ea3b9a86d254f6f2c01d58a7d2d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\TestCase; + +use Magento\Integration\Test\Fixture\Integration; +use Magento\User\Test\Fixture\User; +use Magento\Integration\Test\Page\Adminhtml\IntegrationIndex; +use Magento\Integration\Test\Page\Adminhtml\IntegrationNew; +use Magento\Mtf\TestCase\Injectable; +use Magento\Backend\Test\Page\AdminAuthLogin; + +/** + * Preconditions: + * 1. Create admin user. + * 2. Create integration. + * 3. Configure 'Maximum Login Failures to Lockout Account'. + * + * Steps: + * 1. Log in to backend as admin user. + * 2. Navigate to System > Extensions > Integrations. + * 3. Start to edit existing Integration. + * 4. Fill in all data according to data set (password is incorrect). + * 5. Perform action 4 specified number of times. + * 6. "You have entered an invalid password for current user." appears after each attempt. + * 7. Perform all assertions. + * + * @ZephyrId MAGETWO-49039 + */ +class LockAdminUserWhenEditingIntegrationTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S2'; + /* end tags */ + + /** + * Integration grid page. + * + * @var IntegrationIndex + */ + protected $integrationIndexPage; + + /** + * Integration new page. + * + * @var IntegrationNew + */ + protected $integrationNewPage; + + /** + * Configuration setting. + * + * @var string + */ + protected $configData; + + /** + * @var AdminAuthLogin + */ + protected $adminAuthLogin; + + /** + * Preparing pages for test. + * + * @param IntegrationIndex $integrationIndex + * @param IntegrationNew $integrationNew + * @param AdminAuthLogin $adminAuthLogin + * @return void + */ + public function __inject( + IntegrationIndex $integrationIndex, + IntegrationNew $integrationNew, + AdminAuthLogin $adminAuthLogin + ) { + $this->integrationIndexPage = $integrationIndex; + $this->integrationNewPage = $integrationNew; + $this->adminAuthLogin = $adminAuthLogin; + } + + /** + * Run Lock user when creating new integration test. + * + * @param Integration $initintegration + * @param Integration $integration + * @param int $attempts + * @param User $customAdmin + * @param string $configData + * @return void + */ + public function test( + Integration $initintegration, + Integration $integration, + $attempts, + User $customAdmin, + $configData + ) { + $this->configData = $configData; + + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + $customAdmin->persist(); + $initintegration->persist(); + + // login to backend with new user + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + + // Steps + $filter = ['name' => $initintegration->getName()]; + $this->integrationIndexPage->open(); + $this->integrationIndexPage->getIntegrationGrid()->searchAndOpen($filter); + for ($i = 0; $i < $attempts; $i++) { + $this->integrationNewPage->getIntegrationForm()->fill($integration); + $this->integrationNewPage->getFormPageActions()->save(); + } + + // Reload page + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..6efc88d78cd3836f00db79d2c567d3e6e38d1bb1 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingIntegrationTest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Security\Test\TestCase\LockAdminUserWhenEditingIntegrationTest" summary="Lock admin user after entering incorrect password while editing integration"> + <variation name="LockAdminUserWhenCreatingNewIntegrationTestVariation1"> + <data name="configData" xsi:type="string">user_lockout_failures</data> + <data name="tag" xsi:type="string">severity:S2</data> + <data name="customAdmin/dataset" xsi:type="string">custom_admin_with_default_role</data> + <data name="initintegration/dataset" xsi:type="string">default_active</data> + <data name="integration/data/name" xsi:type="string">Integration%isolation%</data> + <data name="integration/data/current_password" xsi:type="string">incorrect password</data> + <data name="attempts" xsi:type="string">4</data> + <constraint name="Magento\Security\Test\Constraint\AssertUserIsLocked" /> + </variation> + </testCase> +</config> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bd7c8cef92d8a320b43615983a95ea724cce0d78 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.php @@ -0,0 +1,140 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Security\Test\TestCase; + +use Magento\User\Test\Page\Adminhtml\UserRoleEditRole; +use Magento\User\Test\Page\Adminhtml\UserRoleIndex; +use Magento\Mtf\TestCase\Injectable; +use Magento\User\Test\Fixture\User; +use Magento\User\Test\Fixture\Role; +use Magento\Backend\Test\Page\AdminAuthLogin; + +/** + * Preconditions: + * 1. Create new admin user and assign it to new role. + * 2. Configure 'Maximum Login Failures to Lockout Account'. + * + * Steps: + * 1. Log in to backend as new created admin user. + * 2. Navigate to System > User Roles. + * 3. Start editing existing User Role. + * 4. Fill in all data according to data set (password is incorrect). + * 5. Perform action 4 specified number of times. + * 6. Admin account is locked. + * 7. Perform all assertions. + * + * @ZephyrId MAGETWO-49037 + * @Group Security + * + */ +class LockAdminUserWhenEditingRoleTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S2'; + /* end tags */ + + /** + * UserRoleIndex page. + * + * @var UserRoleIndex + */ + protected $userRoleIndex; + + /** + * UserRoleEditRole page. + * + * @var UserRoleEditRole + */ + protected $userRoleEditRole; + + /** + * Configuration setting. + * + * @var string + */ + protected $configData; + + /** + * Admin login Page. + * + * @var AdminAuthLogin + */ + protected $adminAuthLogin; + + /** + * Setup data for test. + * + * @param UserRoleIndex $userRoleIndex + * @param UserRoleEditRole $userRoleEditRole + * @param AdminAuthLogin $adminAuthLogin + * @return void + */ + public function __inject( + UserRoleIndex $userRoleIndex, + UserRoleEditRole $userRoleEditRole, + AdminAuthLogin $adminAuthLogin + ) { + $this->userRoleIndex = $userRoleIndex; + $this->userRoleEditRole = $userRoleEditRole; + $this->adminAuthLogin = $adminAuthLogin; + } + + /** + * Runs Lock admin user when editing existing role test. + * + * @param Role $role + * @param Role $initrole + * @param int $attempts + * @param User $customAdmin + * @param string $configData + * @return void + */ + public function test( + Role $role, + Role $initrole, + $attempts, + User $customAdmin, + $configData + ) { + $this->configData = $configData; + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + $customAdmin->persist(); + $initrole->persist(); + // Steps login to backend with new user + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + $filter = ['rolename' => $initrole->getRolename()]; + $this->userRoleIndex->open(); + $this->userRoleIndex->getRoleGrid()->searchAndOpen($filter); + for ($i = 0; $i < $attempts; $i++) { + $this->userRoleEditRole->getRoleFormTabs()->fill($role); + $this->userRoleEditRole->getPageActions()->save(); + } + // Reload + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d081e5d2dd9af69f90aeed8b787c966238e62de --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingRoleTest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Security\Test\TestCase\LockAdminUserWhenEditingRoleTest" summary="Lock admin user after entering incorrect password while editing existing role"> + <variation name="LockAdminUserWhenEditingUserRoleTestVariation1"> + <data name="configData" xsi:type="string">user_lockout_failures</data> + <data name="tag" xsi:type="string">severity:S2</data> + <data name="initrole/dataset" xsi:type="string">default</data> + <data name="customAdmin/dataset" xsi:type="string">custom_admin_with_default_role</data> + <data name="role/data/rolename" xsi:type="string">NewAdminRole%isolation%</data> + <data name="role/data/current_password" xsi:type="string">incorrect password</data> + <data name="role/data/resource_access" xsi:type="string">All</data> + <data name="attempts" xsi:type="string">4</data> + <constraint name="Magento\Security\Test\Constraint\AssertUserIsLocked" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.php new file mode 100644 index 0000000000000000000000000000000000000000..912ebef9e91054236cc6b85b91c3cd8d973b54e9 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\TestCase; + +use Magento\User\Test\Page\Adminhtml\UserEdit; +use Magento\User\Test\Page\Adminhtml\UserIndex; +use Magento\Backend\Test\Page\AdminAuthLogin; +use Magento\User\Test\Fixture\User; +use Magento\Mtf\TestCase\Injectable; + +/** + * Preconditions: + * 1. Create new admin user. + * 2. Configure 'Maximum Login Failures to Lockout Account'. + * + * Steps: + * 1. Log in to backend as new created admin user. + * 2. Navigate to System > All Users. + * 3. Start editing existing User. + * 4. Fill in all data according to data set (password is incorrect). + * 5. Perform action 4 specified number of times. + * 6. Admin account is locked. + * 7. Perform all assertions. + * + * @ZephyrId MAGETWO-49035 + */ +class LockAdminUserWhenEditingUserTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S2'; + /* end tags */ + + /** + * User grid page + * + * @var UserIndex + */ + protected $userIndexPage; + + /** + * User edit page + * + * @var UserEdit + */ + protected $userEditPage; + + /** + * @var $configData + */ + protected $configData; + + /** + * @var AdminAuthLogin page + */ + protected $adminAuthLogin; + + /** + * Setup data for test. + * @param UserIndex $userIndex + * @param UserEdit $userEdit + * @param AdminAuthLogin $adminAuthLogin + */ + public function __inject( + UserIndex $userIndex, + UserEdit $userEdit, + AdminAuthLogin $adminAuthLogin + ) { + $this->userIndexPage = $userIndex; + $this->userEditPage = $userEdit; + $this->adminAuthLogin = $adminAuthLogin; + } + + /** + * Runs Lock admin user when editing existing role test. + * + * @param User $user + * @param int $attempts + * @param User $customAdmin + * @param string $configData + * @return void + */ + public function test( + $attempts, + User $customAdmin, + User $user, + $configData + ) { + $this->configData = $configData; + + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + $customAdmin->persist(); + + // Steps login to backend with new user + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + // Select user to edit. + $filter = ['username' => $customAdmin->getUsername()]; + $this->userIndexPage->open(); + $this->userIndexPage->getUserGrid()->searchAndOpen($filter); + // Edit user with wrong password + for ($i = 0; $i < $attempts; $i++) { + $this->userEditPage->getUserForm()->fill($user); + $this->userEditPage->getPageActions()->save(); + } + // Reload + $this->adminAuthLogin->open(); + $this->adminAuthLogin->getLoginBlock()->fill($customAdmin); + $this->adminAuthLogin->getLoginBlock()->submit(); + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..e1ec2f79ce6b5d306e694c9d2ae8e8adea0d3387 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenEditingUserTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Security\Test\TestCase\LockAdminUserWhenEditingUserTest" summary="Lock admin user after entering incorrect password while editing existing user"> + <variation name="LockAdminUserWhenEditingUseruserTestVariation1"> + <data name="configData" xsi:type="string">user_lockout_failures</data> + <data name="tag" xsi:type="string">severity:S2</data> + <data name="customAdmin/dataset" xsi:type="string">custom_admin_with_default_role</data> + <data name="user/data/username" xsi:type="string">AdminUser%isolation%</data> + <data name="user/data/firstname" xsi:type="string">FirstName%isolation%</data> + <data name="user/data/lastname" xsi:type="string">LastName%isolation%</data> + <data name="user/data/email" xsi:type="string">email%isolation%@example.com</data> + <data name="user/data/password" xsi:type="string">123123qq</data> + <data name="user/data/password_confirmation" xsi:type="string">123123qq</data> + <data name="user/data/current_password" xsi:type="string">incorrect password</data> + <data name="attempts" xsi:type="string">4</data> + <constraint name="Magento\Security\Test\Constraint\AssertUserIsLocked" /> + </variation> + </testCase> +</config> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php index 80df7e6f8c631722fb314525b2d6733e2ac605ad..95240b5dba0644d7f10d80447399d516a4742ac2 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php @@ -165,7 +165,7 @@ class DataGrid extends Grid */ protected function waitFilterToLoad() { - $this->getTemplateBlock()->waitForElementNotVisible($this->loader); + $this->getTemplateBlock()->waitLoader(); $browser = $this->_rootElement; $selector = $this->filterButton . ', ' . $this->resetButton; $browser->waitUntil( diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/Product.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/Product.php index 076739590e1f19b86fee7bc105221f51efda34cb..683ff71e8ff7212e4e9604d581d04b4cf9b0f238 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/Product.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/Product.php @@ -164,6 +164,18 @@ class Product extends Form $this->_rootElement->find($this->price)->hover(); } + /** + * Returns product price + * + * @param string $currency + * @return string + */ + public function getPrice($currency = '$') + { + $price = $this->_rootElement->find($this->price)->getText(); + return str_replace($currency, '', $price); + } + /** * Get Wish List data for the Product. * diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductPriceIsNotZero.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductPriceIsNotZero.php new file mode 100644 index 0000000000000000000000000000000000000000..24f6222dac09eb34cf3c3d6c5a063341b2acc2ef --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductPriceIsNotZero.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Wishlist\Test\Constraint; + +class AssertProductPriceIsNotZero extends \Magento\Mtf\Constraint\AbstractConstraint +{ + /** + * Assert that product price is not zero in default wishlist. + * + * @param \Magento\Cms\Test\Page\CmsIndex $cmsIndex + * @param \Magento\Customer\Test\Page\CustomerAccountIndex $customerAccountIndex + * @param \Magento\Wishlist\Test\Page\WishlistIndex $wishlistIndex + * @param \Magento\Mtf\Fixture\InjectableFixture $product + * + * @return void + */ + public function processAssert( + \Magento\Cms\Test\Page\CmsIndex $cmsIndex, + \Magento\Customer\Test\Page\CustomerAccountIndex $customerAccountIndex, + \Magento\Wishlist\Test\Page\WishlistIndex $wishlistIndex, + \Magento\Mtf\Fixture\InjectableFixture $product + ) { + $cmsIndex->getLinksBlock()->openLink('My Account'); + $customerAccountIndex->getAccountMenuBlock()->openMenuItem('My Wish List'); + $wishlistItem = $wishlistIndex->getWishlistBlock()->getProductItemsBlock()->getItemProduct($product); + + \PHPUnit_Framework_Assert::assertNotEquals( + '0.00', + $wishlistItem->getPrice(), + $product->getName() . ' has zero price on Wish List page.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Product price is not zero in default Wish List.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php index 9959796828df23943db8fc3f6dfdce4b6387e1ac..bd922feee99d0315b5f7f16ee13b60eb101b626e 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php @@ -48,15 +48,16 @@ class AddProductToWishlistEntityTest extends AbstractWishlistTest * * @param Customer $customer * @param string $product + * @param bool $configure * @return array */ - public function test(Customer $customer, $product) + public function test(Customer $customer, $product, $configure = true) { $product = $this->createProducts($product)[0]; // Steps: $this->loginCustomer($customer); - $this->addToWishlist([$product], true); + $this->addToWishlist([$product], $configure); return ['product' => $product]; } diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml index 0557fa298ce985313593216b561b181175719a70..550cffa488130e16f73a9d89b9bc4344a57a2291 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml @@ -49,5 +49,12 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInCustomerBackendWishlist" /> </variation> + <variation name="AddProductToWishlistEntityTestVariation8"> + <data name="product/0" xsi:type="string">configurableProduct::default</data> + <data name="configure" xsi:type="boolean">false</data> + <constraint name="Magento\Wishlist\Test\Constraint\AssertAddProductToWishlistSuccessMessage" /> + <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> + <constraint name="Magento\Wishlist\Test\Constraint\AssertProductPriceIsNotZero" /> + </variation> </testCase> </config> diff --git a/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/module.xml new file mode 100644 index 0000000000000000000000000000000000000000..ce5ed9cb663a759edf057291f11ccc6a75fe6266 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleDirectoryZipCodes" setup_version="0.0.1" active="true"> + <sequence> + <module name="Magento_Directory"/> + </sequence> + </module> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/zip_codes.xml b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/zip_codes.xml new file mode 100644 index 0000000000000000000000000000000000000000..d4f121c60daeb8c44f327f8149f0148051c252ff --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/etc/zip_codes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Directory:etc/zip_codes.xsd"> + <zip countryCode="NL"> + <codes> + <code id="pattern_1" active="true" example="test1">^[0-9]{4}\s[a-zA-Z]{2}$</code> + <code id="pattern_2" active="true" example="test2">^[0-5]{4}[a-z]{2}$</code> + </codes> + </zip> + <zip countryCode="NL_NEW"> + <codes> + <code id="pattern_1" active="true" example="test1">^[0-2]{4}[A-Z]{2}$</code> + </codes> + </zip> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/registration.php b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/registration.php new file mode 100644 index 0000000000000000000000000000000000000000..2ed368ecf3b23a4bd4db86357f6cbbca0bfc55de --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleDirectoryZipCodes/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleDirectoryZipCodes') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleDirectoryZipCodes', __DIR__); +} diff --git a/dev/tests/integration/_files/Magento/TestModuleSample/composer.json b/dev/tests/integration/_files/Magento/TestModuleSample/composer.json index 4d6383045d27f2123e84146eae970ec74ae77797..3e8e5650b85a77f75c079ae0ad9211a8d4d0764a 100644 --- a/dev/tests/integration/_files/Magento/TestModuleSample/composer.json +++ b/dev/tests/integration/_files/Magento/TestModuleSample/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sample-test", "description": "test sample module", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.1.*", "magento/module-integration": "100.1.*" }, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 0bbf2613febcfe533b5523a8b5a86dabae349076..8775eb7d54d0fd59c8c09aeaf000149a24f3d2bf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1432,4 +1432,113 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase $this->assertEquals(implode(',', [$multiselectOptions[1]->getValue(), $multiselectOptions[2]->getValue()]), $product2->getData('multiselect_attribute')); } + + /** + * @param array $row + * @param string|null $behavior + * @param bool $expectedResult + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + * @dataProvider validateRowDataProvider + */ + public function testValidateRow(array $row, $behavior, $expectedResult) + { + $this->_model->setParameters(['behavior' => $behavior, 'entity' => 'catalog_product']); + $this->assertSame($expectedResult, $this->_model->validateRow($row, 1)); + } + + /** + * @return array + */ + public function validateRowDataProvider() + { + return [ + [ + 'row' => ['sku' => 'simple products'], + 'behavior' => null, + 'expectedResult' => true, + ], + [ + 'row' => ['sku' => 'simple products absent'], + 'behavior' => null, + 'expectedResult' => false, + ], + [ + 'row' => [ + 'sku' => 'simple products absent', + 'name' => 'Test', + 'product_type' => 'simple', + '_attribute_set' => 'Default', + 'price' => 10.20, + ], + 'behavior' => null, + 'expectedResult' => true, + ], + [ + 'row' => ['sku' => 'simple products'], + 'behavior' => Import::BEHAVIOR_ADD_UPDATE, + 'expectedResult' => true, + ], + [ + 'row' => ['sku' => 'simple products absent'], + 'behavior' => Import::BEHAVIOR_ADD_UPDATE, + 'expectedResult' => false, + ], + [ + 'row' => [ + 'sku' => 'simple products absent', + 'name' => 'Test', + 'product_type' => 'simple', + '_attribute_set' => 'Default', + 'price' => 10.20, + ], + 'behavior' => Import::BEHAVIOR_ADD_UPDATE, + 'expectedResult' => true, + ], + [ + 'row' => ['sku' => 'simple products'], + 'behavior' => Import::BEHAVIOR_DELETE, + 'expectedResult' => true, + ], + [ + 'row' => ['sku' => 'simple products absent'], + 'behavior' => Import::BEHAVIOR_DELETE, + 'expectedResult' => false, + ], + [ + 'row' => ['sku' => 'simple products'], + 'behavior' => Import::BEHAVIOR_REPLACE, + 'expectedResult' => false, + ], + [ + 'row' => ['sku' => 'simple products absent'], + 'behavior' => Import::BEHAVIOR_REPLACE, + 'expectedResult' => false, + ], + [ + 'row' => [ + 'sku' => 'simple products absent', + 'name' => 'Test', + 'product_type' => 'simple', + '_attribute_set' => 'Default', + 'price' => 10.20, + ], + 'behavior' => Import::BEHAVIOR_REPLACE, + 'expectedResult' => false, + ], + [ + 'row' => [ + 'sku' => 'simple products', + 'name' => 'Test', + 'product_type' => 'simple', + '_attribute_set' => 'Default', + 'price' => 10.20, + ], + 'behavior' => Import::BEHAVIOR_REPLACE, + 'expectedResult' => true, + ], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 34a484f749b7807a7096e22fc24c21b8b6e6c5dd..54a068dad5d0a26f1564d662eb962eca64527110 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -229,4 +229,51 @@ class CartTest extends \Magento\TestFramework\TestCase\AbstractController } return null; } + + /** + * Test for \Magento\Checkout\Controller\Cart::execute() with simple product + * + * @param string $area + * @param string $expectedPrice + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoAppIsolation enabled + * @dataProvider addAddProductDataProvider + */ + public function testAddToCartSimpleProduct($area, $expectedPrice) + { + $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class); + $postData = [ + 'qty' => '1', + 'product' => '1', + 'custom_price' => 1, + 'form_key' => $formKey->getFormKey(), + 'isAjax' => 1 + ]; + \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea($area); + $this->getRequest()->setPostValue($postData); + + $quote = $this->_objectManager->create(\Magento\Checkout\Model\Cart::class); + /** @var \Magento\Checkout\Controller\Cart\Add $controller */ + $controller = $this->_objectManager->create(\Magento\Checkout\Controller\Cart\Add::class, [$quote]); + $controller->execute(); + + $this->assertContains(json_encode([]), $this->getResponse()->getBody()); + $items = $quote->getItems()->getItems(); + $this->assertTrue(is_array($items), 'Quote doesn\'t have any items'); + $this->assertCount(1, $items, 'Expected quote items not equal to 1'); + $item = reset($items); + $this->assertEquals(1, $item->getProductId(), 'Quote has more than one product'); + $this->assertEquals($expectedPrice, $item->getPrice(), 'Expected product price failed'); + } + + /** + * Data provider for testAddToCartSimpleProduct + */ + public function addAddProductDataProvider() + { + return [ + 'frontend' => ['frontend', 'expected_price' => 10], + 'adminhtml' => ['adminhtml', 'expected_price' => 1] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Backend/BaseurlTest.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Backend/BaseurlTest.php index 0ce30e5b2984120c7e325a151d96c0ef044affa6..5a2db8ccb22fa0484ab71c04f8a6bbc4cbc7b0e6 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Backend/BaseurlTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Backend/BaseurlTest.php @@ -95,33 +95,44 @@ class BaseurlTest extends \PHPUnit_Framework_TestCase $unsecurePlaceholder = '{{unsecure_base_url}}'; $unsecureSuffix = '{{unsecure_base_url}}test/'; $unsecureWrongSuffix = '{{unsecure_base_url}}test'; + $unsecureWrongDomainName = 'http://example.com_test/'; $securePlaceholder = '{{secure_base_url}}'; $secureSuffix = '{{secure_base_url}}test/'; $secureWrongSuffix = '{{secure_base_url}}test'; + $secureWrongDomainName = 'https://example.com_test/'; return [ ['', 'not a valid URL'], ['', 'example.com'], ['', 'http://example.com'], ['', 'http://example.com/uri'], + ['', $unsecureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, ''], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, $baseSuffix], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, $unsecureSuffix], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, $unsecurePlaceholder], + [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, $unsecureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_LINK_URL, ''], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_LINK_URL, $baseSuffix], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_LINK_URL, $unsecureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_LINK_URL, $unsecureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_MEDIA_URL, $unsecureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_MEDIA_URL, $unsecureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_STATIC_URL, $unsecureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_STATIC_URL, $unsecureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, ''], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, $baseSuffix], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, $secureSuffix], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, $securePlaceholder], + [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, $secureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_LINK_URL, ''], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_LINK_URL, $baseSuffix], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_LINK_URL, $secureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_LINK_URL, $secureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_MEDIA_URL, $secureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_MEDIA_URL, $secureWrongDomainName], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_STATIC_URL, $secureWrongSuffix], + [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_STATIC_URL, $secureWrongDomainName], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5db97cc36614924ae915b7b924b2c7a1f575e344 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoAppArea adminhtml + */ +class ConfigurableTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $this->storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testGetProductFinalPriceIfOneOfChildIsDisabled() + { + /** @var Collection $collection */ + $collection = Bootstrap::getObjectManager()->get(CollectionFactory::class) + ->create(); + $configurableProduct = $collection + ->addIdFilter([1]) + ->addMinimalPrice() + ->load() + ->getFirstItem(); + $this->assertEquals(10, $configurableProduct->getMinimalPrice()); + + $childProduct = $this->productRepository->getById(10, false, null, true); + $childProduct->setStatus(Status::STATUS_DISABLED); + // update in global scope + $currentStoreId = $this->storeManager->getStore()->getId(); + $this->storeManager->setCurrentStore(Store::ADMIN_CODE); + $this->productRepository->save($childProduct); + $this->storeManager->setCurrentStore($currentStoreId); + + /** @var Collection $collection */ + $collection = Bootstrap::getObjectManager()->get(CollectionFactory::class) + ->create(); + $configurableProduct = $collection + ->addIdFilter([1]) + ->addMinimalPrice() + ->load() + ->getFirstItem(); + $this->assertEquals(20, $configurableProduct->getMinimalPrice()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php index ff08056e4b18def3f520d2a5d9f2090b168d5ec8..0d8361b4751e38a96ae4dbb7aadb86510dad92dc 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php @@ -1,5 +1,7 @@ <?php /** + * Creates a simple product to be used for test cases. + * * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -72,7 +74,8 @@ $product->setTypeId(Type::TYPE_SIMPLE) 'is_in_stock' => 1, ] )->setCanSaveCustomOptions(true) - ->setHasOptions(true); + ->setHasOptions(true) + ->setCustomAttribute('test_configurable', 42); $oldOptions = [ [ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 1576ff90cb38ee4cd89b68766395fa0746d071d6..5f9b9e928b3178f3ae06a8d5a885022cc8e954cc 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -313,6 +313,8 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle $this->assertEquals(2, count($addresses)); $updatedAddress = $this->addressRepository->getById(1); $this->assertEquals('update firstname', $updatedAddress->getFirstname()); + $this->assertTrue($updatedAddress->isDefaultBilling()); + $this->assertEquals($updatedAddress->getId(), $customer->getDefaultBilling()); $newAddress = $this->accountManagement->getDefaultShippingAddress($customerId); $this->assertEquals('new firstname', $newAddress->getFirstname()); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c44e320e64f4d0cfe1646f97642a2080e604ed74 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Controller\Section; + +class LoadTest extends \Magento\TestFramework\TestCase\AbstractController +{ + public function testLoadInvalidSection() + { + $expected = [ + 'message' => '"section<invalid" section source is not supported', + ]; + $this->dispatch('/customer/section/load/?sections=section<invalid&update_section_id=false&_=147066166394'); + self::assertEquals(json_encode($expected), $this->getResponse()->getBody()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/Config/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/Config/ReaderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2b57fcb43a1a4326fc80dadcbc8211f6ed35b623 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/Config/ReaderTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Directory\Model\Country\Postcode\Config; + +class ReaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Directory\Model\Country\Postcode\Config\Reader + */ + private $reader; + + protected function setUp() + { + $this->reader = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Directory\Model\Country\Postcode\Config\Reader::class + ); + } + + public function testRead() + { + $result = $this->reader->read(); + + $this->assertArrayHasKey('NL', $result); + $this->assertArrayHasKey('pattern_1', $result['NL']); + $this->assertArrayHasKey('pattern_2', $result['NL']); + + $this->assertEquals('test1', $result['NL']['pattern_1']['example']); + $this->assertEquals('^[0-9]{4}\s[a-zA-Z]{2}$', $result['NL']['pattern_1']['pattern']); + + $this->assertEquals('test2', $result['NL']['pattern_2']['example']); + $this->assertEquals('^[0-5]{4}[a-z]{2}$', $result['NL']['pattern_2']['pattern']); + + $this->assertArrayHasKey('NL_NEW', $result); + $this->assertArrayHasKey('pattern_1', $result['NL_NEW']); + + $this->assertEquals('test1', $result['NL_NEW']['pattern_1']['example']); + $this->assertEquals('^[0-2]{4}[A-Z]{2}$', $result['NL_NEW']['pattern_1']['pattern']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php index 8a566a0d4efe55e856716c095f5a3a7e14896af1..14118414859f0d9bced34e393bfb699f22251edf 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php @@ -12,6 +12,7 @@ namespace Magento\Framework\DB\Adapter\Pdo; use Magento\Framework\App\ResourceConnection; +use Zend_Db_Statement_Exception; class MysqlTest extends \PHPUnit_Framework_TestCase { @@ -32,6 +33,7 @@ class MysqlTest extends \PHPUnit_Framework_TestCase restore_error_handler(); } + /** * Test lost connection re-initializing * @@ -125,9 +127,9 @@ class MysqlTest extends \PHPUnit_Framework_TestCase protected function _getConnection() { if (is_null($this->_connection)) { - /** @var $coreResource \Magento\Framework\App\ResourceConnection */ + /** @var $coreResource ResourceConnection */ $coreResource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\Framework\App\ResourceConnection::class); + ->get(ResourceConnection::class); $this->_connection = $coreResource->getConnection(); } return $this->_connection; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php index 9acfa6f1caab150b0a0db2d54e87358adf88cf5b..67a6e416973493887912e2e62674108b1694c315 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php @@ -414,6 +414,37 @@ class AdapterTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expectedRecordsCount, $queryResponse->count()); } + /** + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php + * @magentoConfigFixture current_store catalog/search/engine mysql + */ + public function testAdvancedSearchCompositeProductWithOutOfStockOption() + { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ + $attribute = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable'); + /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $selectOptions */ + $selectOptions = $this->objectManager + ->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class) + ->setAttributeFilter($attribute->getId()); + + $firstOption = $selectOptions->getFirstItem(); + $firstOptionId = $firstOption->getId(); + $this->requestBuilder->bind('test_configurable', $firstOptionId); + $this->requestBuilder->setRequestName('filter_out_of_stock_child'); + + $queryResponse = $this->executeQuery(); + $this->assertEquals(0, $queryResponse->count()); + + $secondOption = $selectOptions->getLastItem(); + $secondOptionId = $secondOption->getId(); + $this->requestBuilder->bind('test_configurable', $secondOptionId); + $this->requestBuilder->setRequestName('filter_out_of_stock_child'); + + $queryResponse = $this->executeQuery(); + $this->assertEquals(1, $queryResponse->count()); + } + public function dateDataProvider() { return [ diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute.php new file mode 100644 index 0000000000000000000000000000000000000000..030e0250c3ec7fed4747aed4e168413207c7daf7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Api\AttributeRepositoryInterface; + +$eavConfig = Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +$eavConfig->clear(); + +/** @var $installer \Magento\Catalog\Setup\CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class); + +if (!$attribute->getId()) { + + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ + $attribute = Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + ); + + /** @var AttributeRepositoryInterface $attributeRepository */ + $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class); + + $attribute->setData( + [ + 'attribute_code' => 'test_configurable', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 1, + 'is_visible_in_advanced_search' => 1, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Test Configurable'], + 'backend_type' => 'int', + 'option' => [ + 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], + 'order' => ['option_0' => 1, 'option_1' => 2], + ], + ] + ); + + $attributeRepository->save($attribute); +} + +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); +$eavConfig->clear(); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..bd18100f6d97b4b9b56c9743e8064e0723fdc6c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$productCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); +foreach ($productCollection as $product) { + $product->delete(); +} + +$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); +if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + && $attribute->getId() +) { + $attribute->delete(); +} +$eavConfig->clear(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php new file mode 100644 index 0000000000000000000000000000000000000000..590f3c8041c6d819fafa14ef7ddc4d1f2ebccbeb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Setup\CategorySetup; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\TestFramework\Helper\Bootstrap; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); + +require __DIR__ . '/configurable_attribute.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(CategorySetup::class); + +/* Create simple products per each option value*/ +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); + +$attributeValues = []; +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$associatedProductIds = []; +$productIds = [10, 20]; +array_shift($options); //remove the first option which is empty + +$isFirstOption = true; +foreach ($options as $option) { + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); + $productId = array_shift($productIds); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setId($productId) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Option' . $option->getLabel()) + ->setSku('simple_' . $productId) + ->setPrice($productId) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => (int)!$isFirstOption, + ] + ); + + $product = $productRepository->save($product); + + /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ + $stockItem = Bootstrap::getObjectManager()->create(\Magento\CatalogInventory\Model\Stock\Item::class); + $stockItem->load($productId, 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($productId); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock((int)!$isFirstOption); + $stockItem->save(); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); + $isFirstOption = false; +} + +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +// Remove any previously created product with the same id. +/** @var \Magento\Framework\Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $productToDelete = $productRepository->getById(1); + $productRepository->delete($productToDelete); + + /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */ + $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class); + $itemResource->getConnection()->delete( + $itemResource->getMainTable(), + 'product_id = ' . $productToDelete->getId() + ); +} catch (\Exception $e) { + // Nothing to remove +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); +// +///** @var \Magento\Catalog\Model\Indexer\Product\Eav\Processor $eavIndexer */ +//$eavIndexer = Bootstrap::getObjectManager() +// ->get(\Magento\Catalog\Model\Indexer\Product\Eav\Processor::class); +//$eavIndexer->reindexAll(); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..8ba4e3abe21cc14a75a66270d22ea262dffeb788 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +foreach (['simple_10', 'simple_20', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +require __DIR__ . '/configurable_attribute_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml index 0cdfa11c3b6b5744abbd911e063553cd23039cc5..4cdc9a93e1f1f0ee093b8e0fd8e555a157fce851 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml @@ -387,4 +387,24 @@ <from>0</from> <size>10</size> </request> + <request query="filter_out_of_stock_child" index="catalogsearch_fulltext"> + <dimensions> + <dimension name="scope" value="default"/> + </dimensions> + <queries> + <query xsi:type="boolQuery" name="filter_out_of_stock_child" boost="1"> + <queryReference clause="must" ref="test_configurable"/> + </query> + <query xsi:type="filteredQuery" name="test_configurable"> + <filterReference clause="must" ref="test_configurable_filter"/> + </query> + </queries> + <filters> + <filter xsi:type="termFilter" name="test_configurable_filter" field="test_configurable" value="$test_configurable$"/> + </filters> + <aggregations/> + <from>0</from> + <size>10</size> + </request> + </requests> diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml index f9f8f1ffba91a55536c9d2221b68dc85b702e7f0..19feb33748c840b6c87f506ffd450897e2ace286 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml @@ -593,7 +593,8 @@ <label>Express Checkout</label> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> <field id="business_account" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="5"> - <label>Email Associated with PayPal Merchant Account</label> + <label>Email Associated with PayPal Merchant Account (Optional)</label> + <frontend_class>not-required</frontend_class> <comment> <![CDATA[<a href="http://www.magentocommerce.com/paypal">Start accepting payments via PayPal!</a>]]> </comment> diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json index 222e5ff822b043185e44719804e0af75d8809c6e..a004d3a86b5b9d0c68ec096ab8beff48fa1e51c5 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json @@ -1,7 +1,7 @@ { "name": "magento/module-a", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.1", "magento/module-b": "0.1" }, diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json index 57944758267fd668f541b34586a8eb9f7acd9c98..0626cc6a84d055757c32d9192cc7f2682696912f 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json @@ -1,7 +1,7 @@ { "name": "magento/module-b", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.74.0-beta6", "magento/module-a": "0.1" }, diff --git a/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6fe9ec2ee3ea13ef4b2b5881e4970f7aa99e9447 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Setup/Controller/UrlCheckTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Setup\Controller; + +use Magento\TestFramework\Helper\Bootstrap; +use Zend\Stdlib\RequestInterface as Request; +use Zend\View\Model\JsonModel; + +class UrlCheckTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var UrlCheck + */ + private $controller; + + protected function setUp() + { + $this->controller = Bootstrap::getObjectManager()->create(UrlCheck::class); + } + + /** + * @param array $requestContent + * @param bool $successUrl + * @param bool $successSecureUrl + * @return void + * @dataProvider indexActionDataProvider + */ + public function testIndexAction($requestContent, $successUrl, $successSecureUrl) + { + $requestMock = $this->getMockBuilder(Request::class) + ->getMockForAbstractClass(); + $requestMock->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode($requestContent)); + + $requestProperty = new \ReflectionProperty(get_class($this->controller), 'request'); + $requestProperty->setAccessible(true); + $requestProperty->setValue($this->controller, $requestMock); + + $resultModel = new JsonModel(['successUrl' => $successUrl, 'successSecureUrl' => $successSecureUrl]); + + $this->assertEquals($resultModel, $this->controller->indexAction()); + } + + /** + * @return array + */ + public function indexActionDataProvider() + { + return [ + [ + 'requestContent' => [ + 'address' => [ + 'actual_base_url' => 'http://example.com/' + ], + 'https' => [ + 'text' => 'https://example.com/', + 'admin' => true, + 'front' => false + ], + ], + 'successUrl' => true, + 'successSecureUrl' => true + ], + [ + 'requestContent' => [ + 'address' => [ + 'actual_base_url' => 'http://example.com/folder/' + ], + 'https' => [ + 'text' => 'https://example.com/folder_name/', + 'admin' => false, + 'front' => true + ], + ], + 'successUrl' => true, + 'successSecureUrl' => true + ], + [ + 'requestContent' => [ + 'address' => [ + 'actual_base_url' => 'ftp://example.com/' + ], + 'https' => [ + 'text' => 'https://example.com_test/', + 'admin' => true, + 'front' => true + ], + ], + 'successUrl' => false, + 'successSecureUrl' => false + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json b/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json index 920dc1289b0cfa00cd696b35fb495434830850f6..47f9eb415d7badb00345e5436e2c10bd64532c2f 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json +++ b/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json @@ -2,7 +2,7 @@ "name": "magento/admin-Magento_Catalog", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.1.0-alpha103" }, "type": "magento2-theme", diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php index 52244463a535693746eddf8f4e0d07b05cf9de08..ff12618b399639fb8c4f56a422828cbb7c0c1e67 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Framework/Search/RequestConfigTest.php @@ -100,7 +100,7 @@ Element 'filterReference': The attribute 'clause' is required but missing. Element 'filterReference': The attribute 'ref' is required but missing. Element 'filter': The attribute 'field' is required but missing. Element 'metric', attribute 'type': [facet 'enumeration'] " . - "The value 'sumasdasd' is not an element of the set {'sum', 'count', 'min', 'max'}. + "The value 'sumasdasd' is not an element of the set {'sum', 'count', 'min', 'max', 'avg'}. Element 'metric', attribute 'type': 'sumasdasd' is not a valid value of the local atomic type. Element 'bucket': Missing child element(s). Expected is one of ( metrics, ranges ). Element 'request': Missing child element(s). Expected is ( from )." diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index cfbeb4f54c9e29d2f7b320b57c51ae7a7b31a676..9af29ab2c458d8a8b09a73e570695d133bd7a123 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -341,6 +341,9 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface /** * Creates a PDO object and connects to the database. * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * * @return void * @throws \Zend_Db_Adapter_Exception */ @@ -371,6 +374,10 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface list($this->_config['host'], $this->_config['port']) = explode(':', $this->_config['host']); } + if (!isset($this->_config['driver_options'][\PDO::MYSQL_ATTR_MULTI_STATEMENTS])) { + $this->_config['driver_options'][\PDO::MYSQL_ATTR_MULTI_STATEMENTS] = false; + } + $this->logger->startTimer(); parent::_connect(); $this->logger->logStats(LoggerInterface::TYPE_CONNECT, ''); @@ -562,6 +569,7 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface * @throws \Zend_Db_Adapter_Exception To re-throw \PDOException. * @throws LocalizedException In case multiple queries are attempted at once, to protect from SQL injection * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @deprecated */ public function multiQuery($sql, $bind = []) { @@ -728,6 +736,8 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + + * @deprecated */ protected function _splitMultiQuery($sql) { diff --git a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php index 10f1289b90c1841c7e5c9c014b3943132ab8e5f3..ad4746a36513c4dd028b1c81d27dbbcec70148eb 100644 --- a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php +++ b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php @@ -31,7 +31,7 @@ class MaliciousCode implements \Zend_Filter_Interface //js attributes '/(ondblclick|onclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onload|onunload|onerror)=[^<]*(?=\/*\>)/Uis', //tags - '/<\/?(script|meta|link|frame|iframe).*>/Uis', + '/<\/?(script|meta|link|frame|iframe|object).*>/Uis', //base64 usage '/src=[^<]*base64[^<]*(?=\/*\>)/Uis', ]; diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php index 512d8e89750bc24c666e58237668ad63f7e440b8..93de72e3c57c5610882aece936c9346be40dea64 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php @@ -89,6 +89,7 @@ class MaliciousCodeTest extends \PHPUnit_Framework_TestCase 'Tag is removed <link>SomeLink</link>', 'Tag is removed <frame>SomeFrame</frame>', 'Tag is removed <iframe>SomeIFrame</iframe>', + 'Tag is removed <object>SomeObject</object>', ], [ 'Tag is removed SomeScript', @@ -96,6 +97,7 @@ class MaliciousCodeTest extends \PHPUnit_Framework_TestCase 'Tag is removed SomeLink', 'Tag is removed SomeFrame', 'Tag is removed SomeIFrame', + 'Tag is removed SomeObject', ], ], 'Base64' => [ diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index fb00e9f59c83097b13274e47e7b0a7a52634ecc2..181783c7b2115d8a216c681077e116e98344d15c 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -20,7 +20,15 @@ class Curl implements \Zend_Http_Client_Adapter_Interface * * @var array */ - protected $_config = []; + protected $_config = [ + 'protocols' => (CURLPROTO_HTTP + | CURLPROTO_HTTPS + | CURLPROTO_FTP + | CURLPROTO_FTPS + ), + 'verifypeer' => true, + 'verifyhost' => 2, + ]; /** * Curl handle @@ -41,7 +49,10 @@ class Curl implements \Zend_Http_Client_Adapter_Interface 'ssl_cert' => CURLOPT_SSLCERT, 'userpwd' => CURLOPT_USERPWD, 'useragent' => CURLOPT_USERAGENT, - 'referer' => CURLOPT_REFERER + 'referer' => CURLOPT_REFERER, + 'protocols' => CURLOPT_PROTOCOLS, + 'verifypeer' => CURLOPT_SSL_VERIFYPEER, + 'verifyhost' => CURLOPT_SSL_VERIFYHOST, ]; /** @@ -55,8 +66,6 @@ class Curl implements \Zend_Http_Client_Adapter_Interface * Apply current configuration array to transport resource * * @return \Magento\Framework\HTTP\Adapter\Curl - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function _applyConfig() { @@ -65,22 +74,28 @@ class Curl implements \Zend_Http_Client_Adapter_Interface curl_setopt($this->_getResource(), $option, $value); } - if (empty($this->_config)) { - return $this; + // apply config options + foreach ($this->getDefaultConfig() as $option => $value) { + curl_setopt($this->_getResource(), $option, $value); } - $verifyPeer = isset($this->_config['verifypeer']) ? $this->_config['verifypeer'] : true; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYPEER, $verifyPeer); - - $verifyHost = isset($this->_config['verifyhost']) ? $this->_config['verifyhost'] : 2; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYHOST, $verifyHost); + return $this; + } - foreach ($this->_config as $param => $curlOption) { + /** + * Get default options + * + * @return array + */ + private function getDefaultConfig() + { + $config = []; + foreach (array_keys($this->_config) as $param) { if (array_key_exists($param, $this->_allowedParams)) { - curl_setopt($this->_getResource(), $this->_allowedParams[$param], $this->_config[$param]); + $config[$this->_allowedParams[$param]] = $this->_config[$param]; } } - return $this; + return $config; } /** @@ -116,7 +131,9 @@ class Curl implements \Zend_Http_Client_Adapter_Interface */ public function setConfig($config = []) { - $this->_config = $config; + foreach ($config as $key => $value) { + $this->_config[$key] = $value; + } return $this; } @@ -268,6 +285,13 @@ class Curl implements \Zend_Http_Client_Adapter_Interface $multihandle = curl_multi_init(); + // add default parameters + foreach ($this->getDefaultConfig() as $defaultOption => $defaultValue) { + if (!isset($options[$defaultOption])) { + $options[$defaultOption] = $defaultValue; + } + } + foreach ($urls as $key => $url) { $handles[$key] = curl_init(); curl_setopt($handles[$key], CURLOPT_URL, $url); diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php index 255be0a5596a622151e9e1091841d4f5ff8bf58b..37cd33d18683062ab48d16ae8aae2733def7a75e 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php @@ -10,10 +10,14 @@ use \Magento\Framework\HTTP\Adapter\Curl; class CurlTest extends \PHPUnit_Framework_TestCase { - /** @var Curl */ + /** + * @var Curl + */ protected $model; - /** @var \Closure */ + /** + * @var \Closure + */ public static $curlExectClosure; protected function setUp() @@ -42,4 +46,3 @@ class CurlTest extends \PHPUnit_Framework_TestCase ]; } } - diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php index 8ddb6255b9b862a37dea1aaefd742a4c03923d24..3d280f1224b8f3bb2e9082436fa7524308730387 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php @@ -71,6 +71,7 @@ class Adapter implements AdapterInterface /** * {@inheritdoc} + * @throws \LogicException */ public function query(RequestInterface $request) { diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php index 5860591eaf702494614ec89a90e816e159ffeb12..cf544eb126e1195096603979bd7378e08f44730d 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Aggregation/Builder/Metrics.php @@ -14,7 +14,7 @@ class Metrics * * @var string[] */ - private $mapMetrics = ['count', 'sum', 'min', 'max', 'avg']; + private $allowedMetrics = ['count', 'sum', 'min', 'max', 'avg']; /** * Build metrics for Select->columns @@ -30,7 +30,7 @@ class Metrics foreach ($metrics as $metric) { $metricType = $metric->getType(); - if (in_array($metricType, $this->mapMetrics)) { + if (in_array($metricType, $this->allowedMetrics, true)) { $selectAggregations[$metricType] = "$metricType(main_table.value)"; } } diff --git a/lib/internal/Magento/Framework/Search/etc/requests.xsd b/lib/internal/Magento/Framework/Search/etc/requests.xsd index f185699c5a5e890757850fa8985b2e0400c56c35..294232513b7d2ec1938e243423a3113e151fd0c0 100644 --- a/lib/internal/Magento/Framework/Search/etc/requests.xsd +++ b/lib/internal/Magento/Framework/Search/etc/requests.xsd @@ -263,6 +263,7 @@ <xs:enumeration value="count" /> <xs:enumeration value="min" /> <xs:enumeration value="max" /> + <xs:enumeration value="avg" /> </xs:restriction> </xs:simpleType> </xs:attribute> diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index e8013b024f4384e2c9b2261daed97a2ccbfb7f03..a517f1fd0b0b744cd44bad6b78ddd9c687a1fab5 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -298,6 +298,7 @@ class SessionManager implements SessionManagerInterface return; } + session_regenerate_id(true); session_destroy(); if ($options['send_expire_cookie']) { $this->expireSessionCookie(); diff --git a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php new file mode 100644 index 0000000000000000000000000000000000000000..3c7bbb3d997236aae0e7cc91994505af78620957 --- /dev/null +++ b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php @@ -0,0 +1,59 @@ +<?php +/** + * Protocol validator + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Validator; + +use \Zend\Uri\Uri; + +/** + * Check is URI starts from allowed protocol + * + * Class AllowedProtocols + * @package Magento\Framework\Validator + */ +class AllowedProtocols extends AbstractValidator +{ + /** + * List of supported protocols + * + * @var array + */ + private $listOfProtocols = [ + 'http', + 'https', + ]; + + /** + * Constructor. + * @param array $listOfProtocols + */ + public function __construct($listOfProtocols = []) + { + if (count($listOfProtocols)) { + $this->listOfProtocols = $listOfProtocols; + } + } + + /** + * Validate URI + * + * @param string $value + * @return bool + */ + public function isValid($value) + { + $uri = new Uri($value); + $isValid = in_array( + strtolower($uri->getScheme()), + $this->listOfProtocols + ); + if (!$isValid) { + $this->_addMessages(["Protocol isn't allowed"]); + } + return $isValid; + } +} diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/UrlTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d66f84289424920d3d8aca1e5b39e76fa1ad1503 --- /dev/null +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/UrlTest.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Validator\Test\Unit; + +use Magento\Framework\Validator\Url as UrlValidator; + +class UrlTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var UrlValidator + */ + private $validator; + + protected function setUp() + { + $this->validator = new UrlValidator(); + } + + /** + * @param array $allowedSchemes + * @param string $url + * @param bool $expectedResult + * @dataProvider isValidDataProvider + */ + public function testIsValid(array $allowedSchemes, $url, $expectedResult) + { + $this->assertSame($expectedResult, $this->validator->isValid($url, $allowedSchemes)); + } + + /** + * @return array + */ + public function isValidDataProvider() + { + return [ + [ + 'allowedSchemes' => [], + 'url' => 'http://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['http'], + 'url' => 'http://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => [], + 'url' => 'https://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['https'], + 'url' => 'https://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => [], + 'url' => 'http://example.com_test', + 'expectedResult' => false, + ], + [ + 'allowedSchemes' => [], + 'url' => 'ftp://example.com', + 'expectedResult' => true, + ], + [ + 'allowedSchemes' => ['ftp'], + 'url' => 'ftp://example.com', + 'expectedResult' => true, + ], + ]; + } +} diff --git a/lib/internal/Magento/Framework/Validator/Url.php b/lib/internal/Magento/Framework/Validator/Url.php new file mode 100644 index 0000000000000000000000000000000000000000..27262009b2d200bc354947208bfd075898a3ac0e --- /dev/null +++ b/lib/internal/Magento/Framework/Validator/Url.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Validator; + +/** + * Class Url validates URL and checks that it has allowed scheme + */ +class Url +{ + /** + * Validate URL and check that it has allowed scheme + * + * @param string $value + * @param array $allowedSchemes + * @return bool + */ + public function isValid($value, array $allowedSchemes = []) + { + $isValid = true; + + if (!filter_var($value, FILTER_VALIDATE_URL)) { + $isValid = false; + } + + if ($isValid && !empty($allowedSchemes)) { + $url = parse_url($value); + if (empty($url['scheme']) || !in_array($url['scheme'], $allowedSchemes)) { + $isValid = false; + } + } + + return $isValid; + } +} diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index 98338ed4b099d202ce7d8e482b2bda9e3a675068..926d4cc54c140efb315921432b8afc5ebd237cf7 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -8,7 +8,7 @@ "AFL-3.0" ], "require": { - "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", "ext-spl": "*", "ext-dom": "*", "ext-simplexml": "*", diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 3041a177185cf1dfa5bda6e233a2c58a0282d0de..eed9c676b4c90ce1a944d9e7c5ce88a815af08cf 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1441,17 +1441,19 @@ * Validate single element. * * @param {Element} element + * @param {Object} config * @returns {*} */ - $.validator.validateSingleElement = function (element) { + $.validator.validateSingleElement = function (element, config) { var errors = {}, valid = true, validateConfig = { errorElement: 'label', ignore: '.ignore-validate' }, - form, validator, classes; + form, validator, classes, elementValue; + $.extend(validateConfig, config); element = $(element).not(validateConfig.ignore); if (!element.length) { @@ -1475,7 +1477,11 @@ validator.toShow = validator.toHide = $([]); $.each(classes, $.proxy(function (i, className) { - if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) { + elementValue = element.val(); + if (element.is(':checkbox') || element.is(':radio')) { + elementValue = element.is(':checked') || null; + } + if (this.methods[className] && !this.methods[className](elementValue, element.get(0))) { valid = false; errors[element.get(0).name] = this.messages[className]; validator.invalid[element.get(0).name] = true; diff --git a/setup/config/di.config.php b/setup/config/di.config.php index 804f18462065aa3af62cbba93d1fa5339b548f7e..b0dcb452ccd4030cf9736e6ab4a521199d1a899f 100644 --- a/setup/config/di.config.php +++ b/setup/config/di.config.php @@ -21,6 +21,7 @@ return [ \Magento\Setup\Controller\Environment::class, \Magento\Setup\Controller\DependencyCheck::class, \Magento\Setup\Controller\DatabaseCheck::class, + \Magento\Setup\Controller\UrlCheck::class, \Magento\Setup\Controller\ValidateAdminCredentials::class, \Magento\Setup\Controller\AddDatabase::class, \Magento\Setup\Controller\WebConfiguration::class, diff --git a/setup/pub/magento/setup/web-configuration.js b/setup/pub/magento/setup/web-configuration.js index 03a0fc7845dda90225d3be867f4bf9ddd1621f63..47458056b33b52f8913017a5f6f7c6317fd98347 100644 --- a/setup/pub/magento/setup/web-configuration.js +++ b/setup/pub/magento/setup/web-configuration.js @@ -5,7 +5,7 @@ 'use strict'; angular.module('web-configuration', ['ngStorage']) - .controller('webConfigurationController', ['$scope', '$state', '$localStorage', function ($scope, $state, $localStorage) { + .controller('webConfigurationController', ['$scope', '$state', '$localStorage', '$http', function ($scope, $state, $localStorage, $http) { $scope.config = { address: { base_url: '', @@ -119,4 +119,28 @@ angular.module('web-configuration', ['ngStorage']) $scope.webconfig.submitted = false; } }); + + // Validate URL + $scope.validateUrl = function () { + if (!$scope.webconfig.submitted) { + $http.post('index.php/url-check', $scope.config) + .success(function (data) { + $scope.validateUrl.result = data; + if ($scope.validateUrl.result.successUrl && $scope.validateUrl.result.successSecureUrl) { + $scope.nextState(); + } + if (!$scope.validateUrl.result.successUrl) { + $scope.webconfig.submitted = true; + $scope.webconfig.base_url.$setValidity('url', false); + } + if (!$scope.validateUrl.result.successSecureUrl) { + $scope.webconfig.submitted = true; + $scope.webconfig.https.$setValidity('url', false); + } + }) + .error(function (data) { + $scope.validateUrl.failed = data; + }); + } + }; }]); diff --git a/setup/src/Magento/Setup/Console/Command/InstallStoreConfigurationCommand.php b/setup/src/Magento/Setup/Console/Command/InstallStoreConfigurationCommand.php index 928fe1e3d4b8d32f7bc4656bf6c00ac500972234..f1098af2db00916e328f515a2a88696553603f78 100644 --- a/setup/src/Magento/Setup/Console/Command/InstallStoreConfigurationCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InstallStoreConfigurationCommand.php @@ -16,11 +16,10 @@ use Magento\Setup\Model\StoreConfigurationDataMapper; use Magento\Setup\Model\ObjectManagerProvider; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Store\Model\Store; -use Magento\Framework\Validator\Locale; -use Magento\Framework\Validator\Timezone; -use Magento\Framework\Validator\Currency; -use Magento\Framework\Url\Validator; +use Magento\Framework\Validator\Locale as LocaleValidator; +use Magento\Framework\Validator\Timezone as TimezoneValidator; +use Magento\Framework\Validator\Currency as CurrencyValidator; +use Magento\Framework\Validator\Url as UrlValidator; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -43,24 +42,57 @@ class InstallStoreConfigurationCommand extends AbstractSetupCommand * Object Manager * * @var ObjectManagerInterface + * @deprecated */ private $objectManager; + /** + * @var LocaleValidator + */ + private $localeValidator; + + /** + * @var TimezoneValidator + */ + private $timezoneValidator; + + /** + * @var CurrencyValidator + */ + private $currencyValidator; + + /** + * @var UrlValidator + */ + private $urlValidator; + /** * Inject dependencies * * @param InstallerFactory $installerFactory * @param DeploymentConfig $deploymentConfig * @param ObjectManagerProvider $objectManagerProvider + * @param LocaleValidator $localeValidator, + * @param TimezoneValidator $timezoneValidator, + * @param CurrencyValidator $currencyValidator, + * @param UrlValidator $urlValidator */ public function __construct( InstallerFactory $installerFactory, DeploymentConfig $deploymentConfig, - ObjectManagerProvider $objectManagerProvider + ObjectManagerProvider $objectManagerProvider, + LocaleValidator $localeValidator, + TimezoneValidator $timezoneValidator, + CurrencyValidator $currencyValidator, + UrlValidator $urlValidator ) { $this->installerFactory = $installerFactory; $this->deploymentConfig = $deploymentConfig; $this->objectManager = $objectManagerProvider->get(); + $this->localeValidator = $localeValidator; + $this->timezoneValidator = $timezoneValidator; + $this->currencyValidator = $currencyValidator; + $this->urlValidator = $urlValidator; parent::__construct(); } @@ -173,6 +205,7 @@ class InstallStoreConfigurationCommand extends AbstractSetupCommand public function validate(InputInterface $input) { $errors = []; + $errorMsg = ''; $options = $input->getOptions(); foreach ($options as $key => $value) { if (!$value) { @@ -180,99 +213,69 @@ class InstallStoreConfigurationCommand extends AbstractSetupCommand } switch ($key) { case StoreConfigurationDataMapper::KEY_BASE_URL: - /** @var Validator $url */ if (strcmp($value, '{{base_url}}') == 0) { break; } - $url = $this->objectManager->get(\Magento\Framework\Url\Validator::class); - if (!$url->isValid($value)) { - $errorMsgs = $url->getMessages(); - $errors[] = '<error>' . 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL - . '\': ' . $errorMsgs[Validator::INVALID_URL] .'</error>'; - } + $errorMsg = $this->validateUrl( + $value, + StoreConfigurationDataMapper::KEY_BASE_URL, + ['http', 'https'] + ); + break; case StoreConfigurationDataMapper::KEY_LANGUAGE: - /** @var Locale $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Locale::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_LANGUAGE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->localeValidator, + $value, + StoreConfigurationDataMapper::KEY_LANGUAGE + ); break; case StoreConfigurationDataMapper::KEY_TIMEZONE: - /** @var Timezone $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Timezone::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_TIMEZONE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->timezoneValidator, + $value, + StoreConfigurationDataMapper::KEY_TIMEZONE + ); break; case StoreConfigurationDataMapper::KEY_CURRENCY: - /** @var Currency $lists */ - $lists = $this->objectManager->get(\Magento\Framework\Validator\Currency::class); - $errorMsg = $this->validateCodes($lists, $value, StoreConfigurationDataMapper::KEY_CURRENCY); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } + $errorMsg = $this->validateCodes( + $this->currencyValidator, + $value, + StoreConfigurationDataMapper::KEY_CURRENCY + ); break; case StoreConfigurationDataMapper::KEY_USE_SEF_URL: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_USE_SEF_URL); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_IS_SECURE: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_IS_SECURE); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_BASE_URL_SECURE: - try { - /** @var Validator $url */ - $url = $this->objectManager->get(\Magento\Framework\Url\Validator::class); - $errorMsgs = ''; - if (!$url->isValid($value)) { - $errorMsgs = $url->getMessages(); - if (!empty($errorMsgs)) { - $errors[] = '<error>' . 'Command option \'' - . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': ' . $errorMsgs[Validator::INVALID_URL] .'</error>'; - } - } - if (empty($errorMsgs) && strpos($value, 'https:') === false) { - throw new LocalizedException(new \Magento\Framework\Phrase("Invalid secure URL.")); - } - } catch (LocalizedException $e) { - $errors[] = '<error>' . 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': ' . $e->getLogMessage() .'</error>'; - } + $errorMsg = $this->validateUrl( + $value, + StoreConfigurationDataMapper::KEY_BASE_URL_SECURE, + ['https'] + ); break; case StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN: $errorMsg = $this->validateBinaryValue($value, StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY: $errorMsg = $this->validateBinaryValue( $value, StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY ); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; case StoreConfigurationDataMapper::KEY_JS_LOGGING: $errorMsg = $this->validateBinaryValue( $value, StoreConfigurationDataMapper::KEY_JS_LOGGING ); - if ($errorMsg !== '') { - $errors[] = $errorMsg; - } break; } + if ($errorMsg !== '') { + $errors[] = $errorMsg; + } } return $errors; } @@ -296,7 +299,7 @@ class InstallStoreConfigurationCommand extends AbstractSetupCommand /** * Validate codes for languages, currencies or timezones * - * @param Locale|Timezone|Currency $lists + * @param LocaleValidator|TimezoneValidator|CurrencyValidator $lists * @param string $code * @param string $type * @return string @@ -310,4 +313,31 @@ class InstallStoreConfigurationCommand extends AbstractSetupCommand } return $errorMsg; } + + /** + * Validate URL + * + * @param string $url + * @param string $option + * @param array $allowedSchemes + * @return string + */ + private function validateUrl($url, $option, array $allowedSchemes) + { + $errorMsg = ''; + + if (!$this->urlValidator->isValid($url, $allowedSchemes)) { + $errorTemplate = '<error>Command option \'%s\': Invalid URL \'%s\'.' + . ' Domain Name should contain only letters, digits and hyphen.' + . ' And you should use only following schemes: \'%s\'.</error>'; + $errorMsg = sprintf( + $errorTemplate, + $option, + $url, + implode(', ', $allowedSchemes) + ); + } + + return $errorMsg; + } } diff --git a/setup/src/Magento/Setup/Controller/UrlCheck.php b/setup/src/Magento/Setup/Controller/UrlCheck.php new file mode 100644 index 0000000000000000000000000000000000000000..08a0d50404c0a2e457a2646a30860d1f958f14d3 --- /dev/null +++ b/setup/src/Magento/Setup/Controller/UrlCheck.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Setup\Controller; + +use Zend\Mvc\Controller\AbstractActionController; +use Zend\View\Model\JsonModel; +use Zend\Json\Json; +use Magento\Framework\Validator\Url as UrlValidator; + +class UrlCheck extends AbstractActionController +{ + /** + * @var UrlValidator + */ + private $urlValidator; + + /** + * @param UrlValidator $urlValidator + */ + public function __construct(UrlValidator $urlValidator) + { + $this->urlValidator = $urlValidator; + } + + /** + * Validate URL + * + * @return JsonModel + */ + public function indexAction() + { + $params = Json::decode($this->getRequest()->getContent(), Json::TYPE_ARRAY); + $result = ['successUrl' => false, 'successSecureUrl' => true]; + + $hasBaseUrl = isset($params['address']['actual_base_url']); + $hasSecureBaseUrl = isset($params['https']['text']); + $hasSecureAdminUrl = !empty($params['https']['admin']); + $hasSecureFrontUrl = !empty($params['https']['front']); + $schemes = ['http', 'https']; + + // Validating of Base URL + if ($hasBaseUrl && $this->urlValidator->isValid($params['address']['actual_base_url'], $schemes)) { + $result['successUrl'] = true; + } + + // Validating of Secure Base URL + if ($hasSecureAdminUrl || $hasSecureFrontUrl) { + if (!($hasSecureBaseUrl && $this->urlValidator->isValid($params['https']['text'], $schemes))) { + $result['successSecureUrl'] = false; + } + } + + return new JsonModel($result); + } +} diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index 6a4b3a72b372c8ff611bcc7e535c99cbef4bb404..935f395a5e219f0546daf7967dbcc0726c695055 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -99,10 +99,10 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface } /** - * Check if user login + * Check if user logged-in and has permissions * * @param \Zend\Mvc\MvcEvent $event - * @return bool + * @return false|\Zend\Http\Response * @throws \Magento\Framework\Exception\LocalizedException */ public function authPreDispatch($event) @@ -115,6 +115,7 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface /** @var Application $application */ $application = $event->getApplication(); $serviceManager = $application->getServiceManager(); + if ($serviceManager->get(\Magento\Framework\App\DeploymentConfig::class)->isAvailable()) { /** @var \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider */ $objectManagerProvider = $serviceManager->get(\Magento\Setup\Model\ObjectManagerProvider::class); @@ -135,17 +136,26 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface 'appState' => $adminAppState ] ); - if (!$objectManager->get(\Magento\Backend\Model\Auth::class)->isLoggedIn()) { + /** @var \Magento\Backend\Model\Auth $auth */ + $authentication = $objectManager->get(\Magento\Backend\Model\Auth::class); + + if ( + !$authentication->isLoggedIn() || + !$adminSession->isAllowed('Magento_Backend::setup_wizard') + ) { $adminSession->destroy(); + /** @var \Zend\Http\Response $response */ $response = $event->getResponse(); $baseUrl = Http::getDistroBaseUrlPath($_SERVER); $response->getHeaders()->addHeaderLine('Location', $baseUrl . 'index.php/session/unlogin'); $response->setStatusCode(302); $event->stopPropagation(); + return $response; } } } + return false; } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallStoreConfigurationCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallStoreConfigurationCommandTest.php index 6f5ab3a5a64c537751cc8964229e35869b06c8e5..c3494b389aa5ecb2a2ab2465b5d848627a5d8e7a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallStoreConfigurationCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallStoreConfigurationCommandTest.php @@ -11,8 +11,14 @@ use Symfony\Component\Console\Tester\CommandTester; use Magento\Setup\Model\Installer; use Magento\Framework\ObjectManagerInterface; use Magento\Setup\Model\StoreConfigurationDataMapper; -use Magento\Framework\Url\Validator; +use Magento\Framework\Validator\Url as UrlValidator; +use Magento\Framework\Validator\Locale as LocaleValidator; +use Magento\Framework\Validator\Timezone as TimezoneValidator; +use Magento\Framework\Validator\Currency as CurrencyValidator; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase { /** @@ -35,6 +41,26 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase */ private $objectManager; + /** + * @var LocaleValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $localeValidatorMock; + + /** + * @var TimezoneValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $timezoneValidatorMock; + + /** + * @var CurrencyValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $currencyValidatorMock; + + /** + * @var UrlValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlValidatorMock; + /** * @var InstallStoreConfigurationCommand */ @@ -42,6 +68,11 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase protected function setUp() { + $this->urlValidatorMock = $this->getMock(UrlValidator::class, [], [], '', false); + $this->localeValidatorMock = $this->getMock(LocaleValidator::class, [], [], '', false); + $this->timezoneValidatorMock = $this->getMock(TimezoneValidator::class, [], [], '', false); + $this->currencyValidatorMock = $this->getMock(CurrencyValidator::class, [], [], '', false); + $this->installerFactory = $this->getMock(\Magento\Setup\Model\InstallerFactory::class, [], [], '', false); $this->deploymentConfig = $this->getMock(\Magento\Framework\App\DeploymentConfig::class, [], [], '', false); $this->installer = $this->getMock(\Magento\Setup\Model\Installer::class, [], [], '', false); @@ -62,7 +93,11 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase $this->command = new InstallStoreConfigurationCommand( $this->installerFactory, $this->deploymentConfig, - $objectManagerProvider + $objectManagerProvider, + $this->localeValidatorMock, + $this->timezoneValidatorMock, + $this->currencyValidatorMock, + $this->urlValidatorMock ); } @@ -102,41 +137,11 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase */ public function testExecuteInvalidData(array $option, $error) { - $url= $this->getMock(\Magento\Framework\Url\Validator::class, [], [], '', false); - $url->expects($this->any())->method('isValid')->will($this->returnValue(false)); - if (!isset($option['--' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE])) { - $url->expects($this->any())->method('getMessages')->will($this->returnValue([ - Validator::INVALID_URL => 'Invalid URL.' - ])); - } - $localeLists= $this->getMock(\Magento\Framework\Validator\Locale::class, [], [], '', false); - $localeLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - $timezoneLists= $this->getMock(\Magento\Framework\Validator\Timezone::class, [], [], '', false); - $timezoneLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - $currencyLists= $this->getMock(\Magento\Framework\Validator\Currency::class, [], [], '', false); - $currencyLists->expects($this->any())->method('isValid')->will($this->returnValue(false)); - - $returnValueMapOM = [ - [ - \Magento\Framework\Url\Validator::class, - $url - ], - [ - \Magento\Framework\Validator\Locale::class, - $localeLists - ], - [ - \Magento\Framework\Validator\Timezone::class, - $timezoneLists - ], - [ - \Magento\Framework\Validator\Currency::class, - $currencyLists - ], - ]; - $this->objectManager->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($returnValueMapOM)); + $this->localeValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->timezoneValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->currencyValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->urlValidatorMock->expects($this->any())->method('isValid')->willReturn(false); + $this->deploymentConfig->expects($this->once()) ->method('isAvailable') ->will($this->returnValue(true)); @@ -144,7 +149,7 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase ->method('create'); $commandTester = new CommandTester($this->command); $commandTester->execute($option); - $this->assertEquals($error . PHP_EOL, $commandTester->getDisplay()); + $this->assertContains($error, $commandTester->getDisplay()); } /** @@ -155,48 +160,54 @@ class InstallStoreConfigurationCommandTest extends \PHPUnit_Framework_TestCase return [ [ ['--' . StoreConfigurationDataMapper::KEY_BASE_URL => 'sampleUrl'], - 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL . '\': Invalid URL.' + 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL . '\': Invalid URL \'sampleUrl\'.' + ], + [ + ['--' . StoreConfigurationDataMapper::KEY_BASE_URL => 'http://example.com_test'], + 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL + . '\': Invalid URL \'http://example.com_test\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_LANGUAGE => 'sampleLanguage'], 'Command option \'' . StoreConfigurationDataMapper::KEY_LANGUAGE - . '\': Invalid value. To see possible values, run command \'bin/magento info:language:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:language:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_TIMEZONE => 'sampleTimezone'], 'Command option \'' . StoreConfigurationDataMapper::KEY_TIMEZONE - . '\': Invalid value. To see possible values, run command \'bin/magento info:timezone:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:timezone:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_CURRENCY => 'sampleLanguage'], 'Command option \'' . StoreConfigurationDataMapper::KEY_CURRENCY - . '\': Invalid value. To see possible values, run command \'bin/magento info:currency:list\'.' + . '\': Invalid value. To see possible values, run command \'bin/magento info:currency:list\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_USE_SEF_URL => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_USE_SEF_URL - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_IS_SECURE => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_IS_SECURE - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE => 'http://www.sample.com'], 'Command option \'' . StoreConfigurationDataMapper::KEY_BASE_URL_SECURE - . '\': Invalid secure URL.' + . '\': Invalid URL \'http://www.sample.com\'.' ], [ ['--' . StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_IS_SECURE_ADMIN - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], [ ['--' . StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY => 'invalidValue'], 'Command option \'' . StoreConfigurationDataMapper::KEY_ADMIN_USE_SECURITY_KEY - . '\': Invalid value. Possible values (0|1).' + . '\': Invalid value. Possible values (0|1).' ], + ]; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0b44887b586d5c69b6c56a1e19a4b9d68ae96bfe --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Controller/UrlCheckTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Setup\Test\Unit\Controller; + +use Magento\Setup\Controller\UrlCheck; +use Zend\Stdlib\RequestInterface; +use Zend\View\Model\JsonModel; +use Magento\Framework\Validator\Url as UrlValidator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class UrlCheckTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param array $requestJson + * @param array $expectedResult + * @dataProvider indexActionDataProvider + */ + public function testIndexAction($requestJson, $expectedResult) + { + /** @var ObjectManagerHelper $objectManagerHelper */ + $objectManagerHelper = new ObjectManagerHelper($this); + + $allowedSchemes = ['http', 'https']; + $returnMap = []; + if (isset($requestJson['address']['actual_base_url'])) { + $returnMap[] = [ + $requestJson['address']['actual_base_url'], + $allowedSchemes, + $expectedResult['successUrl'], + ]; + } + if (isset($requestJson['https']['text'])) { + $returnMap[] = [ + $requestJson['https']['text'], + $allowedSchemes, + $expectedResult['successSecureUrl'], + ]; + } + + /** @var UrlValidator|\PHPUnit_Framework_MockObject_MockObject $validator */ + $validator = $this->getMockBuilder(UrlValidator::class) + ->disableOriginalConstructor() + ->getMock(); + $validator->expects($this->any()) + ->method('isValid') + ->willReturnMap($returnMap); + + /** @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject $requestMock */ + $requestMock = $this->getMockBuilder(RequestInterface::class) + ->getMockForAbstractClass(); + $requestMock->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode($requestJson)); + + $controller = $objectManagerHelper->getObject( + UrlCheck::class, + ['urlValidator' => $validator] + ); + $objectManagerHelper->setBackwardCompatibleProperty($controller, 'request', $requestMock); + + $this->assertEquals(new JsonModel($expectedResult), $controller->indexAction()); + } + + /** + * @return array + */ + public function indexActionDataProvider() + { + return [ + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com_test' + ] + ], + 'expectedResult' => ['successUrl' => false, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com_test' + ], + 'https' => [ + 'admin' => false, + 'front' => false, + 'text' => '' + ] + ], + 'expectedResult' => ['successUrl' => false, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080' + ], + 'https' => [ + 'admin' => true, + 'front' => false, + 'text' => 'https://example.com.ua/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080/folder_name/' + ], + 'https' => [ + 'admin' => false, + 'front' => true, + 'text' => 'https://example.com.ua/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + [ + 'requestJson' => [ + 'address' => [ + 'actual_base_url' => 'http://localhost.com:8080/folder_name/' + ], + 'https' => [ + 'admin' => true, + 'front' => true, + 'text' => 'https://example.com.ua:8090/folder_name/' + ] + ], + 'expectedResult' => ['successUrl' => true, 'successSecureUrl' => true] + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index 558533afe92429582cbfa79e5fcd691b200ecef8..1ace3f452c4f917181c43a86744f2b4db39b9439 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; use \Magento\Setup\Mvc\Bootstrap\InitParamListener; @@ -59,11 +58,11 @@ class InitParamListenerTest extends \PHPUnit_Framework_TestCase ->withConsecutive( [ \Magento\Framework\App\Filesystem\DirectoryList::class, - $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class) + $this->isInstanceOf(\Magento\Framework\App\Filesystem\DirectoryList::class), ], [ \Magento\Framework\Filesystem::class, - $this->isInstanceOf(\Magento\Framework\Filesystem::class) + $this->isInstanceOf(\Magento\Framework\Filesystem::class), ] ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); @@ -130,10 +129,10 @@ class InitParamListenerTest extends \PHPUnit_Framework_TestCase $request->expects($this->any()) ->method('getContent') ->willReturn( - $cliParam ? ['install', '--magento-init-params=' . $cliParam ] : ['install'] + $cliParam ? ['install', '--magento-init-params=' . $cliParam] : ['install'] ); $mvcApplication->expects($this->any())->method('getConfig')->willReturn( - $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig]:[] + $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig] : [] ); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); @@ -150,41 +149,55 @@ class InitParamListenerTest extends \PHPUnit_Framework_TestCase 'mage_mode App' => [['MAGE_MODE' => 'developer'], [], '', ['MAGE_MODE' => 'developer']], 'mage_mode Env' => [[], ['MAGE_MODE' => 'developer'], '', ['MAGE_MODE' => 'developer']], 'mage_mode CLI' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], - 'one MAGE_DIRS CLI' => [[], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + 'one MAGE_DIRS CLI' => [ + [], + [], + 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS CLI' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'mage_mode only' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], 'MAGE_DIRS Env' => [ [], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'Env overwrites App' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'developer'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], + ], 'CLI overwrites Env' => [ ['MAGE_MODE' => 'developerApp'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp'], + ], 'CLI overwrites All' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'production'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production'], + ], ]; } @@ -226,6 +239,168 @@ class InitParamListenerTest extends \PHPUnit_Framework_TestCase [$this->listener, 'onBootstrap'] )->willReturn($this->callbackHandler); $eventManager->expects($this->once())->method('getSharedManager')->willReturn($sharedManager); + return $eventManager; } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAuthPreDispatch() + { + $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + ->disableOriginalConstructor() + ->getMock(); + $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + ->disableOriginalConstructor() + ->getMock(); + $applicationMock = $this->getMockBuilder(\Zend\Mvc\Application::class) + ->disableOriginalConstructor() + ->getMock(); + $serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock->expects($this->once()) + ->method('isAvailable') + ->willReturn(true); + $omProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); + $adminAppStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + ->disableOriginalConstructor() + ->getMock(); + $sessionConfigMock = $this->getMockBuilder(\Magento\Backend\Model\Session\AdminConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppListMock = $this->getMockBuilder(\Magento\Backend\App\BackendAppList::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppMock = $this->getMockBuilder(\Magento\Backend\App\BackendApp::class) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\UrlFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + ->disableOriginalConstructor() + ->getMock(); + $authenticationMock = $this->getMockBuilder(\Magento\Backend\Model\Auth::class) + ->disableOriginalConstructor() + ->getMock(); + $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + ->disableOriginalConstructor() + ->getMock(); + $responseMock = $this->getMockBuilder(\Zend\Http\Response::class) + ->disableOriginalConstructor() + ->getMock(); + $headersMock = $this->getMockBuilder(\Zend\Http\Headers::class) + ->disableOriginalConstructor() + ->getMock(); + + $routeMatchMock->expects($this->once()) + ->method('getParam') + ->with('controller') + ->willReturn('testController'); + $eventMock->expects($this->once()) + ->method('getRouteMatch') + ->willReturn($routeMatchMock); + $eventMock->expects($this->once()) + ->method('getApplication') + ->willReturn($applicationMock); + $serviceManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\DeploymentConfig::class, + true, + $deploymentConfigMock, + ], + [ + \Magento\Setup\Model\ObjectManagerProvider::class, + true, + $omProvider, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\State::class, + $adminAppStateMock, + ], + [ + \Magento\Backend\Model\Session\AdminConfig::class, + $sessionConfigMock, + ], + [ + \Magento\Backend\App\BackendAppList::class, + $backendAppListMock, + ], + [ + \Magento\Backend\Model\UrlFactory::class, + $backendUrlFactoryMock, + ], + [ + \Magento\Backend\Model\Auth::class, + $authenticationMock, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('create') + ->willReturn($adminSessionMock); + $omProvider->expects($this->once()) + ->method('get') + ->willReturn($objectManagerMock); + $adminAppStateMock->expects($this->once()) + ->method('setAreaCode') + ->with(\Magento\Framework\App\Area::AREA_ADMINHTML); + $applicationMock->expects($this->once()) + ->method('getServiceManager') + ->willReturn($serviceManagerMock); + $backendAppMock->expects($this->once()) + ->method('getCookiePath') + ->willReturn(''); + $backendUrlFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($backendUrlMock); + $backendAppListMock->expects($this->once()) + ->method('getBackendApp') + ->willReturn($backendAppMock); + $authenticationMock->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $adminSessionMock->expects($this->once()) + ->method('isAllowed') + ->with('Magento_Backend::setup_wizard', null) + ->willReturn(false); + $adminSessionMock->expects($this->once()) + ->method('destroy'); + $eventMock->expects($this->once()) + ->method('getResponse') + ->willReturn($responseMock); + $responseMock->expects($this->once()) + ->method('getHeaders') + ->willReturn($headersMock); + $headersMock->expects($this->once()) + ->method('addHeaderLine'); + $responseMock->expects($this->once()) + ->method('setStatusCode') + ->with(302); + $eventMock->expects($this->once()) + ->method('stopPropagation'); + + $this->assertSame( + $this->listener->authPreDispatch($eventMock), + $responseMock + ); + } } diff --git a/setup/view/magento/setup/web-configuration.phtml b/setup/view/magento/setup/web-configuration.phtml index 35aa19484ed53f39bf37ae0e90e7b76c7f5efe87..ab2a00342b715b05f215d1d3e12010b0fc4993cc 100644 --- a/setup/view/magento/setup/web-configuration.phtml +++ b/setup/view/magento/setup/web-configuration.phtml @@ -30,7 +30,7 @@ $hints = [ <div class="nav-bar-outer-actions"> <div class="outer-actions-inner-wrap"> <div class="btn-wrap btn-wrap-triangle-right btn-wrap-next"> - <button type="button" class="btn btn-prime" ng-click="nextState()" dis>Next</button> + <button type="button" class="btn btn-prime" ng-click="validateUrl()" dis>Next</button> </div> <div class="btn-wrap btn-wrap-triangle-left btn-wrap-prev"> <button type="button" class="btn" ng-click="previousState()">Back</button> @@ -40,6 +40,13 @@ $hints = [ <h2 class="page-sub-title">{{$state.current.header}}</h2> +<div + class="message message-error" + ng-show="validateUrl.failed !== undefined" +> + <span class="message-text">{{validateUrl.failed}}</span> +</div> + <form name="webconfig" role="form"