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