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/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/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/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 9ab7db147d811d370a4c2609d5baf5ea35225680..d8b2189e9f0d267d24d86ea77c67e84b2e2f73c9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1431,4 +1431,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/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/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/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/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/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/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"