Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • project/amazon_ses
  • issue/amazon_ses-3199493
  • issue/amazon_ses-3250870
  • issue/amazon_ses-2570147
  • issue/amazon_ses-3265114
  • issue/amazon_ses-3272810
  • issue/amazon_ses-3273373
  • issue/amazon_ses-3278501
  • issue/amazon_ses-3316452
  • issue/amazon_ses-3346554
  • issue/amazon_ses-3363913
  • issue/amazon_ses-3416368
  • issue/amazon_ses-3417090
  • issue/amazon_ses-3446166
  • issue/amazon_ses-3450865
  • issue/amazon_ses-3454004
  • issue/amazon_ses-3466077
  • issue/amazon_ses-3466095
  • issue/amazon_ses-3466096
  • issue/amazon_ses-3506259
  • issue/amazon_ses-3506500
  • issue/amazon_ses-3446425
  • issue/amazon_ses-3508814
  • issue/amazon_ses-3460056
24 results
Show changes
Commits on Source (14)
Showing
with 392 additions and 344 deletions
################
# GitLabCI template for Drupal projects.
#
# This template is designed to give any Contrib maintainer everything they need to test, without requiring modification.
# It is also designed to keep up to date with Core Development automatically through the use of include files that can be centrally maintained.
# As long as you include the project, ref and three files below, any future updates added by the Drupal Association will be used in your
# pipelines automatically. However, you can modify this template if you have additional needs for your project.
# The full documentation is on https://project.pages.drupalcode.org/gitlab_templates/
################
# For information on alternative values for 'ref' see https://project.pages.drupalcode.org/gitlab_templates/info/templates-version/
# To test a Drupal 7 project, change the first include filename from .main.yml to .main-d7.yml
include:
- project: $_GITLAB_TEMPLATES_REPO
ref: $_GITLAB_TEMPLATES_REF
file:
- "/includes/include.drupalci.main.yml"
- "/includes/include.drupalci.variables.yml"
- "/includes/include.drupalci.workflows.yml"
################
# Pipeline configuration variables are defined with default values and descriptions in the file
# https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml
# Uncomment the lines below if you want to override any of the variables. The following is just an example.
################
variables:
_CSPELL_WORDS: 'amazonses, davisben, filecontent, sesv'
# Amazon SES
This module is used to send email via Amazon SES, instead of using Drupal's
native mail system.
## Table of contents
- Requirements
- Installation
- Configuration
- Maintainers
## Requirements
This module requires the following modules:
- [Amazon Web Services](https://www.drupal.org/project/aws)
For full functionality, the following AWS permissions are required.
- ses:GetAccount
- ses:CreateEmailIdentity
- ses:DeleteEmailIdentity
- ses:GetEmailIdentity
- ses:ListEmailIdentities
## Installation
To ensure all dependencies are met, it is recommended to install this module
using composer.
`composer require drupal/amazon_ses`
## Configuration
- Configure Amazon Web Services with your AWS credentials.
- Verify your email address or domain at Administration » Configuration »
System » Amazon SES » Verified Identities
- Configure Drupal to use Amazon SES for sending email. The most common way to
do this is to use the [Mail System](https://www.drupal.org/project/mailsystem)
module.
## Maintainers
- Ben Davis - [davisben](https://drupal.org/u/davisben)
......@@ -2,7 +2,7 @@ name: 'Amazon SES'
description: 'Allows site email to be sent using Amazon SES.'
type: module
package: Mail
core_version_requirement: ^9.1 || ^10
core_version_requirement: ^9.1 || ^10 || ^11
dependencies:
- aws:aws
......@@ -71,3 +71,24 @@ function amazon_ses_update_30002() {
->clear('credentials')
->save(TRUE);
}
/**
* Set throttling multiplier to 1.
*/
function amazon_ses_update_30003() {
$config_factory = \Drupal::configFactory();
$config_factory->getEditable('amazon_ses.settings')
->set('multiplier', 1)
->save(TRUE);
}
/**
* Add default values for new configuration.
*/
function amazon_ses_update_30004() {
$config_factory = \Drupal::configFactory();
$config_factory->getEditable('amazon_ses.settings')
->set('from_name', $config_factory->get('system.site')->get('name'))
->set('override_from', FALSE)
->save(TRUE);
}
......@@ -4,7 +4,7 @@ amazon_ses.settings_form:
description: 'Configure the Amazon SES mail service.'
parent: system.admin_config_system
amazon_ses.identities:
title: 'Verfied identities'
title: 'Verified identities'
route_name: amazon_ses.identities
description: 'Amazon SES verified identities.'
parent: amazon_ses.settings_form
......
......@@ -5,10 +5,21 @@ amazon_ses.settings:
from_address:
type: email
label: 'From Address'
from_name:
type: string
label: 'From Name'
nullable: true
override_from:
type: boolean
label: 'Override From Email'
nullable: true
throttle:
type: boolean
label: 'Throttle'
nullable: true
multiplier:
type: integer
label: 'Throttling Multiplier'
queue:
type: boolean
label: 'Queue Emails'
......
parameters:
ignoreErrors:
- "#^Call to an undefined method Drupal\\\\amazon_ses\\\\Plugin\\\\Mail\\\\AmazonSes\\:\\:messenger\\(\\)\\.$#"
- "#^Call to an undefined method Drupal\\\\amazon_ses\\\\Plugin\\\\Mail\\\\AmazonSes\\:\\:t\\(\\)\\.$#"
- "#^Call to an undefined method Drupal\\\\amazon_ses\\\\Plugin\\\\QueueWorker\\\\AmazonSesMailQueue\\:\\:messenger\\(\\)\\.$#"
- "#^Call to an undefined method Drupal\\\\amazon_ses\\\\Plugin\\\\QueueWorker\\\\AmazonSesMailQueue\\:\\:t\\(\\)\\.$#"
includes:
- phpstan-baseline.neon
parameters:
level: 0
fileExtensions:
- php
- module
- inc
- install
- theme
- profile
reportUnmatchedIgnoredErrors: false
ignoreErrors:
# new static() is a best practice in Drupal, so we cannot fix that.
- "#^Unsafe usage of new static#"
......@@ -5,12 +5,12 @@ namespace Drupal\amazon_ses;
use Aws\Exception\CredentialsException;
use Aws\Result;
use Aws\SesV2\Exception\SesV2Exception;
use Drupal\amazon_ses\Event\MailSentEvent;
use Drupal\aws\AwsClientFactoryInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\amazon_ses\Event\MailSentEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mime\Email;
......@@ -28,60 +28,34 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
protected $client;
/**
* The logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The messenger service.
* The account quota.
*
* @var \Drupal\Core\Messenger\MessengerInterface
* @var array
*/
protected $messenger;
/**
* The event dispatcher service.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
protected $quota = [];
public function __construct(
protected AwsClientFactoryInterface $awsClientFactory,
protected LoggerChannelInterface $logger,
protected MessengerInterface $messenger,
protected EventDispatcherInterface $eventDispatcher,
protected ConfigFactoryInterface $configFactory,
) {
$this->client = $awsClientFactory->getClient('sesv2');
}
/**
* Constructs the service.
*
* @param \Drupal\aws\AwsClientFactoryInterface $aws_client_factory
* The AWS client factory.
* @param \Drupal\Core\Logger\LoggerChannelInterface $logger
* The logger factory service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The messenger service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* {@inheritdoc}
*/
public function __construct(AwsClientFactoryInterface $aws_client_factory, LoggerChannelInterface $logger, MessengerInterface $messenger, EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory) {
$this->client = $aws_client_factory->getClient('sesv2');
$this->logger = $logger;
$this->messenger = $messenger;
$this->eventDispatcher = $event_dispatcher;
$this->config = $config_factory->get('amazon_ses.settings');
public function getClient() {
return $this->client;
}
/**
* {@inheritdoc}
*/
public function getClient() {
return $this->client;
public function verifyClient() {
return (bool) $this->client;
}
/**
......@@ -100,7 +74,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
$event = new MailSentEvent($result['MessageId'], $email);
$this->eventDispatcher->dispatch($event, MailSentEvent::SENT);
$throttle = $this->config->get('throttle');
$throttle = $this->configFactory
->get('amazon_ses.settings')
->get('throttle');
if ($throttle) {
$sleep_time = $this->getSleepTime();
usleep($sleep_time);
......@@ -126,7 +103,16 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
*/
protected function getSleepTime() {
$results = $this->getSendQuota();
$per_second = ceil(1000000 / $results['MaxSendRate']);
// Avoid a division by zero if the quota call failed.
if (empty($results)) {
return 0;
}
$multiplier = $this->configFactory
->get('amazon_ses.settings')
->get('multiplier');
$per_second = ceil((1000000 * $multiplier) / $results['MaxSendRate']);
return intval($per_second);
}
......@@ -145,7 +131,6 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
$result = $this->client->getEmailIdentity([
'EmailIdentity' => $identity,
]);
$attributes = $result['DkimAttributes'];
$domain = $result['IdentityType'] == 'DOMAIN';
$item = [
......@@ -155,7 +140,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
];
if ($domain) {
$item['token'] = reset($attributes['Tokens']);
$item['dkim'] = $result['DkimAttributes']['SigningEnabled'] ? 'Enabled' : 'Disabled';
}
else {
$item['dkim'] = '';
}
$identities[] = $item;
......@@ -191,9 +179,17 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
* {@inheritdoc}
*/
public function getSendQuota() {
$result = $this->client->getAccount();
return array_map('number_format', $result['SendQuota']);
if (empty($this->quota)) {
try {
$result = $this->client->getAccount();
$this->quota = array_map('number_format', $result['SendQuota']);
}
catch (SesV2Exception $e) {
$this->logger->error($e->getMessage());
$this->messenger->addError($this->t('Unable to retrieve quota.'));
}
}
return $this->quota;
}
/**
......
......@@ -17,6 +17,14 @@ interface AmazonSesHandlerInterface {
*/
public function getClient();
/**
* Verify that the AWS SES client has been initialized.
*
* @return bool
* TRUE if the client is initialized, FALSE if not.
*/
public function verifyClient();
/**
* Send an email using the AWS SDK.
*
......
......@@ -2,24 +2,26 @@
namespace Drupal\amazon_ses\Controller;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Controller for Amazon SES routes.
*/
class AmazonSesController extends ControllerBase {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = new static();
$instance->setHandler($container->get('amazon_ses.handler'));
return $instance;
return new static(
$container->get('amazon_ses.handler')
);
}
/**
......@@ -29,33 +31,42 @@ class AmazonSesController extends ControllerBase {
* A render array to build the page.
*/
public function statistics() {
if (!$this->verifyClient()) {
return [];
$statistics = [];
if (!$this->handler->verifyClient()) {
$this->messenger()->addError($this->t('Unable to initialize the SES
client. More information may be available in the log.'));
return $statistics;
}
$quota = $this->handler->getSendQuota();
return [
'quota' => [
'#type' => 'details',
'#title' => $this->t('Daily sending limits'),
'#open' => TRUE,
'sending_quota' => [
'#markup' => $this->t('<strong>Quota:</strong> @max_send', [
'@max_send' => $quota['Max24HourSend'],
]) . '<br />',
],
'sent_mail' => [
'#markup' => $this->t('<strong>Sent:</strong> @sent_last', [
'@sent_last' => $quota['SentLast24Hours'],
]) . '<br />',
],
'send_rate' => [
'#markup' => $this->t('<strong>Maximum Send Rate:</strong> @send_rate
emails/second', ['@send_rate' => $quota['MaxSendRate']]),
$result = $this->handler->getSendQuota();
if (!empty($result)) {
$statistics = [
'quota' => [
'#type' => 'details',
'#title' => $this->t('Daily sending limits'),
'#open' => TRUE,
'sending_quota' => [
'#markup' => $this->t('<strong>Quota:</strong> @max_send', [
'@max_send' => $result['Max24HourSend'],
]) . '<br />',
],
'sent_mail' => [
'#markup' => $this->t('<strong>Sent:</strong> @sent_last', [
'@sent_last' => $result['SentLast24Hours'],
]) . '<br />',
],
'send_rate' => [
'#markup' => $this->t('<strong>Maximum Send Rate:</strong> @send_rate
emails/second', ['@send_rate' => $result['MaxSendRate']]),
],
],
],
];
];
}
return $statistics;
}
}
......@@ -12,32 +12,10 @@ class MailSentEvent extends Event {
const SENT = 'amazon_ses.mail_sent';
/**
* The ID of the sent message.
*
* @var string
*/
protected $messageId;
/**
* The Email object representing the sent message.
*
* @var \Symfony\Component\Mime\Email
*/
protected $email;
/**
* Constructs the MailSentEvent object.
*
* @param string $message_id
* The ID of the sent message.
* @param \Symfony\Component\Mime\Email $email
* The Email object representing the sent message.
*/
public function __construct(string $message_id, Email $email) {
$this->messageId = $message_id;
$this->email = $email;
}
public function __construct(
protected string $messageId,
protected Email $email,
) {}
/**
* Gets the message ID.
......
......@@ -2,24 +2,26 @@
namespace Drupal\amazon_ses\Form;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Amazon SES form base class.
*/
abstract class AmazonSesFormBase extends FormBase {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = new static();
$instance->setHandler($container->get('amazon_ses.handler'));
return $instance;
return new static(
$container->get('amazon_ses.handler'),
);
}
}
......@@ -20,6 +20,13 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
if (!$this->handler->verifyClient()) {
$this->messenger()->addError($this->t('Unable to initialize the SES
client. More information may be available in the log.'));
return $form;
}
$form['action'] = [
'#type' => 'select',
'#title' => $this->t('Action'),
......@@ -29,37 +36,20 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase {
$header = [
'identity' => $this->t('Identity'),
'type' => $this->t('Type'),
'record' => $this->t('Domain verification record'),
'dkim' => $this->t('DKIM Signing'),
'status' => $this->t('Status'),
];
$options = [];
if ($this->verifyClient()) {
$identities = $this->handler->getIdentities();
foreach ($identities as $identity) {
$options[$identity['identity']] = [
'identity' => $identity['identity'],
'type' => $identity['type'],
'status' => $identity['status'],
];
if ($identity['type'] == 'Domain') {
$record = "<strong>Name:</strong> _amazonses.{$identity['identity']}<br/>
<strong>Type:</strong> TXT<br/>
<strong>Value:</strong> {$identity['token']}";
$options[$identity['identity']]['record'] = [
'data' => [
'#markup' => $record,
],
];
}
else {
$options[$identity['identity']]['record'] = '';
}
}
$identities = $this->handler->getIdentities();
foreach ($identities as $identity) {
$options[$identity['identity']] = [
'identity' => $identity['identity'],
'type' => $identity['type'],
'dkim' => $identity['dkim'],
'status' => $identity['status'],
];
}
$form['identities'] = [
......
......@@ -39,6 +39,22 @@ class AmazonSesSettingsForm extends ConfigFormBase {
'#required' => TRUE,
];
$form['from_name'] = [
'#type' => 'textfield',
'#title' => $this->t('From Name'),
'#description' => $this->t('The email from name. Defaults to the site name.'),
'#default_value' => $config->get('from_name') ?? $this->config('system.site')->get('name'),
];
$form['override_from'] = [
'#type' => 'checkbox',
'#title' => $this->t('Override From Email'),
'#description' => $this->t('If set, all emails will be sent using the
configured name and address. Any value set by another module will be
overridden.'),
'#default_value' => $config->get('override_from'),
];
$form['throttle'] = [
'#type' => 'checkbox',
'#title' => $this->t('Throttle'),
......@@ -47,6 +63,20 @@ class AmazonSesSettingsForm extends ConfigFormBase {
'#default_value' => $config->get('throttle'),
];
$form['multiplier'] = [
'#type' => 'number',
'#title' => $this->t('Throttling Multiplier'),
'#description' => $this->t('It is useful to set a multiplier when you have
multiple PHP workers running in parallel. Setting this to the number of
workers ensures you stay under the rate limit.'),
'#default_value' => $config->get('multiplier') ?? 1,
'#states' => [
'visible' => [
':input[name="throttle"]' => ['checked' => TRUE],
],
],
];
$form['queue'] = [
'#type' => 'checkbox',
'#title' => $this->t('Queue emails'),
......@@ -63,7 +93,10 @@ class AmazonSesSettingsForm extends ConfigFormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('amazon_ses.settings')
->set('from_address', $form_state->getValue('from_address'))
->set('from_name', $form_state->getValue('from_name'))
->set('override_from', $form_state->getValue('override_from'))
->set('throttle', $form_state->getValue('throttle'))
->set('multiplier', (int) $form_state->getValue('multiplier'))
->set('queue', $form_state->getValue('queue'))
->save();
......
......@@ -2,8 +2,8 @@
namespace Drupal\amazon_ses\Form;
use Drupal\amazon_ses\Traits\MessageBuilderTrait;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\amazon_ses\MessageBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
......@@ -13,83 +13,24 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* Amazon SES test form.
*/
class AmazonSesTestForm extends AmazonSesFormBase {
use MessageBuilderTrait;
/**
* The Mail Manager service.
*
* @var \Drupal\Core\Mail\MailManagerInterface
*/
protected $mailManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
public function __construct(
protected AmazonSesHandlerInterface $handler,
protected AccountProxyInterface $currentUser,
protected MailManagerInterface $mailManager,
protected MessageBuilderInterface $messageBuilder,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
/** @var self $instance */
$instance = parent::create($container);
$instance
->setMessageBuilder($container->get('amazon_ses.message_builder'))
->setHandler($container->get('amazon_ses.handler'))
->setMailManager($container->get('plugin.manager.mail'))
->setCurrentUser($container->get('current_user'))
->setConfig($container->get('config.factory'));
return $instance;
}
/**
* Set the Mail Manager.
*
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
* The Mail Manger object.
*
* @return $this
*/
protected function setMailManager(MailManagerInterface $mail_manager) {
$this->mailManager = $mail_manager;
return $this;
}
/**
* Set the current user.
*
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
*
* @return $this
*/
protected function setCurrentUser(AccountProxyInterface $current_user) {
$this->currentUser = $current_user;
return $this;
}
/**
* Set the config object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory object.
*
* @return $this
*/
protected function setConfig(ConfigFactoryInterface $config_factory) {
$this->config = $config_factory->get('amazon_ses.settings');
return $this;
return new static(
$container->get('amazon_ses.handler'),
$container->get('current_user'),
$container->get('plugin.manager.mail'),
$container->get('amazon_ses.message_builder'),
);
}
/**
......@@ -136,10 +77,13 @@ class AmazonSesTestForm extends AmazonSesFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$to = $form_state->getValue('to');
$from = $this->config->get('from_address');
$body = $this->t('This is a test of the Amazon SES module. The module has
been configured successfully!');
$name = $this->config->get('from_name');
$address = $this->config->get('from_address');
$from = "$name <$address>";
$message = [
'to' => $to,
'from' => $from,
......
......@@ -27,8 +27,8 @@ class AmazonSesVerifyIdentityForm extends AmazonSesFormBase {
$form['info'] = [
'#type' => 'markup',
'#markup' => '<p>' . $this->t('Amazon SES requires verified identies to
send mail. For more information about verifing identities, see the
'#markup' => '<p>' . $this->t('Amazon SES requires verified identities to
send mail. For more information about verifying identities, see the
@link.', ['@link' => $link->toString()]) . '</p>',
];
......
......@@ -6,6 +6,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
......@@ -15,68 +16,24 @@ use Symfony\Component\Mime\MimeTypeGuesserInterface;
class MessageBuilder implements MessageBuilderInterface {
use StringTranslationTrait;
/**
* The logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The MIME type guesser service.
*
* @var \Symfony\Component\Mime\MimeTypeGuesserInterface
*/
protected $mimeTypeGuesser;
/**
* Constructs the message builder service.
*
* @param \Drupal\Core\Logger\LoggerChannelInterface $logger
* The logger factory service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Symfony\Component\Mime\MimeTypeGuesserInterface $mime_type_guesser
* The MIME type guesser service.
*/
public function __construct(LoggerChannelInterface $logger, ConfigFactoryInterface $config_factory, FileSystemInterface $file_system, MimeTypeGuesserInterface $mime_type_guesser) {
$this->logger = $logger;
$this->config = $config_factory->get('amazon_ses.settings');
$this->fileSystem = $file_system;
$this->mimeTypeGuesser = $mime_type_guesser;
}
public function __construct(
protected LoggerChannelInterface $logger,
protected ConfigFactoryInterface $configFactory,
protected FileSystemInterface $fileSystem,
protected MimeTypeGuesserInterface $mimeTypeGuesser,
) {}
/**
* {@inheritdoc}
*/
public function buildMessage(array $message) {
if (isset($message['from'])) {
$from = $message['from'];
}
else {
$from = $this->config->get('from_address');
}
$to = preg_split('/[,;]/', $message['to']);
$from = $this->parseAddress($message['from']);
$from_address = new Address($from['address'], $from['name']);
$email = (new Email())
->from($from)
->from($from_address)
->to(...$to)
->subject($message['subject']);
......@@ -154,6 +111,30 @@ class MessageBuilder implements MessageBuilderInterface {
return $email;
}
/**
* Parse an email address into a separate name and email.
*
* @param string $address
* The email address to parse.
*
* @return array
* An array of email address components.
*/
protected function parseAddress(string $address) {
if (preg_match('/^"?(?<name>.*?)"?\s\<(?<address>.*)\>$/', $address, $matches)) {
return [
'name' => $matches['name'],
'address' => $matches['address'],
];
}
else {
return [
'name' => '',
'address' => $address,
];
}
}
/**
* Determine the content type of a message.
*
......
......@@ -2,12 +2,12 @@
namespace Drupal\amazon_ses\Plugin\Mail;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Drupal\amazon_ses\Traits\MessageBuilderTrait;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\amazon_ses\MessageBuilderInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Mail\MailInterface;
use Drupal\Core\Mail\Plugin\Mail\PhpMail;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -21,77 +21,50 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* )
*/
class AmazonSes extends PhpMail implements MailInterface, ContainerFactoryPluginInterface {
use HandlerTrait;
use MessageBuilderTrait;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* The queue object.
*
* @var \Drupal\Core\Queue\QueueInterface
*/
protected $queue;
public function __construct(
protected ConfigFactoryInterface $configFactory,
protected AmazonSesHandlerInterface $handler,
protected MessageBuilderInterface $messageBuilder,
protected QueueFactory $queueFactory,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = new static(
$container,
$configuration,
$plugin_id,
$plugin_definition
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$container->get('config.factory'),
$container->get('amazon_ses.handler'),
$container->get('amazon_ses.message_builder'),
$container->get('queue'),
);
$instance
->setMessageBuilder($container->get('amazon_ses.message_builder'))
->setHandler($container->get('amazon_ses.handler'))
->setConfig($container->get('config.factory'))
->setQueue($container->get('queue'));
return $instance;
}
/**
* Set the config object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory object.
*
* @return $this
*/
protected function setConfig(ConfigFactoryInterface $config_factory) {
$this->config = $config_factory->get('amazon_ses.settings');
return $this;
}
/**
* Set the queue object.
*
* @param \Drupal\Core\Queue\QueueFactory $queue_factory
* The queue factory service.
*
* @return $this
*/
protected function setQueue(QueueFactory $queue_factory) {
$this->queue = $queue_factory->get('amazon_ses_mail_queue');
return $this;
}
/**
* {@inheritdoc}
*/
public function mail(array $message) {
if ($this->config->get('override_from') || !isset($message['from'])) {
$name = $this->config->get('from_name');
$address = $this->config->get('from_address');
$message['from'] = "$name <$address>";
}
$email = $this->messageBuilder->buildMessage($message);
$queue = $this->queueFactory->get('amazon_ses_mail_queue');
$queue_enabled = $this->configFactory
->get('amazon_ses.settings')
->get('queue');
if ($this->config->get('queue')) {
$result = $this->queue->createItem($email);
if ($queue_enabled) {
$result = $queue->createItem($email);
return (bool) $result;
}
......
......@@ -2,7 +2,7 @@
namespace Drupal\amazon_ses\Plugin\QueueWorker;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -17,21 +17,23 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* )
*/
class AmazonSesMailQueue extends QueueWorkerBase implements ContainerFactoryPluginInterface {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = new static(
$configuration,
$plugin_id,
$plugin_definition
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$container->get('amazon_ses.handler'),
);
$instance->setHandler($container->get('amazon_ses.handler'));
return $instance;
}
/**
......