Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBX-7369, IBX-7371: Added UserRegister and UserPasswordReset notification #70

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading