diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index a4ce77aa0db0226332905ae1cb8df5d744a03dcc..fcd8b838c1b5d24467d90bcbfd9c2e1dd7435922 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -16,26 +16,28 @@ </strong> </div> <div class="fieldset-wrapper-content in collapse" id="manage-titles-content"> - <fieldset class="fieldset"> - <table class="admin__control-table" id="attribute-labels-table"> - <thead> - <tr> - <?php foreach ($block->getStores() as $_store): ?> - <th class="col-store-view"><?php /* @escapeNotVerified */ echo $_store->getName() ?></th> - <?php endforeach; ?> - </tr> - </thead> - <tbody> - <tr> - <?php $_labels = $block->getLabelValues() ?> - <?php foreach ($block->getStores() as $_store): ?> - <td class="col-store-view"> - <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?php /* @escapeNotVerified */ echo $_store->getId() ?>]" value="<?php echo $block->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($block->getReadOnly()):?> disabled="disabled"<?php endif;?>/> - </td> - <?php endforeach; ?> - </tr> - </tbody> - </table> + <fieldset class="admin__fieldset fieldset"> + <div class="admin__control-table-wrapper"> + <table class="admin__control-table" id="attribute-labels-table"> + <thead> + <tr> + <?php foreach ($block->getStores() as $_store): ?> + <th class="col-store-view"><?php /* @escapeNotVerified */ echo $_store->getName() ?></th> + <?php endforeach; ?> + </tr> + </thead> + <tbody> + <tr> + <?php $_labels = $block->getLabelValues() ?> + <?php foreach ($block->getStores() as $_store): ?> + <td class="col-store-view"> + <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?php /* @escapeNotVerified */ echo $_store->getId() ?>]" value="<?php echo $block->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($block->getReadOnly()):?> disabled="disabled"<?php endif;?>/> + </td> + <?php endforeach; ?> + </tr> + </tbody> + </table> + </div> </fieldset> </div> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml index 932d3dc50dc7f879453a1ef0671140c553017742..1f1dc0925f2c0199c49d3e244fd69d07eb8a697f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml @@ -10,11 +10,11 @@ $stores = $block->getStoresSortedBySortOrder(); ?> -<fieldset class="fieldset"> +<fieldset class="admin__fieldset fieldset"> <legend class="legend"> <span><?php echo $block->escapeHtml(__('Manage Options (Values of Your Attribute)')); ?></span> - </legend> - <div id="manage-options-panel" data-index="attribute_options_select_container"> + </legend><br /> + <div class="admin__control-table-wrapper" id="manage-options-panel" data-index="attribute_options_select_container"> <table class="admin__control-table" data-index="attribute_options_select"> <thead> <tr id="attribute-options-table"> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index f7475d0bb816828dd3c214600e8eeb48d03e39a8..c2aa0b8c6962258f253241d440703067b9c196e3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -23,6 +23,9 @@ <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item> <item name="update_url" xsi:type="url" path="mui/index/render"/> + <item name="storageConfig" xsi:type="array"> + <item name="dataScope" xsi:type="string">filters.store_id</item> + </item> </item> </argument> </argument> diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml index a5f96e8d27cf07714562e242c96ef29008acbd8c..d9c7402a3e1588fc1c6c24f0fcc1b3afc5af9f3b 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml @@ -10,9 +10,11 @@ $stores = $block->getStoresSortedBySortOrder(); ?> -<fieldset class="fieldset"> - <legend class="legend"><span><?php echo $block->escapeHtml( __('Manage Swatch (Values of Your Attribute)')); ?></span></legend> - <div id="swatch-visual-options-panel"> +<fieldset class="admin__fieldset fieldset"> + <legend class="legend"> + <span><?php echo $block->escapeHtml( __('Manage Swatch (Values of Your Attribute)')); ?></span> + </legend><br /> + <div class="admin__control-table-wrapper" id="swatch-visual-options-panel"> <table class="data-table clearfix" cellspacing="0"> <thead> <tr id="swatch-visual-options-table"> @@ -116,5 +118,4 @@ $stores = $block->getStoresSortedBySortOrder(); } } </script> - </fieldset> diff --git a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js index 950e87b2bfb58762bf63ab6d2200a63a7c8e5049..642c9b607274516ed8494dc09fd77181289e1256 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js @@ -20,6 +20,7 @@ define([ method: 'GET', dataType: 'json' }, + dataScope: '', data: {} }, @@ -29,8 +30,16 @@ define([ * @returns {DataStorage} Chainable. */ initConfig: function () { + var scope; + this._super(); + scope = this.dataScope; + + if (typeof scope === 'string') { + this.dataScope = scope ? [scope] : []; + } + this._requests = []; return this; @@ -77,10 +86,12 @@ define([ * @returns {jQueryPromise} */ getData: function (params, options) { - var cachedRequest = this.getRequest(params); + var cachedRequest; - if (params && params.filters && params.filters['store_id']) { - cachedRequest = false; + if (this.hasScopeChanged(params)) { + this.clearRequests(); + } else { + cachedRequest = this.getRequest(params); } options = options || {}; @@ -90,6 +101,30 @@ define([ this.requestData(params); }, + /** + * Tells whether one of the parameters defined in the "dataScope" has + * changed since the last request. + * + * @param {Object} params - Request parameters. + * @returns {Boolean} + */ + hasScopeChanged: function (params) { + var lastRequest = _.last(this._requests), + keys, + diff; + + if (!lastRequest) { + return false; + } + + diff = utils.compare(lastRequest.params, params); + + keys = _.pluck(diff.changes, 'path'); + keys = keys.concat(Object.keys(diff.containers)); + + return _.intersection(this.dataScope, keys).length > 0; + }, + /** * Extends records of current data object * with the provided records collection. diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less index 43f04e52a8e6040f3d554d8c73946cdaa069a5d8..3ae7e9bf31178f155a118ece3e28c5927e209bb9 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less @@ -131,6 +131,7 @@ &._required { span { &:after { + .lib-css(margin, @form-field-label-asterisk__margin); color: @validation__color; content: '*'; } diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 3a8cc65a597b27b9558f6d507308cc9b716a8137..d8c894788dd8252724f344f342fc69a867b9f313 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -744,6 +744,7 @@ // -------------------------------------- fieldset { + min-width: 0; padding: 20px; } @@ -5049,6 +5050,10 @@ } } } + + .admin__control-table-wrapper { + clear: both; + } } .catalog-product-set-index { diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php index 7b26568132bbe4da15ec4796a4676d4efd8f4435..272e3158d3c27021d4a8bddd1130edfa8ba9a832 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php @@ -29,13 +29,16 @@ class Cache extends Cli const PARAM_CACHE_ENABLE = 'cache:enable'; /** - * Flush cache. + * Flush Cache. + * If no parameters are set, all cache types are flushed. * + * @param array $cacheTypes * @return void */ - public function flush() + public function flush(array $cacheTypes = []) { - parent::execute(Cache::PARAM_CACHE_FLUSH); + $options = empty($cacheTypes) ? '' : ' ' . implode(' ', $cacheTypes); + parent::execute(Cache::PARAM_CACHE_FLUSH . $options); } /** diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php new file mode 100644 index 0000000000000000000000000000000000000000..225b99b0283f6d8d03bace79d7124847169a9539 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Mtf\Util\Command\Cli; + +use Magento\Mtf\Util\Command\Cli; + +/** + * Merchant Developer deploys static view files during test executions so that Storefront UI updates are applied. + */ +class StaticContent extends Cli +{ + /** + * Parameter for deploy static view files. + */ + const PARAM_SETUP_STATIC_CONTENT_DEPLOY = 'setup:static-content:deploy'; + + /** + * Deploy static view files. + * + * @return void + */ + public function deploy() + { + parent::execute(StaticContent::PARAM_SETUP_STATIC_CONTENT_DEPLOY); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php new file mode 100644 index 0000000000000000000000000000000000000000..e7937da1e4ec8ffa4e37999e54829837ad569474 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Customer\Test\Fixture\Customer; +use Magento\Customer\Test\TestStep\LoginCustomerOnFrontendStep as LogInCustomerOnStorefront; +use Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep as LogOutCustomerOnStorefront; + +/** + * Assert that http is used all over the Storefront. + */ +class AssertHttpUsedOnFrontend extends AbstractConstraint +{ + /** + * Unsecured protocol format. + * + * @var string + */ + private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + + /** + * Browser interface. + * + * @var BrowserInterface + */ + protected $browser; + + /** + * Customer account. + * + * @var Customer + */ + protected $customer; + + /** + * Validations execution. + * + * @param BrowserInterface $browser + * @param Customer $customer + * @return void + */ + public function processAssert(BrowserInterface $browser, Customer $customer) + { + $this->browser = $browser; + $this->customer = $customer; + $this->customer->persist(); + + // Log in to Customer Account on Storefront to assert that http is used indeed. + $this->objectManager->create(LogInCustomerOnStorefront::class, ['customer' => $this->customer])->run(); + $this->assertUsedProtocol($this->unsecuredProtocol); + + // Log out from Customer Account on Storefront to assert that JS is deployed validly as a part of statics. + $this->objectManager->create(LogOutCustomerOnStorefront::class)->run(); + $this->assertUsedProtocol($this->unsecuredProtocol); + } + + /** + * Assert that specified protocol is used on current page. + * + * @param string $expectedProtocol + * @return void + */ + protected function assertUsedProtocol($expectedProtocol) + { + if (substr($expectedProtocol, -3) !== "://") { + $expectedProtocol .= '://'; + } + + \PHPUnit_Framework_Assert::assertStringStartsWith( + $expectedProtocol, + $this->browser->getUrl(), + "$expectedProtocol is not used." + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Unsecured URLs are used for Storefront pages.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php new file mode 100644 index 0000000000000000000000000000000000000000..34019f5849be6ee997a362fc154e1af6abd96ca6 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; + +/** + * Assert that https protocol is used all over the Admin panel. + */ +class AssertHttpsUsedOnBackend extends AbstractConstraint +{ + /** + * Secured protocol format. + * + * @var string + */ + private $securedProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTPS; + + /** + * Unsecured protocol format. + * + * @var string + */ + private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + + /** + * Browser interface. + * + * @var BrowserInterface + */ + protected $browser; + + /** + * Validations execution. + * + * @param BrowserInterface $browser + * @param Dashboard $adminDashboardPage + * @param string $navMenuPath + * @return void + */ + public function processAssert(BrowserInterface $browser, Dashboard $adminDashboardPage, $navMenuPath) + { + $this->browser = $browser; + + // Open specified Admin page using Navigation Menu to assert that JS is deployed validly as a part of statics. + $adminDashboardPage->open()->getMenuBlock()->navigate($navMenuPath); + $this->assertUsedProtocol($this->securedProtocol); + $this->assertDirectHttpUnavailable(); + } + + /** + * Assert that specified protocol is used on current page. + * + * @param string $expectedProtocol + * @return void + */ + protected function assertUsedProtocol($expectedProtocol) + { + if (substr($expectedProtocol, -3) !== "://") { + $expectedProtocol .= '://'; + } + + \PHPUnit_Framework_Assert::assertStringStartsWith( + $expectedProtocol, + $this->browser->getUrl(), + "$expectedProtocol is not used." + ); + } + + /** + * Assert that Merchant is redirected to https if trying to access the page directly via http. + * + * @return void + */ + protected function assertDirectHttpUnavailable() + { + $fakeUrl = str_replace($this->securedProtocol, $this->unsecuredProtocol, $this->browser->getUrl()); + $this->browser->open($fakeUrl); + \PHPUnit_Framework_Assert::assertStringStartsWith( + $this->securedProtocol, + $this->browser->getUrl(), + 'Merchant is not redirected to https if tries to access the Admin panel page directly via http.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Secured URLs are used for Admin panel pages.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml index eb65b5daa9e9d756bcae958d9ef17553d32acbe7..25e6442e5105bf6e5be2bac04323ca81ab4665f6 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> <repository class="Magento\Config\Test\Repository\ConfigData"> <dataset name="store_information_US"> <field name="general/store_information/name" xsi:type="array"> @@ -165,12 +166,14 @@ <item name="scope_id" xsi:type="number">0</item> <item name="label" xsi:type="string">Yes</item> <item name="value" xsi:type="number">1</item> + <item name="inherit" xsi:type="number">1</item> </field> <field name="web/secure/use_in_adminhtml" xsi:type="array"> <item name="scope" xsi:type="string">default</item> <item name="scope_id" xsi:type="number">0</item> <item name="label" xsi:type="string">Yes</item> <item name="value" xsi:type="number">1</item> + <item name="inherit" xsi:type="number">1</item> </field> </dataset> @@ -220,6 +223,21 @@ </field> </dataset> + <dataset name="disable_https_frontend_admin"> + <field name="web/secure/use_in_frontend" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + <field name="web/secure/use_in_adminhtml" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + </dataset> + <dataset name="custom_allowed_country"> <field name="general/country/allow" xsi:type="array"> <item name="scope" xsi:type="string">default</item> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d6508cac810757323f50ce6683aeb5a24df0025d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\TestCase; + +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit; +use Magento\Mtf\Util\Command\Cli\Cache; +use Magento\Mtf\Util\Command\Cli\StaticContent; + +/** + * Verify that Merchant can configure secure URLs for Storefront and/or Admin panel in order to improve Store security. + * + * Preconditions: + * 1. SSL on server is configured. + * 2. Secure URLs are disabled for Storefront & Admin (out-of-the-box Magento state). + * + * Steps: + * 1. Log in to Admin panel. + * 2. Go to "Stores > Configuration" page. + * 3. Select needed scope. + * 4. Go to "General > Web > Base URLs (Secure)" section. + * 5. Specify Base URL with Secure protocol in the same format as a Secure Base URL. + * (i) Make sure that Secure Base URL ends with a "/". + * 6. Enable Secure URLs for Storefront if there is a need. + * 7. Enable Secure URLs for Admin if there is a need. + * 8. Save the Config & refresh invalidated caches (Configuration, Page Cache). + * 9. Deploy static view files. + * + * 10. If Secure URLs for Storefront were enabled: + * 1. Assert that https is used all over the Storefront. + * 2. Assert that static content is deployed validly (ex: JS functionality works on Storefront). + * 3. Assert that Customer is redirected to https if trying to access the page directly via http. + * 11. If secure URLs for Storefront were disabled: + * 1. Assert that http is used all over the Storefront. + * 2. Assert that static content is deployed validly (ex: JS functionality works on Storefront). + * + * 12. If secure URLs for Admin were enabled: + * 1. Assert that https is used all over the Admin panel. + * 2. Assert that static content is deployed validly (ex: JS functionality works in Admin panel). + * 3. Assert that Merchant is redirected to https if trying to access the page directly via http. + * 13. If secure URLs for Admin were disabled: + * 1. Assert that http is used all over the Admin panel. + * 2. Assert that static content is deployed validly (ex: JS functionality works in Admin panel). + * 3. Assert that Merchant is redirected to http if trying to access the page directly via https. + * + * Postconditions: + * 1. Turn the Secure URLs usage off (with further cache refreshing & static content deploying). + * + * @ZephyrId MAGETWO-35408 + */ +class ConfigureSecureUrlsTest extends Injectable +{ + /* tags */ + const MVP = 'no'; + const SEVERITY = 'S1'; + /* end tags */ + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + protected $fixtureFactory; + + /** + * "Configuration" page in Admin panel. + * + * @var SystemConfigEdit + */ + protected $configurationAdminPage; + + /** + * Cache CLI. + * + * @var Cache + */ + protected $cache; + + /** + * Static content CLI. + * + * @var StaticContent + */ + protected $staticContent; + + /** + * Prepare data for further test execution. + * + * @param FixtureFactory $fixtureFactory + * @param SystemConfigEdit $configurationAdminPage + * @param Cache $cache + * @param StaticContent $staticContent + * @return void + */ + public function __inject( + FixtureFactory $fixtureFactory, + SystemConfigEdit $configurationAdminPage, + Cache $cache, + StaticContent $staticContent + ) { + $this->fixtureFactory = $fixtureFactory; + $this->configurationAdminPage = $configurationAdminPage; + $this->cache = $cache; + $this->staticContent = $staticContent; + } + + /** + * Test execution. + * + * @param string $configData + * @return void + */ + public function test($configData) + { + $data = [ + 'web/secure/base_url' => [ + 'scope' => 'default', + 'scope_id' => 0, + 'value' => str_replace(['http', 'index.php/'], ['https', ''], $_ENV['app_frontend_url']) + ] + ]; + $config = $this->fixtureFactory->createByCode('configData', ['dataset' => $configData, 'data' => $data]); + $config->persist(); + + // Workaround until MTA-3879 is delivered. + $this->configurationAdminPage->open(); + $this->configurationAdminPage->getForm() + ->getGroup('web', 'secure') + ->setValue('web', 'secure', 'use_in_adminhtml', 'Yes'); + $this->configurationAdminPage->getPageActions()->save(); + $_ENV['app_backend_url'] = str_replace('http', 'https', $_ENV['app_backend_url']); + + $this->cache->flush(['config', 'full_page']); + $this->staticContent->deploy(); + } + + /** + * Revert all applied high-level changes. + * + * @return void + */ + public function tearDown() + { + $this->configurationAdminPage->open(); + $this->configurationAdminPage->getForm() + ->getGroup('web', 'secure') + ->setValue('web', 'secure', 'use_in_adminhtml', 'No'); + $this->configurationAdminPage->getPageActions()->save(); + $this->cache->flush(['config', 'full_page']); + $this->staticContent->deploy(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..d63e0639ea030ad9b5ada22a89d9dc100c2372d4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2013-2017 Magento, Inc. 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\Backend\Test\TestCase\ConfigureSecureUrlsTest" summary="Configure secure URLs" ticketId="MAGETWO-35408"> + <variation name="ConfigureSecureUrlsHttpForStorefrontHttpsForAdmin" summary="http for Storefront, https for Admin" ticketId="MAGETWO-35408"> + <data name="configData" xsi:type="string">disable_https_frontend_admin</data> + <data name="navMenuPath" xsi:type="string">Marketing>Catalog Price Rule</data> + <constraint name="Magento\Backend\Test\Constraint\AssertHttpUsedOnFrontend" /> + <constraint name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnBackend" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml index ecba481f0e9480c63fa604cb64ff8bceb517b924..722279d5ec65914ece2ea22887a817ae41abfea2 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml @@ -11,6 +11,16 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> + <type name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnBackend"> + <arguments> + <argument name="severity" xsi:type="string">high</argument> + </arguments> + </type> + <type name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnFrontend"> + <arguments> + <argument name="severity" xsi:type="string">middle</argument> + </arguments> + </type> <type name="Magento\Backend\Test\Constraint\AssertStoreCanBeLocalized"> <arguments> <argument name="severity" xsi:type="string">high</argument> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js new file mode 100644 index 0000000000000000000000000000000000000000..febbde30f8e9c72b6af3be18a5b4d217ad9e8d83 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js @@ -0,0 +1,76 @@ +/** + * Copyright © 2017 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ +/*jscs:disable requireCamelCaseOrUpperCaseIdentifiers*/ +define([ + 'mageUtils', + 'Magento_Ui/js/grid/data-storage' +], function (utils, DataStorage) { + 'use strict'; + + describe('Magento_Ui/js/grid/data-storage', function () { + describe('costructor', function () { + it('converts dataScope property to array', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + expect(model.dataScope).toEqual(['magento']); + }); + }); + + describe('hasScopeChanged', function () { + it('is function', function () { + var model = new DataStorage({ + dataScope: '' + }); + + expect(model.hasScopeChanged).toBeDefined(); + expect(typeof model.hasScopeChanged).toEqual('function'); + }); + + it('returns false if no requests have been made', function () { + var model = new DataStorage({ + dataScope: '' + }); + + expect(model.hasScopeChanged()).toBeFalsy(); + }); + + it('tells whether parameters defined in the dataScope property have changed', function () { + var params, newParams, model; + + params = { + namespace: 'magento', + search: '', + filters: { + store_id: 0 + }, + sorting: {}, + paging: {} + }; + + newParams = utils.extend({}, params, { + search: 'magento', + filters: { + store_id: 1 + } + }); + + model = new DataStorage({ + dataScope: 'filters.store_id' + }); + + model.cacheRequest({ + totalRecords: 0 + }, params); + + expect(model.hasScopeChanged(params)).toBeFalsy(); + expect(model.hasScopeChanged(newParams)).toBeTruthy(); + }); + }); + }); +}); diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index 004ebd4d4f4e7b4ba6ea0fdae1e604677f6c491c..279f4476b5f9ddc18dd16cca59e41e394b7f4b43 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -106,7 +106,7 @@ class Write extends Read implements WriteInterface $targetDirectory->create($this->driver->getParentDirectory($newPath)); } $absolutePath = $this->driver->getAbsolutePath($this->path, $path); - $absoluteNewPath = $targetDirectory->driver->getAbsolutePath($this->path, $newPath); + $absoluteNewPath = $targetDirectory->getAbsolutePath($newPath); return $this->driver->rename($absolutePath, $absoluteNewPath, $targetDirectory->driver); } diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php index 46cbfdb2077ad314f274d1ba21b8288cdb8d84f5..7ab182f40906d8d14c905153bf083ad7b031ed62 100644 --- a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php @@ -7,6 +7,9 @@ */ namespace Magento\Framework\Filesystem\Test\Unit\Directory; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverInterface; + class WriteTest extends \PHPUnit_Framework_TestCase { /** @@ -68,7 +71,7 @@ class WriteTest extends \PHPUnit_Framework_TestCase public function testGetDriver() { $this->assertInstanceOf( - \Magento\Framework\Filesystem\DriverInterface::class, + DriverInterface::class, $this->write->getDriver(), 'getDriver method expected to return instance of Magento\Framework\Filesystem\DriverInterface' ); @@ -90,8 +93,7 @@ class WriteTest extends \PHPUnit_Framework_TestCase public function testCreateSymlinkTargetDirectoryExists() { - $targetDir = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) - ->getMock(); + $targetDir = $this->getMockBuilder(WriteInterface::class)->getMock(); $targetDir->driver = $this->driver; $sourcePath = 'source/path/file'; $destinationDirectory = 'destination/path'; @@ -159,4 +161,66 @@ class WriteTest extends \PHPUnit_Framework_TestCase { return $this->path . $path; } + + /** + * @param string $sourcePath + * @param string $targetPath + * @param WriteInterface $targetDir + * @dataProvider getFilePathsDataProvider + */ + public function testRenameFile($sourcePath, $targetPath, $targetDir) + { + if ($targetDir !== null) { + $targetDir->driver = $this->getMockBuilder(DriverInterface::class)->getMockForAbstractClass(); + $targetDirPath = 'TARGET_PATH/'; + $targetDir->expects($this->once()) + ->method('getAbsolutePath') + ->with($targetPath) + ->willReturn($targetDirPath . $targetPath); + $targetDir->expects($this->once()) + ->method('isExists') + ->with(dirname($targetPath)) + ->willReturn(false); + $targetDir->expects($this->once()) + ->method('create') + ->with(dirname($targetPath)); + } + + $this->driver->expects($this->any()) + ->method('getAbsolutePath') + ->willReturnMap([ + [$this->path, $sourcePath, null, $this->getAbsolutePath($sourcePath)], + [$this->path, $targetPath, null, $this->getAbsolutePath($targetPath)], + ]); + $this->driver->expects($this->any()) + ->method('isFile') + ->willReturnMap([ + [$this->getAbsolutePath($sourcePath), true], + [$this->getAbsolutePath($targetPath), true], + ]); + $this->driver->expects($this->any()) + ->method('getParentDirectory') + ->with($targetPath) + ->willReturn(dirname($targetPath)); + $this->write->renameFile($sourcePath, $targetPath, $targetDir); + } + + /** + * @return array + */ + public function getFilePathsDataProvider() + { + return [ + [ + 'path/to/source.file', + 'path/to/target.file', + null, + ], + [ + 'path/to/source.file', + 'path/to/target.file', + $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(), + ], + ]; + } } diff --git a/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php b/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php index 424794c4f30648c04a8a9adf4ca24c0dd01f43bb..c493d4eacb5fa464470d852c6873965564042bd7 100644 --- a/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php +++ b/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php @@ -13,11 +13,6 @@ use Magento\Framework\App\Filesystem\DirectoryList; */ class FallbackContext extends Context { - /** - * Secure path - */ - const SECURE_PATH = 'secure'; - /** * @var string */ @@ -33,24 +28,17 @@ class FallbackContext extends Context */ private $locale; - /** - * @var bool - */ - private $isSecure; - /** * @param string $baseUrl * @param string $areaType * @param string $themePath * @param string $localeCode - * @param bool $isSecure */ - public function __construct($baseUrl, $areaType, $themePath, $localeCode, $isSecure = false) + public function __construct($baseUrl, $areaType, $themePath, $localeCode) { $this->area = $areaType; $this->theme = $themePath; $this->locale = $localeCode; - $this->isSecure = $isSecure; parent::__construct($baseUrl, DirectoryList::STATIC_VIEW, $this->generatePath()); } @@ -103,6 +91,6 @@ class FallbackContext extends Context */ public function getConfigPath() { - return $this->getPath() . ($this->isSecure ? '/' . self::SECURE_PATH : ''); + return $this->getPath(); } } diff --git a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php index 1e20e27ee538aeab2aa35bab346c0facd32a99ff..de9248b8fe10accfb78f9267c22b71389e800f38 100644 --- a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php +++ b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php @@ -50,8 +50,11 @@ class Direct implements \Magento\Framework\View\Asset\MergeStrategyInterface public function merge(array $assetsToMerge, Asset\LocalInterface $resultAsset) { $mergedContent = $this->composeMergedContent($assetsToMerge, $resultAsset); - $dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); - $dir->writeFile($resultAsset->getPath(), $mergedContent); + $filePath = $resultAsset->getPath(); + $staticDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + $tmpDir = $this->filesystem->getDirectoryWrite(DirectoryList::TMP); + $tmpDir->writeFile($filePath, $mergedContent); + $tmpDir->renameFile($filePath, $filePath, $staticDir); } /** diff --git a/lib/internal/Magento/Framework/View/Asset/Repository.php b/lib/internal/Magento/Framework/View/Asset/Repository.php index f0ab3d0ae36c1084af9746e270c03651a1bd53e7..28309b7d5836bb811d29407f258d0a91a89f7808 100644 --- a/lib/internal/Magento/Framework/View/Asset/Repository.php +++ b/lib/internal/Magento/Framework/View/Asset/Repository.php @@ -268,8 +268,7 @@ class Repository 'baseUrl' => $url, 'areaType' => $area, 'themePath' => $themePath, - 'localeCode' => $locale, - 'isSecure' => $isSecure + 'localeCode' => $locale ] ); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php index a338df7ce296b960a5a92ec0c8c7cc2a5c532947..a6d5d2c4c29a5d4bae8b2e97cd04999295bd1fc0 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php @@ -31,7 +31,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase * @param string $areaType * @param string $themePath * @param string $localeCode - * @param bool $isSecure * @param string $expectedResult * @dataProvider getConfigPathDataProvider */ @@ -40,7 +39,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase $areaType, $themePath, $localeCode, - $isSecure, $expectedResult ) { $this->fallbackContext = $this->objectManager->getObject( @@ -49,8 +47,7 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'baseUrl' => $baseUrl, 'areaType' => $areaType, 'themePath' => $themePath, - 'localeCode' => $localeCode, - 'isSecure' => $isSecure + 'localeCode' => $localeCode ] ); $this->assertEquals($expectedResult, $this->fallbackContext->getConfigPath()); @@ -64,7 +61,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => false, 'expectedResult' => 'frontend/Magento/blank/en_US' ], 'https' => [ @@ -72,8 +68,7 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => true, - 'expectedResult' => 'frontend/Magento/blank/en_US/secure' + 'expectedResult' => 'frontend/Magento/blank/en_US' ] ]; } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php index 17ba6714246d9afa44151cc2b1a72cf3a7e18542..33ad9608109e93dcd34ea9df21aa4f84d23586b5 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\View\Test\Unit\Asset\MergeStrategy; +use Magento\Framework\Filesystem\Directory\WriteInterface; use \Magento\Framework\View\Asset\MergeStrategy\Direct; use Magento\Framework\App\Filesystem\DirectoryList; @@ -22,9 +23,14 @@ class DirectTest extends \PHPUnit_Framework_TestCase protected $cssUrlResolver; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Filesystem\Directory\WriteInterface + * @var \PHPUnit_Framework_MockObject_MockObject|WriteInterface */ - protected $writeDir; + protected $staticDir; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|WriteInterface + */ + protected $tmpDir; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\View\Asset\LocalInterface @@ -35,11 +41,14 @@ class DirectTest extends \PHPUnit_Framework_TestCase { $this->cssUrlResolver = $this->getMock(\Magento\Framework\View\Url\CssResolver::class); $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); - $this->writeDir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); + $this->staticDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->tmpDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); $filesystem->expects($this->any()) ->method('getDirectoryWrite') - ->with(DirectoryList::STATIC_VIEW) - ->will($this->returnValue($this->writeDir)); + ->willReturnMap([ + [DirectoryList::STATIC_VIEW, \Magento\Framework\Filesystem\DriverPool::FILE, $this->staticDir], + [DirectoryList::TMP, \Magento\Framework\Filesystem\DriverPool::FILE, $this->tmpDir], + ]); $this->resultAsset = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); $this->object = new Direct($filesystem, $this->cssUrlResolver); } @@ -47,7 +56,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase public function testMergeNoAssets() { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge([], $this->resultAsset); } @@ -55,7 +66,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); $assets = $this->prepareAssetsToMerge([' one', 'two']); // note leading space intentionally - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } @@ -73,7 +86,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase ->method('aggregateImportDirectives') ->with('12') ->will($this->returnValue('1020')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php index 7e212fc7980a6ceefd72fb1ff0be97934f3de81d..3f818aaa4e7cab96d1772e06678b658edb8f5f68 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php @@ -185,8 +185,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase 'baseUrl' => '', 'areaType' => '', 'themePath' => 'Default', - 'localeCode' => '', - 'isSecure' => '', + 'localeCode' => '' ] ) ->willReturn($fallbackContextMock); @@ -251,8 +250,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase 'baseUrl' => '', 'areaType' => 'area', 'themePath' => '', - 'localeCode' => 'locale', - 'isSecure' => '', + 'localeCode' => 'locale' ] ) ->willReturn($fallbackContextMock);