diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml index 456032ebcbc24..1c4ad3105a2b5 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml @@ -17,10 +17,8 @@ 'listing' => [ 'displayMode' => 'grid' ], - 'column' => [ - 'image' => [ - 'imageCode' => 'recently_viewed_products_images_names_widget' - ] + 'image' => [ + 'imageCode' => 'recently_viewed_products_images_names_widget' ] ] ); diff --git a/app/code/Magento/Deploy/App/Mode/ConfigProvider.php b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php new file mode 100644 index 0000000000000..142e3fe819438 --- /dev/null +++ b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php @@ -0,0 +1,58 @@ + [ + * 'production' => [ + * {{setting_path}} => {{setting_value}} + * ] + * ] + * ] + * + * @var array + */ + private $config; + + /** + * @param array $config + */ + public function __construct(array $config = []) + { + $this->config = $config; + } + + /** + * Provide configuration while switching from $currentMode to $targetMode + * This method used in \Magento\Deploy\Model\Mode::setStoreMode + * + * For example: while switching from developer mode to production mode + * need to turn off 'dev/debug/debug_logging' setting in this case method + * will return array + * [ + * {{setting_path}} => {{setting_value}} + * ] + * + * @param string $currentMode + * @param string $targetMode + * @return array + */ + public function getConfigs($currentMode, $targetMode) + { + if (isset($this->config[$currentMode][$targetMode])) { + return $this->config[$currentMode][$targetMode]; + } + return []; + } +} diff --git a/app/code/Magento/Deploy/Model/Mode.php b/app/code/Magento/Deploy/Model/Mode.php index 75de873f71e44..ba3e8652fd443 100644 --- a/app/code/Magento/Deploy/Model/Mode.php +++ b/app/code/Magento/Deploy/Model/Mode.php @@ -6,6 +6,8 @@ namespace Magento\Deploy\Model; +use Magento\Deploy\App\Mode\ConfigProvider; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\DeploymentConfig\Reader; use Magento\Framework\App\DeploymentConfig\Writer; use Magento\Framework\App\Filesystem\DirectoryList; @@ -14,7 +16,10 @@ use Magento\Framework\Config\File\ConfigFilePool; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; +use Magento\Config\Console\Command\EmulatedAdminhtmlAreaProcessor; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\ObjectManager; /** * A class to manage Magento modes @@ -44,11 +49,35 @@ class Mode */ private $reader; + /** + * @var MaintenanceMode + */ + private $maintenanceMode; + /** * @var Filesystem */ private $filesystem; + /** + * @var ConfigProvider + */ + private $configProvider; + + /** + * The factory for processor facade. + * + * @var ProcessorFacadeFactory + */ + private $processorFacadeFactory; + + /** + * Emulator adminhtml area for CLI command. + * + * @var EmulatedAdminhtmlAreaProcessor + */ + private $emulatedAreaProcessor; + /** * @param InputInterface $input * @param OutputInterface $output @@ -56,6 +85,9 @@ class Mode * @param Reader $reader * @param MaintenanceMode $maintenanceMode * @param Filesystem $filesystem + * @param ConfigProvider $configProvider + * @param ProcessorFacadeFactory $processorFacadeFactory + * @param EmulatedAdminhtmlAreaProcessor $emulatedAreaProcessor */ public function __construct( InputInterface $input, @@ -63,7 +95,10 @@ public function __construct( Writer $writer, Reader $reader, MaintenanceMode $maintenanceMode, - Filesystem $filesystem + Filesystem $filesystem, + ConfigProvider $configProvider = null, + ProcessorFacadeFactory $processorFacadeFactory = null, + EmulatedAdminhtmlAreaProcessor $emulatedAreaProcessor = null ) { $this->input = $input; $this->output = $output; @@ -71,6 +106,13 @@ public function __construct( $this->reader = $reader; $this->maintenanceMode = $maintenanceMode; $this->filesystem = $filesystem; + + $this->configProvider = + $configProvider ?: ObjectManager::getInstance()->get(ConfigProvider::class); + $this->processorFacadeFactory = + $processorFacadeFactory ?: ObjectManager::getInstance()->get(ProcessorFacadeFactory::class); + $this->emulatedAreaProcessor = + $emulatedAreaProcessor ?: ObjectManager::getInstance()->get(EmulatedAdminhtmlAreaProcessor::class); } /** @@ -145,6 +187,7 @@ public function getMode() */ protected function setStoreMode($mode) { + $this->saveAppConfigs($mode); $data = [ ConfigFilePool::APP_ENV => [ State::PARAM_MODE => $mode @@ -153,6 +196,29 @@ protected function setStoreMode($mode) $this->writer->saveConfig($data); } + /** + * Save application configs while switching mode + * + * @param string $mode + * @return void + */ + private function saveAppConfigs($mode) + { + $configs = $this->configProvider->getConfigs($this->getMode(), $mode); + foreach ($configs as $path => $value) { + $this->emulatedAreaProcessor->process(function () use ($path, $value) { + $this->processorFacadeFactory->create()->process( + $path, + $value, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + null, + true + ); + }); + $this->output->writeln('Config "' . $path . ' = ' . $value . '" has been saved.'); + } + } + /** * Enable maintenance mode * diff --git a/app/code/Magento/Deploy/Test/Unit/App/Mode/ConfigProviderTest.php b/app/code/Magento/Deploy/Test/Unit/App/Mode/ConfigProviderTest.php new file mode 100644 index 0000000000000..ffb5c593f9e7e --- /dev/null +++ b/app/code/Magento/Deploy/Test/Unit/App/Mode/ConfigProviderTest.php @@ -0,0 +1,27 @@ + '{{setting_value}}' + ]; + $configProvider = new ConfigProvider( + [ + 'developer' => [ + 'production' => $expectedValue + ] + ] + ); + $this->assertEquals($expectedValue, $configProvider->getConfigs('developer', 'production')); + $this->assertEquals([], $configProvider->getConfigs('undefined', 'production')); + } +} diff --git a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php index 52ee9f1e65a7b..f80c6cb69f1a9 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php @@ -5,8 +5,13 @@ */ namespace Magento\Deploy\Test\Unit\Model; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacadeFactory; +use Magento\Config\Console\Command\ConfigSet\ProcessorFacade; +use Magento\Config\Console\Command\EmulatedAdminhtmlAreaProcessor; +use Magento\Deploy\App\Mode\ConfigProvider; use Magento\Deploy\Model\Filesystem; use Magento\Deploy\Model\Mode; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\DeploymentConfig\Reader; use Magento\Framework\App\DeploymentConfig\Writer; use Magento\Framework\App\MaintenanceMode; @@ -19,6 +24,7 @@ /** * @inheritdoc + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ModeTest extends \PHPUnit\Framework\TestCase { @@ -57,6 +63,26 @@ class ModeTest extends \PHPUnit\Framework\TestCase */ private $filesystemMock; + /** + * @var ConfigProvider|Mock + */ + private $configProvider; + + /** + * @var ProcessorFacadeFactory|Mock + */ + private $processorFacadeFactory; + + /** + * @var ProcessorFacade|Mock + */ + private $processorFacade; + + /** + * @var EmulatedAdminhtmlAreaProcessor|Mock + */ + private $emulatedAreaProcessor; + protected function setUp() { $this->inputMock = $this->getMockBuilder(InputInterface::class) @@ -75,6 +101,19 @@ protected function setUp() $this->filesystemMock = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() ->getMock(); + $this->configProvider = $this->getMockBuilder(ConfigProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $this->processorFacadeFactory = $this->getMockBuilder(ProcessorFacadeFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); + $this->processorFacade = $this->getMockBuilder(ProcessorFacade::class) + ->disableOriginalConstructor() + ->getMock(); + $this->emulatedAreaProcessor = $this->getMockBuilder(EmulatedAdminhtmlAreaProcessor::class) + ->disableOriginalConstructor() + ->getMock(); $this->model = new Mode( $this->inputMock, @@ -82,7 +121,10 @@ protected function setUp() $this->writerMock, $this->readerMock, $this->maintenanceMock, - $this->filesystemMock + $this->filesystemMock, + $this->configProvider, + $this->processorFacadeFactory, + $this->emulatedAreaProcessor ); } @@ -112,6 +154,9 @@ public function testEnableProductionMode() State::PARAM_MODE => State::MODE_DEVELOPER, ], ]; + $this->configProvider->expects($this->any()) + ->method('getConfigs') + ->willReturn([]); $this->writerMock->expects($this->once()) ->method("saveConfig") ->willReturnCallback(function ($data) use (&$dataStorage) { @@ -145,6 +190,12 @@ public function testEnableDeveloperModeOnFail() State::PARAM_MODE => State::MODE_DEVELOPER, ], ]; + $this->readerMock->expects($this->any()) + ->method('load') + ->willReturn([State::PARAM_MODE => State::MODE_DEVELOPER]); + $this->configProvider->expects($this->any()) + ->method('getConfigs') + ->willReturn([]); $this->writerMock->expects($this->exactly(2)) ->method("saveConfig") ->withConsecutive( @@ -165,4 +216,41 @@ public function testEnableDeveloperModeOnFail() $this->model->enableProductionMode(); $this->assertEquals(State::MODE_PRODUCTION, $mode); } + + public function testEnableProductionModeMinimal() + { + $this->readerMock->expects($this->once()) + ->method('load') + ->willReturn([State::PARAM_MODE => State::MODE_DEVELOPER]); + $this->configProvider->expects($this->once()) + ->method('getConfigs') + ->with('developer', 'production') + ->willReturn([ + 'dev/debug/debug_logging' => 0 + ]); + $this->emulatedAreaProcessor->expects($this->once()) + ->method('process') + ->willReturnCallback(function (\Closure $closure) { + return $closure->call($this->model); + }); + + $this->processorFacadeFactory->expects($this->once()) + ->method('create') + ->willReturn($this->processorFacade); + $this->processorFacade + ->expects($this->once()) + ->method('process') + ->with( + 'dev/debug/debug_logging', + 0, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + null, + true + ); + $this->outputMock->expects($this->once()) + ->method('writeln') + ->with('Config "dev/debug/debug_logging = 0" has been saved.'); + + $this->model->enableProductionModeMinimal(); + } } diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index 52a2503a13678..e47fca3a6b946 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -70,4 +70,15 @@ + + + + + + 0 + + + + + diff --git a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php index ec5aff6891c11..9bfee42fa6a83 100644 --- a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php +++ b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php @@ -60,7 +60,6 @@ public function isHandling(array $record) if ($this->deploymentConfig->isAvailable()) { return parent::isHandling($record) - && $this->state->getMode() !== State::MODE_PRODUCTION && $this->scopeConfig->getValue('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE); } 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 index 8b0136248fcf1..c116775d582bb 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php @@ -85,9 +85,8 @@ public function testHandle() $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->once()) - ->method('getMode') - ->willReturn(State::MODE_DEVELOPER); + $this->stateMock->expects($this->never()) + ->method('getMode'); $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) @@ -101,10 +100,9 @@ public function testHandleDisabledByProduction() $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->once()) - ->method('getMode') - ->willReturn(State::MODE_PRODUCTION); - $this->scopeConfigMock->expects($this->never()) + $this->stateMock->expects($this->never()) + ->method('getMode'); + $this->scopeConfigMock->expects($this->once()) ->method('getValue'); $this->assertFalse($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); @@ -115,9 +113,8 @@ public function testHandleDisabledByConfig() $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->once()) - ->method('getMode') - ->willReturn(State::MODE_DEVELOPER); + $this->stateMock->expects($this->never()) + ->method('getMode'); $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php new file mode 100644 index 0000000000000..71e61162d29c9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -0,0 +1,166 @@ +create(Filesystem::class); + $this->etcDirectory = $filesystem->getDirectoryWrite(DirectoryList::CONFIG); + $this->etcDirectory->copyFile('env.php', 'env.base.php'); + + $this->inputMock = $this->getMockBuilder(InputInterface::class) + ->getMockForAbstractClass(); + $this->outputMock = $this->getMockBuilder(OutputInterface::class) + ->getMockForAbstractClass(); + $this->logger = Bootstrap::getObjectManager()->get(Monolog::class); + $this->mode = Bootstrap::getObjectManager()->create( + Mode::class, + [ + 'input' => $this->inputMock, + 'output' => $this->outputMock + ] + ); + $this->configSetCommand = Bootstrap::getObjectManager()->create(ConfigSetCommand::class); + $this->appConfig = Bootstrap::getObjectManager()->create(Config::class); + + // Preconditions + $this->mode->enableDeveloperMode(); + $this->enableDebugging(); + if (file_exists($this->getDebuggerLogPath())) { + unlink($this->getDebuggerLogPath()); + } + } + + public function tearDown() + { + $this->etcDirectory->delete('env.php'); + $this->etcDirectory->renameFile('env.base.php', 'env.php'); + } + + private function enableDebugging() + { + $this->inputMock = $this->getMockBuilder(InputInterface::class) + ->getMockForAbstractClass(); + $this->outputMock = $this->getMockBuilder(OutputInterface::class) + ->getMockForAbstractClass(); + $this->inputMock->expects($this->exactly(2)) + ->method('getArgument') + ->withConsecutive([ConfigSetCommand::ARG_PATH], [ConfigSetCommand::ARG_VALUE]) + ->willReturnOnConsecutiveCalls('dev/debug/debug_logging', 1); + $this->inputMock->expects($this->exactly(3)) + ->method('getOption') + ->withConsecutive( + [ConfigSetCommand::OPTION_SCOPE], + [ConfigSetCommand::OPTION_SCOPE_CODE], + [ConfigSetCommand::OPTION_LOCK] + ) + ->willReturnOnConsecutiveCalls( + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + null, + true + ); + $this->outputMock->expects($this->once()) + ->method('writeln') + ->with('Value was saved and locked.'); + $this->assertFalse((bool)$this->configSetCommand->run($this->inputMock, $this->outputMock)); + } + + public function testDebugInProductionMode() + { + $message = 'test message'; + + $this->mode->enableProductionModeMinimal(); + $this->logger->debug($message); + $this->assertFileNotExists($this->getDebuggerLogPath()); + $this->assertFalse((bool)$this->appConfig->getValue('dev/debug/debug_logging')); + + $this->enableDebugging(); + $this->logger->debug($message); + + $this->assertFileExists($this->getDebuggerLogPath()); + $this->assertContains($message, file_get_contents($this->getDebuggerLogPath())); + } + + /** + * @return bool|string + */ + private function getDebuggerLogPath() + { + foreach ($this->logger->getHandlers() as $handler) { + if ($handler instanceof Debug) { + return $handler->getUrl(); + } + } + return false; + } +}