diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..20b88f8 --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,17 @@ +root('wmc_swiftmailer_twig'); + + return $treeBuilder; + } +} diff --git a/DependencyInjection/WMCSwiftmailerTwigExtension.php b/DependencyInjection/WMCSwiftmailerTwigExtension.php new file mode 100644 index 0000000..9fe4e79 --- /dev/null +++ b/DependencyInjection/WMCSwiftmailerTwigExtension.php @@ -0,0 +1,31 @@ +load('services.xml'); + } + + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasExtension('fos_user')) { + $container->removeDefinition('wmc.swiftmailer_twig.fosub'); + } + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fd385c6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 WeMakeCustom + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Mailer/FOSUserMailer.php b/Mailer/FOSUserMailer.php new file mode 100644 index 0000000..da4b221 --- /dev/null +++ b/Mailer/FOSUserMailer.php @@ -0,0 +1,63 @@ +mailer = $mailer; + $this->router = $router; + $this->helper = $helper; + $this->parameters = $parameters; + } + + public function sendConfirmationEmailMessage(UserInterface $user) + { + $template = $this->parameters['template']['confirmation']; + $url = $this->router->generate('fos_user_registration_confirm', array('token' => $user->getConfirmationToken()), true); + $context = array( + 'user' => $user, + 'confirmationUrl' => $url + ); + + $this->sendMessage($template, $context, $this->parameters['from_email']['confirmation'], $user->getEmail()); + } + + public function sendResettingEmailMessage(UserInterface $user) + { + $template = $this->parameters['template']['resetting']; + $url = $this->router->generate('fos_user_resetting_reset', array('token' => $user->getConfirmationToken()), true); + $context = array( + 'user' => $user, + 'confirmationUrl' => $url + ); + $this->sendMessage($template, $context, $this->parameters['from_email']['resetting'], $user->getEmail()); + } + + /** + * @param string $templateName + * @param array $context + */ + protected function sendMessage($templateName, $context, $fromEmail, $toEmail) + { + $message = Swift_Message::newInstance() + ->setFrom($fromEmail) + ->setTo($toEmail); + + $this->helper->populateMessage($message, $templateName, $context); + $this->mailer->send($message); + } +} diff --git a/Mailer/TwigSwiftHelper.php b/Mailer/TwigSwiftHelper.php new file mode 100644 index 0000000..8276dae --- /dev/null +++ b/Mailer/TwigSwiftHelper.php @@ -0,0 +1,112 @@ +twig = $twig; + $this->web_directory = $web_directory; + } + + protected function extractMessageContent($template_name, $context, $parts = array('subject', 'content' => 'body_text')) + { + $template = $this->twig->loadTemplate($template_name); + $context = $this->twig->mergeGlobals($context); + + $data = array(); + + foreach ($parts as $k => $part) { + $key = is_numeric($k) ? $part : $k; + $data[$key] = $template->renderBlock($part, $context); + } + + return $data; + } + + /** + * Replace all src of img.inline-image with an embedded image + * + * @param Swift_Message $message + */ + protected function inlineImages(Swift_Message $message) + { + $html = $message->getBody(); + + $crawler = new Crawler(); + $crawler->addHtmlContent($html); + + $imgs = array(); + $replaces = array(); + + foreach ($crawler->filterXPath("//img[contains(concat(' ',normalize-space(@class), ' '), ' inline-image ')]") as $img) { + $normalized_src = $src = $img->getAttribute('src'); + + if (isset($replaces['src="'.$src.'"'])) { + continue; + } + + // if starting with one slash, use local file + if (preg_match('#^/[^/]#', $normalized_src)) { + $normalized_src = $this->web_directory . parse_url($src, PHP_URL_PATH); + } + + if (!isset($imgs[$normalized_src])) { + $swift_image = Swift_Image::fromPath($normalized_src); + $imgs[$normalized_src] = $message->embed($swift_image); + } + + $replaces['src=\''.$src.'\''] = 'src="'.$imgs[$normalized_src].'"'; + $replaces['src="' .$src.'"' ] = 'src="'.$imgs[$normalized_src].'"'; + } + + if (count($replaces)) { + $html = str_replace(array_keys($replaces), array_values($replaces), $html); + $message->setBody($html); + } + } + + public function populateMessage(Swift_Message $message, $template_name, $context) + { + $data = $this->extractMessageContent($template_name, $context, array('subject', 'text' => 'body_text', 'html' => 'body_html')); + + if (empty($data['subject']) || empty($data['text'])) { + throw new \InvalidArgumentException('The mail template must have (non-empty) subject ('.$data['subject'].') and body_text ('.$data['text'].') blocks'); + } + + $message->setSubject($data['subject']); + + if (!empty($data['html'])) { + $message->setBody($data['html'], 'text/html') + ->addPart($data['text'], 'text/plain'); + + $this->inlineImages($message); + } else { + $message->setBody($data['text']); + } + + return $message; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c509a76 --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# WMC SwiftMailer Twig bridge + +This bundle provides an easy way to create email templates using Twig for the +SwiftMailer library. + +This helper is inspired from +[FOSUB TwigSwiftMailer](https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Mailer/TwigSwiftMailer.php). + +If you're using FOS User Bundle, we also provide a mailer service drop-in +replacement to support our additional features. + +## Installation + +### With Symfony +The best way to install this extension is through composer: + +First, require the bundle: + +```sh +composer require wemakecustom/swiftmailer-twig-bundle "^1.0" +``` + +Second, enable it: + +```php + 'Jonh Smith', 'email' => 'recipient@example.com']; + +$message = $mailer->createMessage()->setTo(['recipient@example.com' => 'John Smith']); +$swiftMailerTemplateHelper->populateMessage($message, 'AppBundle:Mail:my_email.mail.twig', $data); +$mailer->send($message); +``` + +```twig +{# my_email.mail.twig #} + +{% block subject -%} + My email Subject +{%- endblock %} + +{% block body_text %} + Hello {{ recipient.name }}, + + {# ... Awesome plain text email ... #} + + Best Regards, + Keep being awesome! +{% endblock %} + +{% block body_html %} +
+ {# ... Awesome HTML email ... #} +
+ +
+ Best Regards,
+ Keep being awesome!
+