diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index a0f1e25cf6512..84c3e7e7b5f5a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2042,6 +2042,7 @@ protected function uploadMediaFiles($fileName, $renameFileOff = false) $res = $this->_getUploader()->move($fileName, $renameFileOff); return $res['file']; } catch (\Exception $e) { + $this->_logger->critical($e); return ''; } } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 862276d35bac5..8461e3830cbec 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -1194,6 +1194,70 @@ public function testParseAttributesWithWrappedValuesWillReturnsLowercasedAttribu $this->assertArrayNotHasKey('PARAM2', $attributes); } + /** + * Test that errors occurred during importing images are logged. + * + * @param string $fileName + * @param bool $throwException + * @dataProvider uploadMediaFilesDataProvider + */ + public function testUploadMediaFiles(string $fileName, bool $throwException) + { + $exception = new \Exception(); + $expectedFileName = $fileName; + if ($throwException) { + $expectedFileName = ''; + $this->_logger->expects($this->once())->method('critical')->with($exception); + } + + $fileUploaderMock = $this + ->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Uploader::class) + ->disableOriginalConstructor() + ->getMock(); + + $fileUploaderMock + ->expects($this->once()) + ->method('move') + ->willReturnCallback( + function ($name) use ($throwException, $exception) { + if ($throwException) { + throw $exception; + } + return ['file' => $name]; + } + ); + + $this->setPropertyValue( + $this->importProduct, + '_fileUploader', + $fileUploaderMock + ); + + $actualFileName = $this->invokeMethod( + $this->importProduct, + 'uploadMediaFiles', + [$fileName] + ); + + $this->assertEquals( + $expectedFileName, + $actualFileName + ); + } + + /** + * Data provider for testUploadMediaFiles. + * + * @return array + */ + public function uploadMediaFilesDataProvider() + { + return [ + ['test1.jpg', false], + ['test2.jpg', true], + ]; + } + public function getImagesFromRowDataProvider() { return [ diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 8da1bb1763ece..092b721b82435 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -567,7 +567,6 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource ProcessingError::ERROR_LEVEL_CRITICAL, null, null, - null, $e->getMessage() ); } diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Pdf/Creditmemo.php index 6e2265315245d..74f3eebf8fcb9 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Creditmemo.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Creditmemo.php @@ -75,12 +75,12 @@ public function __construct( protected function _drawHeader(\Zend_Pdf_Page $page) { $this->_setFontRegular($page, 10); - $page->setFillColor(new \Zend_Pdf_Color_RGB(0.93, 0.92, 0.92)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92)); $page->setLineColor(new \Zend_Pdf_Color_GrayScale(0.5)); $page->setLineWidth(0.5); $page->drawRectangle(25, $this->y, 570, $this->y - 30); $this->y -= 10; - $page->setFillColor(new \Zend_Pdf_Color_RGB(0, 0, 0)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0, 0, 0)); //columns headers $lines[0][] = ['text' => __('Products'), 'feed' => 35]; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php index 2912969a99718..f294128a72f9f 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php @@ -82,12 +82,12 @@ protected function _drawHeader(\Zend_Pdf_Page $page) { /* Add table head */ $this->_setFontRegular($page, 10); - $page->setFillColor(new \Zend_Pdf_Color_RGB(0.93, 0.92, 0.92)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92)); $page->setLineColor(new \Zend_Pdf_Color_GrayScale(0.5)); $page->setLineWidth(0.5); $page->drawRectangle(25, $this->y, 570, $this->y - 15); $this->y -= 10; - $page->setFillColor(new \Zend_Pdf_Color_RGB(0, 0, 0)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0, 0, 0)); //columns headers $lines[0][] = ['text' => __('Products'), 'feed' => 35]; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Shipment.php b/app/code/Magento/Sales/Model/Order/Pdf/Shipment.php index be74efda2d265..b171fccdeb05b 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Shipment.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Shipment.php @@ -80,12 +80,12 @@ protected function _drawHeader(\Zend_Pdf_Page $page) { /* Add table head */ $this->_setFontRegular($page, 10); - $page->setFillColor(new \Zend_Pdf_Color_RGB(0.93, 0.92, 0.92)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92)); $page->setLineColor(new \Zend_Pdf_Color_GrayScale(0.5)); $page->setLineWidth(0.5); $page->drawRectangle(25, $this->y, 570, $this->y - 15); $this->y -= 10; - $page->setFillColor(new \Zend_Pdf_Color_RGB(0, 0, 0)); + $page->setFillColor(new \Zend_Pdf_Color_Rgb(0, 0, 0)); //columns headers $lines[0][] = ['text' => __('Products'), 'feed' => 100]; diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html index 2af1a430a6e00..eab2523b06e41 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/filters.html @@ -14,7 +14,7 @@ -
+
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 0f9cb373e59ed..5debe398f085c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -26,7 +26,7 @@ use Magento\Framework\Filesystem; use Magento\ImportExport\Model\Import; use Magento\Store\Model\Store; -use Magento\UrlRewrite\Model\UrlRewrite; +use Psr\Log\LoggerInterface; /** * Class ProductTest @@ -62,11 +62,20 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase */ protected $objectManager; + /** + * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $logger; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->logger = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CatalogImportExport\Model\Import\Product::class + \Magento\CatalogImportExport\Model\Import\Product::class, + ['logger' => $this->logger] ); parent::setUp(); } @@ -659,6 +668,21 @@ public function testSaveMediaImage() $this->assertEquals('Additional Image Label Two', $additionalImageTwoItem->getLabel()); } + /** + * Test that errors occurred during importing images are logged. + * + * @magentoDataIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture mediaImportImageFixtureError + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testSaveMediaImageError() + { + $this->logger->expects(self::once())->method('critical'); + $this->importDataForMediaTest('import_media.csv', 1); + } + /** * Copy fixture images into media import directory */ @@ -717,6 +741,30 @@ public static function mediaImportImageFixtureRollback() $mediaDirectory->delete('catalog'); } + /** + * Copy incorrect fixture image into media import directory. + */ + public static function mediaImportImageFixtureError() + { + /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ + $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Framework\Filesystem::class + )->getDirectoryWrite( + DirectoryList::MEDIA + ); + $dirPath = $mediaDirectory->getAbsolutePath('import'); + $items = [ + [ + 'source' => __DIR__ . '/_files/magento_additional_image_error.jpg', + 'dest' => $dirPath . '/magento_additional_image_two.jpg', + ], + ]; + foreach ($items as $item) { + copy($item['source'], $item['dest']); + } + } + + /** * Export CSV string to array * @@ -1564,8 +1612,9 @@ public function testProductWithWrappedAdditionalAttributes() * Import and check data from file * * @param string $fileName + * @param int $expectedErrors */ - private function importDataForMediaTest($fileName) + private function importDataForMediaTest(string $fileName, int $expectedErrors = 0) { $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); @@ -1603,7 +1652,7 @@ private function importDataForMediaTest($fileName) $this->assertTrue($errors->getErrorsCount() == 0); $this->_model->importData(); - $this->assertTrue($this->_model->getErrorAggregator()->getErrorsCount() == 0); + $this->assertTrue($this->_model->getErrorAggregator()->getErrorsCount() == $expectedErrors); } /** diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/magento_additional_image_error.jpg b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/magento_additional_image_error.jpg new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php index 5ba955430021f..332ca5fba75c7 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php @@ -135,6 +135,31 @@ public function testValidateSourceException() $this->_model->validateSource($source); } + public function testValidateSourceExceptionMessage() + { + $exceptionMessage = 'Test Exception Message.'; + + $validationStrategy = ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_STOP_ON_ERROR; + $this->_model->setEntity('catalog_product'); + $this->_model->setData(\Magento\ImportExport\Model\Import::FIELD_NAME_VALIDATION_STRATEGY, $validationStrategy); + $this->_model->setData(\Magento\ImportExport\Model\Import::FIELD_NAME_ALLOWED_ERROR_COUNT, 0); + + /** @var \Magento\ImportExport\Model\Import\AbstractSource|\PHPUnit_Framework_MockObject_MockObject $source */ + $source = $this->getMockForAbstractClass( + \Magento\ImportExport\Model\Import\AbstractSource::class, + [['sku', 'name']] + ); + $source->expects($this->any())->method('_getNextRow')->willThrowException( + new \Exception($exceptionMessage) + ); + + $this->assertFalse($this->_model->validateSource($source)); + $this->assertEquals( + $exceptionMessage, + $this->_model->getErrorAggregator()->getAllErrors()[0]->getErrorMessage() + ); + } + public function testGetEntity() { $entityName = 'entity_name'; diff --git a/lib/internal/Magento/Framework/Config/Converter/Dom/Flat.php b/lib/internal/Magento/Framework/Config/Converter/Dom/Flat.php index 67b8937442310..7f64705a6bde0 100644 --- a/lib/internal/Magento/Framework/Config/Converter/Dom/Flat.php +++ b/lib/internal/Magento/Framework/Config/Converter/Dom/Flat.php @@ -102,9 +102,9 @@ public function convert(\DOMNode $source, $basePath = '') } } else { if ($result) { - $result['value'] = $value; + $result['value'] = trim($value); } else { - $result = $value; + $result = trim($value); } } return $result; diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/result.php b/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/result.php index c724d6ef43b5b..c9894c96c20e4 100644 --- a/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/result.php +++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/result.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// @codingStandardsIgnoreFile return [ 'root' => [ 'node_one' => [ @@ -11,14 +12,20 @@ 'subnode' => [ ['attributeThree' => '30'], ['attributeThree' => '40', 'attributeFour' => '40', 'value' => 'Value1'], + ['attributeThree' => '50', 'value' => 'value_from_new_line'], + ['attributeThree' => '60', 'value' => 'auto_formatted_by_ide_value_due_to_line_size_restriction'] ], 'books' => ['attributeFive' => '50'], ], 'multipleNode' => [ 'one' => ['id' => 'one', 'name' => 'name1', 'value' => '1'], 'two' => ['id' => 'two', 'name' => 'name2', 'value' => '2'], + 'three' => ['id' => 'three', 'name' => 'name3', 'value' => 'value_from_new_line'], + 'four' => ['id' => 'four', 'name' => 'name4', 'value' => 'auto_formatted_by_ide_value_due_to_line_size_restriction'], ], 'someOtherVal' => '', 'someDataVal' => '', + 'valueFromNewLine' => 'value_from_new_line', + 'autoFormattedValue' => 'auto_formatted_by_ide_value_due_to_line_size_restriction' ] ]; diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/source.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/source.xml index e6b16b8552dd8..5071bdd286e64 100644 --- a/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/source.xml +++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/converter/dom/flat/source.xml @@ -9,6 +9,11 @@ Value1 + + value_from_new_line + + auto_formatted_by_ide_value_due_to_line_size_restriction + @@ -19,4 +24,18 @@ + + + value_from_new_line + + + + auto_formatted_by_ide_value_due_to_line_size_restriction + + + + value_from_new_line + + auto_formatted_by_ide_value_due_to_line_size_restriction + diff --git a/setup/src/Magento/Setup/Console/Command/InstallCommand.php b/setup/src/Magento/Setup/Console/Command/InstallCommand.php index b0ccbba8c296f..a2e8715b02233 100644 --- a/setup/src/Magento/Setup/Console/Command/InstallCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InstallCommand.php @@ -13,6 +13,9 @@ use Magento\Framework\Setup\ConsoleLogger; use Symfony\Component\Console\Input\InputOption; use Magento\Setup\Model\ConfigModel; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Helper\QuestionHelper; /** * Command to install Magento application @@ -35,6 +38,16 @@ class InstallCommand extends AbstractSetupCommand */ const INPUT_KEY_USE_SAMPLE_DATA = 'use-sample-data'; + /** + * Parameter indicating command for interactive setup + */ + const INPUT_KEY_INTERACTIVE_SETUP = 'interactive'; + + /** + * Parameter indicating command shortcut for interactive setup + */ + const INPUT_KEY_INTERACTIVE_SETUP_SHORTCUT = 'i'; + /** * Regex for sales_order_increment_prefix validation. */ @@ -109,7 +122,13 @@ protected function configure() null, InputOption::VALUE_NONE, 'Use sample data' - ) + ), + new InputOption( + self::INPUT_KEY_INTERACTIVE_SETUP, + self::INPUT_KEY_INTERACTIVE_SETUP_SHORTCUT, + InputOption::VALUE_NONE, + 'Interactive Magento instalation' + ), ]); $this->setName('setup:install') ->setDescription('Installs the Magento application') @@ -139,12 +158,25 @@ protected function initialize(InputInterface $input, OutputInterface $output) { $inputOptions = $input->getOptions(); - $configOptionsToValidate = []; - foreach ($this->configModel->getAvailableOptions() as $option) { - if (array_key_exists($option->getName(), $inputOptions)) { - $configOptionsToValidate[$option->getName()] = $inputOptions[$option->getName()]; + if ($inputOptions['interactive']) { + $configOptionsToValidate = $this->interactiveQuestions($input, $output); + } else { + $configOptionsToValidate = []; + foreach ($this->configModel->getAvailableOptions() as $option) { + if (array_key_exists($option->getName(), $inputOptions)) { + $configOptionsToValidate[$option->getName()] = $inputOptions[$option->getName()]; + } + } + } + + if ($inputOptions['interactive']) { + $command = ''; + foreach ($configOptionsToValidate as $key => $value) { + $command .= " --{$key}={$value}"; } + $output->writeln("Try re-running command: php bin/magento setup:install{$command}"); } + $errors = $this->configModel->validate($configOptionsToValidate); $errors = array_merge($errors, $this->adminUser->validate($input)); $errors = array_merge($errors, $this->validate($input)); @@ -177,4 +209,133 @@ public function validate(InputInterface $input) } return $errors; } + + /** + * Runs interactive questions + * + * It will ask users for interactive questionst regarding setup configuration. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return string[] Array of inputs + */ + private function interactiveQuestions(InputInterface $input, OutputInterface $output) + { + $helper = $this->getHelper('question'); + $configOptionsToValidate = []; + + foreach ($this->configModel->getAvailableOptions() as $option) { + $configOptionsToValidate[$option->getName()] = $this->askQuestion( + $input, + $output, + $helper, + $option, + true + ); + } + + $output->writeln(""); + + foreach ($this->userConfig->getOptionsList() as $option) { + $configOptionsToValidate[$option->getName()] = $this->askQuestion( + $input, + $output, + $helper, + $option + ); + } + + $output->writeln(""); + + foreach ($this->adminUser->getOptionsList() as $option) { + $configOptionsToValidate[$option->getName()] = $this->askQuestion( + $input, + $output, + $helper, + $option + ); + } + + $output->writeln(""); + + $returnConfigOptionsToValidate = []; + foreach ($configOptionsToValidate as $key => $value) { + if ($value != '') { + $returnConfigOptionsToValidate[$key] = $value; + } + } + + return $returnConfigOptionsToValidate; + } + + /** + * Runs interactive questions + * + * It will ask users for interactive questionst regarding setup configuration. + * + * @param InputInterface $input + * @param OutputInterface $output + * @param QuestionHelper $helper + * @param TextConfigOption|FlagConfigOption\SelectConfigOption $option + * @param Boolean $validateInline + * @return string[] Array of inputs + */ + private function askQuestion( + InputInterface $input, + OutputInterface $output, + QuestionHelper $helper, + $option, + $validateInline = false + ) { + if ($option instanceof \Magento\Framework\Setup\Option\SelectConfigOption) { + if ($option->isValueRequired()) { + $question = new ChoiceQuestion( + $option->getDescription() . '? ', + $option->getSelectOptions(), + $option->getDefault() + ); + } else { + $question = new ChoiceQuestion( + $option->getDescription() . ' [optional]? ', + $option->getSelectOptions(), + $option->getDefault() + ); + } + } else { + if ($option->isValueRequired()) { + $question = new Question( + $option->getDescription() . '? ', + $option->getDefault() + ); + } else { + $question = new Question( + $option->getDescription() . ' [optional]? ', + $option->getDefault() + ); + } + } + + $question->setValidator(function ($answer) use ($option, $validateInline) { + + if ($option instanceof \Magento\Framework\Setup\Option\SelectConfigOption) { + $answer = $option->getSelectOptions()[$answer]; + } + + if ($answer == null) { + $answer = ''; + } else { + $answer = trim($answer); + } + + if ($validateInline) { + $option->validate($answer); + } + + return $answer; + }); + + $value = $helper->ask($input, $output, $question); + + return $value; + } }