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-8137: Moved from swift mailer to symfony mailer #84

Merged
merged 6 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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 @@ -985,16 +980,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
39 changes: 3 additions & 36 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 @@ -234,40 +228,13 @@ private function updateUserToken(User $user): string

private function sendResetPasswordMessage(User $user, string $hashKey): void
{
if ($this->isNotifierConfigured()) {
$this->sendNotification($user, $hashKey);

if (!$this->isNotifierConfigured()) {
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))],
);
Expand Down
80 changes: 80 additions & 0 deletions src/contracts/Notification/UserInvitation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?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\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 UserInvitation extends Notification implements EmailNotificationInterface, SmsNotificationInterface
{
private Invitation $invitation;

private ConfigResolverInterface $configResolver;

private Environment $twig;

public function __construct(
Invitation $invitation,
ConfigResolverInterface $configResolver,
Environment $twig
) {
parent::__construct();

$this->configResolver = $configResolver;
$this->twig = $twig;
$this->invitation = $invitation;
}
ViniTou marked this conversation as resolved.
Show resolved Hide resolved

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);
}

public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage
{
return null;
}
Steveb-p marked this conversation as resolved.
Show resolved Hide resolved
}
32 changes: 30 additions & 2 deletions src/contracts/Notification/UserPasswordReset.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,61 @@
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
{
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);

$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 asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage
Expand Down
55 changes: 23 additions & 32 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,37 @@ 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()
)
);

$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,
]);
if (!$this->isNotifierConfigured()) {
return;
}

$message = (new Swift_Message())
->setSubject($subject)
->setTo($invitation->getEmail())
->setBody($body, 'text/html');
$this->notificationService->send(
new SymfonyNotificationAdapter(
new UserInvitation($invitation, $this->configResolver, $this->twig),
),
Steveb-p marked this conversation as resolved.
Show resolved Hide resolved
[new SymfonyRecipientAdapter(new Recipient($invitation->getEmail()))],
);
}

if (empty($from) === false) {
$message->setFrom($from);
}
private function isNotifierConfigured(): bool
{
$subscriptions = $this->configResolver->getParameter('notifications.subscriptions');

$this->mailer->send($message);
return array_key_exists(UserInvitation::class, $subscriptions)
&& !empty($subscriptions[UserInvitation::class]['channels']);
}
}
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
6 changes: 6 additions & 0 deletions tests/lib/Invitation/InvitationServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ class InvitationServiceTest extends TestCase
{
private InvitationService $invitationService;

/** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface&\PHPUnit\Framework\MockObject\MockObject */
private SiteAccessServiceInterface $siteAccessService;

/** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface&\PHPUnit\Framework\MockObject\MockObject */
private ConfigResolverInterface $configResolver;

protected function setUp(): void
{
$this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
Expand Down
Loading