Commit b08df2ff authored by bojanz's avatar bojanz

Issue #3030095 by bojanz, Maithri Shetty: Improve the MailHandler API

parent 4e43daa9
......@@ -9,6 +9,25 @@ use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Implements hook_mail().
*
* Prepares emails sent by the MailHandler service.
*/
function commerce_mail($key, &$message, $params) {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
if (isset($params['headers'])) {
$message['headers'] = array_merge($message['headers'], $params['headers']);
}
if (!empty($params['from'])) {
$message['from'] = $params['from'];
}
$message['subject'] = $params['subject'];
$message['body'][] = $renderer->render($params['body']);
}
/**
* Implements hook_toolbar_alter().
*/
......@@ -123,23 +142,6 @@ function commerce_get_entity_display($entity_type, $bundle, $display_context) {
return $display;
}
/**
* Implements hook_mail().
*
* Prepares emails sent by the MailHandler service.
*/
function commerce_mail($key, &$message, $params) {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
if (isset($params['headers'])) {
$message['headers'] = array_merge($message['headers'], $params['headers']);
}
$message['from'] = $params['from'];
$message['subject'] = $params['subject'];
$message['body'][] = $renderer->render($params['body']);
}
/**
* Helper for providing entity theme suggestions.
*
......
......@@ -75,4 +75,4 @@ services:
commerce.mail_handler:
class: Drupal\commerce\MailHandler
arguments: ['@entity_type.manager', '@language.default', '@language_manager', '@plugin.manager.mail']
arguments: ['@language.default', '@language_manager', '@plugin.manager.mail']
......@@ -94,12 +94,12 @@ class OrderReceiptSubscriber implements EventSubscriberInterface {
}
$params = [
'id' => 'order_receipt',
'to' => $to,
'from' => $order->getStore()->getEmail(),
'bcc' => $order_type->getReceiptBcc(),
'langcode' => $order->getCustomer()->getPreferredLangcode(),
'order' => $order,
];
$this->mailHandler->sendEmail($order->getCustomer(), $subject, $body, $params);
$this->mailHandler->sendMail($to, $subject, $body, $params);
}
}
......@@ -8,6 +8,22 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Implements hook_mail_alter().
*
* Sets the default "from" address to the current store email.
*/
function commerce_store_mail_alter(&$message) {
if (substr($message['id'], 0, 9) == 'commerce_' && empty($message['params']['from'])) {
/** @var \Drupal\commerce_store\CurrentStoreInterface $current_store */
$current_store = \Drupal::service('commerce_store.current_store');
$current_store = $current_store->getStore();
if ($current_store) {
$message['from'] = $current_store->getEmail();
}
}
}
/**
* Implements hook_theme().
*/
......
......@@ -2,25 +2,16 @@
namespace Drupal\commerce;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationManager;
use Drupal\user\UserInterface;
class MailHandler implements MailHandlerInterface {
use StringTranslationTrait;
/**
* The store storage.
*
* @var \Drupal\commerce_store\StoreStorageInterface
*/
protected $storeStorage;
/**
* The language default.
*
......@@ -45,8 +36,6 @@ class MailHandler implements MailHandlerInterface {
/**
* Constructs a new MailHandler object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The current store.
* @param \Drupal\Core\Language\LanguageDefault $language_default
* The language default.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
......@@ -54,8 +43,7 @@ class MailHandler implements MailHandlerInterface {
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
* The mail manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, LanguageDefault $language_default, LanguageManagerInterface $language_manager, MailManagerInterface $mail_manager) {
$this->storeStorage = $entity_type_manager->getStorage('commerce_store');
public function __construct(LanguageDefault $language_default, LanguageManagerInterface $language_manager, MailManagerInterface $mail_manager) {
$this->languageDefault = $language_default;
$this->languageManager = $language_manager;
$this->mailManager = $mail_manager;
......@@ -64,39 +52,21 @@ class MailHandler implements MailHandlerInterface {
/**
* {@inheritdoc}
*/
public function sendEmail(UserInterface $account, $subject, array $body, array $params = []) {
if ($account->isAnonymous() && empty($params['to'])) {
throw new \InvalidArgumentException('The "to" parameter is required when emailing an anonymous user.');
}
$to = '';
if (!empty($params['to'])) {
$to = $params['to'];
}
elseif ($account->isAuthenticated()) {
$to = $account->getEmail();
}
// The user has no email set, and no override was provided. Stop here.
if (!$to) {
public function sendMail($to, $subject, array $body, array $params = []) {
if (empty($to)) {
return FALSE;
}
// Change the active language to the one preferred by the customer
// to ensure the email is properly translated.
$default_langcode = $this->languageManager->getDefaultLanguage()->getId();
$preferred_langcode = $account->getPreferredLangcode();
if ($default_langcode !== $preferred_langcode) {
$this->changeActiveLanguage($preferred_langcode);
}
$default_store = $this->storeStorage->loadDefault();
$default_params = [
'headers' => [
'Content-Type' => 'text/html; charset=UTF-8;',
'Content-Transfer-Encoding' => '8Bit',
],
'id' => 'mail',
'from' => $default_store->getEmail(),
// The 'from' address will be set by commerce_store_mail_alter().
'from' => '',
'subject' => $subject,
'langcode' => $this->languageManager->getDefaultLanguage()->getId(),
// The body will be rendered in commerce_mail(), because that's what
// MailManager expects. The correct theme and render context aren't
// setup until then.
......@@ -107,11 +77,16 @@ class MailHandler implements MailHandlerInterface {
}
$params = array_replace($default_params, $params);
$message = $this->mailManager->mail('commerce', $params['id'], $to, $preferred_langcode, $params);
// Change the active language to ensure the email is properly translated.
if ($params['langcode'] != $default_params['langcode']) {
$this->changeActiveLanguage($params['langcode']);
}
$message = $this->mailManager->mail('commerce', $params['id'], $to, $params['langcode'], $params);
// Revert back to the original active language.
if ($default_langcode !== $preferred_langcode) {
$this->changeActiveLanguage($default_langcode);
if ($params['langcode'] != $default_params['langcode']) {
$this->changeActiveLanguage($default_params['langcode']);
}
return (bool) $message['result'];
......
......@@ -2,8 +2,6 @@
namespace Drupal\commerce;
use Drupal\user\UserInterface;
/**
* Handles the assembly and dispatch of HTML emails.
*
......@@ -18,8 +16,8 @@ interface MailHandlerInterface {
/**
* Sends an email to a user.
*
* @param \Drupal\user\UserInterface $account
* The user account.
* @param string $to
* The address the email will be sent to. Must comply with RFC 2822.
* @param string $subject
* The subject. Must not contain any newline characters.
* @param array $body
......@@ -29,16 +27,15 @@ interface MailHandlerInterface {
* - id: A unique identifier of the email type.
* Allows hook_mail_alter() implementations to identify specific emails.
* Defaults to "mail". Automatically prefixed with "commerce_".
* - to: The address the email will be sent to.
* Must comply with RFC 2822. Defaults to the user's email address.
* Required if the user is anonymous.
* - from: The address the email will be marked as being from.
* Defaults to the default store email.
* Defaults to the current store email.
* - bcc: The BCC address or addresses. No default value.
* - langcode: The email langcode. Every translatable string and entity
* will be rendered in this language.
*
* @return bool
* TRUE if the email sent successfully, FALSE otherwise.
*/
public function sendEmail(UserInterface $account, $subject, array $body, array $params = []);
public function sendMail($to, $subject, array $body, array $params = []);
}
......@@ -4,7 +4,6 @@ namespace Drupal\Tests\commerce\Kernel;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\user\Entity\User;
/**
* Tests the sending of customer emails.
......@@ -23,20 +22,12 @@ class MailHandlerTest extends CommerceKernelTestBase {
*/
protected $mailHandler;
/**
* A sample user.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->user = $this->createUser(['mail' => 'customer@example.com']);
$this->mailHandler = $this->container->get('commerce.mail_handler');
}
......@@ -44,11 +35,10 @@ class MailHandlerTest extends CommerceKernelTestBase {
* Tests sending a basic email, without any custom parameters.
*/
public function testBasicEmail() {
$this->assertTrue($this->user->isAuthenticated());
$body = [
'#markup' => '<p>' . $this->t('Mail Handler Test') . '</p>',
];
$result = $this->mailHandler->sendEmail($this->user, 'Test subject', $body);
$result = $this->mailHandler->sendMail('customer@example.com', 'Test subject', $body);
$this->assertTrue($result);
$emails = $this->getMails();
......@@ -56,27 +46,15 @@ class MailHandlerTest extends CommerceKernelTestBase {
$email = reset($emails);
$this->assertEquals('text/html; charset=UTF-8;', $email['headers']['Content-Type']);
$this->assertEquals('commerce_mail', $email['id']);
$this->assertEquals($this->user->getEmail(), $email['to']);
$this->assertEquals('customer@example.com', $email['to']);
$this->assertFalse(isset($email['headers']['Bcc']));
$this->assertEquals($this->store->getEmail(), $email['from']);
$this->assertEquals('Test subject', $email['subject']);
$this->assertContains('Mail Handler Test', $email['body']);
// No email should be sent if the authenticated user has no email specified.
$another_user = $this->createUser();
$result = $this->mailHandler->sendEmail($another_user, 'Test subject', $body);
// No email should be sent if the recipient is empty.
$result = $this->mailHandler->sendMail('', 'Test subject', $body);
$this->assertFalse($result);
// An exception should be thrown when trying to email an anonymous user
// without a "to" parameter.
$exception_thrown = FALSE;
try {
$this->mailHandler->sendEmail(User::getAnonymousUser(), 'Test subject', $body);
}
catch (\InvalidArgumentException $e) {
$exception_thrown = TRUE;
}
$this->assertTrue($exception_thrown);
}
/**
......@@ -88,28 +66,25 @@ class MailHandlerTest extends CommerceKernelTestBase {
];
$params = [
'id' => 'custom',
'to' => 'you@example.com',
'from' => 'me@example.com',
'bcc' => 'other@example.com',
'uid' => $this->user->id(),
'langcode' => 'fr',
// Custom parameters are passed through.
'foo' => 'bar',
];
// Test with both an authenticated and an anonymous user, to confirm that
// the "to" parameter is used in both cases.
$users = [$this->user, User::getAnonymousUser()];
foreach ($users as $user) {
$result = $this->mailHandler->sendEmail($user, 'Hello #' . $user->id(), $body, $params);
$this->assertTrue($result);
$result = $this->mailHandler->sendMail('you@example.com', 'Hello', $body, $params);
$this->assertTrue($result);
$emails = $this->getMails();
$email = end($emails);
$this->assertEquals('commerce_custom', $email['id']);
$this->assertEquals('you@example.com', $email['to']);
$this->assertEquals('other@example.com', $email['headers']['Bcc']);
$this->assertEquals('me@example.com', $email['from']);
$this->assertEquals('Hello #' . $user->id(), $email['subject']);
$this->assertContains('Custom Mail Handler Test', $email['body']);
$this->assertEquals($this->user->id(), $email['params']['uid']);
}
$emails = $this->getMails();
$email = end($emails);
$this->assertEquals('commerce_custom', $email['id']);
$this->assertEquals('you@example.com', $email['to']);
$this->assertEquals('other@example.com', $email['headers']['Bcc']);
$this->assertEquals('me@example.com', $email['from']);
$this->assertEquals('Hello', $email['subject']);
$this->assertContains('Custom Mail Handler Test', $email['body']);
$this->assertEquals('fr', $email['langcode']);
$this->assertEquals('bar', $email['params']['foo']);
}
}
......@@ -65,7 +65,7 @@ class MailHandlerThemeTest extends CommerceKernelTestBase {
'#theme' => 'username',
'#account' => $this->user,
];
$this->mailHandler->sendEmail($this->user, 'Hello, customer!', $body);
$this->mailHandler->sendMail($this->user->getEmail(), 'Hello, customer!', $body);
$emails = $this->getMails();
$this->assertEquals(1, count($emails));
......@@ -94,7 +94,7 @@ class MailHandlerThemeTest extends CommerceKernelTestBase {
'#theme' => 'username',
'#account' => $this->user,
];
$this->mailHandler->sendEmail($this->user, 'Hello, customer!', $body);
$this->mailHandler->sendMail($this->user->getEmail(), 'Hello, customer!', $body);
$emails = $this->getMails();
$this->assertEquals(1, count($emails));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment