From 9881c999e103fff74806c381b5380cedb5da8815 Mon Sep 17 00:00:00 2001 From: Maciej Kobus Date: Fri, 12 Jan 2024 14:22:47 +0100 Subject: [PATCH] IBX-7369, IBX-7371: Added UserRegister and UserPasswordReset notification (#70) * IBX-7369: Added UserRegister notification * IBX-7371: Added UserPasswordReset notification * Registered Notifications Bundle in IbexaTestKernel --- composer.json | 4 ++ .../Controller/PasswordResetController.php | 65 +++++++++++++------ .../UserAwareNotificationInterface.php | 16 +++++ .../Notification/UserPasswordReset.php | 57 ++++++++++++++++ src/contracts/Notification/UserRegister.php | 47 ++++++++++++++ .../Processor/UserRegisterFormProcessor.php | 45 ++++++++----- tests/integration/IbexaTestKernel.php | 2 + 7 files changed, 200 insertions(+), 36 deletions(-) create mode 100644 src/contracts/Notification/UserAwareNotificationInterface.php create mode 100644 src/contracts/Notification/UserPasswordReset.php create mode 100644 src/contracts/Notification/UserRegister.php diff --git a/composer.json b/composer.json index 6a1c950..f06eb98 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "php": "^7.4 || ^8.0", "ibexa/core": "~4.6.0@dev", "ibexa/content-forms": "~4.6.0@dev", + "ibexa/notifications": "~4.6.0@dev", "jms/translation-bundle": "^1.5", "symfony/dependency-injection": "^5.0", "symfony/http-kernel": "^5.0", @@ -69,5 +70,8 @@ "branch-alias": { "dev-main": "4.6.x-dev" } + }, + "config": { + "allow-plugins": false } } diff --git a/src/bundle/Controller/PasswordResetController.php b/src/bundle/Controller/PasswordResetController.php index 2e9ea82..01edfda 100644 --- a/src/bundle/Controller/PasswordResetController.php +++ b/src/bundle/Controller/PasswordResetController.php @@ -17,6 +17,11 @@ use Ibexa\Contracts\Core\Repository\Values\User\User; use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Contracts\Notifications\Service\NotificationServiceInterface; +use Ibexa\Contracts\Notifications\Value\Notification\SymfonyNotificationAdapter; +use Ibexa\Contracts\Notifications\Value\Recipent\SymfonyRecipientAdapter; +use Ibexa\Contracts\Notifications\Value\Recipent\UserRecipient; +use Ibexa\Contracts\User\Notification\UserPasswordReset; use Ibexa\User\ExceptionHandler\ActionResultHandler; use Ibexa\User\Form\Data\UserPasswordResetData; use Ibexa\User\Form\Factory\FormFactory; @@ -34,26 +39,21 @@ class PasswordResetController extends Controller { - /** @var \Ibexa\User\Form\Factory\FormFactory */ - private $formFactory; + private FormFactory $formFactory; - /** @var \Ibexa\Contracts\Core\Repository\UserService */ - private $userService; + private UserService $userService; - /** @var \Swift_Mailer */ - private $mailer; + private Swift_Mailer $mailer; - /** @var \Twig\Environment */ - private $twig; + private Environment $twig; - /** @var \Ibexa\User\ExceptionHandler\ActionResultHandler */ - private $actionResultHandler; + private ActionResultHandler $actionResultHandler; - /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ - private $permissionResolver; + private PermissionResolver $permissionResolver; - /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ - private $configResolver; + private ConfigResolverInterface $configResolver; + + private NotificationServiceInterface $notificationService; public function __construct( FormFactory $formFactory, @@ -62,7 +62,8 @@ public function __construct( Environment $twig, ActionResultHandler $actionResultHandler, PermissionResolver $permissionResolver, - ConfigResolverInterface $configResolver + ConfigResolverInterface $configResolver, + NotificationServiceInterface $notificationService ) { $this->formFactory = $formFactory; $this->userService = $userService; @@ -71,6 +72,7 @@ public function __construct( $this->actionResultHandler = $actionResultHandler; $this->permissionResolver = $permissionResolver; $this->configResolver = $configResolver; + $this->notificationService = $notificationService; } /** @@ -96,7 +98,7 @@ public function userForgotPasswordAction(Request $request, ?string $reason = nul $user = reset($users); $token = $this->updateUserToken($user); - $this->sendResetPasswordMessage($user->email, $token); + $this->sendResetPasswordMessage($user, $token); } return new SuccessView(null); @@ -136,7 +138,7 @@ public function userForgotPasswordLoginAction(Request $request) } $token = $this->updateUserToken($user); - $this->sendResetPasswordMessage($user->email, $token); + $this->sendResetPasswordMessage($user, $token); return new SuccessView(null); } @@ -230,8 +232,15 @@ private function updateUserToken(User $user): string return $struct->hashKey; } - private function sendResetPasswordMessage(string $to, string $hashKey): void + private function sendResetPasswordMessage(User $user, string $hashKey): void { + if ($this->isNotifierConfigured()) { + $this->sendNotification($user, $hashKey); + + return; + } + + // Swiftmailer delivery has to be kept to maintain backwards compatibility $template = $this->twig->load($this->configResolver->getParameter('user_forgot_password.templates.mail')); $senderAddress = $this->configResolver->hasParameter('sender_address', 'swiftmailer.mailer') @@ -244,7 +253,7 @@ private function sendResetPasswordMessage(string $to, string $hashKey): void $message = (new Swift_Message()) ->setSubject($subject) - ->setTo($to) + ->setTo($user->email) ->setBody($body, 'text/html'); if (empty($from) === false) { @@ -253,6 +262,24 @@ private function sendResetPasswordMessage(string $to, string $hashKey): void $this->mailer->send($message); } + + private function sendNotification($user, string $token): void + { + $this->notificationService->send( + new SymfonyNotificationAdapter( + new UserPasswordReset($user, $token), + ), + [new SymfonyRecipientAdapter(new UserRecipient($user))], + ); + } + + private function isNotifierConfigured(): bool + { + $subscriptions = $this->configResolver->getParameter('notifications.subscriptions'); + + return array_key_exists(UserPasswordReset::class, $subscriptions) + && !empty($subscriptions[UserPasswordReset::class]['channels']); + } } class_alias(PasswordResetController::class, 'EzSystems\EzPlatformUserBundle\Controller\PasswordResetController'); diff --git a/src/contracts/Notification/UserAwareNotificationInterface.php b/src/contracts/Notification/UserAwareNotificationInterface.php new file mode 100644 index 0000000..018a038 --- /dev/null +++ b/src/contracts/Notification/UserAwareNotificationInterface.php @@ -0,0 +1,16 @@ +user = $user; + $this->token = $token; + } + + public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage + { + return null; + } + + public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage + { + return null; + } + + public function getUser(): User + { + return $this->user; + } + + public function getToken(): string + { + return $this->token; + } +} diff --git a/src/contracts/Notification/UserRegister.php b/src/contracts/Notification/UserRegister.php new file mode 100644 index 0000000..3132c15 --- /dev/null +++ b/src/contracts/Notification/UserRegister.php @@ -0,0 +1,47 @@ +user = $user; + } + + public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage + { + return null; + } + + public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage + { + return null; + } + + public function getUser(): User + { + return $this->user; + } +} diff --git a/src/lib/Form/Processor/UserRegisterFormProcessor.php b/src/lib/Form/Processor/UserRegisterFormProcessor.php index 5bd4d7c..53bf033 100644 --- a/src/lib/Form/Processor/UserRegisterFormProcessor.php +++ b/src/lib/Form/Processor/UserRegisterFormProcessor.php @@ -10,10 +10,17 @@ use Ibexa\Contracts\Core\Repository\Repository; use Ibexa\Contracts\Core\Repository\RoleService; use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Notifications\Service\NotificationServiceInterface; +use Ibexa\Contracts\Notifications\Value\Notification\SymfonyNotificationAdapter; +use Ibexa\Contracts\Notifications\Value\Recipent\SymfonyRecipientAdapter; +use Ibexa\Contracts\Notifications\Value\Recipent\UserRecipient; +use Ibexa\Contracts\User\Notification\UserRegister; use Ibexa\User\Form\Data\UserRegisterData; use Ibexa\User\Form\UserFormEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; /** @@ -21,27 +28,28 @@ */ class UserRegisterFormProcessor implements EventSubscriberInterface { - /** @var \Ibexa\Contracts\Core\Repository\UserService */ - private $userService; + private UserService $userService; - /** @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface */ - private $urlGenerator; + private UrlGeneratorInterface $urlGenerator; - /** @var \Ibexa\Core\Repository\Repository */ - private $repository; + private Repository $repository; private RoleService $roleService; + private NotificationServiceInterface $notificationService; + public function __construct( Repository $repository, UserService $userService, RouterInterface $router, - RoleService $roleService + RoleService $roleService, + NotificationServiceInterface $notificationService ) { $this->userService = $userService; $this->urlGenerator = $router; $this->repository = $repository; $this->roleService = $roleService; + $this->notificationService = $notificationService; } public static function getSubscribedEvents() @@ -64,22 +72,15 @@ public function processRegister(FormActionEvent $event) } $form = $event->getForm(); - $this->createUser($data, $form->getConfig()->getOption('languageCode')); + $user = $this->createUser($data, $form->getConfig()->getOption('languageCode')); + $this->sendNotification($user); $redirectUrl = $this->urlGenerator->generate('ibexa.user.register_confirmation'); $event->setResponse(new RedirectResponse($redirectUrl)); $event->stopPropagation(); } - /** - * @param \Ibexa\User\Form\Data\UserRegisterData $data - * @param $languageCode - * - * @return \Ibexa\Contracts\Core\Repository\Values\User\User - * - * @throws \Exception - */ - private function createUser(UserRegisterData $data, $languageCode) + private function createUser(UserRegisterData $data, string $languageCode): User { foreach ($data->fieldsData as $fieldDefIdentifier => $fieldData) { $data->setField($fieldDefIdentifier, $fieldData->value, $languageCode); @@ -96,6 +97,16 @@ function () use ($data) { } ); } + + private function sendNotification(User $user): void + { + $this->notificationService->send( + new SymfonyNotificationAdapter( + new UserRegister($user), + ), + [new SymfonyRecipientAdapter(new UserRecipient($user))], + ); + } } class_alias(UserRegisterFormProcessor::class, 'EzSystems\EzPlatformUser\Form\Processor\UserRegisterFormProcessor'); diff --git a/tests/integration/IbexaTestKernel.php b/tests/integration/IbexaTestKernel.php index 1a23b43..4e8bee2 100644 --- a/tests/integration/IbexaTestKernel.php +++ b/tests/integration/IbexaTestKernel.php @@ -8,6 +8,7 @@ namespace Ibexa\Tests\Integration\User; +use Ibexa\Bundle\Notifications\IbexaNotificationsBundle; use Ibexa\Bundle\User\IbexaUserBundle; use Ibexa\ContentForms\Form\ActionDispatcher\UserDispatcher; use Ibexa\Contracts\Core\Test\IbexaTestKernel as BaseIbexaTestKernel; @@ -35,6 +36,7 @@ public function registerBundles(): iterable yield from [ new IbexaUserBundle(), + new IbexaNotificationsBundle(), ]; }