Skip to content

Commit

Permalink
IBX-7369, IBX-7371: Added UserRegister and UserPasswordReset notifica…
Browse files Browse the repository at this point in the history
…tion (#70)

* IBX-7369: Added UserRegister notification

* IBX-7371: Added UserPasswordReset notification

* Registered Notifications Bundle in IbexaTestKernel
  • Loading branch information
webhdx authored Jan 12, 2024
1 parent c558e93 commit 9881c99
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 36 deletions.
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -69,5 +70,8 @@
"branch-alias": {
"dev-main": "4.6.x-dev"
}
},
"config": {
"allow-plugins": false
}
}
65 changes: 46 additions & 19 deletions src/bundle/Controller/PasswordResetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -71,6 +72,7 @@ public function __construct(
$this->actionResultHandler = $actionResultHandler;
$this->permissionResolver = $permissionResolver;
$this->configResolver = $configResolver;
$this->notificationService = $notificationService;
}

/**
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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')
Expand All @@ -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) {
Expand All @@ -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');
16 changes: 16 additions & 0 deletions src/contracts/Notification/UserAwareNotificationInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\User\Notification;

use Ibexa\Contracts\Core\Repository\Values\User\User;

interface UserAwareNotificationInterface
{
public function getUser(): User;
}
57 changes: 57 additions & 0 deletions src/contracts/Notification/UserPasswordReset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\User\Notification;

use Ibexa\Contracts\Core\Repository\Values\User\User;
use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\SmsNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\SmsRecipientInterface;

final class UserPasswordReset
extends Notification
implements EmailNotificationInterface, SmsNotificationInterface, UserAwareNotificationInterface
{
private User $user;

private string $token;

public function __construct(
User $user,
string $token
) {
parent::__construct();

$this->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;
}
}
47 changes: 47 additions & 0 deletions src/contracts/Notification/UserRegister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\User\Notification;

use Ibexa\Contracts\Core\Repository\Values\User\User;
use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\SmsNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\SmsRecipientInterface;

final class UserRegister
extends Notification
implements EmailNotificationInterface, SmsNotificationInterface, UserAwareNotificationInterface
{
private User $user;

public function __construct(User $user)
{
parent::__construct();

$this->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;
}
}
45 changes: 28 additions & 17 deletions src/lib/Form/Processor/UserRegisterFormProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,46 @@
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;

/**
* Listens for and processes User register events.
*/
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()
Expand All @@ -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);
Expand All @@ -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');
2 changes: 2 additions & 0 deletions tests/integration/IbexaTestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -35,6 +36,7 @@ public function registerBundles(): iterable

yield from [
new IbexaUserBundle(),
new IbexaNotificationsBundle(),
];
}

Expand Down

0 comments on commit 9881c99

Please sign in to comment.