diff --git a/CHANGELOG.md b/CHANGELOG.md index 1762dbb..ade3c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# Version 13.1.0 + +## Features + +### PHP 8.4 Compatibility + +* Update dependencies +* Add PHP 8.4 support +* Replace abandoned `swiftmailer/swiftmailer` with `symfony/mailer` + # Version 13.0.0 ## Features diff --git a/composer.json b/composer.json index c3213fa..0df440f 100755 --- a/composer.json +++ b/composer.json @@ -5,12 +5,12 @@ "require": { "php": "^8.1", "ramsey/uuid": "^4.2|^4.7", - "symfony/config": "~4.0|~5.0|~6.0", + "symfony/config": "~5.0|~6.0|~7.0", "symfony/console": "~4.0|~5.0|~6.0|~7.0", "symfony/expression-language": "~6.0", - "symfony/dependency-injection": "~4.0|~5.0|~6.0", - "techdivision/import-app-simple": "^19.0.0", - "techdivision/import-configuration-jms": "^18.0.0" + "symfony/dependency-injection": "~5.0|~6.0|~7.0", + "techdivision/import-app-simple": "^19.0", + "techdivision/import-configuration-jms": "^18.1" }, "require-dev": { "doctrine/dbal": "^4.0.4", diff --git a/config.json.simple b/config.json.simple index cb36b0e..7b002c2 100644 --- a/config.json.simple +++ b/config.json.simple @@ -308,8 +308,8 @@ { "id": "import_cli.plugin.debug.send", "subjects": [], - "swift-mailer": { - "id": "import.logger.factory.transport.swift.sendmail", + "mailer": { + "id": "import.logger.factory.transport.mailer.sendmail", "transport": { "params": { "smtp-host": "localhost", @@ -1848,7 +1848,8 @@ "callbacks": { "0": { "path": [ - "import.callback.custom.regex.validator" + "import.callback.custom.regex.validator", + "import_category.callback.valid_category_path" ], "store_view_code": [ "import.callback.store.view.code.validator" @@ -9673,7 +9674,38 @@ } } }, - "extension-libraries": [], + "extension-libraries": [ + "techdivision\/import-app-simple", + "techdivision\/import", + "techdivision\/import-cli", + "techdivision\/import-ee", + "techdivision\/import-attribute", + "techdivision\/import-attribute-set", + "techdivision\/import-category", + "techdivision\/import-category-ee", + "techdivision\/import-customer", + "techdivision\/import-customer-address", + "techdivision\/import-product", + "techdivision\/import-product-ee", + "techdivision\/import-product-msi", + "techdivision\/import-product-tier-price", + "techdivision\/import-product-url-rewrite", + "techdivision\/import-product-bundle", + "techdivision\/import-product-bundle-ee", + "techdivision\/import-product-link", + "techdivision\/import-product-link-ee", + "techdivision\/import-product-media", + "techdivision\/import-product-media-ee", + "techdivision\/import-product-variant", + "techdivision\/import-product-variant-ee", + "techdivision\/import-product-grouped", + "techdivision\/import-product-grouped-ee", + "techdivision\/import-converter", + "techdivision\/import-converter-ee", + "techdivision\/import-converter-product-category", + "techdivision\/import-converter-product-attribute", + "techdivision\/import-converter-customer-attribute" + ], "header-mappings": { "eav_entity_attribute": { "entity_type_code": "entity_type_id", diff --git a/etc/configuration/operations.json b/etc/configuration/operations.json index 51693cf..53ff8d8 100644 --- a/etc/configuration/operations.json +++ b/etc/configuration/operations.json @@ -13,8 +13,8 @@ "plugins": { "debug.send": { "id": "import_cli.plugin.debug.send", - "swift-mailer" : { - "id" : "import.logger.factory.transport.swift.sendmail", + "mailer" : { + "id" : "import.logger.factory.transport.mailer.sendmail", "params" : { "to" : "helpdesk@techdivision.com", "from" : "pacemaker@localhost", diff --git a/src/Application.php b/src/Application.php index 65df047..d91855d 100644 --- a/src/Application.php +++ b/src/Application.php @@ -16,7 +16,6 @@ use TechDivision\Import\Cli\Utils\DependencyInjectionKeys; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; /** * The M2IF - Console Tool implementation. @@ -30,7 +29,7 @@ * @link https://github.com/techdivision/import-cli-simple * @link http://www.techdivision.com */ -class Application extends \Symfony\Component\Console\Application implements ContainerAwareInterface +class Application extends \Symfony\Component\Console\Application { /** @@ -43,9 +42,9 @@ class Application extends \Symfony\Component\Console\Application implements Cont /** * The DI container instance. * - * @var \Symfony\Component\DependencyInjection\ContainerInterface + * @var ContainerInterface|null */ - protected $container; + protected ?ContainerInterface $container = null; /** * The constructor to initialize the instance. @@ -85,7 +84,7 @@ public function __construct(ContainerInterface $container) * * @return void */ - public function setContainer(ContainerInterface $container = null) + public function setContainer(?ContainerInterface $container = null): void { $this->container = $container; } @@ -95,7 +94,7 @@ public function setContainer(ContainerInterface $container = null) * * @return \Symfony\Component\DependencyInjection\ContainerInterface|null The DI container instance */ - public function getContainer() + public function getContainer(): ?ContainerInterface { return $this->container; } diff --git a/src/Command/AbstractSimpleImportCommand.php b/src/Command/AbstractSimpleImportCommand.php index 9c2f4fc..77ac4b1 100644 --- a/src/Command/AbstractSimpleImportCommand.php +++ b/src/Command/AbstractSimpleImportCommand.php @@ -85,11 +85,11 @@ protected function getContainer() * @param \Symfony\Component\Console\Input\InputInterface $input An InputInterface instance * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance * - * @return null|int null or 0 if everything went fine, or an error code + * @return int 0 if everything went fine, or an error code * @throws \LogicException When this abstract method is not implemented * @see \Symfony\Component\Console\Command\Command::execute() */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { // try to load the configuration file @@ -110,10 +110,11 @@ protected function createSerializer(): Serializer $builder = SerializerBuilder::create(); $builder->addDefaultSerializationVisitors(); $namingStrategy = new SerializedNameAnnotationStrategy(new IdenticalPropertyNamingStrategy()); - - // register the visitor in the builder instance - $visitor = new JsonSerializationVisitorFactory($namingStrategy); - $visitor->setOptions(JSON_PRETTY_PRINT); + // set naming strategy on builder (JMS Serializer v3+) + $builder->setPropertyNamingStrategy($namingStrategy); + // register the visitor factory in the builder instance + $visitor = new JsonSerializationVisitorFactory(); + $visitor->setOptions(JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION); $builder->setSerializationVisitor($format, $visitor); return $builder->build(); } @@ -125,7 +126,7 @@ protected function createSerializer(): Serializer * @param \Symfony\Component\Console\Input\InputInterface $input An InputInterface instance * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance * - * @return void + * @return int */ abstract protected function executeSimpleCommand( ConfigurationInterface $configuration, diff --git a/src/Command/ImportClearPidFileCommand.php b/src/Command/ImportClearPidFileCommand.php index 9ff18c5..06ae313 100644 --- a/src/Command/ImportClearPidFileCommand.php +++ b/src/Command/ImportClearPidFileCommand.php @@ -55,7 +55,7 @@ protected function configure() * @param \Symfony\Component\Console\Input\InputInterface $input An InputInterface instance * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance * - * @return void + * @return int */ protected function executeSimpleCommand( ConfigurationInterface $configuration, @@ -75,5 +75,6 @@ protected function executeSimpleCommand( // write a message to the console $output->writeln(sprintf('PID file %s not available', $pidFilename)); } + return 0; } } diff --git a/src/Command/ImportConvertValueCommand.php b/src/Command/ImportConvertValueCommand.php index 77f6a8e..93e8b88 100644 --- a/src/Command/ImportConvertValueCommand.php +++ b/src/Command/ImportConvertValueCommand.php @@ -61,13 +61,13 @@ protected function configure() * @param \Symfony\Component\Console\Input\InputInterface $input An InputInterface instance * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance * - * @return void + * @return int */ protected function executeSimpleCommand( ConfigurationInterface $configuration, InputInterface $input, OutputInterface $output - ) { + ): int { // initialize the default CSV serializer $serializer = new ValueCsvSerializer(); @@ -117,5 +117,6 @@ protected function executeSimpleCommand( // second serialization that simulates the framework parsing the CSV file $output->write($serializer->serialize($serialize)); + return 0; } } diff --git a/src/Command/ImportCreateConfigurationFileCommand.php b/src/Command/ImportCreateConfigurationFileCommand.php index 44346c1..73172ab 100644 --- a/src/Command/ImportCreateConfigurationFileCommand.php +++ b/src/Command/ImportCreateConfigurationFileCommand.php @@ -15,8 +15,8 @@ namespace TechDivision\Import\Cli\Command; use JMS\Serializer\SerializerBuilder; -use JMS\Serializer\XmlSerializationVisitor; -use JMS\Serializer\JsonSerializationVisitor; +use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; +use JMS\Serializer\Visitor\Factory\XmlSerializationVisitorFactory; use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy; use JMS\Serializer\Naming\SerializedNameAnnotationStrategy; use Symfony\Component\Console\Input\InputInterface; @@ -64,7 +64,7 @@ protected function configure() * @param \Symfony\Component\Console\Input\InputInterface $input An InputInterface instance * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance * - * @return void + * @return int */ protected function executeSimpleCommand( ConfigurationInterface $configuration, @@ -90,19 +90,21 @@ protected function executeSimpleCommand( // initialize the naming strategy $namingStrategy = new SerializedNameAnnotationStrategy(new IdenticalPropertyNamingStrategy()); + // set the naming strategy on the builder (JMS Serializer v3+) + $builder->setPropertyNamingStrategy($namingStrategy); // create the configuration based on the given configuration file suffix switch ($format) { // initialize the JSON visitor case 'json': - // initialize the visitor because we want to set JSON options - $visitor = new JsonSerializationVisitor($namingStrategy); - $visitor->setOptions(JSON_PRETTY_PRINT); + // configure the factory because we want to set JSON options + $visitor = new JsonSerializationVisitorFactory(); + $visitor->setOptions(JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION); break; // initialize the XML visitor case 'xml': - $visitor = new XmlSerializationVisitor($namingStrategy); + $visitor = new XmlSerializationVisitorFactory(); break; // throw an execption in all other cases @@ -122,5 +124,6 @@ protected function executeSimpleCommand( } else { $output->writeln(sprintf('Can\'t write configuration file %s', $configurationFilename)); } + return 0; } } diff --git a/src/Plugins/DebugCreatePlugin.php b/src/Plugins/DebugCreatePlugin.php index 731add3..2ae1867 100644 --- a/src/Plugins/DebugCreatePlugin.php +++ b/src/Plugins/DebugCreatePlugin.php @@ -129,6 +129,14 @@ public function process() // finally create the array with the available serials to render on the console $availableSerials = array_slice(array_keys($availableSerials), 0, $this->getInput()->getOption(InputOptionKeysInterface::RENDER_DEBUG_SERIALS)); + // if no serials are available, abort gracefully instead of asking an empty \Symfony\Component\Console\Question\ChoiceQuestion + if (empty($availableSerials)) { + $this->getOutput()->writeln( + 'No debug artefacts or import directories found to create a debug dump for. Aborting.' + ); + return; + } + // this is, when the import:debug send command has been invoked // WITHOUT the --serial= parameter or an invalid serial if (!in_array($serial = $this->getSerial(), $availableSerials, true)) { diff --git a/src/Plugins/DebugSendPlugin.php b/src/Plugins/DebugSendPlugin.php index e950c99..2c72f8e 100644 --- a/src/Plugins/DebugSendPlugin.php +++ b/src/Plugins/DebugSendPlugin.php @@ -14,10 +14,14 @@ namespace TechDivision\Import\Cli\Plugins; -use Symfony\Component\Console\Question\Question; +use Exception; +use InvalidArgumentException; use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mailer\Exception\TransportExceptionInterface; +use TechDivision\Import\Configuration\MailerConfigurationInterface; use TechDivision\Import\Utils\RegistryKeys; -use TechDivision\Import\Configuration\SwiftMailerConfigurationInterface; /** * Plugin that creates and sends a debug report via email. @@ -35,7 +39,7 @@ class DebugSendPlugin extends AbstractConsolePlugin * Process the plugin functionality. * * @return void - * @throws \InvalidArgumentException Is thrown if either the directory nor a artefact for the given serial is available + * @throws InvalidArgumentException|Exception Is thrown if either the directory nor a artefact for the given serial is available */ public function process() { @@ -49,15 +53,15 @@ public function process() $serial = $status[RegistryKeys::DEBUG_SERIAL]; } - // retrieve the SwiftMailer configuration - $swiftMailerConfiguration = $this->getPluginConfiguration()->getSwiftMailer(); + // retrieve the mailer configuration + $mailerConfiguration = $this->getPluginConfiguration()->getMailer(); // retrieve the question helper $questionHelper = $this->getHelper('question'); - // use the configured SwiftMail recipient address as default if possible - if ($swiftMailerConfiguration instanceof SwiftMailerConfigurationInterface && $swiftMailerConfiguration->hasParam('to')) { - $recipient = $swiftMailerConfiguration->getParam('to'); + // use the configured mailer recipient address as default if possible + if ($mailerConfiguration instanceof MailerConfigurationInterface && $mailerConfiguration->hasParam('to')) { + $recipient = $mailerConfiguration->getParam('to'); // ask the user for the recipient address to send the debug report to $recipientQuestion = new Question( "Please enter the email address of the debug report recipient (Configured: " . $recipient . "):\n", @@ -80,26 +84,21 @@ public function process() // abort the operation if the user does not confirm with 'y' or enter if (!$questionHelper->ask($this->getInput(), $this->getOutput(), $confirmationQuestion)) { - $this->getOutput()->writeln('Aborting operation - debug report has NOT been sent.'); + $this->getOutput()->writeln('Aborting operation - debug report has NOT been sent.'); return; } // try to load the mailer instance - if ($mailer = $this->getSwiftMailer()) { + $mailer = $this->getMailer(); + + if ($mailer) { // initialize the message body $body = sprintf('This mail contains the debug dump for import with serial "%s"', $serial); // initialize the message template - /** @var \Swift_Message $message */ - $message = $mailer->createMessage() - ->setSubject('Test') - ->setFrom($swiftMailerConfiguration->getParam('from')) - ->setTo($recipient) - ->setBody($body, 'text/html'); - - // initialize the archive file - $archiveFile = null; - + $from = $mailerConfiguration->getParam('from'); + $to = (array)$recipient; + $email = (new Email())->subject('Test')->from($from)->to(... $to)->html($body); // query whether or not the archive file is available if (!is_file($archiveFile = sprintf('%s/%s.zip', sys_get_temp_dir(), $serial))) { $this->getOutput()->writeln(sprintf('Can\'t find either a directory or ZIP artefact for serial "%s"', $serial)); @@ -107,39 +106,30 @@ public function process() } // attach the CSV files with zipped artefacts - $message->attach(\Swift_Attachment::fromPath($archiveFile)); + $email->attachFromPath($archiveFile); - // initialize the array with the failed recipients - $failedRecipients = array(); + try { + // send the mail + $mailer->send($email); - // send the mail - $recipientsAccepted = $mailer->send($message, $failedRecipients); - - // query whether or not all recipients have been accepted - if (sizeof($failedRecipients) > 0) { - $this->getSystemLogger()->error(sprintf('Can\'t send mail to %s', implode(', ', $failedRecipients))); - } - - // if at least one recipient has been accepted - if ($recipientsAccepted > 0) { // cast 'to' into an array if not already - is_array($recipient) ?: $recipient = (array)$recipient; + is_array($recipient) ?: $recipient = $to; - // remove the NOT accepted recipients - $acceptedRecipients = array_diff($recipient, $failedRecipients); - - // log a message with the accepted receivers + // log a message with the receivers $this->getSystemLogger()->info( sprintf( - 'Mail successfully sent to %d recipient(s) (%s)', - $recipientsAccepted, - implode(', ', $acceptedRecipients) + 'Mail successfully sent to recipient(s) (%s)', + implode(', ', $recipient) ) ); + } catch (TransportExceptionInterface $e) { + $this->getSystemLogger()->error(sprintf('Can\'t send mail: %s', $e->getMessage())); } } else { // write a message to the console, that the mailer configuration has not been available - $this->getOutput()->writeln('The mailer configuration is not available or mailer can not be loaded'); + $this->getOutput()->writeln( + 'The mailer configuration is not available or mailer can not be loaded' + ); } } }