-
-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make configured mailer transports selectable in the back end (see #1830)
Description ----------- | Q | A | -----------------| --- | Fixed issues | Fixes #1613 | Docs PR or issue | contao/docs#465 _Note:_ this PR depends and is based on #1829. It will be rebased, once #1829 got merged. This is the alternative version of #1469, based on the [Symfony Mailer Component](https://symfony.com/doc/4.4/mailer.html) instead of the Swiftmailer Bundle. It makes the configured `framework.mailer.transports` selectable in the back end. Example: ```yml # config/config.yml framework: mailer: transports: app: smtps://[email protected]:[email protected]:465 page: smtps://[email protected]:[email protected]:465 forms: smtps://[email protected]:[email protected]:465 newsletter: smtps://[email protected]:[email protected]:465 contao: mailer: transports: page: ~ forms: ~ newsletter: ~ ``` <img src="https://user-images.githubusercontent.com/4970961/84607561-e3a06d00-aea5-11ea-9f89-daac495b8a85.png" alt="mailer_transport_01" width="576"> _Note:_ only the transports configured in `contao.mailer.transports` will be available for selection. You can also provide translations: ```yml # translations/mailer_transports.en.yml page: 'Page' forms: 'Forms' newsletter: 'Newsletters' ``` <img src="https://user-images.githubusercontent.com/4970961/84607577-f7e46a00-aea5-11ea-9cd1-59271843609b.png" alt="mailer_transport_02" width="576"> And you can override the `From` address for each transport in the Contao configuration: ```yml contao: mailer: transports: page: from: Contao Page <[email protected]> forms: from: Contao Forms <[email protected]> newsletter: from: Contao Newsletter <[email protected]> ``` <img src="https://user-images.githubusercontent.com/4970961/84607478-670d8e80-aea5-11ea-9bdd-1cb2b090fd5a.png" alt="mailer_transport_03" width="576"> _Note:_ only the transports configured in `contao.mailer.transports` will be available for selection. Using the Symfony Mailer Component for this seems more elegant to me, since it requires no change whatsoever in the `\Contao\Email` class ([see the comparison](fritzmg/contao@feature/symfony-mailer...feature/available-symfony-mailers)). With the Symfony Mailer Component, the transport to be used is simply chosen with an `X-Transport` header in the email message itself. This PR decorates the `mailer` service and automatically sets an `X-Transport` header based on the website root settings - and automatically overrides the `From` address based on the chosen transport. Commits ------- fd3da56a switch to symfony/mailer 6c21d672 change MAILER_DSN back to MAILER_URL eeaea321 dynamically add default mailer 6ad300c4 add \Swift_Mailer fallback 71a5553a use MAILER_DSN with MAILER_URL fallback cc348243 remove Email deprecation 8c2480a7 increase symfony/mailer dependency 39267663 switch to symfony/mailer 3fd2799c dynamically add default mailer 43aa11c3 provide mailer transport selection and from override c38e8646 add missing model property 8cee6db5 fix AvailableTransportsTest 08c9b201 fix code style a8d9b591 fix yml style ca478014 only show configured mailer transports within Contao e4f774f6 change translation domain 1722e810 restore previous version requirement 21b55d24 code style fix 1abfc387 rename mailer_transport DCA field to mailerTransport af8563c3 use Annotations for mailerTransport options callback 058da895 implement some early outs 4585e9dd add missing model methods a2da52c8 fix code style f8020035 Merge remote-tracking branch 'origin/master' into feature/available-symfony-mailers a5d7c747 add more unit tests 94203d73 use assertSame 38c3de95 improve testAnnotatedCallbacks test 3ca8e024 Rearrange the form fields in the back end 33f38c43 add missing methods b7159fe0 change wording to 'mailer transport' ba4f4232 merge with master b8ffb367 Apply suggestions from code review Co-authored-by: Leo Feyer <[email protected]>
- Loading branch information
Showing
20 changed files
with
787 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
src/DependencyInjection/Compiler/AddAvailableTransportsPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* (c) Leo Feyer | ||
* | ||
* @license LGPL-3.0-or-later | ||
*/ | ||
|
||
namespace Contao\CoreBundle\DependencyInjection\Compiler; | ||
|
||
use Contao\CoreBundle\Mailer\AvailableTransports; | ||
use Contao\CoreBundle\Mailer\TransportConfig; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
|
||
class AddAvailableTransportsPass implements CompilerPassInterface | ||
{ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
if (!$container->has(AvailableTransports::class)) { | ||
return; | ||
} | ||
|
||
$contaoConfig = array_merge(...$container->getExtensionConfig('contao')); | ||
$contaoMailerConfig = $contaoConfig['mailer']['transports'] ?? []; | ||
|
||
if (empty($contaoMailerConfig)) { | ||
return; | ||
} | ||
|
||
$frameworkConfig = $container->getExtensionConfig('framework'); | ||
$definition = $container->findDefinition(AvailableTransports::class); | ||
|
||
foreach ($frameworkConfig as $v) { | ||
if (!isset($v['mailer']['transports'])) { | ||
continue; | ||
} | ||
|
||
foreach (array_keys($v['mailer']['transports']) as $transportName) { | ||
if (!\array_key_exists($transportName, $contaoMailerConfig)) { | ||
continue; | ||
} | ||
|
||
$from = $contaoMailerConfig[$transportName]['from'] ?? null; | ||
|
||
$definition->addMethodCall( | ||
'addTransport', | ||
[new Definition(TransportConfig::class, [$transportName, $from])] | ||
); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* (c) Leo Feyer | ||
* | ||
* @license LGPL-3.0-or-later | ||
*/ | ||
|
||
namespace Contao\CoreBundle\Mailer; | ||
|
||
use Contao\CoreBundle\ServiceAnnotation\Callback; | ||
use Symfony\Contracts\Translation\TranslatorInterface; | ||
use Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface; | ||
|
||
class AvailableTransports implements ServiceAnnotationInterface | ||
{ | ||
/** | ||
* @var array<TransportConfig> | ||
*/ | ||
private $transports = []; | ||
|
||
/** | ||
* @var TranslatorInterface | ||
*/ | ||
private $translator; | ||
|
||
public function __construct(?TranslatorInterface $translator = null) | ||
{ | ||
$this->translator = $translator; | ||
} | ||
|
||
public function addTransport(TransportConfig $transportConfig): void | ||
{ | ||
$this->transports[$transportConfig->getName()] = $transportConfig; | ||
} | ||
|
||
/** | ||
* @return array<string, TransportConfig> | ||
*/ | ||
public function getTransports(): array | ||
{ | ||
return $this->transports; | ||
} | ||
|
||
/** | ||
* Returns the available transports as options suitable for widgets. | ||
* | ||
* @return array<string, string> | ||
* | ||
* @Callback(table="tl_page", target="fields.mailerTransport.options") | ||
* @Callback(table="tl_form", target="fields.mailerTransport.options") | ||
*/ | ||
public function getTransportOptions(): array | ||
{ | ||
$options = []; | ||
|
||
foreach ($this->transports as $name => $config) { | ||
$label = null !== $this->translator ? $this->translator->trans($name, [], 'mailer_transports') : $name; | ||
|
||
if (null !== ($from = $config->getFrom())) { | ||
$label .= ' ('.$from.')'; | ||
} | ||
|
||
$options[$name] = htmlentities($label); | ||
} | ||
|
||
return $options; | ||
} | ||
|
||
/** | ||
* Returns a specific transport configuration by the transport name. | ||
*/ | ||
public function getTransport(string $name): ?TransportConfig | ||
{ | ||
return $this->transports[$name] ?? null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* (c) Leo Feyer | ||
* | ||
* @license LGPL-3.0-or-later | ||
*/ | ||
|
||
namespace Contao\CoreBundle\Mailer; | ||
|
||
use Contao\PageModel; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Component\Mailer\Envelope; | ||
use Symfony\Component\Mailer\MailerInterface; | ||
use Symfony\Component\Mime\Email; | ||
use Symfony\Component\Mime\Message; | ||
use Symfony\Component\Mime\RawMessage; | ||
|
||
final class ContaoMailer implements MailerInterface | ||
{ | ||
/** | ||
* @var MailerInterface | ||
*/ | ||
private $mailer; | ||
|
||
/** | ||
* @var AvailableTransports | ||
*/ | ||
private $transports; | ||
|
||
/** | ||
* @var RequestStack | ||
*/ | ||
private $requestStack; | ||
|
||
public function __construct(MailerInterface $mailer, AvailableTransports $transports, RequestStack $requestStack) | ||
{ | ||
$this->mailer = $mailer; | ||
$this->transports = $transports; | ||
$this->requestStack = $requestStack; | ||
} | ||
|
||
public function send(RawMessage $message, ?Envelope $envelope = null): void | ||
{ | ||
if ($message instanceof Message) { | ||
$this->setTransport($message); | ||
} | ||
|
||
if ($message instanceof Email) { | ||
$this->setFrom($message); | ||
} | ||
|
||
$this->mailer->send($message, $envelope); | ||
} | ||
|
||
/** | ||
* Sets the transport defined in the website root. | ||
*/ | ||
private function setTransport(Message $message): void | ||
{ | ||
if ($message->getHeaders()->has('X-Transport')) { | ||
return; | ||
} | ||
|
||
$request = $this->requestStack->getCurrentRequest(); | ||
|
||
if (null === $request) { | ||
return; | ||
} | ||
|
||
$attributes = $this->requestStack->getCurrentRequest()->attributes; | ||
|
||
if (!$attributes->has('pageModel')) { | ||
return; | ||
} | ||
|
||
$page = $attributes->get('pageModel'); | ||
|
||
if (!$page instanceof PageModel) { | ||
return; | ||
} | ||
|
||
/** @var PageModel $page */ | ||
$page->loadDetails(); | ||
|
||
if (empty($page->mailerTransport) || null === $this->transports->getTransport($page->mailerTransport)) { | ||
return; | ||
} | ||
|
||
$message->getHeaders()->addTextHeader('X-Transport', $page->mailerTransport); | ||
} | ||
|
||
/** | ||
* Overrides the from address according to the transport. | ||
*/ | ||
private function setFrom(Email $message): void | ||
{ | ||
if (!$message->getHeaders()->has('X-Transport')) { | ||
return; | ||
} | ||
|
||
$transportName = $message->getHeaders()->get('X-Transport')->getBodyAsString(); | ||
$transport = $this->transports->getTransport($transportName); | ||
|
||
if (null === $transport) { | ||
return; | ||
} | ||
|
||
$from = $transport->getFrom(); | ||
|
||
if (null === $from) { | ||
return; | ||
} | ||
|
||
$message->from($from); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of Contao. | ||
* | ||
* (c) Leo Feyer | ||
* | ||
* @license LGPL-3.0-or-later | ||
*/ | ||
|
||
namespace Contao\CoreBundle\Mailer; | ||
|
||
final class TransportConfig | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private $name; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $from; | ||
|
||
public function __construct(string $name, string $from = null) | ||
{ | ||
$this->name = $name; | ||
$this->from = $from; | ||
} | ||
|
||
public function getName(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function getFrom(): ?string | ||
{ | ||
return $this->from; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.