Merge branch 'short-message-use-notifier-component' into 'master'

Send short messages using Notifier integration

See merge request Chill-Projet/chill-bundles!782
This commit is contained in:
Julien Fastré 2025-01-20 14:17:01 +00:00
commit 3f1a4fe353
25 changed files with 254 additions and 308 deletions

4
.env
View File

@ -88,3 +88,7 @@ REDIS_HOST=redis
REDIS_PORT=6379 REDIS_PORT=6379
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
###< chill-project/chill-bundles ### ###< chill-project/chill-bundles ###
###> symfony/ovh-cloud-notifier ###
# OVHCLOUD_DSN=ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME
###< symfony/ovh-cloud-notifier ###

View File

@ -16,8 +16,8 @@
"ext-zlib": "*", "ext-zlib": "*",
"champs-libres/wopi-bundle": "dev-master@dev", "champs-libres/wopi-bundle": "dev-master@dev",
"champs-libres/wopi-lib": "dev-master@dev", "champs-libres/wopi-lib": "dev-master@dev",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/data-fixtures": "^1.8", "doctrine/data-fixtures": "^1.8",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.13.0", "doctrine/orm": "^2.13.0",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
@ -58,7 +58,9 @@
"symfony/messenger": "^5.4", "symfony/messenger": "^5.4",
"symfony/mime": "^5.4", "symfony/mime": "^5.4",
"symfony/monolog-bundle": "^3.5", "symfony/monolog-bundle": "^3.5",
"symfony/notifier": "^5.4",
"symfony/options-resolver": "^5.4", "symfony/options-resolver": "^5.4",
"symfony/ovh-cloud-notifier": "^5.4",
"symfony/process": "^5.4", "symfony/process": "^5.4",
"symfony/property-access": "^5.4", "symfony/property-access": "^5.4",
"symfony/property-info": "^5.4", "symfony/property-info": "^5.4",
@ -161,7 +163,8 @@
"assets:install %PUBLIC_DIR%": "symfony-cmd" "assets:install %PUBLIC_DIR%": "symfony-cmd"
}, },
"php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none", "php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none",
"phpstan": "phpstan --no-progress" "phpstan": "phpstan --no-progress",
"rector": "rector --no-progress-bar"
}, },
"extra": { "extra": {
"symfony": { "symfony": {

View File

@ -0,0 +1,13 @@
framework:
notifier:
texter_transports:
#ovhcloud: '%env(OVHCLOUD_DSN)%'
#ovhcloud: '%env(SHORT_MESSAGE_DSN)%'
channel_policy:
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
urgent: ['email']
high: ['email']
medium: ['email']
low: ['email']
admin_recipients:
- { email: admin@example.com }

View File

@ -60,7 +60,6 @@ Available bundles
* chill docs store: to store documents to people, but also entities, * chill docs store: to store documents to people, but also entities,
* chill task: to register task with people, * chill task: to register task with people,
* chill third party: to register third parties, * chill third party: to register third parties,
* chill family members: to register family members
You will also found the following projects : You will also found the following projects :

View File

@ -20,6 +20,10 @@ return static function (RectorConfig $rectorConfig): void {
__DIR__ . '/src', __DIR__ . '/src',
]); ]);
$rectorConfig->skip([
\Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class => __DIR__ . 'src/Bundle/ChillMainBundle/Service/Notifier/LegacyOvhCloudFactory.php'
]);
$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/test/App_KernelTestDebugContainer.xml '); $rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/test/App_KernelTestDebugContainer.xml ');
$rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php'); $rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php');

View File

