diff --git a/README.txt b/README.txt index 35b10f8..88e4954 100644 --- a/README.txt +++ b/README.txt @@ -58,6 +58,8 @@ e-mails should reflect the look and feel of the website it was sent from. The Swift Mailer modules delegates theming to the Drupal theming system. This means that you can theme e-mails the same way you theme other content. The theme hook you need to use when interacting with the theming system is 'swiftmailer'. +Add custom css to the template with the preprocess hook and convert them to +inline-styles with the corresponding option under the message settings. 2.1 Theme File diff --git a/composer.json b/composer.json index 2bcc238..8dffc29 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "type": "drupal-module", "license": "GPL-2.0+", "require": { + "drupal/mailsystem": "^4", "swiftmailer/swiftmailer": "~5.4.5", - "html2text/html2text": "~4.0.1" + "html2text/html2text": "~4.0.1", + "tijsverkoyen/css-to-inline-styles": "^2.2" } } diff --git a/config/install/swiftmailer.message.yml b/config/install/swiftmailer.message.yml index 20a47be..43c2f3f 100644 --- a/config/install/swiftmailer.message.yml +++ b/config/install/swiftmailer.message.yml @@ -1,5 +1,6 @@ format: text/plain filter_format: plain_text respect_format: true +css_inliner: true convert_mode: false character_set: UTF-8 diff --git a/config/schema/swiftmailer.schema.yml b/config/schema/swiftmailer.schema.yml index 365c792..c02d6f4 100644 --- a/config/schema/swiftmailer.schema.yml +++ b/config/schema/swiftmailer.schema.yml @@ -11,6 +11,9 @@ swiftmailer.message: respect_format: type: boolean label: 'Respect format' + css_inliner: + type: boolean + label: 'Inline CSS' convert_mode: type: boolean label: 'Respect format' diff --git a/src/Form/MessagesForm.php b/src/Form/MessagesForm.php index 2a3fb9c..522700f 100644 --- a/src/Form/MessagesForm.php +++ b/src/Form/MessagesForm.php @@ -64,6 +64,13 @@ class MessagesForm extends ConfigFormBase { as plain text as this is the content type Drupal by default will apply to all e-mails.'), ]; + $form['format']['css_inliner'] = [ + '#type' => 'checkbox', + '#title' => t('Inline CSS'), + '#default_value' => $config->get('css_inliner'), + '#description' => t('Convert CSS to inline styles to assure solid rendering in all email clients.'), + ]; + $form['convert'] = [ '#type' => 'fieldset', '#title' => $this->t('Plain Text Version'), @@ -107,6 +114,7 @@ class MessagesForm extends ConfigFormBase { $config = $this->config('swiftmailer.message'); $config->set('format', $form_state->getValue(['format', 'type'])); $config->set('respect_format', $form_state->getValue(['format', 'respect'])); + $config->set('css_inliner', $form_state->getValue(['format', 'css_inliner'])); $config->set('convert_mode', $form_state->getValue(['convert', 'mode'])); $config->set('character_set', $form_state->getValue(['character_set', 'type'])); diff --git a/src/Plugin/Mail/SwiftMailer.php b/src/Plugin/Mail/SwiftMailer.php index 3e89311..4214787 100644 --- a/src/Plugin/Mail/SwiftMailer.php +++ b/src/Plugin/Mail/SwiftMailer.php @@ -6,14 +6,16 @@ use Drupal\Component\Render\MarkupInterface; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Asset\AssetResolverInterface; +use Drupal\Core\Asset\AttachedAssets; use Drupal\Core\Config\ImmutableConfig; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\File\FileSystem; use Drupal\Core\Mail\MailInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\Markup; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Site\Settings; -use Drupal\key\KeyInterface; use Drupal\swiftmailer\Utility\Conversion; use Exception; use Html2Text\Html2Text; @@ -30,6 +32,10 @@ use Swift_SendmailTransport; use Swift_SmtpTransport; use Swift_SpoolTransport; use Symfony\Component\DependencyInjection\ContainerInterface; +use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; +use Drupal\Core\Asset\LibraryDiscoveryParser; +use Drupal\Core\Mail\MailManagerInterface; +use Drupal\mailsystem\MailsystemManager; /** * Provides a 'Swift Mailer' plugin to send emails. @@ -43,40 +49,93 @@ use Symfony\Component\DependencyInjection\ContainerInterface; class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { /** + * The configuration. + * * @var array */ protected $config; /** + * The logger. + * * @var \Psr\Log\LoggerInterface */ protected $logger; /** + * The renderer. + * * @var \Drupal\Core\Render\RendererInterface */ protected $renderer; /** + * The module handler. + * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ protected $moduleHandler; /** + * The mail manager. + * + * @var \Drupal\Core\Mail\MailManagerInterface + */ + protected $mailManager; + + /** + * The asset library discovery parser. + * + * @var \Drupal\Core\Asset\LibraryDiscoveryParser + */ + protected $libraryDiscoverParser; + + /** + * The asset resolver. + * + * @var \Drupal\Core\Asset\AssetResolverInterface + */ + protected $assetResolver; + + /** + * The filesystem service. + * + * @var \Drupal\Core\File\FileSystem + */ + protected $fileSystem; + + /** * SwiftMailer constructor. * * @param \Drupal\Core\Config\ImmutableConfig $transport + * The transport config. * @param \Drupal\Core\Config\ImmutableConfig $message + * The message config. * @param \Psr\Log\LoggerInterface $logger + * The loger. * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager + * The mail manager. + * @param \Drupal\Core\Asset\LibraryDiscoveryParser $library_discover_parser + * The asset library discovery parser. + * @param \Drupal\Core\Asset\AssetResolverInterface $asset_resolver + * The asset resolver. + * @param \Drupal\Core\File\FileSystem $file_system + * The filesystem service. */ - public function __construct(ImmutableConfig $transport, ImmutableConfig $message, LoggerInterface $logger, RendererInterface $renderer, ModuleHandlerInterface $module_handler) { + public function __construct(ImmutableConfig $transport, ImmutableConfig $message, LoggerInterface $logger, RendererInterface $renderer, ModuleHandlerInterface $module_handler, MailManagerInterface $mail_manager, LibraryDiscoveryParser $library_discover_parser, AssetResolverInterface $asset_resolver, FileSystem $file_system) { $this->config['transport'] = $transport->get(); $this->config['message'] = $message->get(); $this->logger = $logger; $this->renderer = $renderer; $this->moduleHandler = $module_handler; + $this->mailManager = $mail_manager; + $this->libraryDiscoverParser = $library_discover_parser; + $this->assetResolver = $asset_resolver; + $this->fileSystem = $file_system; } /** @@ -88,15 +147,17 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { $container->get('config.factory')->get('swiftmailer.message'), $container->get('logger.factory')->get('swiftmailer'), $container->get('renderer'), - $container->get('module_handler') + $container->get('module_handler'), + $container->get('plugin.manager.mail'), + $container->get('library.discovery.parser'), + $container->get('asset.resolver'), + $container->get('file_system') ); } /** * Formats a message composed by drupal_mail(). * - * @see http://api.drupal.org/api/drupal/includes--mail.inc/interface/MailSystemInterface/7 - * * @param array $message * A message array holding all relevant details for the message. * @@ -111,9 +172,21 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { // Theme message if format is set to be HTML. if ($applicable_format == SWIFTMAILER_FORMAT_HTML) { + // Attempt to use the mail theme defined in MailSystem. + if ($this->mailManager instanceof MailsystemManager) { + $mail_theme = $this->mailManager->getMailTheme(); + } + // Default to the active theme if MailsystemManager isn't used. + else { + $mail_theme = $this->themeManager->getActiveTheme()->getName(); + } + $render = [ '#theme' => isset($message['params']['theme']) ? $message['params']['theme'] : 'swiftmailer', '#message' => $message, + '#attached' => [ + 'library' => ["$mail_theme/swiftmailer"], + ], ]; $message['body'] = $this->renderer->renderPlain($render); @@ -122,6 +195,17 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { $converter = new Html2Text($message['body']); $message['plain'] = $converter->getText(); } + + if ($this->config['message']['css_inliner']) { + // Process CSS from libraries. + $assets = AttachedAssets::createFromRenderArray($render); + $css = ''; + foreach ($this->assetResolver->getCssAssets($assets, FALSE) as $css_asset) { + $css .= file_get_contents($this->fileSystem->realpath($css_asset['data'])); + } + + $message['body'] = (new CssToInlineStyles())->convert($message['body'], $css); + } } // Process any images specified by 'image:' which are to be added later @@ -159,8 +243,6 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { /** * Sends a message composed by drupal_mail(). * - * @see http://api.drupal.org/api/drupal/includes--mail.inc/interface/MailSystemInterface/7 - * * @param array $message * A message array holding all relevant details for the message. * @@ -302,7 +384,7 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { $host = $this->config['transport']['smtp_host']; $port = $this->config['transport']['smtp_port']; $encryption = $this->config['transport']['smtp_encryption']; - $provider = $this->config['transport']['smtp_credential_provider']; + $provider = $this->config['transport']['smtp_credential_provider']; $username = NULL; $password = NULL; if ($provider === 'swiftmailer') { @@ -385,6 +467,7 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { throw new \LogicException('The transport method is undefined.'); } + /** @var \Swift_Mailer $mailer */ $mailer = Swift_Mailer::newInstance($transport); // Allows other modules to customize the message. @@ -392,7 +475,6 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { // Send the message. Conversion::swiftmailer_filter_message($m); - /** @var Swift_Mailer $mailer */ return (bool) $mailer->send($m); } catch (Exception $e) { @@ -539,7 +621,7 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { * @return string * A string being the applicable format. */ - private function getApplicableFormat($message) { + private function getApplicableFormat(array $message) { // Get the configured default format. $default_format = $this->config['message']['format']; @@ -579,7 +661,7 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { * @return string * A string being the applicable charset. */ - private function getApplicableCharset($message) { + private function getApplicableCharset(array $message) { // Get the configured default format. $default_charset = $this->config['message']['character_set']; @@ -620,6 +702,7 @@ class SwiftMailer implements MailInterface, ContainerFactoryPluginInterface { * The message. * * @return array + * The render array for mesage body. */ public function massageMessageBody(array $message) { // Get default mail line endings and merge all lines in the e-mail body diff --git a/swiftmailer.info.yml b/swiftmailer.info.yml index b6c14b0..6e504e2 100644 --- a/swiftmailer.info.yml +++ b/swiftmailer.info.yml @@ -5,4 +5,4 @@ core: 8.x package: Mail configure: swiftmailer.transport_settings dependencies: - - mailsystem + - mailsystem:mailsystem diff --git a/swiftmailer.install b/swiftmailer.install index 2e48116..0a9ccd3 100644 --- a/swiftmailer.install +++ b/swiftmailer.install @@ -56,3 +56,12 @@ function swiftmailer_update_8102() { $config->save(); } } + +/** + * Initialise new setting for inline CSS. + */ +function swiftmailer_update_8103() { + Drupal::configFactory()->getEditable('swiftmailer.message') + ->set('css_inliner', true) + ->save(); +}