diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index 0a7f9c2dd9634..b754e178d48c2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -502,7 +502,7 @@ public function getAttributeRawValue($entityId, $attribute, $store) $staticTable, $staticAttributes )->join( - ['e' => $this->getTable('catalog_product_entity')], + ['e' => $this->getTable($this->getEntityTable())], 'e.' . $this->getLinkField() . ' = ' . $staticTable . '.' . $this->getLinkField() )->where( 'e.entity_id = :entity_id' @@ -523,7 +523,7 @@ public function getAttributeRawValue($entityId, $attribute, $store) $select = $connection->select() ->from(['default_value' => $table], ['attribute_id']) ->join( - ['e' => $this->getTable('catalog_product_entity')], + ['e' => $this->getTable($this->getEntityTable())], 'e.' . $this->getLinkField() . ' = ' . 'default_value.' . $this->getLinkField(), '' )->where('default_value.attribute_id IN (?)', array_keys($_attributes)) diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php index 38032a1c2c5d2..546483a660bd7 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php @@ -213,7 +213,6 @@ private function prepareMeta() 'dataScope' => 'qty', 'validation' => [ 'validate-number' => true, - 'validate-digits' => true, 'less-than-equals-to' => StockDataFilter::MAX_QTY_VALUE, ], 'imports' => [ diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index d7de033ea3b76..362aac7c8cf30 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -95,7 +95,6 @@ quantity_and_stock_status.qty true - true 99999999 200 diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js index b14527c9c9ec6..f45d32d6482d6 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js +++ b/app/code/Magento/CatalogInventory/view/adminhtml/web/js/components/qty-validator-changer.js @@ -19,8 +19,7 @@ define([ handleChanges: function (value) { var isDigits = value !== 1; - this.validation['validate-number'] = !isDigits; - this.validation['validate-digits'] = isDigits; + this.validation['validate-integer'] = isDigits; this.validation['less-than-equals-to'] = isDigits ? 99999999 : 99999999.9999; this.validate(); } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php index c357045a3d084..64f9b7e51769a 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php @@ -15,6 +15,13 @@ */ class MassDelete extends \Magento\Backend\App\Action { + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Cms::page_delete'; + /** * @var Filter */ diff --git a/app/code/Magento/Cms/etc/frontend/page_types.xml b/app/code/Magento/Cms/etc/frontend/page_types.xml index 76dec5cdcba6b..c5df71b7904ff 100644 --- a/app/code/Magento/Cms/etc/frontend/page_types.xml +++ b/app/code/Magento/Cms/etc/frontend/page_types.xml @@ -9,7 +9,6 @@ - diff --git a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php new file mode 100644 index 0000000000000..e05f008f70aa8 --- /dev/null +++ b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php @@ -0,0 +1,56 @@ +state = $state; + $this->scopeConfig = $scopeConfig; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return + parent::isHandling($record) + && $this->state->getMode() !== State::MODE_PRODUCTION + && $this->scopeConfig->getValue('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE); + } +} \ No newline at end of file diff --git a/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php b/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php new file mode 100644 index 0000000000000..7eae4020e6768 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php @@ -0,0 +1,117 @@ +filesystemMock = $this->getMockBuilder(DriverInterface::class) + ->getMockForAbstractClass(); + $this->stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->getMockForAbstractClass(); + $this->formatterMock = $this->getMockBuilder(FormatterInterface::class) + ->getMockForAbstractClass(); + + $this->formatterMock->expects($this->any()) + ->method('format') + ->willReturn(null); + + $this->model = (new ObjectManager($this))->getObject(Debug::class, [ + 'filesystem' => $this->filesystemMock, + 'state' => $this->stateMock, + 'scopeConfig' => $this->scopeConfigMock, + ]); + $this->model->setFormatter($this->formatterMock); + } + + public function testHandle() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) + ->willReturn(true); + + $this->model->handle(['formatted' => false, 'level' => Logger::DEBUG]); + } + + public function testHandleDisabledByProduction() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_PRODUCTION); + $this->scopeConfigMock->expects($this->never()) + ->method('getValue'); + + $this->model->handle(['formatted' => false, 'level' => Logger::DEBUG]); + } + + public function testHandleDisabledByConfig() + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) + ->willReturn(false); + + $this->model->handle(['formatted' => false, 'level' => Logger::DEBUG]); + } + + public function testHandleDisabledByLevel() + { + $this->stateMock->expects($this->never()) + ->method('getMode'); + $this->scopeConfigMock->expects($this->never()) + ->method('getValue'); + + $this->model->handle(['formatted' => false, 'level' => Logger::API]); + } +} diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index 551704598ee70..11b07c2794baf 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -23,6 +23,13 @@ Magento\Developer\Model\Config\Backend\AllowedIps + + + + Not available in production mode. + Magento\Config\Model\Config\Source\Yesno + + diff --git a/app/code/Magento/Developer/etc/di.xml b/app/code/Magento/Developer/etc/di.xml index a7fc549a0d993..90f63f93104b3 100644 --- a/app/code/Magento/Developer/etc/di.xml +++ b/app/code/Magento/Developer/etc/di.xml @@ -9,6 +9,7 @@ + diff --git a/app/code/Magento/Paypal/etc/frontend/page_types.xml b/app/code/Magento/Paypal/etc/frontend/page_types.xml index 1c8b200632ab5..1649f23ff4339 100644 --- a/app/code/Magento/Paypal/etc/frontend/page_types.xml +++ b/app/code/Magento/Paypal/etc/frontend/page_types.xml @@ -6,7 +6,7 @@ */ --> - + @@ -18,5 +18,5 @@ - + diff --git a/app/code/Magento/Sales/etc/frontend/page_types.xml b/app/code/Magento/Sales/etc/frontend/page_types.xml index f4e7fa7035d75..d8069d7714fa6 100644 --- a/app/code/Magento/Sales/etc/frontend/page_types.xml +++ b/app/code/Magento/Sales/etc/frontend/page_types.xml @@ -6,24 +6,24 @@ */ --> - - - - - - - + + + + + + + - - - - - + + + + + - - + + diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php index bfd0985ee7e96..afc123590625a 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Delete.php @@ -11,6 +11,16 @@ class Delete extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @var \Magento\Sitemap\Model\SitemapFactory + */ + private $sitemapFactory; + /** * Delete action * @@ -18,30 +28,24 @@ class Delete extends \Magento\Sitemap\Controller\Adminhtml\Sitemap */ public function execute() { - /** @var \Magento\Framework\Filesystem\Directory\Write $directory */ - $directory = $this->_objectManager->get( - 'Magento\Framework\Filesystem' - )->getDirectoryWrite( - DirectoryList::ROOT - ); - + $directory = $this->getFilesystem()->getDirectoryWrite(DirectoryList::ROOT); // check if we know what should be deleted $id = $this->getRequest()->getParam('sitemap_id'); if ($id) { try { // init model and delete - $model = $this->_objectManager->create('Magento\Sitemap\Model\Sitemap'); - $model->setId($id); - // init and load sitemap model - - /* @var $sitemap \Magento\Sitemap\Model\Sitemap */ - $model->load($id); + /** @var \Magento\Sitemap\Model\Sitemap $sitemap */ + $sitemap = $this->getSitemapFactory()->create(); + $sitemap->load($id); // delete file - $path = $directory->getRelativePath($model->getPreparedFilename()); - if ($model->getSitemapFilename() && $directory->isFile($path)) { + $sitemapPath = $sitemap->getSitemapPath(); + $sitemapFilename = $sitemap->getSitemapFilename(); + + $path = $directory->getRelativePath($sitemapPath . $sitemapFilename); + if ($sitemap->getSitemapFilename() && $directory->isFile($path)) { $directory->delete($path); } - $model->delete(); + $sitemap->delete(); // display success message $this->messageManager->addSuccess(__('You deleted the sitemap.')); // go to grid @@ -60,4 +64,38 @@ public function execute() // go to grid $this->_redirect('adminhtml/*/'); } + + /** + * The getter function to get Filesystem object for real application code + * + * @return \Magento\Framework\Filesystem + * + * @deprecated + */ + private function getFilesystem() + { + if ($this->filesystem === null) { + $this->filesystem = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\Filesystem::class + ); + } + return $this->filesystem; + } + + /** + * The getter function to get SitemapFactory object for real application code + * + * @return \Magento\Sitemap\Model\SitemapFactory + * + * @deprecated + */ + private function getSitemapFactory() + { + if ($this->sitemapFactory === null) { + $this->sitemapFactory = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Sitemap\Model\SitemapFactory::class + ); + } + return $this->sitemapFactory; + } } diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php new file mode 100644 index 0000000000000..8035f057d6b66 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/DeleteTest.php @@ -0,0 +1,149 @@ +filesystemMock = $this->getMockBuilder(\Magento\Framework\Filesystem::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->sitemapFactoryMock = $this->getMockBuilder(\Magento\Sitemap\Model\SitemapFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMockForAbstractClass(); + $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setRedirect']) + ->getMockForAbstractClass(); + $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMockForAbstractClass(); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->deleteController = $objectManagerHelper->getObject( + \Magento\Sitemap\Controller\Adminhtml\Sitemap\Delete::class, + [ + 'request' => $this->requestMock, + 'response' => $this->responseMock, + 'messageManager' => $this->messageManagerMock, + 'filesystem' => $this->filesystemMock, + 'sitemapFactory' => $this->sitemapFactoryMock + ] + ); + } + + public function testExecuteWithoutSitemapId() + { + $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); + $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn(0); + $this->responseMock->expects($this->once())->method('setRedirect'); + $this->messageManagerMock->expects($this->any()) + ->method('addError') + ->with('We can\'t find a sitemap to delete.'); + + $this->deleteController->execute(); + } + + public function testExecuteCannotFindSitemap() + { + $id = 1; + $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); + $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn($id); + + $sitemapMock = $this->getMockBuilder(\Magento\Sitemap\Model\Sitemap::class) + ->disableOriginalConstructor() + ->setMethods(['load']) + ->getMock(); + + $sitemapMock->expects($this->once())->method('load')->with($id)->willThrowException(new \Exception); + $this->sitemapFactoryMock->expects($this->once())->method('create')->willReturn($sitemapMock); + $this->responseMock->expects($this->once())->method('setRedirect'); + $this->messageManagerMock->expects($this->any()) + ->method('addError'); + + $this->deleteController->execute(); + } + + public function testExecute() + { + $id = 1; + $sitemapPath = '/'; + $sitemapFilename = 'sitemap.xml'; + $relativePath = '/sitemap.xml'; + $writeDirectoryMock = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->filesystemMock->expects($this->any())->method('getDirectoryWrite')->willReturn($writeDirectoryMock); + $this->requestMock->expects($this->any())->method('getParam')->with('sitemap_id')->willReturn($id); + + $sitemapMock = $this->getMockBuilder(\Magento\Sitemap\Model\Sitemap::class) + ->disableOriginalConstructor() + ->setMethods(['getSitemapPath', 'getSitemapFilename', 'load', 'delete']) + ->getMock(); + $sitemapMock->expects($this->once())->method('load')->with($id); + $sitemapMock->expects($this->once())->method('delete'); + $sitemapMock->expects($this->any())->method('getSitemapPath')->willReturn($sitemapPath); + $sitemapMock->expects($this->any())->method('getSitemapFilename')->willReturn($sitemapFilename); + $this->sitemapFactoryMock->expects($this->once())->method('create')->willReturn($sitemapMock); + $writeDirectoryMock->expects($this->any()) + ->method('getRelativePath') + ->with($sitemapPath . $sitemapFilename) + ->willReturn($relativePath); + $writeDirectoryMock->expects($this->once())->method('isFile')->with($relativePath)->willReturn(true); + $writeDirectoryMock->expects($this->once())->method('delete')->with($relativePath)->willReturn(true); + + $this->responseMock->expects($this->once())->method('setRedirect'); + $this->messageManagerMock->expects($this->any()) + ->method('addSuccess'); + + $this->deleteController->execute(); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 555c10ee04b70..ea6268789c059 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -572,6 +572,15 @@ define([ }, $.mage.__('Please enter a valid number in this field.') ], + 'validate-integer': [ + function(value) { + return ( + utils.isEmptyNoTrim(value) + || (!isNaN(utils.parseNumber(value)) && /^\s*-?\d*\s*$/.test(value)) + ); + }, + $.mage.__('Please enter a valid integer in this field.') + ], "validate-number-range": [ function(value, param) { if (utils.isEmptyNoTrim(value)) { diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 2235df9aff077..481e15f3122b5 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -313,13 +313,13 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) $value = $parameters[$name]->getValue(); } } - if ($value) { + + if (isset($value)) { $directive .= sprintf(' %s="%s"', $name, $value); } } $directive .= $this->getWidgetPageVarName($params); - $directive .= '}}'; if ($asIs) { diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 2d03ac62c1563..be04a62830fd7 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Widget\Test\Unit\Model; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + class WidgetTest extends \PHPUnit_Framework_TestCase { /** @@ -101,7 +103,7 @@ public function testGetConfigAsObject() $this->assertSame('Magento_Cms::images/widget_page_link.png', $resultObject->getPlaceholderImage()); $resultParameters = $resultObject->getParameters(); - $this->assertInstanceOf('Magento\Framework\DataObject', $resultParameters['page_id' ]); + $this->assertInstanceOf('Magento\Framework\DataObject', $resultParameters['page_id']); $this->assertInstanceOf('Magento\Framework\DataObject', $resultParameters['anchor_text']); $this->assertInstanceOf('Magento\Framework\DataObject', $resultParameters['template']); @@ -162,4 +164,46 @@ public function testGetWidgetDeclaration() $this->assertContains('conditions_encoded="encoded-conditions-string"', $result); $this->assertContains('page_var_name="pasdf"}}', $result); } + + public function testGetWidgetDeclarationWithZeroValueParam() + { + $mathRandomMock = $this->getMock('\Magento\Framework\Math\Random', ['getRandomString'], [], '', false); + $mathRandomMock->expects($this->any()) + ->method('getRandomString') + ->willReturn('asdf'); + + (new ObjectManager($this))->setBackwardCompatibleProperty( + $this->widget, + 'mathRandom', + $mathRandomMock + ); + + $conditions = [ + [ + 'type' => 'Magento\CatalogWidget\Model\Rule\Condition\Combine', + 'aggregator' => 'all', + 'value' => '1', + 'new_child' => '' + ] + ]; + $params = [ + 'title' => 'my widget', + 'show_pager' => '1', + 'products_per_page' => '5', + 'products_count' => '0', + 'template' => 'product/widget/content/grid.phtml', + 'conditions' => $conditions + ]; + + $this->conditionsHelper->expects($this->once()) + ->method('encode') + ->with($conditions) + ->willReturn('encoded-conditions-string'); + + $result = $this->widget->getWidgetDeclaration('Magento\CatalogWidget\Block\Product\ProductsList', $params); + $this->assertContains('{{widget type="Magento\CatalogWidget\Block\Product\ProductsList"', $result); + $this->assertContains('conditions_encoded="encoded-conditions-string"', $result); + $this->assertContains('page_var_name="pasdf"}}', $result); + $this->assertContains('products_count="0"', $result); + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/MultiselectgrouplistElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/MultiselectgrouplistElement.php index cad083a69b854..f179d7d905340 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/MultiselectgrouplistElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/MultiselectgrouplistElement.php @@ -324,4 +324,17 @@ protected function getSelectedOptions() return $options; } + + /** + * {@inheritdoc} + */ + public function deselectAll() + { + $options = $this->getSelectedOptions(); + + /** @var SimpleElement $option */ + foreach ($options as $option) { + $option->click(); + } + } } diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml index 6f9a6354411f2..79b707b504abd 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Block/Edit/CmsForm.xml @@ -12,7 +12,6 @@ multiselectgrouplist - .admin__actions-switch-label switcher diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotOnCategoryPage.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotOnCategoryPage.php index 1057e7d058f26..93ae1f121fbbd 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotOnCategoryPage.php +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Constraint/AssertCmsBlockNotOnCategoryPage.php @@ -11,6 +11,7 @@ use Magento\Cms\Test\Page\CmsIndex; use Magento\Mtf\Constraint\AbstractConstraint; use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Catalog\Test\Fixture\Category; /** * Assert that created CMS block non visible on frontend category page. @@ -25,25 +26,29 @@ class AssertCmsBlockNotOnCategoryPage extends AbstractConstraint * @param CmsBlock $cmsBlock * @param CatalogCategoryView $catalogCategoryView * @param FixtureFactory $fixtureFactory + * @param Category|null $category [optional] * @return void */ public function processAssert( CmsIndex $cmsIndex, CmsBlock $cmsBlock, CatalogCategoryView $catalogCategoryView, - FixtureFactory $fixtureFactory + FixtureFactory $fixtureFactory, + Category $category = null ) { - $category = $fixtureFactory->createByCode( - 'category', - [ - 'dataset' => 'default_subcategory', - 'data' => [ - 'display_mode' => 'Static block and products', - 'landing_page' => $cmsBlock->getTitle(), + if ($category === null) { + $category = $fixtureFactory->createByCode( + 'category', + [ + 'dataset' => 'default_subcategory', + 'data' => [ + 'display_mode' => 'Static block and products', + 'landing_page' => $cmsBlock->getTitle(), + ] ] - ] - ); - $category->persist(); + ); + $category->persist(); + } $cmsIndex->open(); $cmsIndex->getTopmenu()->selectCategoryByName($category->getName()); diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsBlockEntityTest.php b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsBlockEntityTest.php index 6c231797d89d5..c4897ea36269d 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsBlockEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsBlockEntityTest.php @@ -10,6 +10,8 @@ use Magento\Cms\Test\Page\Adminhtml\CmsBlockIndex; use Magento\Cms\Test\Page\Adminhtml\CmsBlockNew; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Catalog\Test\Fixture\Category; /** * Preconditions: @@ -46,37 +48,73 @@ class DeleteCmsBlockEntityTest extends Injectable */ protected $cmsBlockNew; + /** + * Fixture Factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + /** * Injection data. * * @param CmsBlockIndex $cmsBlockIndex * @param CmsBlockNew $cmsBlockNew + * @param FixtureFactory $fixtureFactory * @return void */ public function __inject( CmsBlockIndex $cmsBlockIndex, - CmsBlockNew $cmsBlockNew + CmsBlockNew $cmsBlockNew, + FixtureFactory $fixtureFactory ) { $this->cmsBlockIndex = $cmsBlockIndex; $this->cmsBlockNew = $cmsBlockNew; + $this->fixtureFactory = $fixtureFactory; } /** * Delete CMS Block. * * @param CmsBlock $cmsBlock - * @return void + * @return array */ public function test(CmsBlock $cmsBlock) { // Precondition $cmsBlock->persist(); $filter = ['identifier' => $cmsBlock->getIdentifier()]; + $category = $this->createCategory($cmsBlock); // Steps $this->cmsBlockIndex->open(); $this->cmsBlockIndex->getCmsBlockGrid()->searchAndOpen($filter); $this->cmsBlockNew->getFormPageActions()->delete(); $this->cmsBlockNew->getModalBlock()->acceptAlert(); + + return ['category' => $category]; + } + + /** + * Create category. + * + * @param CmsBlock $cmsBlock + * @return Category + */ + private function createCategory(CmsBlock $cmsBlock) + { + $category = $this->fixtureFactory->createByCode( + 'category', + [ + 'dataset' => 'default_subcategory', + 'data' => [ + 'display_mode' => 'Static block and products', + 'landing_page' => $cmsBlock->getTitle(), + ] + ] + ); + $category->persist(); + + return $category; } } diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index cf5a8aae3b501..9c9ad3e44adf3 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -890,6 +890,16 @@ private function accumulateThemeStaticFiles($area, $locale, $filePattern, &$resu throw new \UnexpectedValueException("Could not parse theme static file '$file'"); } } + + if (!$files) { + $result[] = [ + $themeArea, + $themePackage->getVendor() . '/' . $themePackage->getName(), + null, + null, + null, + ]; + } } } } diff --git a/lib/internal/Magento/Framework/Logger/Monolog.php b/lib/internal/Magento/Framework/Logger/Monolog.php index 9840d61b1846a..bde27b2164cfb 100644 --- a/lib/internal/Magento/Framework/Logger/Monolog.php +++ b/lib/internal/Magento/Framework/Logger/Monolog.php @@ -10,12 +10,25 @@ class Monolog extends Logger { + /** + * {@inheritdoc} + */ + public function __construct($name, array $handlers = [], array $processors = []) + { + /** + * TODO: This should be eliminated with MAGETWO-53989 + */ + $handlers = array_values($handlers); + + parent::__construct($name, $handlers, $processors); + } + /** * Adds a log record. * - * @param integer $level The logging level - * @param string $message The log message - * @param array $context The log context + * @param integer $level The logging level + * @param string $message The log message + * @param array $context The log context * @return Boolean Whether the record has been processed */ public function addRecord($level, $message, array $context = []) diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 1700186de7b20..6f979066d6fc2 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -167,6 +167,11 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl */ protected $_scopeConfig; + /** + * @var \Magento\Framework\App\CacheInterface + */ + protected $_cache; + /** * Constructor * @@ -1004,14 +1009,20 @@ protected function getCacheTags() /** * Get block cache life time * - * @return int + * @return int|bool|null */ protected function getCacheLifetime() { if (!$this->hasData('cache_lifetime')) { return null; } - return $this->getData('cache_lifetime'); + + $cacheLifetime = $this->getData('cache_lifetime'); + if (false === $cacheLifetime || null === $cacheLifetime) { + return $cacheLifetime; + } + + return (int)$cacheLifetime; } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index 0605210f360ee..dad0d22d188c9 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -12,20 +12,80 @@ use Magento\Framework\View\Element\Context; use Magento\Framework\Config\View; use Magento\Framework\View\ConfigInterface; +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Cache\StateInterface as CacheStateInterface; +use Magento\Framework\App\CacheInterface; +use Magento\Framework\Session\SidResolverInterface; +use Magento\Framework\Session\SessionManagerInterface; class AbstractBlockTest extends \PHPUnit_Framework_TestCase { /** * @var AbstractBlock */ - protected $block; + private $block; + + /** + * @var EventManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventManagerMock; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var CacheStateInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $cacheStateMock; + + /** + * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $cacheMock; + + /** + * @var SidResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $sidResolverMock; + + /** + * @var SessionManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $sessionMock; /** * @return void */ protected function setUp() { + $this->eventManagerMock = $this->getMockForAbstractClass(EventManagerInterface::class); + $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $this->cacheStateMock = $this->getMockForAbstractClass(CacheStateInterface::class); + $this->cacheMock = $this->getMockForAbstractClass(CacheInterface::class); + $this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class); + $this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class); $contextMock = $this->getMock(Context::class, [], [], '', false); + $contextMock->expects($this->once()) + ->method('getEventManager') + ->willReturn($this->eventManagerMock); + $contextMock->expects($this->once()) + ->method('getScopeConfig') + ->willReturn($this->scopeConfigMock); + $contextMock->expects($this->once()) + ->method('getCacheState') + ->willReturn($this->cacheStateMock); + $contextMock->expects($this->once()) + ->method('getCache') + ->willReturn($this->cacheMock); + $contextMock->expects($this->once()) + ->method('getSidResolver') + ->willReturn($this->sidResolverMock); + $contextMock->expects($this->once()) + ->method('getSession') + ->willReturn($this->sessionMock); $this->block = $this->getMockForAbstractClass( AbstractBlock::class, ['context' => $contextMock] @@ -135,4 +195,125 @@ public function testGetCacheKeyByName() $cacheKey = sha1($nameInLayout); $this->assertEquals(AbstractBlock::CACHE_KEY_PREFIX . $cacheKey, $this->block->getCacheKey()); } + + /** + * @return void + */ + public function testToHtmlWhenModuleIsDisabled() + { + $moduleName = 'Test'; + $this->block->setData('module_name', $moduleName); + + $this->eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with('view_block_abstract_to_html_before', ['block' => $this->block]); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with('advanced/modules_disable_output/' . $moduleName, \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn(true); + + $this->assertSame('', $this->block->toHtml()); + } + + /** + * @param string|bool $cacheLifetime + * @param string|bool $dataFromCache + * @param string $dataForSaveCache + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expectsCacheLoad + * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expectsCacheSave + * @param string $expectedResult + * @return void + * @dataProvider getCacheLifetimeDataProvider + */ + public function testGetCacheLifetimeViaToHtml( + $cacheLifetime, + $dataFromCache, + $dataForSaveCache, + $expectsCacheLoad, + $expectsCacheSave, + $expectedResult + ) { + $moduleName = 'Test'; + $cacheKey = 'testKey'; + $this->block->setData('cache_key', $cacheKey); + $this->block->setData('module_name', $moduleName); + $this->block->setData('cache_lifetime', $cacheLifetime); + + $this->eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with('view_block_abstract_to_html_before', ['block' => $this->block]); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with('advanced/modules_disable_output/' . $moduleName, \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn(false); + $this->cacheStateMock->expects($this->any()) + ->method('isEnabled') + ->with(AbstractBlock::CACHE_GROUP) + ->willReturn(true); + $this->cacheMock->expects($expectsCacheLoad) + ->method('load') + ->with(AbstractBlock::CACHE_KEY_PREFIX . $cacheKey) + ->willReturn($dataFromCache); + $this->cacheMock->expects($expectsCacheSave) + ->method('save') + ->with($dataForSaveCache, AbstractBlock::CACHE_KEY_PREFIX . $cacheKey); + $this->sidResolverMock->expects($this->any()) + ->method('getSessionIdQueryParam') + ->with($this->sessionMock) + ->willReturn('sessionIdQueryParam'); + $this->sessionMock->expects($this->any()) + ->method('getSessionId') + ->willReturn('sessionId'); + + $this->assertSame($expectedResult, $this->block->toHtml()); + } + + /** + * @return array + */ + public function getCacheLifetimeDataProvider() + { + return [ + [ + 'cacheLifetime' => null, + 'dataFromCache' => 'dataFromCache', + 'dataForSaveCache' => '', + 'expectsCacheLoad' => $this->never(), + 'expectsCacheSave' => $this->never(), + 'expectedResult' => '', + ], + [ + 'cacheLifetime' => false, + 'dataFromCache' => 'dataFromCache', + 'dataForSaveCache' => '', + 'expectsCacheLoad' => $this->once(), + 'expectsCacheSave' => $this->never(), + 'expectedResult' => 'dataFromCache', + ], + [ + 'cacheLifetime' => 120, + 'dataFromCache' => 'dataFromCache', + 'dataForSaveCache' => '', + 'expectsCacheLoad' => $this->once(), + 'expectsCacheSave' => $this->never(), + 'expectedResult' => 'dataFromCache', + ], + [ + 'cacheLifetime' => '120string', + 'dataFromCache' => 'dataFromCache', + 'dataForSaveCache' => '', + 'expectsCacheLoad' => $this->once(), + 'expectsCacheSave' => $this->never(), + 'expectedResult' => 'dataFromCache', + ], + [ + 'cacheLifetime' => 120, + 'dataFromCache' => false, + 'dataForSaveCache' => '', + 'expectsCacheLoad' => $this->once(), + 'expectsCacheSave' => $this->once(), + 'expectedResult' => '', + ], + ]; + } } diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 7ddd91482210e..5089bde34912d 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -31,12 +31,14 @@ define([ }, openDialog: function(url, width, height, title, options) { var windowId = this.windowId, - content = '', + content = '', self = this; if (this.modal) { this.modal.html($(content).html()); - this.modal.modal('option', 'closed', options.closed); + if (options && typeof options.closed !== 'undefined') { + this.modal.modal('option', 'closed', options.closed); + } } else { this.modal = $(content).modal($.extend({ title: title || 'Insert File...', diff --git a/pub/errors/processor.php b/pub/errors/processor.php index f5415d14ab534..218628f349e5e 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -568,7 +568,7 @@ protected function _validate() */ protected function _setSkin($value, \stdClass $config = null) { - if (preg_match('/^[a-z0-9_]+$/i', $value) && is_dir($this->_indexDir . self::ERROR_DIR . '/' . $value)) { + if (preg_match('/^[a-z0-9_]+$/i', $value) && is_dir($this->_errorDir . $value)) { if (!$config) { if ($this->_config) { $config = $this->_config;