From dd15a76e71560dc13e4d2a88236ecf89e7015721 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 16 Jan 2024 16:37:26 +0100 Subject: [PATCH] Add a mailer implementation based on symfony/mailer --- DependencyInjection/FOSUserExtension.php | 2 + Mailer/TwigSymfonyMailer.php | 94 ++++++++++++++++++++++++ Resources/config/mailer.xml | 24 ++++++ Resources/doc/emails.rst | 20 +++-- composer.json | 2 + 5 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 Mailer/TwigSymfonyMailer.php diff --git a/DependencyInjection/FOSUserExtension.php b/DependencyInjection/FOSUserExtension.php index a8509df42..b7d3a46c3 100644 --- a/DependencyInjection/FOSUserExtension.php +++ b/DependencyInjection/FOSUserExtension.php @@ -207,6 +207,7 @@ private function loadRegistration(array $config, ContainerBuilder $container, Xm unset($config['confirmation']['from_email']); } $container->setParameter('fos_user.registration.confirmation.from_email', [$fromEmail['address'] => $fromEmail['sender_name']]); + $container->setParameter('fos_user.registration.confirmation.from_address', $fromEmail); $this->remapParametersNamespaces($config, $container, [ 'confirmation' => 'fos_user.registration.confirmation.%s', @@ -234,6 +235,7 @@ private function loadResetting(array $config, ContainerBuilder $container, XmlFi unset($config['email']['from_email']); } $container->setParameter('fos_user.resetting.email.from_email', [$fromEmail['address'] => $fromEmail['sender_name']]); + $container->setParameter('fos_user.resetting.email.from_address', $fromEmail); $this->remapParametersNamespaces($config, $container, [ '' => [ diff --git a/Mailer/TwigSymfonyMailer.php b/Mailer/TwigSymfonyMailer.php new file mode 100644 index 000000000..17947b972 --- /dev/null +++ b/Mailer/TwigSymfonyMailer.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\UserBundle\Mailer; + +use FOS\UserBundle\Model\UserInterface; +use Symfony\Component\Mailer\MailerInterface as SymfonyMailerInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Twig\Environment; + +/** + * @author Christophe Coevoet + */ +final class TwigSymfonyMailer implements MailerInterface +{ + private SymfonyMailerInterface $mailer; + private UrlGeneratorInterface $router; + private Environment $twig; + private array $parameters; + + public function __construct(SymfonyMailerInterface $mailer, UrlGeneratorInterface $router, Environment $twig, array $parameters) + { + $this->mailer = $mailer; + $this->router = $router; + $this->twig = $twig; + $this->parameters = $parameters; + } + + public function sendConfirmationEmailMessage(UserInterface $user): void + { + $template = $this->parameters['template']['confirmation']; + $url = $this->router->generate('fos_user_registration_confirm', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL); + + $context = [ + 'user' => $user, + 'confirmationUrl' => $url, + ]; + + $this->sendMessage($template, $context, $this->parameters['from_email']['confirmation'], $user->getEmail()); + } + + public function sendResettingEmailMessage(UserInterface $user): void + { + $template = $this->parameters['template']['resetting']; + $url = $this->router->generate('fos_user_resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL); + + $context = [ + 'user' => $user, + 'confirmationUrl' => $url, + ]; + + $this->sendMessage($template, $context, $this->parameters['from_email']['resetting'], $user->getEmail()); + } + + /** + * @param array $context + * @param array{address: string, sender_name: string} $fromEmail + */ + private function sendMessage(string $templateName, array $context, array $fromEmail, string $toEmail): void + { + $template = $this->twig->load($templateName); + $subject = $template->renderBlock('subject', $context); + $textBody = $template->renderBlock('body_text', $context); + + $htmlBody = ''; + + if ($template->hasBlock('body_html', $context)) { + $htmlBody = $template->renderBlock('body_html', $context); + } + + $message = (new Email()) + ->subject($subject) + ->from(new Address($fromEmail['address'], $fromEmail['sender_name'])) + ->to($toEmail) + ->text($textBody) + ; + + if (!empty($htmlBody)) { + $message->html($htmlBody); + } + + $this->mailer->send($message); + } +} diff --git a/Resources/config/mailer.xml b/Resources/config/mailer.xml index 8fb376fea..147740c2a 100644 --- a/Resources/config/mailer.xml +++ b/Resources/config/mailer.xml @@ -13,6 +13,14 @@ Acme Ltd + + no-registration@acme.com + Acme Ltd + + + no-resetting@acme.com + Acme Ltd + @@ -33,6 +41,22 @@ + + + + + + + %fos_user.registration.confirmation.template% + %fos_user.resetting.email.template% + + + %fos_user.registration.confirmation.from_address% + %fos_user.resetting.email.from_address% + + + + diff --git a/Resources/doc/emails.rst b/Resources/doc/emails.rst index a16643b40..1d75549db 100644 --- a/Resources/doc/emails.rst +++ b/Resources/doc/emails.rst @@ -38,9 +38,10 @@ a form to enter in a new password. Default Mailer Implementations ------------------------------ -The bundle comes with 2 mailer implementations. They are listed below +The bundle comes with 3 mailer implementations. They are listed below by service id: +- ``fos_user.mailer.twig_symfony`` uses symfony/mailer to send emails and Twig blocks to render the message. - ``fos_user.mailer.twig_swift`` uses Swiftmailer to send emails and Twig blocks to render the message. - ``fos_user.mailer.noop`` is a mailer implementation which performs no operation, so no emails are sent. @@ -48,7 +49,7 @@ by service id: The ``fos_user.mailer.noop`` mailer service should be used in the case where you do not want the bundle to send emails and you do not want to - include the SwiftmailerBundle in your app. + include an actual mailer in your app. Configuring the Sender Email Address ------------------------------------ @@ -103,13 +104,12 @@ the password reset request email: Sending HTML mails ------------------ -The default mailer only supports sending plain text messages. If you want -to send multipart messages, the easiest solution is to use the TwigSwiftMailer -implementation instead. It expects your twig template to define 3 blocks: +The default mailers supports sending multipart messages. They expect your twig template +to define 3 blocks: - ``subject`` containing the email subject - ``body_text`` rendering the plain text version of the message -- ``body_html`` rendering the html mail +- ``body_html`` rendering the html mail (this block is optional) Here is how you can use it, you can use either of the two methods of referencing the email template below. @@ -120,7 +120,7 @@ of referencing the email template below. fos_user: # ... service: - mailer: fos_user.mailer.twig_swift + mailer: fos_user.mailer.twig_symfony resetting: email: template: email/password_resetting.email.twig @@ -166,10 +166,8 @@ You can view the default email templates at Using A Custom Mailer --------------------- -The default mailer service used by FOSUserBundle relies on the Swiftmailer -library to send mail. If you would like to use a different library to send -emails, want to send HTML emails or simply change the content of the email you -may do so by defining your own service. +If you would like to use a different library to send emails, want to send HTML emails +or simply change the content of the email you may do so by defining your own service. First you must create a new class which implements ``FOS\UserBundle\Mailer\MailerInterface`` which is listed below. diff --git a/composer.json b/composer.json index 56b157917..af249ac57 100644 --- a/composer.json +++ b/composer.json @@ -51,6 +51,8 @@ "friendsofphp/php-cs-fixer": "^3.0.2, !=3.5.0", "swiftmailer/swiftmailer": "^4.3 || ^5.0 || ^6.0", "symfony/console": "^4.4 || ^5.0 || ^6.0", + "symfony/mailer": "^4.4 || ^5.0 || ^6.0", + "symfony/mime": "^4.4 || ^5.0 || ^6.0", "symfony/phpunit-bridge": "^6.1", "symfony/yaml": "^4.4 || ^5.0 || ^6.0" },