diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Checkbox.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Checkbox.php index 9b4224271d5818a45ec3df606ea635a18e694c41..3329f8aaf93c6b5d9a813f06de710f75634eb939 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Checkbox.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Checkbox.php @@ -77,17 +77,25 @@ class Checkbox extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Abstra { $values = $this->_getValues(); $value = $row->getData($this->getColumn()->getIndex()); + $checked = ''; if (is_array($values)) { $checked = in_array($value, $values) ? ' checked="checked"' : ''; } else { - $checked = $value === $this->getColumn()->getValue() ? ' checked="checked"' : ''; + $checkedValue = $this->getColumn()->getValue(); + if ($checkedValue !== null) { + $checked = $value === $checkedValue ? ' checked="checked"' : ''; + } } + $disabled = ''; $disabledValues = $this->getColumn()->getDisabledValues(); if (is_array($disabledValues)) { $disabled = in_array($value, $disabledValues) ? ' disabled="disabled"' : ''; } else { - $disabled = $value === $this->getColumn()->getDisabledValue() ? ' disabled="disabled"' : ''; + $disabledValue = $this->getColumn()->getDisabledValue(); + if ($disabledValue !== null) { + $disabled = $value === $disabledValue ? ' disabled="disabled"' : ''; + } } $this->setDisabled($disabled); diff --git a/app/code/Magento/Backend/Model/Session/AdminConfig.php b/app/code/Magento/Backend/Model/Session/AdminConfig.php index c3fa55ed8d0ad5b736c150177ba16880c181466a..155a9b09528fc1862222df8e54826edc36dce220 100644 --- a/app/code/Magento/Backend/Model/Session/AdminConfig.php +++ b/app/code/Magento/Backend/Model/Session/AdminConfig.php @@ -30,14 +30,14 @@ class AdminConfig extends Config protected $_frontNameResolver; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var \Magento\Backend\App\BackendAppList */ - protected $_storeManager; + private $backendAppList; /** - * @var \Magento\Backend\App\BackendAppList + * @var \Magento\Backend\Model\UrlFactory */ - private $backendAppList; + private $backendUrlFactory; /** * @param \Magento\Framework\ValidatorFactory $validatorFactory @@ -49,7 +49,7 @@ class AdminConfig extends Config * @param string $scopeType * @param \Magento\Backend\App\BackendAppList $backendAppList * @param FrontNameResolver $frontNameResolver - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Backend\Model\UrlFactory $backendUrlFactory * @param string $lifetimePath * @param string $sessionName * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -64,7 +64,7 @@ class AdminConfig extends Config $scopeType, \Magento\Backend\App\BackendAppList $backendAppList, FrontNameResolver $frontNameResolver, - \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Backend\Model\UrlFactory $backendUrlFactory, $lifetimePath = self::XML_PATH_COOKIE_LIFETIME, $sessionName = self::SESSION_NAME_ADMIN ) { @@ -79,8 +79,8 @@ class AdminConfig extends Config $lifetimePath ); $this->_frontNameResolver = $frontNameResolver; - $this->_storeManager = $storeManager; $this->backendAppList = $backendAppList; + $this->backendUrlFactory = $backendUrlFactory; $adminPath = $this->extractAdminPath(); $this->setCookiePath($adminPath); $this->setName($sessionName); @@ -95,7 +95,7 @@ class AdminConfig extends Config { $backendApp = $this->backendAppList->getCurrentApp(); $cookiePath = null; - $baseUrl = parse_url($this->_storeManager->getStore()->getBaseUrl(), PHP_URL_PATH); + $baseUrl = parse_url($this->backendUrlFactory->create()->getBaseUrl(), PHP_URL_PATH); if (!$backendApp) { $cookiePath = $baseUrl . $this->_frontNameResolver->getFrontName(); return $cookiePath; diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php index 6b00b71e4c8517532503f064f7aa2009a6c6fbff..24baab2a0298e6e7f3bfdfe01ac4366d823204de 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Session/AdminConfigTest.php @@ -29,9 +29,9 @@ class AdminConfigTest extends \PHPUnit_Framework_TestCase private $objectManager; /** - * @var \Magento\Store\Model\StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Backend\Model\UrlFactory | \PHPUnit_Framework_MockObject_MockObject */ - private $storeManagerMock; + private $backendUrlFactory; /** * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject @@ -56,13 +56,10 @@ class AdminConfigTest extends \PHPUnit_Framework_TestCase $this->validatorFactory = $this->getMockBuilder('Magento\Framework\ValidatorFactory') ->disableOriginalConstructor() ->getMock(); - - $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store') - ->disableOriginalConstructor() - ->getMock(); - $storeMock->expects($this->once())->method('getBaseUrl')->will($this->returnValue('/')); - $this->storeManagerMock = $this->getMockForAbstractClass('\Magento\Store\Model\StoreManagerInterface'); - $this->storeManagerMock->expects($this->once())->method('getStore')->will($this->returnValue($storeMock)); + $backendUrl = $this->getMock('\Magento\Backend\Model\Url', [], [], '', false); + $backendUrl->expects($this->once())->method('getBaseUrl')->will($this->returnValue('/')); + $this->backendUrlFactory = $this->getMock('Magento\Backend\Model\UrlFactory', ['create'], [], '', false); + $this->backendUrlFactory->expects($this->any())->method('create')->willReturn($backendUrl); $this->filesystemMock = $this->getMock('\Magento\Framework\Filesystem', [], [], '', false); $dirMock = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\WriteInterface'); @@ -99,7 +96,7 @@ class AdminConfigTest extends \PHPUnit_Framework_TestCase 'validatorFactory' => $this->validatorFactory, 'request' => $this->requestMock, 'frontNameResolver' => $mockFrontNameResolver, - 'storeManager' => $this->storeManagerMock, + 'backendUrlFactory' => $this->backendUrlFactory, 'filesystem' => $this->filesystemMock, ] ); @@ -134,7 +131,7 @@ class AdminConfigTest extends \PHPUnit_Framework_TestCase 'validatorFactory' => $this->validatorFactory, 'request' => $this->requestMock, 'sessionName' => $sessionName, - 'storeManager' => $this->storeManagerMock, + 'backendUrlFactory' => $this->backendUrlFactory, 'filesystem' => $this->filesystemMock, ] ); diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/renderer/fieldset.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/renderer/fieldset.phtml index fedec572af962a86661de9308ea78095fff09b64..41f6bf34afc8a9184e5bab7dca4e516e0c2055c4 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/renderer/fieldset.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/renderer/fieldset.phtml @@ -22,7 +22,7 @@ if (!isset($advancedLabel)) { $advancedLabel = __('Additional Settings'); } -$cssClass = ($isField) ? 'field ' . $element->getClass() : 'fieldset admin__fieldset' . $element->getClass(); +$cssClass = ($isField) ? 'field ' . $element->getClass() : 'fieldset admin__fieldset ' . $element->getClass(); if ($isField) { $count = $element->getCountBasicChildren(); diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/cc-form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/cc-form.html index f28cb33ceb4b1e1b13c85cd7d537fcf95377a434..a120355d9080ac35b377082eda8f1b4bd1076b16 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/cc-form.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/cc-form.html @@ -80,7 +80,10 @@ <!-- ko if: (isCcDetectionEnabled())--> <ul class="credit-card-types"> <!-- ko foreach: {data: getCcAvailableTypesValues(), as: 'item'} --> - <li class="item" data-bind="css: {_active: $parent.selectedCardType() == item.value} "> + <li class="item" data-bind="css: { + _active: $parent.selectedCardType() == item.value, + _inactive: $parent.selectedCardType() != null && $parent.selectedCardType() != item.value + } "> <!--ko if: $parent.getIcons(item.value) --> <img data-bind="attr: { 'src': $parent.getIcons(item.value).url, diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 3ab486011b5a660d3a2e28b05de7de8f321b322e..7bd251153314e365e0b9ed8c27de9939ac8f5a8a 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -494,6 +494,11 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Console\Command\ImagesResizeCommand"> + <arguments> + <argument name="productRepository" xsi:type="object">Magento\Catalog\Api\ProductRepositoryInterface\Proxy</argument> + </arguments> + </type> <type name="Magento\Framework\Config\View"> <arguments> <argument name="xpath" xsi:type="array"> diff --git a/app/code/Magento/Cookie/composer.json b/app/code/Magento/Cookie/composer.json index 4c1eb56566a7f4c771d7667adcb3a7f79c73faf0..58ab73368af0ccef8200703206e9ec1d919ed57e 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.4.11|~5.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/module-store": "1.0.0-beta", "magento/framework": "1.0.0-beta" }, diff --git a/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php b/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php index 275b185492ea51d74e7625af89de766f95cd8a86..92bfb1d5c7fac309014d25396284b5f2aa50b401 100644 --- a/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php +++ b/app/code/Magento/Customer/Console/Command/UpgradeHashAlgorithmCommand.php @@ -40,7 +40,6 @@ class UpgradeHashAlgorithmCommand extends Command ) { parent::__construct(); $this->customerCollectionFactory = $customerCollectionFactory; - $this->collection = $customerCollectionFactory->create(); $this->encryptor = $encryptor; } @@ -58,6 +57,7 @@ class UpgradeHashAlgorithmCommand extends Command */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->collection = $this->customerCollectionFactory->create(); $this->collection->addAttributeToSelect('*'); $customerCollection = $this->collection->getItems(); /** @var $customer Customer */ diff --git a/app/code/Magento/Deploy/composer.json b/app/code/Magento/Deploy/composer.json index 48b8e7f4ae081385d4623224c53b8fdfeb971a7e..691d71fb387070b5f0447d8c00a97b34fa76fedc 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/framework": "1.0.0-beta", "magento/module-developer": "1.0.0-beta", "magento/module-store": "1.0.0-beta", diff --git a/app/code/Magento/DownloadableImportExport/composer.json b/app/code/Magento/DownloadableImportExport/composer.json index 4bfa578ff8b5333214dbd2d0cfa435c0eb4cac6f..c8fccff51ad959d91664ed9eaf314689d8b967a3 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/module-catalog": "1.0.0-beta", "magento/module-import-export": "1.0.0-beta", "magento/module-catalog-import-export": "1.0.0-beta", diff --git a/app/code/Magento/EncryptionKey/composer.json b/app/code/Magento/EncryptionKey/composer.json index 590cc443f9032812b8beebe8b4bfb11287c45dc6..9a8aee83c4caec8fbe1996fc94247ef2e8e733c2 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/module-config": "1.0.0-beta", "magento/module-backend": "1.0.0-beta", "magento/framework": "1.0.0-beta" diff --git a/app/code/Magento/Marketplace/composer.json b/app/code/Magento/Marketplace/composer.json index c472be8a8fdac281ec2864811e986df0f73a4b40..de0198668102e1fa0bf39b3fcca9b1729c3da74f 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/framework": "1.0.0-beta", "magento/module-backend": "1.0.0-beta" }, diff --git a/app/code/Magento/NewRelicReporting/composer.json b/app/code/Magento/NewRelicReporting/composer.json index d3336380128e1d464495ca6ac7143b043fe806b9..8e8301f161e9a351a9d23222044980c3d9b19aab 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/module-store": "1.0.0-beta", "magento/module-backend": "1.0.0-beta", "magento/module-customer": "1.0.0-beta", diff --git a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php index fd04f1fe4ca2fbe28854f837b0a3323d70164473..d86d8df059161ffdf7b0b04bfde642c328a3a02f 100644 --- a/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php +++ b/app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php @@ -76,6 +76,11 @@ class BuiltinPlugin $this->kernel->process($result); } } else { + json_decode($result->getContent()); + if (json_last_error() == JSON_ERROR_NONE) { + // reset profiler to avoid appending profiling stat to JSON response + \Magento\Framework\Profiler::reset(); + } $this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'HIT', true); } return $result; diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php index 9e4102c889052f63cf2f358577d5059fa7aebc51..ae38b76db495f0e4fefbc1dd3763516ba55d0ef0 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/BuiltinPluginTest.php @@ -200,6 +200,7 @@ class BuiltinPluginTest extends \PHPUnit_Framework_TestCase $this->responseMock->expects($this->never()) ->method('setHeader'); } + $this->responseMock->expects($this->once())->method('getContent'); $this->assertSame( $this->responseMock, $this->plugin->aroundDispatch($this->frontControllerMock, $this->closure, $this->requestMock) diff --git a/app/code/Magento/Swagger/composer.json b/app/code/Magento/Swagger/composer.json index 878f5c8dfefd02d7623023db544e2c8f52ac8d38..0ba900539c248226038ac89225340001a5e1e8ec 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.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/framework": "1.0.0-beta" }, "type": "magento2-module", diff --git a/app/code/Magento/Variable/composer.json b/app/code/Magento/Variable/composer.json index c0543d01623a4f261d9db5e34c532b4bb3c106f4..cf3b3e25417384e931f35422d2ba46bd3751a4b4 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.4.11|~5.5.0|~5.6.0", + "php": "~5.5.0|~5.6.0|~7.0.0", "magento/module-backend": "1.0.0-beta", "magento/module-email": "1.0.0-beta", "magento/module-store": "1.0.0-beta", diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less index 3b4404bc0da1e9336efa2b62783dfb7c48e1bfa0..d9a3ab7292a8a53c423895f1483024a57520645e 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less @@ -305,7 +305,7 @@ body._in-resize { width: 7rem; img { border: 1px solid @data-grid-td__border-color; - max-width: 5rem; + width: 5rem; } } .data-grid-multicheck-cell { diff --git a/composer.json b/composer.json index 0fa24640ae43b12e18e5f1a33654b4e88e8bdc0c..a9e71e4b11044065e63828380d9c3203185e9599 100644 --- a/composer.json +++ b/composer.json @@ -69,6 +69,7 @@ "ext-intl": "*", "ext-xsl": "*", "ext-mbstring": "*", + "ext-openssl": "*", "sjparkinson/static-review": "~4.1", "fabpot/php-cs-fixer": "~1.2", "lusitanian/oauth": "~0.3 <=0.7.0" diff --git a/composer.lock b/composer.lock index 30da2611948979b879c9c4272ed8ec235595f617..9da039729694a59f2cb2ed8d294c322ed126c17a 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": "c4198e8a51c13b0d2c96bb54023cf2c6", - "content-hash": "444606690390cbbc73e906d61746c25d", + "hash": "80867d6202a3ae5d2f4c079e2cfd702f", + "content-hash": "41493176956dcfd2401ac1181d4d4782", "packages": [ { "name": "braintree/braintree_php", @@ -2571,16 +2571,16 @@ }, { "name": "fabpot/php-cs-fixer", - "version": "v1.10", + "version": "v1.10.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "8e21b4fb32c4618a425817d9f0daf3d57a9808d1" + "reference": "12dbcd1462f1e3a5a96c6c7398af26b28e092a8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/8e21b4fb32c4618a425817d9f0daf3d57a9808d1", - "reference": "8e21b4fb32c4618a425817d9f0daf3d57a9808d1", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/12dbcd1462f1e3a5a96c6c7398af26b28e092a8a", + "reference": "12dbcd1462f1e3a5a96c6c7398af26b28e092a8a", "shasum": "" }, "require": { @@ -2621,7 +2621,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2015-07-27 20:56:10" + "time": "2015-10-12 20:13:46" }, { "name": "league/climate", @@ -3941,6 +3941,7 @@ "ext-iconv": "*", "ext-intl": "*", "ext-xsl": "*", - "ext-mbstring": "*" + "ext-mbstring": "*", + "ext-openssl": "*" } } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/composer3.json b/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/composer3.json index 436ee0afe3eb6e42dae48d57e59774fced9ac23e..d8c398fe8f3b096e0c6e2f3648d57dba2da96fc6 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/composer3.json +++ b/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/composer3.json @@ -2,7 +2,7 @@ "name": "magento/module-module1", "description": "N/A", "require": { - "php": "~5.5.0|~5.6.0" + "php": "~5.5.0|~5.6.0|~7.0.0" }, "type": "magento2-module", "version": "0.1.0-alpha103" diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 530d99c68dcdfbfa1feeaaee620ba570a10deb2e..215df89727a1decb4e2587f2ddf31c7d5e2a6308 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -154,6 +154,7 @@ class ComposerTest extends \PHPUnit_Framework_TestCase $xml = simplexml_load_file("$dir/etc/module.xml"); $this->assertConsistentModuleName($xml, $json->name); $this->assertDependsOnPhp($json->require); + $this->assertPhpVersionInSync($json->name, $json->require->php); $this->assertDependsOnFramework($json->require); $this->assertRequireInSync($json); $this->assertAutoload($json); @@ -166,12 +167,14 @@ class ComposerTest extends \PHPUnit_Framework_TestCase case 'magento2-theme': $this->assertRegExp('/^magento\/theme-(?:adminhtml|frontend)(\-[a-z0-9_]+)+$/', $json->name); $this->assertDependsOnPhp($json->require); + $this->assertPhpVersionInSync($json->name, $json->require->php); $this->assertDependsOnFramework($json->require); $this->assertRequireInSync($json); break; case 'magento2-library': $this->assertDependsOnPhp($json->require); $this->assertRegExp('/^magento\/framework*/', $json->name); + $this->assertPhpVersionInSync($json->name, $json->require->php); $this->assertRequireInSync($json); $this->assertAutoload($json); break; @@ -277,6 +280,22 @@ class ComposerTest extends \PHPUnit_Framework_TestCase ); } + /** + * Assert that PHP versions in root composer.json and Magento component's composer.json are not out of sync + * + * @param string $name + * @param string $phpVersion + */ + private function assertPhpVersionInSync($name, $phpVersion) + { + $this->assertEquals( + self::$rootJson['require']['php'], + $phpVersion, + "PHP version {$phpVersion} in component {$name} is inconsistent with version " + . self::$rootJson['require']['php'] . ' in root composer.json' + ); + } + /** * Make sure requirements of components are reflected in root composer.json * diff --git a/lib/internal/Magento/Framework/App/ErrorHandler.php b/lib/internal/Magento/Framework/App/ErrorHandler.php index 59119102e6955a742beb3398588d305b663b7ab8..99eaab78f51b8a9194d6ae18d3a26754b86fffa9 100644 --- a/lib/internal/Magento/Framework/App/ErrorHandler.php +++ b/lib/internal/Magento/Framework/App/ErrorHandler.php @@ -51,17 +51,11 @@ class ErrorHandler return false; } - if (strpos($errorStr, 'Automatically populating $HTTP_RAW_POST_DATA is deprecated') !== false) { - // this warning should be suppressed as it is a known bug in php 5.6.0 https://bugs.php.net/bug.php?id=66763 - // and workaround suggested here (http://php.net/manual/en/ini.core.php#ini.always-populate-raw-post-data) - // is not compatible with HHVM - return false; - } - $errorNo = $errorNo & error_reporting(); if ($errorNo == 0) { return false; } + $msg = isset($this->errorPhrases[$errorNo]) ? $this->errorPhrases[$errorNo] : "Unknown error ({$errorNo})"; $msg .= ": {$errorStr} in {$errorFile} on line {$errorLine}"; throw new \Exception($msg); diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php index 897985cc0982b52298fed8665e9ba7ac518b347e..f87e64ea889377b1dcedd146ca96092bdc025faf 100644 --- a/lib/internal/Magento/Framework/App/Http.php +++ b/lib/internal/Magento/Framework/App/Http.php @@ -184,9 +184,12 @@ class Http implements \Magento\Framework\AppInterface $this->_response->setRedirect($setupInfo->getUrl()); $this->_response->sendHeaders(); } else { - $newMessage = $exception->getMessage() . "\nNOTE: web setup wizard is not accessible.\n" - . 'In order to install, use Magento Setup CLI or configure web access to the following directory: ' - . $setupInfo->getDir($projectRoot); + $newMessage = $exception->getMessage() . "\nNOTE: You cannot install Magento using the Setup Wizard " + . "because the Magento setup directory cannot be accessed. \n" + . 'You can install Magento using either the command line or you must restore access ' + . 'to the following directory: ' . $setupInfo->getDir($projectRoot) . "\n"; + $newMessage .= 'If you are using the sample nginx configuration, please go to ' + . $this->_request->getScheme(). '://' . $this->_request->getHttpHost() . $setupInfo->getUrl(); throw new \Exception($newMessage, 0, $exception); } } diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php index c2c928b2376efe8589e86741e5fa8d3df1c4966b..44258c7f135ce0e122f3a63d6f0fe1fd26737fe4 100644 --- a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php +++ b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php @@ -48,7 +48,7 @@ class ComponentRegistrar implements ComponentRegistrarInterface if (isset(self::$paths[$type][$componentName])) { throw new \LogicException('\'' . $componentName . '\' component already exists'); } else { - self::$paths[$type][$componentName] = $path; + self::$paths[$type][$componentName] = str_replace('\\', '/', $path); } } diff --git a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php index 5bb8e80888b6b6e2be74875430165d6e7c0e6169..4fbeaecc85d18ad7c22f386ecfd00d838d5b904d 100644 --- a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php +++ b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php @@ -97,4 +97,9 @@ class ConfigOptionsListConstants * Key for modules */ const KEY_MODULES = 'modules'; + + /** + * Size of random string generated for store's encryption key + */ + const STORE_KEY_RANDOM_STRING_SIZE = 32; } diff --git a/lib/internal/Magento/Framework/Controller/Result/Json.php b/lib/internal/Magento/Framework/Controller/Result/Json.php index f1d480efec72e9ec8b610e4209bb581cbf1cb773..bc3855448ccc264e16b34f061518a3e0507998cd 100644 --- a/lib/internal/Magento/Framework/Controller/Result/Json.php +++ b/lib/internal/Magento/Framework/Controller/Result/Json.php @@ -63,6 +63,8 @@ class Json extends AbstractResult */ protected function render(ResponseInterface $response) { + // reset profiler to avoid appending profiling stat to JSON response + \Magento\Framework\Profiler::reset(); $this->translateInline->processResponseBody($this->json, true); $response->representJson($this->json); return $this; diff --git a/lib/internal/Magento/Framework/DB/Ddl/Table.php b/lib/internal/Magento/Framework/DB/Ddl/Table.php index a623ba4c0fb3398a107d72140fe711adfa19b946..0b168e4c94a50cddc3ebbf59ef9c796f1d652c73 100644 --- a/lib/internal/Magento/Framework/DB/Ddl/Table.php +++ b/lib/internal/Magento/Framework/DB/Ddl/Table.php @@ -310,8 +310,8 @@ class Table case self::TYPE_DECIMAL: case self::TYPE_NUMERIC: $match = []; - $scale = 10; - $precision = 0; + $scale = 0; + $precision = 10; // parse size value if (is_array($size)) { if (count($size) == 2) { diff --git a/lib/internal/Magento/Framework/Math/Random.php b/lib/internal/Magento/Framework/Math/Random.php index 03c0727efd26e5938e3b3f2c74bcab7131777050..8ed28cb6021a976337e2e8b9cd6da0c5a98d724c 100644 --- a/lib/internal/Magento/Framework/Math/Random.php +++ b/lib/internal/Magento/Framework/Math/Random.php @@ -24,9 +24,10 @@ class Random /** * Get random string * - * @param int $length + * @param int $length * @param null|string $chars * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function getRandomString($length, $chars = null) { @@ -53,12 +54,9 @@ class Random } fclose($fp); } else { - // fallback to mt_rand() if all else fails - mt_srand(10000000 * (double)microtime()); - for ($i = 0, $lc = strlen($chars) - 1; $i < $length; $i++) { - $rand = mt_rand(0, $lc); // random integer from 0 to $lc - $str .= $chars[$rand]; // random character in $chars - } + throw new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase("Please make sure you have 'openssl' extension installed") + ); } return $str; @@ -70,6 +68,7 @@ class Random * @param $min [optional] * @param $max [optional] * @return int A random integer value between min (or 0) and max + * @throws \Magento\Framework\Exception\LocalizedException */ public static function getRandomNumber($min = 0, $max = null) { @@ -91,9 +90,9 @@ class Random $offset = abs(hexdec($hex) % $range); // random integer from 0 to $range fclose($fp); } else { - // fallback to mt_rand() if all else fails - mt_srand(mt_rand() + (100000000 * microtime()) % PHP_INT_MAX); - return mt_rand($min, $max); // random integer from $min to $max + throw new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase("Please make sure you have 'openssl' extension installed") + ); } return $min + $offset; // random integer from $min to $max diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index 061a0f9c765c68772f18406aa59a0f1bb77a6eb8..3e39db19a43cf4ace7b87d7d46ad2970be4add27 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -17,6 +17,7 @@ "ext-curl": "*", "ext-iconv": "*", "ext-gd": "*", + "ext-openssl": "*", "lib-libxml": "*", "ext-xsl": "*" }, diff --git a/nginx.conf.sample b/nginx.conf.sample index 137ef8205deda016379d2b4a2abd4f103f8f9882..d1fae8af249431a258df786406c23f9be30a17a6 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -36,7 +36,7 @@ location /setup { include fastcgi_params; } - location ~ /setup/(?!pub/). { + location ~ ^/setup/(?!pub/). { deny all; } } @@ -44,7 +44,7 @@ location /setup { location /update { root $MAGE_ROOT; - location ~ /update/index.php { + location ~ ^/update/index.php { fastcgi_pass fastcgi_backend; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; @@ -52,7 +52,7 @@ location /update { } # deny everything but index.php - location ~ /update/(?!pub/). { + location ~ ^/update/(?!pub/). { deny all; } } diff --git a/setup/pub/magento/setup/create-admin-account.js b/setup/pub/magento/setup/create-admin-account.js index 423ef0a341f8e86db3bc7c91e2427725054c2f10..ecd31b3459fa1535bf70cac879818382444000b5 100644 --- a/setup/pub/magento/setup/create-admin-account.js +++ b/setup/pub/magento/setup/create-admin-account.js @@ -18,16 +18,17 @@ angular.module('create-admin-account', ['ngStorage']) return; } var p = $scope.admin.password; - if (p.length >= 6 && p.match(/[\d]+/) && p.match(/[a-z]+/) && p.match(/[A-Z]+/) && p.match(/[!@#$%^*()_\/\\\-\+=]+/)) { + var MIN_ADMIN_PASSWORD_LENGTH = 7; + if (p.length >= MIN_ADMIN_PASSWORD_LENGTH && p.match(/[\d]+/) && p.match(/[a-z]+/) && p.match(/[A-Z]+/) && p.match(/[!@#$%^*()_\/\\\-\+=]+/)) { $scope.admin.passwordStatus.class = 'strong'; $scope.admin.passwordStatus.label = 'Strong'; - } else if (p.length >= 6 && p.match(/[\d]+/) && p.match(/[a-z]+/) && p.match(/[A-Z]+/)) { + } else if (p.length >= MIN_ADMIN_PASSWORD_LENGTH && p.match(/[\d]+/) && p.match(/[a-z]+/) && p.match(/[A-Z]+/)) { $scope.admin.passwordStatus.class = 'good'; $scope.admin.passwordStatus.label = 'Good'; - } else if (p.length >= 6 && p.match(/[\d]+/) && p.match(/[a-zA-Z]+/)) { + } else if (p.length >= MIN_ADMIN_PASSWORD_LENGTH && p.match(/[\d]+/) && p.match(/[a-zA-Z]+/)) { $scope.admin.passwordStatus.class = 'fair'; $scope.admin.passwordStatus.label = 'Fair'; - } else if (p.length >= 6) { + } else if (p.length >= MIN_ADMIN_PASSWORD_LENGTH) { $scope.admin.passwordStatus.class = 'weak'; $scope.admin.passwordStatus.label = 'Weak'; } else { diff --git a/setup/pub/magento/setup/readiness-check.js b/setup/pub/magento/setup/readiness-check.js index f2b8cf9e48c5f3a1e4cb63d86cdec1249756ab75..d150e2f5cb1376818e275bde259daa02961965f5 100644 --- a/setup/pub/magento/setup/readiness-check.js +++ b/setup/pub/magento/setup/readiness-check.js @@ -112,6 +112,7 @@ angular.module('readiness-check', []) 'php-version': { url:'index.php/environment/php-version', params: $scope.actionFrom, + useGet: true, show: function() { $scope.startProgress(); $scope.version.visible = true; @@ -129,6 +130,7 @@ angular.module('readiness-check', []) 'php-settings': { url:'index.php/environment/php-settings', params: $scope.actionFrom, + useGet: true, show: function() { $scope.startProgress(); $scope.settings.visible = true; @@ -146,6 +148,7 @@ angular.module('readiness-check', []) 'php-extensions': { url:'index.php/environment/php-extensions', params: $scope.actionFrom, + useGet: true, show: function() { $scope.startProgress(); $scope.extensions.visible = true; @@ -285,11 +288,25 @@ angular.module('readiness-check', []) $scope.query = function(item) { if (item.params) { - return $http.post(item.url, item.params) - .success(function(data) { item.process(data) }) - .error(function(data, status) { - item.fail(); - }); + if (item.useGet === true) { + // The http request type has been changed from POST to GET for a reason. The POST request + // results in PHP throwing a warning regards to 'always_populate_raw_post_data' + // being incorrectly set to a value different than -1. This warning is throw during the initial + // boot up sequence when POST request is received before the control gets transferred over to + // the Magento customer error handler, hence not catchable. To avoid that warning, the HTTP + // request type is being changed from POST to GET for select queries. Those queries are: + // (1) PHP Version Check (2) PHP Settings Check and (3) PHP Extensions Check. + + item.url = item.url + '?type=' + item.params; + } else { + return $http.post(item.url, item.params) + .success(function (data) { + item.process(data) + }) + .error(function (data, status) { + item.fail(); + }); + } } // setting 1 minute timeout to prevent system from timing out return $http.get(item.url, {timeout: 60000}) diff --git a/setup/src/Magento/Setup/Controller/Environment.php b/setup/src/Magento/Setup/Controller/Environment.php index 57c8a509abadf2ca6ce2d64f69ebf397d56959b9..ac646bcbceff322156bef1fb5379352eaa22a0ff 100644 --- a/setup/src/Magento/Setup/Controller/Environment.php +++ b/setup/src/Magento/Setup/Controller/Environment.php @@ -111,7 +111,8 @@ class Environment extends AbstractActionController */ public function phpVersionAction() { - $type = $this->getRequest()->getContent(); + $type = $this->getRequest()->getQuery('type'); + $data = []; if ($type == ReadinessCheckInstaller::INSTALLER) { $data = $this->phpReadinessCheck->checkPhpVersion(); @@ -128,7 +129,8 @@ class Environment extends AbstractActionController */ public function phpSettingsAction() { - $type = $this->getRequest()->getContent(); + $type = $this->getRequest()->getQuery('type'); + $data = []; if ($type == ReadinessCheckInstaller::INSTALLER) { $data = $this->phpReadinessCheck->checkPhpSettings(); @@ -145,7 +147,8 @@ class Environment extends AbstractActionController */ public function phpExtensionsAction() { - $type = $this->getRequest()->getContent(); + $type = $this->getRequest()->getQuery('type'); + $data = []; if ($type == ReadinessCheckInstaller::INSTALLER) { $data = $this->phpReadinessCheck->checkPhpExtensions(); diff --git a/setup/src/Magento/Setup/Model/ConfigGenerator.php b/setup/src/Magento/Setup/Model/ConfigGenerator.php index db1e6727b703f2bf7cae32b9c0c45007c21e80c9..812cbd1830a38c71f1bcfbebfcd2120915984e1e 100644 --- a/setup/src/Magento/Setup/Model/ConfigGenerator.php +++ b/setup/src/Magento/Setup/Model/ConfigGenerator.php @@ -98,7 +98,7 @@ class ConfigGenerator if ($currentKey === null) { $configData->set( ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, - md5($this->random->getRandomString(10)) + md5($this->random->getRandomString(ConfigOptionsListConstants::STORE_KEY_RANDOM_STRING_SIZE)) ); } } diff --git a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php index a7b0b923216b21fd50366d168fd32766356052eb..b6bed9a9d1cb26c3d5076c4a153884b4dbfc61b0 100644 --- a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php +++ b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php @@ -95,7 +95,8 @@ class PhpReadinessCheck $responseType = ResponseTypeInterface::RESPONSE_TYPE_SUCCESS; $settings = array_merge( - $this->checkXDebugNestedLevel() + $this->checkXDebugNestedLevel(), + $this->checkPopulateRawPostSetting() ); foreach ($settings as $setting) { @@ -178,4 +179,47 @@ class PhpReadinessCheck return $data; } + + /** + * Checks if PHP version >= 5.6.0 and always_populate_raw_post_data is set to -1 + * + * Beginning PHP 7.0, support for 'always_populate_raw_post_data' is going to removed. + * And beginning PHP 5.6, a deprecated message is displayed if 'always_populate_raw_post_data' + * is set to a value other than -1. + * + * @return array + */ + private function checkPopulateRawPostSetting() + { + // HHVM does not support 'always_populate_raw_post_data' to be set to -1 + if (defined('HHVM_VERSION')) { + return []; + } + + $data = []; + $error = false; + $iniSetting = intVal(ini_get('always_populate_raw_post_data')); + + if (version_compare(PHP_VERSION, '5.6.0') >= 0 && $iniSetting !== -1) { + $error = true; + } + + $message = sprintf( + 'Your PHP Version is %s, but always_populate_raw_post_data = %d. + $HTTP_RAW_POST_DATA is deprecated from PHP 5.6 onwards and will stop the installer from running. + Please open your php.ini file and set always_populate_raw_post_data to -1. + If you need more help please call your hosting provider. + ', + PHP_VERSION, + intVal(ini_get('always_populate_raw_post_data')) + ); + + $data['always_populate_raw_post_data'] = [ + 'message' => $message, + 'helpUrl' => 'http://php.net/manual/en/ini.core.php#ini.always-populate-settings-data', + 'error' => $error + ]; + + return $data; + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php index fc3fee7684e9ea15f76998244ed84678e72ff947..e34be41d76be26d8f7976af10d205171b1968dd1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/EnvironmentTest.php @@ -109,8 +109,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckInstaller::INSTALLER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckInstaller::INSTALLER); $this->phpReadinessCheck->expects($this->once())->method('checkPhpVersion'); $this->environment->setEvent($mvcEvent); $this->environment->dispatch($request, $response); @@ -128,8 +127,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckUpdater::UPDATER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckUpdater::UPDATER); $this->phpReadinessCheck->expects($this->never())->method('checkPhpVersion'); $read = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\ReadInterface', [], '', false); $this->filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($read); @@ -152,8 +150,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckInstaller::INSTALLER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckInstaller::INSTALLER); $this->phpReadinessCheck->expects($this->once())->method('checkPhpSettings'); $this->environment->setEvent($mvcEvent); $this->environment->dispatch($request, $response); @@ -171,8 +168,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckUpdater::UPDATER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckUpdater::UPDATER); $this->phpReadinessCheck->expects($this->never())->method('checkPhpSettings'); $read = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\ReadInterface', [], '', false); $this->filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($read); @@ -195,8 +191,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckInstaller::INSTALLER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckInstaller::INSTALLER); $this->phpReadinessCheck->expects($this->once())->method('checkPhpExtensions'); $this->environment->setEvent($mvcEvent); $this->environment->dispatch($request, $response); @@ -214,8 +209,7 @@ class EnvironmentTest extends \PHPUnit_Framework_TestCase $mvcEvent->expects($this->once())->method('setResponse')->with($response)->willReturn($mvcEvent); $mvcEvent->expects($this->once())->method('setTarget')->with($this->environment)->willReturn($mvcEvent); $mvcEvent->expects($this->any())->method('getRouteMatch')->willReturn($routeMatch); - $content = ReadinessCheckUpdater::UPDATER; - $request->expects($this->any())->method('getContent')->willReturn($content); + $request->expects($this->once())->method('getQuery')->willReturn(ReadinessCheckUpdater::UPDATER); $this->phpReadinessCheck->expects($this->never())->method('checkPhpExtensions'); $read = $this->getMockForAbstractClass('Magento\Framework\Filesystem\Directory\ReadInterface', [], '', false); $this->filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($read); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index 098e210824389a8848265a9d2b072d53fa24fd2d..0364f22e3b400003d81fd23651634da2bfd3e684 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -6,8 +6,8 @@ namespace Magento\Setup\Test\Unit\Model; -use Magento\Backend\Setup\ConfigOptionsList as BackendConfigOptionsList; -use Magento\Framework\Config\ConfigOptionsListConstants as SetupConfigOptionsList; +use Magento\Backend\Setup\ConfigOptionsList; +use Magento\Framework\Config\ConfigOptionsListConstants; use \Magento\Setup\Model\Installer; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\DriverPool; @@ -137,10 +137,10 @@ class InstallerTest extends \PHPUnit_Framework_TestCase * @var array */ private static $dbConfig = [ - SetupConfigOptionsList::KEY_HOST => '127.0.0.1', - SetupConfigOptionsList::KEY_NAME => 'magento', - SetupConfigOptionsList::KEY_USER => 'magento', - SetupConfigOptionsList::KEY_PASSWORD => '', + ConfigOptionsListConstants::KEY_HOST => '127.0.0.1', + ConfigOptionsListConstants::KEY_NAME => 'magento', + ConfigOptionsListConstants::KEY_USER => 'magento', + ConfigOptionsListConstants::KEY_PASSWORD => '', ]; /** @@ -227,11 +227,11 @@ class InstallerTest extends \PHPUnit_Framework_TestCase public function testInstall() { $request = [ - SetupConfigOptionsList::INPUT_KEY_DB_HOST => '127.0.0.1', - SetupConfigOptionsList::INPUT_KEY_DB_NAME => 'magento', - SetupConfigOptionsList::INPUT_KEY_DB_USER => 'magento', - SetupConfigOptionsList::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', - BackendConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', + ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', + ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', + ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', + ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', ]; $this->config->expects($this->atLeastOnce())->method('isAvailable')->willReturn(true); $allModules = ['Foo_One' => [], 'Bar_Two' => []]; @@ -436,7 +436,7 @@ class InstallerTest extends \PHPUnit_Framework_TestCase $this->config->expects($this->once())->method('isAvailable')->willReturn(true); $this->config->expects($this->once()) ->method('get') - ->with(SetupConfigOptionsList::CONFIG_PATH_DB_CONNECTION_DEFAULT) + ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT) ->willReturn(self::$dbConfig); $this->connection->expects($this->at(0))->method('quoteIdentifier')->with('magento')->willReturn('`magento`'); $this->connection->expects($this->at(1))->method('query')->with('DROP DATABASE IF EXISTS `magento`'); @@ -489,3 +489,15 @@ class InstallerTest extends \PHPUnit_Framework_TestCase return $newObject; } } + +namespace Magento\Setup\Model; + +/** + * Mocking autoload function + * + * @returns array + */ +function spl_autoload_functions() +{ + return ['mock_function_one', 'mock_function_two']; +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PhpReadinessCheckTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PhpReadinessCheckTest.php index 26289d9ba52fe368db84c326610dce05891f0974..3080faf35d90ba555a310f1079c3209c90081cda 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/PhpReadinessCheckTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/PhpReadinessCheckTest.php @@ -179,19 +179,32 @@ class PhpReadinessCheckTest extends \PHPUnit_Framework_TestCase { $this->phpInfo->expects($this->once())->method('getCurrent')->willReturn(['xdebug']); $this->phpInfo->expects($this->once())->method('getRequiredMinimumXDebugNestedLevel')->willReturn(50); - $message = sprintf( + $xdebugMessage = sprintf( 'Your current setting of xdebug.max_nesting_level=%d. Magento 2 requires it to be set to %d or more. Edit your config, restart web server, and try again.', 100, 50 ); + $rawPostMessage = sprintf( + 'Your PHP Version is %s, but always_populate_raw_post_data = -1. + $HTTP_RAW_POST_DATA is deprecated from PHP 5.6 onwards and will stop the installer from running. + Please open your php.ini file and set always_populate_raw_post_data to -1. + If you need more help please call your hosting provider. + ', + PHP_VERSION + ); $expected = [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, 'data' => [ 'xdebug_max_nesting_level' => [ - 'message' => $message, + 'message' => $xdebugMessage, 'error' => false, + ], + 'always_populate_raw_post_data' => [ + 'message' => $rawPostMessage, + 'helpUrl' => 'http://php.net/manual/en/ini.core.php#ini.always-populate-settings-data', + 'error' => false ] ] ]; @@ -202,19 +215,32 @@ class PhpReadinessCheckTest extends \PHPUnit_Framework_TestCase { $this->phpInfo->expects($this->once())->method('getCurrent')->willReturn(['xdebug']); $this->phpInfo->expects($this->once())->method('getRequiredMinimumXDebugNestedLevel')->willReturn(200); - $message = sprintf( + $xdebugMessage = sprintf( 'Your current setting of xdebug.max_nesting_level=%d. Magento 2 requires it to be set to %d or more. Edit your config, restart web server, and try again.', 100, 200 ); + $rawPostMessage = sprintf( + 'Your PHP Version is %s, but always_populate_raw_post_data = -1. + $HTTP_RAW_POST_DATA is deprecated from PHP 5.6 onwards and will stop the installer from running. + Please open your php.ini file and set always_populate_raw_post_data to -1. + If you need more help please call your hosting provider. + ', + PHP_VERSION + ); $expected = [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_ERROR, 'data' => [ 'xdebug_max_nesting_level' => [ - 'message' => $message, + 'message' => $xdebugMessage, 'error' => true, + ], + 'always_populate_raw_post_data' => [ + 'message' => $rawPostMessage, + 'helpUrl' => 'http://php.net/manual/en/ini.core.php#ini.always-populate-settings-data', + 'error' => false ] ] ]; @@ -224,7 +250,24 @@ class PhpReadinessCheckTest extends \PHPUnit_Framework_TestCase public function testCheckPhpSettingsNoXDebug() { $this->phpInfo->expects($this->once())->method('getCurrent')->willReturn([]); - $expected = ['responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, 'data' => []]; + $rawPostMessage = sprintf( + 'Your PHP Version is %s, but always_populate_raw_post_data = -1. + $HTTP_RAW_POST_DATA is deprecated from PHP 5.6 onwards and will stop the installer from running. + Please open your php.ini file and set always_populate_raw_post_data to -1. + If you need more help please call your hosting provider. + ', + PHP_VERSION + ); + $expected = [ + 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, + 'data' => [ + 'always_populate_raw_post_data' => [ + 'message' => $rawPostMessage, + 'helpUrl' => 'http://php.net/manual/en/ini.core.php#ini.always-populate-settings-data', + 'error' => false + ] + ] + ]; $this->assertEquals($expected, $this->phpReadinessCheck->checkPhpSettings()); } @@ -282,7 +325,11 @@ class PhpReadinessCheckTest extends \PHPUnit_Framework_TestCase namespace Magento\Setup\Model; -function ini_get() +function ini_get($param) { - return 100; + if ($param === 'xdebug.max_nesting_level') { + return 100; + } elseif ($param === 'always_populate_raw_post_data') { + return -1; + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php index 3f833f10a80e6aa2d6abc5c987659cdf919f437f..ad7f8285196d29543b94473189461b93aa6adb02 100644 --- a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php @@ -46,9 +46,31 @@ class DbValidatorTest extends \PHPUnit_Framework_TestCase $this->connection ->expects($this->atLeastOnce()) ->method('query') - ->with('SHOW GRANTS FOR current_user()') ->willReturn($pdo); - $pdo->expects($this->once())->method('fetchAll')->willReturn([['GRANT ALL PRIVILEGES ON `name.*` TO']]); + $pdo->expects($this->once()) + ->method('fetchAll') + ->willReturn( + [ + ['SELECT'], + ['INSERT'], + ['UPDATE'], + ['DELETE'], + ['CREATE'], + ['DROP'], + ['REFERENCES'], + ['INDEX'], + ['ALTER'], + ['CREATE TEMPORARY TABLES'], + ['LOCK TABLES'], + ['EXECUTE'], + ['CREATE VIEW'], + ['SHOW VIEW'], + ['CREATE ROUTINE'], + ['ALTER ROUTINE'], + ['EVENT'], + ['TRIGGER'], + ] + ); $this->assertEquals(true, $this->dbValidator->checkDatabaseConnection('name', 'host', 'user', 'password')); } @@ -65,11 +87,10 @@ class DbValidatorTest extends \PHPUnit_Framework_TestCase ->willReturn('5.6.0-0ubuntu0.12.04.1'); $pdo = $this->getMockForAbstractClass('Zend_Db_Statement_Interface', [], '', false); $this->connection - ->expects($this->once()) + ->expects($this->atLeastOnce()) ->method('query') - ->with('SHOW GRANTS FOR current_user()') ->willReturn($pdo); - $pdo->expects($this->once())->method('fetchAll')->willReturn([['GRANT SELECT ON *.* TO']]); + $pdo->expects($this->atLeastOnce())->method('fetchAll')->willReturn([['SELECT']]); $this->dbValidator->checkDatabaseConnection('name', 'host', 'user', 'password'); } diff --git a/setup/src/Magento/Setup/Validator/DbValidator.php b/setup/src/Magento/Setup/Validator/DbValidator.php index 1470ecf6c1814942d3656971f854fa7fc40161ef..e7b7aea7f0bf02bd6058d1811ddce51fe2ec24f6 100644 --- a/setup/src/Magento/Setup/Validator/DbValidator.php +++ b/setup/src/Magento/Setup/Validator/DbValidator.php @@ -62,8 +62,9 @@ class DbValidator */ public function checkDatabaseConnection($dbName, $dbHost, $dbUser, $dbPass = '') { + // establish connection to information_schema view to retrieve information about user and table privileges $connection = $this->connectionFactory->create([ - ConfigOptionsListConstants::KEY_NAME => $dbName, + ConfigOptionsListConstants::KEY_NAME => 'information_schema', ConfigOptionsListConstants::KEY_HOST => $dbHost, ConfigOptionsListConstants::KEY_USER => $dbUser, ConfigOptionsListConstants::KEY_PASSWORD => $dbPass, @@ -86,6 +87,7 @@ class DbValidator } } } + return $this->checkDatabasePrivileges($connection, $dbName); } @@ -99,12 +101,60 @@ class DbValidator */ private function checkDatabasePrivileges(\Magento\Framework\DB\Adapter\AdapterInterface $connection, $dbName) { - $grantInfo = $connection->query('SHOW GRANTS FOR current_user()')->fetchAll(\PDO::FETCH_NUM); + $requiredPrivileges = [ + 'SELECT', + 'INSERT', + 'UPDATE', + 'DELETE', + 'CREATE', + 'DROP', + 'REFERENCES', + 'INDEX', + 'ALTER', + 'CREATE TEMPORARY TABLES', + 'LOCK TABLES', + 'EXECUTE', + 'CREATE VIEW', + 'SHOW VIEW', + 'CREATE ROUTINE', + 'ALTER ROUTINE', + 'EVENT', + 'TRIGGER' + ]; + + // check global privileges + $userPrivilegesQuery = "SELECT PRIVILEGE_TYPE FROM USER_PRIVILEGES " + . "WHERE REPLACE(GRANTEE, '\'', '') = current_user()"; + $grantInfo = $connection->query($userPrivilegesQuery)->fetchAll(\PDO::FETCH_NUM); + if (empty(array_diff($requiredPrivileges, $this->parseGrantInfo($grantInfo)))) { + return true; + } + + // check table privileges + $schemaPrivilegesQuery = "SELECT PRIVILEGE_TYPE FROM SCHEMA_PRIVILEGES " . + "WHERE '$dbName' LIKE TABLE_SCHEMA AND REPLACE(GRANTEE, '\'', '') = current_user()"; + $grantInfo = $connection->query($schemaPrivilegesQuery)->fetchAll(\PDO::FETCH_NUM); + if (empty(array_diff($requiredPrivileges, $this->parseGrantInfo($grantInfo)))) { + return true; + } + + $errorMessage = 'Database user does not have enough privileges. Please make sure ' + . implode(', ', $requiredPrivileges) . " privileges are granted to table '$dbName'."; + throw new \Magento\Setup\Exception($errorMessage); + } + + /** + * Parses query result + * + * @param array $grantInfo + * @return array + */ + private function parseGrantInfo(array $grantInfo) + { + $result = []; foreach ($grantInfo as $grantRow) { - if (preg_match('/(ALL|ALL\sPRIVILEGES)\sON\s[^a-zA-Z\d\s]?(\*|' . $dbName . ')/', $grantRow[0]) === 1) { - return true; - } + $result[] = $grantRow[0]; } - throw new \Magento\Setup\Exception('Database user does not have enough privileges.'); + return $result; } } diff --git a/setup/view/magento/setup/create-admin-account.phtml b/setup/view/magento/setup/create-admin-account.phtml index 379974ec0c6698b8d8641ff73f4f0cc377ee9b80..fa58a132d6de365ea907f8abc44a3c39b8bac666 100644 --- a/setup/view/magento/setup/create-admin-account.phtml +++ b/setup/view/magento/setup/create-admin-account.phtml @@ -19,7 +19,7 @@ $passwordWizard = sprintf( </div> <p>%s</p>', 'Password Strength:', - 'Enter a mix of 6 or more numbers and letters. For a stronger password, include at least one small letter, big letter, and symbol (Ex: BuyIt$54).' + 'Enter a mix of 7 or more numbers and letters. For a stronger password, include at least one small letter, big letter, and symbol (Ex: BuyIt$54).' ); ?> @@ -146,7 +146,7 @@ $passwordWizard = sprintf( > <div class="error-container"> <span ng-show="account.adminPassword.$error.checkPassword"> - Please enter a mix of at least 6 alpha-numeric characters. + Please enter a mix of at least 7 alpha-numeric characters. </span> <span ng-show="account.adminPassword.$error.required"> Please enter your new password. diff --git a/setup/view/magento/setup/readiness-check/progress.phtml b/setup/view/magento/setup/readiness-check/progress.phtml index 964f9c403e472b31c115fbd5d30da479b40382e3..53ca61521c556249a18ffa14ec85d6e5b90bfeb4 100755 --- a/setup/view/magento/setup/readiness-check/progress.phtml +++ b/setup/view/magento/setup/readiness-check/progress.phtml @@ -285,7 +285,7 @@ </div> <p ng-show="componentDependency.expanded">For additional assistance, see - <a href="http://devdocs.magento.com/guides/v2.0/install-gde/trouble/trouble/php/tshoot_php-set.html" + <a href="http://devdocs.magento.com/guides/v2.0/install-gde/trouble/php/tshoot_php-set.html" target="_blank">PHP settings check help </a>. </p>