Skip to content

Commit

Permalink
IBX-8137: Moved from swift mailer to symfony mailer (#84)
Browse files Browse the repository at this point in the history
* IBX-8137: Moved from swift mailer to symfony mailer

* Dropped null sms related messages

* Dropped checks for configured notifier as it just silently fails if config is missing

* Added promotion constructor notation

* Moved phpdoc to union typehint

* Update src/contracts/Notification/UserInvitation.php

Co-authored-by: Konrad Oboza <[email protected]>

---------

Co-authored-by: Konrad Oboza <[email protected]>
  • Loading branch information
ViniTou and konradoboza authored Jul 24, 2024
1 parent 4398c55 commit 536f68b
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 118 deletions.
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"symfony/routing": "^5.0",
"symfony/security-core": "^5.0",
"symfony/security-http": "^5.0",
"symfony/swiftmailer-bundle": "^3.4",
"symfony/translation": "^5.0",
"symfony/validator": "^5.0",
"twig/twig": "^3.0"
Expand Down
15 changes: 0 additions & 15 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ parameters:
count: 1
path: src/bundle/Controller/PasswordChangeController.php

-
message: "#^Method Ibexa\\\\Bundle\\\\User\\\\Controller\\\\PasswordResetController\\:\\:sendNotification\\(\\) has parameter \\$user with no type specified\\.$#"
count: 1
path: src/bundle/Controller/PasswordResetController.php

-
message: "#^Parameter \\#1 \\$user of method Ibexa\\\\Bundle\\\\User\\\\Controller\\\\PasswordResetController\\:\\:sendResetPasswordMessage\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|false given\\.$#"
count: 1
Expand Down Expand Up @@ -980,16 +975,6 @@ parameters:
count: 1
path: tests/lib/Form/Type/ChoiceList/Loader/AvailableLocaleChoiceLoaderTest.php

-
message: "#^Access to an undefined property Ibexa\\\\Tests\\\\User\\\\Invitation\\\\InvitationServiceTest\\:\\:\\$configResolver\\.$#"
count: 2
path: tests/lib/Invitation/InvitationServiceTest.php

-
message: "#^Access to an undefined property Ibexa\\\\Tests\\\\User\\\\Invitation\\\\InvitationServiceTest\\:\\:\\$siteAccessService\\.$#"
count: 2
path: tests/lib/Invitation/InvitationServiceTest.php

-
message: "#^Method Ibexa\\\\Tests\\\\User\\\\Invitation\\\\InvitationServiceTest\\:\\:invitationProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
Expand Down
49 changes: 2 additions & 47 deletions src/bundle/Controller/PasswordResetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
use Ibexa\User\View\ResetPassword\FormView as UserResetPasswordFormView;
use Ibexa\User\View\ResetPassword\InvalidLinkView;
use Ibexa\User\View\ResetPassword\SuccessView as UserResetPasswordSuccessView;
use Swift_Mailer;
use Swift_Message;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;
Expand All @@ -43,8 +41,6 @@ class PasswordResetController extends Controller

private UserService $userService;

private Swift_Mailer $mailer;

private Environment $twig;

private ActionResultHandler $actionResultHandler;
Expand All @@ -58,7 +54,6 @@ class PasswordResetController extends Controller
public function __construct(
FormFactory $formFactory,
UserService $userService,
Swift_Mailer $mailer,
Environment $twig,
ActionResultHandler $actionResultHandler,
PermissionResolver $permissionResolver,
Expand All @@ -67,7 +62,6 @@ public function __construct(
) {
$this->formFactory = $formFactory;
$this->userService = $userService;
$this->mailer = $mailer;
$this->twig = $twig;
$this->actionResultHandler = $actionResultHandler;
$this->permissionResolver = $permissionResolver;
Expand All @@ -89,7 +83,7 @@ public function userForgotPasswordAction(Request $request, ?string $reason = nul
$data = $form->getData();
$users = $this->userService->loadUsersByEmail($data->getEmail());

/** Because is is possible to have multiple user accounts with same email address we must gain a user login. */
/** Because it is possible to have multiple user accounts with same email address we must gain a user login. */
if (\count($users) > 1) {
return $this->redirectToRoute('ibexa.user.forgot_password.login');
}
Expand Down Expand Up @@ -233,51 +227,12 @@ private function updateUserToken(User $user): string
}

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')
? $this->configResolver->getParameter('sender_address', 'swiftmailer.mailer')
: '';

$subject = $template->renderBlock('subject', []);
$from = $template->renderBlock('from', []) ?: $senderAddress;
$body = $template->renderBlock('body', ['hash_key' => $hashKey]);

$message = (new Swift_Message())
->setSubject($subject)
->setTo($user->email)
->setBody($body, 'text/html');

if (empty($from) === false) {
$message->setFrom($from);
}

$this->mailer->send($message);
}

private function sendNotification($user, string $token): void
{
$this->notificationService->send(
new SymfonyNotificationAdapter(
new UserPasswordReset($user, $token),
new UserPasswordReset($user, $hashKey, $this->configResolver, $this->twig),
),
[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']);
}
}
62 changes: 62 additions & 0 deletions src/contracts/Notification/UserInvitation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?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\SiteAccess\ConfigResolverInterface;
use Ibexa\Contracts\User\Invitation\Invitation;
use Symfony\Bridge\Twig\Mime\NotificationEmail;
use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Twig\Environment;

final class UserInvitation extends Notification implements EmailNotificationInterface
{
public function __construct(
private readonly Invitation $invitation,
private readonly ConfigResolverInterface $configResolver,
private readonly Environment $twig
) {
parent::__construct();
}

public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage
{
$templatePath = $this->twig->load(
$this->configResolver->getParameter(
'user_invitation.templates.mail',
null,
$this->invitation->getSiteAccessIdentifier()
)
);

$template = $this->twig->load($templatePath);

$subject = $template->renderBlock('subject');
$from = $template->renderBlock('from') ?: null;
$body = $template->renderBlock('body', [
'invite_hash' => $this->invitation->getHash(),
'siteaccess' => $this->invitation->getSiteAccessIdentifier(),
'invitation' => $this->invitation,
]);

$email = NotificationEmail::asPublicEmail()
->html($body)
->to($recipient->getEmail())
->subject($subject)
;

if ($from !== null) {
$email->from($from);
}

return new EmailMessage($email);
}
}
40 changes: 30 additions & 10 deletions src/contracts/Notification/UserPasswordReset.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,58 @@
namespace Ibexa\Contracts\User\Notification;

use Ibexa\Contracts\Core\Repository\Values\User\User;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Symfony\Bridge\Twig\Mime\NotificationEmail;
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;
use Twig\Environment;

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

private string $token;

private ConfigResolverInterface $configResolver;

private Environment $twig;

public function __construct(
User $user,
string $token
string $token,
ConfigResolverInterface $configResolver,
Environment $twig
) {
parent::__construct();

$this->user = $user;
$this->token = $token;
$this->configResolver = $configResolver;
$this->twig = $twig;
}

public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage
{
return null;
}
$templatePath = $this->configResolver->getParameter('user_forgot_password.templates.mail');
$template = $this->twig->load($templatePath);

public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage
{
return null;
$subject = $template->renderBlock('subject');
$from = $template->renderBlock('from') ?: null;
$body = $template->renderBlock('body', ['hash_key' => $this->token]);

$email = NotificationEmail::asPublicEmail()
->html($body)
->to($recipient->getEmail())
->subject($subject)
;

if ($from !== null) {
$email->from($from);
}

return new EmailMessage($email);
}

public function getUser(): User
Expand Down
10 changes: 1 addition & 9 deletions src/contracts/Notification/UserRegister.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@

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
final class UserRegister extends Notification implements EmailNotificationInterface, UserAwareNotificationInterface
{
private User $user;

Expand All @@ -33,11 +30,6 @@ public function asEmailMessage(EmailRecipientInterface $recipient, string $trans
return null;
}

public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage
{
return null;
}

public function getUser(): User
{
return $this->user;
Expand Down
47 changes: 13 additions & 34 deletions src/lib/Invitation/MailSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
namespace Ibexa\User\Invitation;

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\User\Invitation\Invitation;
use Ibexa\Contracts\User\Invitation\InvitationSender;
use Swift_Mailer;
use Swift_Message;
use Ibexa\Contracts\User\Notification\UserInvitation;
use Symfony\Component\Notifier\Recipient\Recipient;
use Twig\Environment;

final class MailSender implements InvitationSender
Expand All @@ -21,49 +24,25 @@ final class MailSender implements InvitationSender

private ConfigResolverInterface $configResolver;

private Swift_Mailer $mailer;
private NotificationServiceInterface $notificationService;

public function __construct(
Environment $twig,
ConfigResolverInterface $configResolver,
Swift_Mailer $mailer
NotificationServiceInterface $notificationService
) {
$this->twig = $twig;
$this->configResolver = $configResolver;
$this->mailer = $mailer;
$this->notificationService = $notificationService;
}

public function sendInvitation(Invitation $invitation): void
{
$template = $this->twig->load(
$this->configResolver->getParameter(
'user_invitation.templates.mail',
null,
$invitation->getSiteAccessIdentifier()
)
$this->notificationService->send(
new SymfonyNotificationAdapter(
new UserInvitation($invitation, $this->configResolver, $this->twig),
),
[new SymfonyRecipientAdapter(new Recipient($invitation->getEmail()))],
);

$senderAddress = $this->configResolver->hasParameter('sender_address', 'swiftmailer.mailer')
? $this->configResolver->getParameter('sender_address', 'swiftmailer.mailer')
: '';

$subject = $template->renderBlock('subject', []);
$from = $template->renderBlock('from', []) ?: $senderAddress;
$body = $template->renderBlock('body', [
'invite_hash' => $invitation->getHash(),
'siteaccess' => $invitation->getSiteAccessIdentifier(),
'invitation' => $invitation,
]);

$message = (new Swift_Message())
->setSubject($subject)
->setTo($invitation->getEmail())
->setBody($body, 'text/html');

if (empty($from) === false) {
$message->setFrom($from);
}

$this->mailer->send($message);
}
}
2 changes: 0 additions & 2 deletions tests/integration/IbexaTestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use Ibexa\Contracts\Core\Test\IbexaTestKernel as BaseIbexaTestKernel;
use Ibexa\Contracts\User\Invitation\InvitationService;
use LogicException;
use Swift_Mailer;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
Expand Down Expand Up @@ -55,7 +54,6 @@ public function registerContainerConfiguration(LoaderInterface $loader): void
$container->setParameter('locale_fallback', 'en');

self::createSyntheticService($container, UserDispatcher::class);
self::createSyntheticService($container, Swift_Mailer::class);

$container->loadFromExtension('framework', [
'router' => [
Expand Down
Loading

0 comments on commit 536f68b

Please sign in to comment.