@ -21,9 +21,7 @@ namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface; use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Service\ShortMessage\ShortMessageTransporterInterface;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\PersonRepository;
use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumber;
@ -36,6 +34,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\Question;
use Symfony\Component\Notifier\TexterInterface;
class SendTestShortMessageOnCalendarCommand extends Command class SendTestShortMessageOnCalendarCommand extends Command
{ {
@ -44,9 +43,8 @@ class SendTestShortMessageOnCalendarCommand extends Command
public function __construct( public function __construct(
private readonly PersonRepository $personRepository, private readonly PersonRepository $personRepository,
private readonly PhoneNumberUtil $phoneNumberUtil, private readonly PhoneNumberUtil $phoneNumberUtil,
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder, private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
private readonly ShortMessageTransporterInterface $transporter, private readonly TexterInterface $transporter,
private readonly UserRepositoryInterface $userRepository, private readonly UserRepositoryInterface $userRepository,
) { ) {
parent::__construct('chill:calendar:test-send-short-message'); parent::__construct('chill:calendar:test-send-short-message');
@ -152,10 +150,6 @@ class SendTestShortMessageOnCalendarCommand extends Command
return $phone; return $phone;
}); });
$phone = $helper->ask($input, $output, $question);
$question = new ConfirmationQuestion('really send the message to the phone ?');
$reallySend = (bool) $helper->ask($input, $output, $question);
$messages = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar); $messages = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar);
@ -165,8 +159,12 @@ class SendTestShortMessageOnCalendarCommand extends Command
foreach ($messages as $key => $message) { foreach ($messages as $key => $message) {
$output->writeln("The short message for SMS {$key} will be: "); $output->writeln("The short message for SMS {$key} will be: ");
$output->writeln($message->getContent()); $output->writeln($message->getSubject());
$message->setPhoneNumber($phone); $output->writeln('The destination number will be:');
$output->writeln($message->getPhone());
$question = new ConfirmationQuestion('really send the message to the phone ?');
$reallySend = (bool) $helper->ask($input, $output, $question);
if ($reallySend) { if ($reallySend) {
$this->transporter->send($message); $this->transporter->send($message);

View File

@ -21,11 +21,17 @@ namespace Chill\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Notifier\TexterInterface;
class BulkCalendarShortMessageSender class BulkCalendarShortMessageSender
{ {
public function __construct(private readonly CalendarForShortMessageProvider $provider, private readonly EntityManagerInterface $em, private readonly LoggerInterface $logger, private readonly MessageBusInterface $messageBus, private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder) {} public function __construct(
private readonly CalendarForShortMessageProvider $provider,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
private readonly TexterInterface $texter,
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
) {}
public function sendBulkMessageToEligibleCalendars() public function sendBulkMessageToEligibleCalendars()
{ {
@ -36,7 +42,7 @@ class BulkCalendarShortMessageSender
$smses = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar); $smses = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar);
foreach ($smses as $sms) { foreach ($smses as $sms) {
$this->messageBus->dispatch($sms); $this->texter->send($sms);
++$countSms; ++$countSms;
} }

View File

@ -19,12 +19,26 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Service\ShortMessageNotification; namespace Chill\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\MainBundle\Service\ShortMessage\ShortMessage; use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\Notifier\Message\SmsMessage;
class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBuilderInterface class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBuilderInterface
{ {
public function __construct(private readonly \Twig\Environment $engine) {} private readonly PhoneNumberUtil $phoneUtil;
public function __construct(private readonly \Twig\Environment $engine)
{
$this->phoneUtil = PhoneNumberUtil::getInstance();
}
/**
* @return list<SmsMessage>
*
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public function buildMessageForCalendar(Calendar $calendar): array public function buildMessageForCalendar(Calendar $calendar): array
{ {
if (true !== $calendar->getSendSMS()) { if (true !== $calendar->getSendSMS()) {
@ -39,16 +53,14 @@ class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBu
} }
if (Calendar::SMS_PENDING === $calendar->getSmsStatus()) { if (Calendar::SMS_PENDING === $calendar->getSmsStatus()) {
$toUsers[] = new ShortMessage( $toUsers[] = new SmsMessage(
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message.txt.twig', ['calendar' => $calendar]), $this->engine->render('@ChillCalendar/CalendarShortMessage/short_message.txt.twig', ['calendar' => $calendar]),
$person->getMobilenumber(),
ShortMessage::PRIORITY_LOW
); );
} elseif (Calendar::SMS_CANCEL_PENDING === $calendar->getSmsStatus()) { } elseif (Calendar::SMS_CANCEL_PENDING === $calendar->getSmsStatus()) {
$toUsers[] = new ShortMessage( $toUsers[] = new SmsMessage(
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message_canceled.txt.twig', ['calendar' => $calendar]), $this->engine->render('@ChillCalendar/CalendarShortMessage/short_message_canceled.txt.twig', ['calendar' => $calendar]),
$person->getMobilenumber(),
ShortMessage::PRIORITY_LOW
); );
} }
} }

View File

@ -19,12 +19,12 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Service\ShortMessageNotification; namespace Chill\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\MainBundle\Service\ShortMessage\ShortMessage; use Symfony\Component\Notifier\Message\SmsMessage;
interface ShortMessageForCalendarBuilderInterface interface ShortMessageForCalendarBuilderInterface
{ {
/** /**
* @return array|ShortMessage[] * @return list<SmsMessage>
*/ */
public function buildMessageForCalendar(Calendar $calendar): array; public function buildMessageForCalendar(Calendar $calendar): array;
} }

View File

@ -23,17 +23,16 @@ use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessa
use Chill\CalendarBundle\Service\ShortMessageNotification\CalendarForShortMessageProvider; use Chill\CalendarBundle\Service\ShortMessageNotification\CalendarForShortMessageProvider;
use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface; use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Service\ShortMessage\ShortMessage;
use Chill\MainBundle\Test\PrepareUserTrait; use Chill\MainBundle\Test\PrepareUserTrait;
use Chill\PersonBundle\DataFixtures\Helper\PersonRandomHelper; use Chill\PersonBundle\DataFixtures\Helper\PersonRandomHelper;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use libphonenumber\PhoneNumberUtil;
use Prophecy\Argument; use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger; use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Messenger\Envelope; use Symfony\Component\Notifier\Message\SentMessage;
use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\TexterInterface;
/** /**
* @internal * @internal
@ -101,24 +100,23 @@ final class BulkCalendarShortMessageSenderTest extends KernelTestCase
$messageBuilder->buildMessageForCalendar(Argument::type(Calendar::class)) $messageBuilder->buildMessageForCalendar(Argument::type(Calendar::class))
->willReturn( ->willReturn(
[ [
new ShortMessage( new SmsMessage(
'+32470123456',
'content', 'content',
PhoneNumberUtil::getInstance()->parse('+32470123456', 'BE'),
ShortMessage::PRIORITY_MEDIUM
), ),
] ]
); );
$bus = $this->prophesize(MessageBusInterface::class); $texter = $this->prophesize(TexterInterface::class);
$bus->dispatch(Argument::type(ShortMessage::class)) $texter->send(Argument::type(SmsMessage::class))
->willReturn(new Envelope(new \stdClass())) ->will(fn ($args): SentMessage => new SentMessage($args[0], 'sms'))
->shouldBeCalledTimes(1); ->shouldBeCalledTimes(1);
$bulk = new BulkCalendarShortMessageSender( $bulk = new BulkCalendarShortMessageSender(
$provider->reveal(), $provider->reveal(),
$em, $em,
new NullLogger(), new NullLogger(),
$bus->reveal(), $texter->reveal(),
$messageBuilder->reveal() $messageBuilder->reveal()
); );

View File

@ -23,7 +23,6 @@ use Chill\CalendarBundle\Service\ShortMessageNotification\DefaultShortMessageFor
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil; use libphonenumber\PhoneNumberUtil;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Prophecy\Argument; use Prophecy\Argument;
@ -90,10 +89,9 @@ final class DefaultShortMessageForCalendarBuilderTest extends TestCase
$this->assertCount(1, $sms); $this->assertCount(1, $sms);
$this->assertEquals( $this->assertEquals(
'+32470123456', '+32470123456',
$this->phoneNumberUtil->format($sms[0]->getPhoneNumber(), PhoneNumberFormat::E164) $sms[0]->getPhone()
); );
$this->assertEquals('message content', $sms[0]->getContent()); $this->assertEquals('message content', $sms[0]->getSubject());
$this->assertEquals('low', $sms[0]->getPriority());
// if the calendar is canceled // if the calendar is canceled
$calendar $calendar
@ -105,9 +103,8 @@ final class DefaultShortMessageForCalendarBuilderTest extends TestCase
$this->assertCount(1, $sms); $this->assertCount(1, $sms);
$this->assertEquals( $this->assertEquals(
'+32470123456', '+32470123456',
$this->phoneNumberUtil->format($sms[0]->getPhoneNumber(), PhoneNumberFormat::E164) $sms[0]->getRecipientId(),
); );
$this->assertEquals('message canceled', $sms[0]->getContent()); $this->assertEquals('message canceled', $sms[0]->getSubject());
$this->assertEquals('low', $sms[0]->getPriority());
} }
} }

View File

@ -18,7 +18,6 @@ use Chill\MainBundle\DependencyInjection\CompilerPass\ExportsCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\ShortMessageCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass; use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass;
use Chill\MainBundle\DependencyInjection\CompilerPass\WidgetsCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\WidgetsCompilerPass;
use Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass; use Chill\MainBundle\DependencyInjection\ConfigConsistencyCompilerPass;
@ -73,6 +72,5 @@ class ChillMainBundle extends Bundle
$container->addCompilerPass(new MenuCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new MenuCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
$container->addCompilerPass(new ACLFlagsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new ACLFlagsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
$container->addCompilerPass(new CRUDControllerCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new CRUDControllerCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
$container->addCompilerPass(new ShortMessageCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
} }
} }

View File

@ -234,6 +234,8 @@ class ChillMainExtension extends Extension implements
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
{ {
$this->prependNotifierTexterWithLegacyData($container);
// add installation_name and date_format to globals // add installation_name and date_format to globals
$chillMainConfig = $container->getExtensionConfig($this->getAlias()); $chillMainConfig = $container->getExtensionConfig($this->getAlias());
$config = $this->processConfiguration($this $config = $this->processConfiguration($this
@ -357,6 +359,44 @@ class ChillMainExtension extends Extension implements
// Note: the controller are loaded inside compiler pass // Note: the controller are loaded inside compiler pass
} }
/**
* This method prepend framework configuration with legacy configuration from "ovhCloudTransporter".
*
* It can be safely removed when the option chill_main.short_message.dsn will be removed.
*/
private function prependNotifierTexterWithLegacyData(ContainerBuilder $container): void
{
$configs = $container->getExtensionConfig('chill_main');
$notifierSet = false;
foreach (array_reverse($configs) as $config) {
if (!array_key_exists('short_messages', $config)) {
continue;
}
if (array_key_exists('dsn', $config['short_messages'])) {
$container->prependExtensionConfig('framework', [
'notifier' => [
'texter_transports' => [
'ovh_legacy' => $config['short_messages']['dsn'],
],
],
]);
$notifierSet = true;
}
}
if (!$notifierSet) {
$container->prependExtensionConfig('framework', [
'notifier' => [
'texter_transports' => [
'dummy' => 'null://null',
],
],
]);
}
}
protected function prependCruds(ContainerBuilder $container) protected function prependCruds(ContainerBuilder $container)
{ {
$container->prependExtensionConfig('chill_main', [ $container->prependExtensionConfig('chill_main', [

View File

@ -1,91 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Chill\MainBundle\Service\ShortMessage\NullShortMessageSender;
use Chill\MainBundle\Service\ShortMessage\ShortMessageTransporter;
use Chill\MainBundle\Service\ShortMessageOvh\OvhShortMessageSender;
use libphonenumber\PhoneNumberUtil;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
class ShortMessageCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$config = $container->resolveEnvPlaceholders($container->getParameter('chill_main.short_messages'), true);
// weird fix for special characters
$config['dsn'] = str_replace(['%%'], ['%'], (string) $config['dsn']);
$dsn = parse_url($config['dsn']);
parse_str($dsn['query'] ?? '', $dsn['queries']);
if ('null' === $dsn['scheme'] || false === $config['enabled']) {
$defaultTransporter = new Reference(NullShortMessageSender::class);
} elseif ('ovh' === $dsn['scheme']) {
if (!class_exists('\\'.\Ovh\Api::class)) {
throw new RuntimeException('Class \Ovh\Api not found');
}
foreach (['user', 'host', 'pass'] as $component) {
if (!\array_key_exists($component, $dsn)) {
throw new RuntimeException(sprintf('The component %s does not exist in dsn. Please provide a dsn like ovh://applicationKey:applicationSecret@endpoint?consumerKey=xxxx&sender=yyyy&service_name=zzzz', $component));
}
$container->setParameter('chill_main.short_messages.ovh_config_'.$component, $dsn[$component]);
}
foreach (['consumer_key', 'sender', 'service_name'] as $param) {
if (!\array_key_exists($param, $dsn['queries'])) {
throw new RuntimeException(sprintf('The parameter %s does not exist in dsn. Please provide a dsn like ovh://applicationKey:applicationSecret@endpoint?consumerKey=xxxx&sender=yyyy&service_name=zzzz', $param));
}
$container->setParameter('chill_main.short_messages.ovh_config_'.$param, $dsn['queries'][$param]);
}
$ovh = new Definition();
$ovh
->setClass('\\'.\Ovh\Api::class)
->setArgument(0, $dsn['user'])
->setArgument(1, $dsn['pass'])
->setArgument(2, $dsn['host'])
->setArgument(3, $dsn['queries']['consumer_key']);
$container->setDefinition(\Ovh\Api::class, $ovh);
$ovhSender = new Definition();
$ovhSender
->setClass(OvhShortMessageSender::class)
->setArgument(0, new Reference(\Ovh\Api::class))
->setArgument(1, $dsn['queries']['service_name'])
->setArgument(2, $dsn['queries']['sender'])
->setArgument(3, new Reference(LoggerInterface::class))
->setArgument(4, new Reference(PhoneNumberUtil::class));
$container->setDefinition(OvhShortMessageSender::class, $ovhSender);
$defaultTransporter = new Reference(OvhShortMessageSender::class);
} else {
throw new RuntimeException(sprintf('Cannot find a sender for this dsn: %s', $config['dsn']));
}
$container->getDefinition(ShortMessageTransporter::class)
->setArgument(0, $defaultTransporter);
}
}

View File

@ -123,6 +123,7 @@ class Configuration implements ConfigurationInterface
->end() ->end()
->end() ->end()
->arrayNode('short_messages') ->arrayNode('short_messages')
->setDeprecated('chill-project/chill-bundles', '3.7.0', 'Since 3.7.0, Chill use the Notifier component to send message. Configure the notifier instead. In the meantime, the previous available OVH configuration will be append to the notifier component.')
->canBeEnabled() ->canBeEnabled()
->children() ->children()
->scalarNode('dsn')->cannotBeEmpty()->defaultValue('null://null') ->scalarNode('dsn')->cannotBeEmpty()->defaultValue('null://null')

View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\Notifier;
use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
use Symfony\Component\Notifier\Transport\Dsn;
use Symfony\Component\Notifier\Transport\TransportInterface;
/**
* This is a legacy ovh cloud provider, to provide the regular OvhCloudTransporter from the previous configuration.
*
* This is only for transition purpose from the previous ovh dsn, which was existing in chill.
*/
class LegacyOvhCloudFactory extends AbstractTransportFactory
{
protected function getSupportedSchemes(): array
{
return ['ovh'];
}
public function create(Dsn $dsn): TransportInterface
{
$scheme = $dsn->getScheme();
if ('ovh' !== $scheme) {
throw new UnsupportedSchemeException($dsn, 'ovh', $this->getSupportedSchemes());
}
if (!class_exists($class = '\Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransport')) {
throw new \RuntimeException(sprintf('The class %s is missing, please add the dependency with "composer require symfony/ovh-cloud-notifier".', $class));
}
$applicationKey = $this->getUser($dsn);
$applicationSecret = $this->getPassword($dsn);
$consumerKey = $dsn->getRequiredOption('consumer_key');
$serviceName = $dsn->getRequiredOption('service_name');
$sender = $dsn->getOption('sender');
$host = null;
$port = $dsn->getPort();
return (new $class($applicationKey, $applicationSecret, $consumerKey, $serviceName, $this->client, $this->dispatcher))
->setHost($host)->setPort($port)->setSender($sender);
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\Notifier;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Notifier\Event\SentMessageEvent;
final readonly class SentMessageEventSubscriber implements EventSubscriberInterface
{
public function __construct(
private LoggerInterface $logger,
) {}
public static function getSubscribedEvents()
{
return [
SentMessageEvent::class => ['onSentMessage', 0],
];
}
public function onSentMessage(SentMessageEvent $event): void
{
$message = $event->getMessage();
$this->logger->warning('[sms] a sms was sent', ['validReceiversI' => $message->getOriginalMessage()->getRecipientId(), 'idsI' => $message->getMessageId()]);
}
}

View File

@ -1,24 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\ShortMessage;
class NullShortMessageSender implements ShortMessageSenderInterface
{
public function send(ShortMessage $shortMessage): void {}
}

View File

@ -20,6 +20,8 @@ namespace Chill\MainBundle\Service\ShortMessage;
use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumber;
trigger_deprecation('chill-project/chill-bundles', '3.7', 'Short Messages are deprecated, use SmsMessage and Notifier component instead');
class ShortMessage class ShortMessage
{ {
final public const PRIORITY_LOW = 'low'; final public const PRIORITY_LOW = 'low';

View File

@ -18,17 +18,33 @@ declare(strict_types=1);
namespace Chill\MainBundle\Service\ShortMessage; namespace Chill\MainBundle\Service\ShortMessage;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\TexterInterface;
/** /**
* @AsMessageHandler * @AsMessageHandler
*/ */
class ShortMessageHandler implements MessageHandlerInterface class ShortMessageHandler implements MessageHandlerInterface
{ {
public function __construct(private readonly ShortMessageTransporterInterface $messageTransporter) {} private readonly PhoneNumberUtil $phoneNumberUtil;
public function __construct(private readonly TexterInterface $texter)
{
$this->phoneNumberUtil = PhoneNumberUtil::getInstance();
}
public function __invoke(ShortMessage $message): void public function __invoke(ShortMessage $message): void
{ {
$this->messageTransporter->send($message); trigger_deprecation('Chill-project/chill-bundles', '3.7.0', 'Send message using Notifier component');
$this->texter->send(
new SmsMessage(
$this->phoneNumberUtil->format($message->getPhoneNumber(), PhoneNumberFormat::E164),
$message->getContent(),
),
);
} }
} }

View File

@ -1,24 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\ShortMessage;
interface ShortMessageSenderInterface
{
public function send(ShortMessage $shortMessage): void;
}

View File

@ -1,29 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\ShortMessage;
class ShortMessageTransporter implements ShortMessageTransporterInterface
{
public function __construct(private readonly ShortMessageSenderInterface $sender) {}
public function send(ShortMessage $shortMessage): void
{
$this->sender->send($shortMessage);
}
}

View File

@ -1,24 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\ShortMessage;
interface ShortMessageTransporterInterface
{
public function send(ShortMessage $shortMessage);
}

View File

@ -1,65 +0,0 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Service\ShortMessageOvh;
use Chill\MainBundle\Service\ShortMessage\ShortMessage;
use Chill\MainBundle\Service\ShortMessage\ShortMessageSenderInterface;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Ovh\Api;
use Psr\Log\LoggerInterface;
class OvhShortMessageSender implements ShortMessageSenderInterface
{
public function __construct(
private readonly Api $api,
// for DI, must remains as first argument
private readonly string $serviceName,
// for di, must remains as second argument
private readonly string $sender,
// for DI, must remains as third argument
private readonly LoggerInterface $logger,
private readonly PhoneNumberUtil $phoneNumberUtil,
) {}
public function send(ShortMessage $shortMessage): void
{
$receiver = $this->phoneNumberUtil->format($shortMessage->getPhoneNumber(), PhoneNumberFormat::E164);
$response = $this->api->post(
strtr('/sms/{serviceName}/jobs', ['{serviceName}' => $this->serviceName]),
[
'message' => $shortMessage->getContent(),
'receivers' => [$receiver],
'sender' => $this->sender,
'noStopClause' => true,
'coding' => '7bit',
'charset' => 'UTF-8',
'priority' => $shortMessage->getPriority(),
]
);
$improved = array_merge([
'validReceiversI' => implode(',', $response['validReceivers']),
'idsI' => implode(',', $response['ids']),
], $response);
$this->logger->warning('[sms] a sms was sent', $improved);
}
}

View File

@ -296,6 +296,27 @@
"config/packages/monolog.yaml" "config/packages/monolog.yaml"
] ]
}, },
"symfony/notifier": {
"version": "5.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.0",
"ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc"
},
"files": [
"config/packages/notifier.yaml"
]
},
"symfony/ovh-cloud-notifier": {
"version": "5.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.1",
"ref": "fe2e382c22d60eae9ad54cb22862b1c15291fdf8"
}
},
"symfony/phpunit-bridge": { "symfony/phpunit-bridge": {
"version": "7.1", "version": "7.1",
"recipe": { "recipe": {