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' ...@@ -2,7 +2,7 @@ name: 'Amazon SES'
description: 'Allows site email to be sent using Amazon SES.' description: 'Allows site email to be sent using Amazon SES.'
type: module type: module
package: Mail package: Mail
core_version_requirement: ^9.1 || ^10 core_version_requirement: ^9.1 || ^10 || ^11
dependencies: dependencies:
- aws:aws - aws:aws
...@@ -71,3 +71,24 @@ function amazon_ses_update_30002() { ...@@ -71,3 +71,24 @@ function amazon_ses_update_30002() {
->clear('credentials') ->clear('credentials')
->save(TRUE); ->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: ...@@ -4,7 +4,7 @@ amazon_ses.settings_form:
description: 'Configure the Amazon SES mail service.' description: 'Configure the Amazon SES mail service.'
parent: system.admin_config_system parent: system.admin_config_system
amazon_ses.identities: amazon_ses.identities:
title: 'Verfied identities' title: 'Verified identities'
route_name: amazon_ses.identities route_name: amazon_ses.identities
description: 'Amazon SES verified identities.' description: 'Amazon SES verified identities.'
parent: amazon_ses.settings_form parent: amazon_ses.settings_form
......
...@@ -5,10 +5,21 @@ amazon_ses.settings: ...@@ -5,10 +5,21 @@ amazon_ses.settings:
from_address: from_address:
type: email type: email
label: 'From Address' label: 'From Address'
from_name:
type: string
label: 'From Name'
nullable: true
override_from:
type: boolean
label: 'Override From Email'
nullable: true
throttle: throttle:
type: boolean type: boolean
label: 'Throttle' label: 'Throttle'
nullable: true nullable: true
multiplier:
type: integer
label: 'Throttling Multiplier'
queue: queue:
type: boolean type: boolean
label: 'Queue Emails' 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; ...@@ -5,12 +5,12 @@ namespace Drupal\amazon_ses;
use Aws\Exception\CredentialsException; use Aws\Exception\CredentialsException;
use Aws\Result; use Aws\Result;
use Aws\SesV2\Exception\SesV2Exception; use Aws\SesV2\Exception\SesV2Exception;
use Drupal\amazon_ses\Event\MailSentEvent;
use Drupal\aws\AwsClientFactoryInterface; use Drupal\aws\AwsClientFactoryInterface;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\amazon_ses\Event\MailSentEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Email;
...@@ -28,60 +28,34 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -28,60 +28,34 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
protected $client; protected $client;
/** /**
* The logger channel. * The account quota.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The messenger service.
* *
* @var \Drupal\Core\Messenger\MessengerInterface * @var array
*/ */
protected $messenger; protected $quota = [];
/** public function __construct(
* The event dispatcher service. protected AwsClientFactoryInterface $awsClientFactory,
* protected LoggerChannelInterface $logger,
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface protected MessengerInterface $messenger,
*/ protected EventDispatcherInterface $eventDispatcher,
protected $eventDispatcher; protected ConfigFactoryInterface $configFactory,
) {
/** $this->client = $awsClientFactory->getClient('sesv2');
* The config object. }
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/** /**
* Constructs the service. * {@inheritdoc}
*
* @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.
*/ */
public function __construct(AwsClientFactoryInterface $aws_client_factory, LoggerChannelInterface $logger, MessengerInterface $messenger, EventDispatcherInterface $event_dispatcher, ConfigFactoryInterface $config_factory) { public function getClient() {
$this->client = $aws_client_factory->getClient('sesv2'); return $this->client;
$this->logger = $logger;
$this->messenger = $messenger;
$this->eventDispatcher = $event_dispatcher;
$this->config = $config_factory->get('amazon_ses.settings');
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getClient() { public function verifyClient() {
return $this->client; return (bool) $this->client;
} }
/** /**
...@@ -100,7 +74,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -100,7 +74,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
$event = new MailSentEvent($result['MessageId'], $email); $event = new MailSentEvent($result['MessageId'], $email);
$this->eventDispatcher->dispatch($event, MailSentEvent::SENT); $this->eventDispatcher->dispatch($event, MailSentEvent::SENT);
$throttle = $this->config->get('throttle'); $throttle = $this->configFactory
->get('amazon_ses.settings')
->get('throttle');
if ($throttle) { if ($throttle) {
$sleep_time = $this->getSleepTime(); $sleep_time = $this->getSleepTime();
usleep($sleep_time); usleep($sleep_time);
...@@ -126,7 +103,16 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -126,7 +103,16 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
*/ */
protected function getSleepTime() { protected function getSleepTime() {
$results = $this->getSendQuota(); $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); return intval($per_second);
} }
...@@ -145,7 +131,6 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -145,7 +131,6 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
$result = $this->client->getEmailIdentity([ $result = $this->client->getEmailIdentity([
'EmailIdentity' => $identity, 'EmailIdentity' => $identity,
]); ]);
$attributes = $result['DkimAttributes'];
$domain = $result['IdentityType'] == 'DOMAIN'; $domain = $result['IdentityType'] == 'DOMAIN';
$item = [ $item = [
...@@ -155,7 +140,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -155,7 +140,10 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
]; ];
if ($domain) { if ($domain) {
$item['token'] = reset($attributes['Tokens']); $item['dkim'] = $result['DkimAttributes']['SigningEnabled'] ? 'Enabled' : 'Disabled';
}
else {
$item['dkim'] = '';
} }
$identities[] = $item; $identities[] = $item;
...@@ -191,9 +179,17 @@ class AmazonSesHandler implements AmazonSesHandlerInterface { ...@@ -191,9 +179,17 @@ class AmazonSesHandler implements AmazonSesHandlerInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getSendQuota() { public function getSendQuota() {
$result = $this->client->getAccount(); if (empty($this->quota)) {
try {
return array_map('number_format', $result['SendQuota']); $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 { ...@@ -17,6 +17,14 @@ interface AmazonSesHandlerInterface {
*/ */
public function getClient(); 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. * Send an email using the AWS SDK.
* *
......
...@@ -2,24 +2,26 @@ ...@@ -2,24 +2,26 @@
namespace Drupal\amazon_ses\Controller; namespace Drupal\amazon_ses\Controller;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Controller for Amazon SES routes. * Controller for Amazon SES routes.
*/ */
class AmazonSesController extends ControllerBase { class AmazonSesController extends ControllerBase {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
$instance = new static(); return new static(
$instance->setHandler($container->get('amazon_ses.handler')); $container->get('amazon_ses.handler')
);
return $instance;
} }
/** /**
...@@ -29,33 +31,42 @@ class AmazonSesController extends ControllerBase { ...@@ -29,33 +31,42 @@ class AmazonSesController extends ControllerBase {
* A render array to build the page. * A render array to build the page.
*/ */
public function statistics() { public function statistics() {
if (!$this->verifyClient()) { $statistics = [];
return [];
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(); $result = $this->handler->getSendQuota();
return [ if (!empty($result)) {
'quota' => [ $statistics = [
'#type' => 'details', 'quota' => [
'#title' => $this->t('Daily sending limits'), '#type' => 'details',
'#open' => TRUE, '#title' => $this->t('Daily sending limits'),
'sending_quota' => [ '#open' => TRUE,
'#markup' => $this->t('<strong>Quota:</strong> @max_send', [ 'sending_quota' => [
'@max_send' => $quota['Max24HourSend'], '#markup' => $this->t('<strong>Quota:</strong> @max_send', [
]) . '<br />', '@max_send' => $result['Max24HourSend'],
], ]) . '<br />',
'sent_mail' => [ ],
'#markup' => $this->t('<strong>Sent:</strong> @sent_last', [ 'sent_mail' => [
'@sent_last' => $quota['SentLast24Hours'], '#markup' => $this->t('<strong>Sent:</strong> @sent_last', [
]) . '<br />', '@sent_last' => $result['SentLast24Hours'],
], ]) . '<br />',
'send_rate' => [ ],
'#markup' => $this->t('<strong>Maximum Send Rate:</strong> @send_rate 'send_rate' => [
emails/second', ['@send_rate' => $quota['MaxSendRate']]), '#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 { ...@@ -12,32 +12,10 @@ class MailSentEvent extends Event {
const SENT = 'amazon_ses.mail_sent'; const SENT = 'amazon_ses.mail_sent';
/** public function __construct(
* The ID of the sent message. protected string $messageId,
* protected Email $email,
* @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;
}
/** /**
* Gets the message ID. * Gets the message ID.
......
...@@ -2,24 +2,26 @@ ...@@ -2,24 +2,26 @@
namespace Drupal\amazon_ses\Form; namespace Drupal\amazon_ses\Form;
use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\amazon_ses\Traits\HandlerTrait;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Amazon SES form base class. * Amazon SES form base class.
*/ */
abstract class AmazonSesFormBase extends FormBase { abstract class AmazonSesFormBase extends FormBase {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
$instance = new static(); return new static(
$instance->setHandler($container->get('amazon_ses.handler')); $container->get('amazon_ses.handler'),
);
return $instance;
} }
} }
...@@ -20,6 +20,13 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase { ...@@ -20,6 +20,13 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function buildForm(array $form, FormStateInterface $form_state) { 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'] = [ $form['action'] = [
'#type' => 'select', '#type' => 'select',
'#title' => $this->t('Action'), '#title' => $this->t('Action'),
...@@ -29,37 +36,20 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase { ...@@ -29,37 +36,20 @@ class AmazonSesIdentitiesForm extends AmazonSesFormBase {
$header = [ $header = [
'identity' => $this->t('Identity'), 'identity' => $this->t('Identity'),
'type' => $this->t('Type'), 'type' => $this->t('Type'),
'record' => $this->t('Domain verification record'), 'dkim' => $this->t('DKIM Signing'),
'status' => $this->t('Status'), 'status' => $this->t('Status'),
]; ];
$options = []; $options = [];
$identities = $this->handler->getIdentities();
if ($this->verifyClient()) {
$identities = $this->handler->getIdentities(); foreach ($identities as $identity) {
$options[$identity['identity']] = [
foreach ($identities as $identity) { 'identity' => $identity['identity'],
$options[$identity['identity']] = [ 'type' => $identity['type'],
'identity' => $identity['identity'], 'dkim' => $identity['dkim'],
'type' => $identity['type'], 'status' => $identity['status'],
'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'] = '';
}
}
} }
$form['identities'] = [ $form['identities'] = [
......
...@@ -39,6 +39,22 @@ class AmazonSesSettingsForm extends ConfigFormBase { ...@@ -39,6 +39,22 @@ class AmazonSesSettingsForm extends ConfigFormBase {
'#required' => TRUE, '#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'] = [ $form['throttle'] = [
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => $this->t('Throttle'), '#title' => $this->t('Throttle'),
...@@ -47,6 +63,20 @@ class AmazonSesSettingsForm extends ConfigFormBase { ...@@ -47,6 +63,20 @@ class AmazonSesSettingsForm extends ConfigFormBase {
'#default_value' => $config->get('throttle'), '#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'] = [ $form['queue'] = [
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => $this->t('Queue emails'), '#title' => $this->t('Queue emails'),
...@@ -63,7 +93,10 @@ class AmazonSesSettingsForm extends ConfigFormBase { ...@@ -63,7 +93,10 @@ class AmazonSesSettingsForm extends ConfigFormBase {
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('amazon_ses.settings') $this->config('amazon_ses.settings')
->set('from_address', $form_state->getValue('from_address')) ->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('throttle', $form_state->getValue('throttle'))
->set('multiplier', (int) $form_state->getValue('multiplier'))
->set('queue', $form_state->getValue('queue')) ->set('queue', $form_state->getValue('queue'))
->save(); ->save();
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
namespace Drupal\amazon_ses\Form; namespace Drupal\amazon_ses\Form;
use Drupal\amazon_ses\Traits\MessageBuilderTrait; use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\amazon_ses\MessageBuilderInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Mail\MailManagerInterface; use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\Session\AccountProxyInterface;
...@@ -13,83 +13,24 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -13,83 +13,24 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* Amazon SES test form. * Amazon SES test form.
*/ */
class AmazonSesTestForm extends AmazonSesFormBase { class AmazonSesTestForm extends AmazonSesFormBase {
use MessageBuilderTrait;
/** public function __construct(
* The Mail Manager service. protected AmazonSesHandlerInterface $handler,
* protected AccountProxyInterface $currentUser,
* @var \Drupal\Core\Mail\MailManagerInterface protected MailManagerInterface $mailManager,
*/ protected MessageBuilderInterface $messageBuilder,
protected $mailManager; ) {}
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
/** @var self $instance */ return new static(
$instance = parent::create($container); $container->get('amazon_ses.handler'),
$container->get('current_user'),
$instance $container->get('plugin.manager.mail'),
->setMessageBuilder($container->get('amazon_ses.message_builder')) $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;
} }
/** /**
...@@ -136,10 +77,13 @@ class AmazonSesTestForm extends AmazonSesFormBase { ...@@ -136,10 +77,13 @@ class AmazonSesTestForm extends AmazonSesFormBase {
*/ */
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
$to = $form_state->getValue('to'); $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 $body = $this->t('This is a test of the Amazon SES module. The module has
been configured successfully!'); been configured successfully!');
$name = $this->config->get('from_name');
$address = $this->config->get('from_address');
$from = "$name <$address>";
$message = [ $message = [
'to' => $to, 'to' => $to,
'from' => $from, 'from' => $from,
......
...@@ -27,8 +27,8 @@ class AmazonSesVerifyIdentityForm extends AmazonSesFormBase { ...@@ -27,8 +27,8 @@ class AmazonSesVerifyIdentityForm extends AmazonSesFormBase {
$form['info'] = [ $form['info'] = [
'#type' => 'markup', '#type' => 'markup',
'#markup' => '<p>' . $this->t('Amazon SES requires verified identies to '#markup' => '<p>' . $this->t('Amazon SES requires verified identities to
send mail. For more information about verifing identities, see the send mail. For more information about verifying identities, see the
@link.', ['@link' => $link->toString()]) . '</p>', @link.', ['@link' => $link->toString()]) . '</p>',
]; ];
......
...@@ -6,6 +6,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; ...@@ -6,6 +6,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\MimeTypeGuesserInterface; use Symfony\Component\Mime\MimeTypeGuesserInterface;
...@@ -15,68 +16,24 @@ use Symfony\Component\Mime\MimeTypeGuesserInterface; ...@@ -15,68 +16,24 @@ use Symfony\Component\Mime\MimeTypeGuesserInterface;
class MessageBuilder implements MessageBuilderInterface { class MessageBuilder implements MessageBuilderInterface {
use StringTranslationTrait; use StringTranslationTrait;
/** public function __construct(
* The logger channel. protected LoggerChannelInterface $logger,
* protected ConfigFactoryInterface $configFactory,
* @var \Drupal\Core\Logger\LoggerChannelInterface protected FileSystemInterface $fileSystem,
*/ protected MimeTypeGuesserInterface $mimeTypeGuesser,
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;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function buildMessage(array $message) { public function buildMessage(array $message) {
if (isset($message['from'])) {
$from = $message['from'];
}
else {
$from = $this->config->get('from_address');
}
$to = preg_split('/[,;]/', $message['to']); $to = preg_split('/[,;]/', $message['to']);
$from = $this->parseAddress($message['from']);
$from_address = new Address($from['address'], $from['name']);
$email = (new Email()) $email = (new Email())
->from($from) ->from($from_address)
->to(...$to) ->to(...$to)
->subject($message['subject']); ->subject($message['subject']);
...@@ -154,6 +111,30 @@ class MessageBuilder implements MessageBuilderInterface { ...@@ -154,6 +111,30 @@ class MessageBuilder implements MessageBuilderInterface {
return $email; 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. * Determine the content type of a message.
* *
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
namespace Drupal\amazon_ses\Plugin\Mail; namespace Drupal\amazon_ses\Plugin\Mail;
use Drupal\amazon_ses\Traits\HandlerTrait; use Drupal\amazon_ses\AmazonSesHandlerInterface;
use Drupal\amazon_ses\Traits\MessageBuilderTrait; use Drupal\amazon_ses\MessageBuilderInterface;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Mail\MailInterface; use Drupal\Core\Mail\MailInterface;
use Drupal\Core\Mail\Plugin\Mail\PhpMail; use Drupal\Core\Mail\Plugin\Mail\PhpMail;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueFactory; use Drupal\Core\Queue\QueueFactory;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
...@@ -21,77 +21,50 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -21,77 +21,50 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* ) * )
*/ */
class AmazonSes extends PhpMail implements MailInterface, ContainerFactoryPluginInterface { class AmazonSes extends PhpMail implements MailInterface, ContainerFactoryPluginInterface {
use HandlerTrait;
use MessageBuilderTrait;
/**
* The config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/** public function __construct(
* The queue object. protected ConfigFactoryInterface $configFactory,
* protected AmazonSesHandlerInterface $handler,
* @var \Drupal\Core\Queue\QueueInterface protected MessageBuilderInterface $messageBuilder,
*/ protected QueueFactory $queueFactory,
protected $queue; ) {}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { public static function create(
$instance = new static( ContainerInterface $container,
$container, array $configuration,
$configuration, $plugin_id,
$plugin_id, $plugin_definition,
$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} * {@inheritdoc}
*/ */
public function mail(array $message) { 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); $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')) { if ($queue_enabled) {
$result = $this->queue->createItem($email); $result = $queue->createItem($email);
return (bool) $result; return (bool) $result;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace Drupal\amazon_ses\Plugin\QueueWorker; 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\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase; use Drupal\Core\Queue\QueueWorkerBase;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
...@@ -17,21 +17,23 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -17,21 +17,23 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* ) * )
*/ */
class AmazonSesMailQueue extends QueueWorkerBase implements ContainerFactoryPluginInterface { class AmazonSesMailQueue extends QueueWorkerBase implements ContainerFactoryPluginInterface {
use HandlerTrait;
public function __construct(
protected AmazonSesHandlerInterface $handler,
) {}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { public static function create(
$instance = new static( ContainerInterface $container,
$configuration, array $configuration,
$plugin_id, $plugin_id,
$plugin_definition $plugin_definition,
) {
return new static(
$container->get('amazon_ses.handler'),
); );
$instance->setHandler($container->get('amazon_ses.handler'));
return $instance;
} }
/** /**
......