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'
+ );
}
}
}