Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
182 lines (157 sloc) 6.73 KB
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/
namespace craft\mail;
use Craft;
use craft\elements\User;
use craft\helpers\App;
use craft\helpers\Template;
use yii\base\InvalidConfigException;
use yii\helpers\Markdown;
use yii\mail\MessageInterface;
/**
* The Mailer component provides APIs for sending email in Craft.
* An instance of the Mailer component is globally accessible in Craft via [[\craft\web\Application::mailer|`Craft::$app->mailer`]].
*
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 3.0
*/
class Mailer extends \yii\swiftmailer\Mailer
{
// Properties
// =========================================================================
/**
* @var string|null The email template that should be used
*/
public $template;
/**
* @var string|array|User|User[]|null $from The default sender’s email address, or their user model(s).
*/
public $from;
// Public Methods
// =========================================================================
/**
* Composes a new email based on a given key.
*
* Craft has four predefined email keys: account_activation, verify_new_email, forgot_password, and test_email.
* Plugins can register additional email keys using the
* [registerEmailMessages](http://craftcms.com/docs/plugins/hooks-reference#registerEmailMessages) hook, and
* by providing the corresponding language strings.
*
* ```php
* Craft::$app->mailer->composeFromKey('account_activation', [
* 'link' => $activationUrl
* ]);
* ```
*
* @param string $key The email key
* @param array $variables Any variables that should be passed to the email body template
* @return Message The new email message
* @throws InvalidConfigException if [[messageConfig]] or [[class]] is not configured to use [[Message]]
*/
public function composeFromKey(string $key, array $variables = []): Message
{
$message = $this->createMessage();
if (!$message instanceof Message) {
throw new InvalidConfigException('Mailer must be configured to create messages with craft\mail\Message (or a subclass).');
}
$message->key = $key;
$message->variables = $variables;
return $message;
}
/**
* Sends the given email message.
*
* This method will log a message about the email being sent.
* If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]].
* Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s).
* Child classes should implement [[sendMessage()]] with the actual email sending logic.
*
* @param MessageInterface $message The email message instance to be sent.
* @return bool Whether the message has been sent successfully.
*/
public function send($message)
{
if ($message instanceof Message && $message->key !== null) {
if ($message->language === null) {
// Default to the current language
$message->language = Craft::$app->getRequest()->getIsSiteRequest()
? Craft::$app->language
: Craft::$app->getSites()->getPrimarySite()->language;
}
$systemMessage = Craft::$app->getSystemMessages()->getMessage($message->key, $message->language);
$subjectTemplate = $systemMessage->subject;
$textBodyTemplate = $systemMessage->body;
// Use the site template mode
$view = Craft::$app->getView();
$templateMode = $view->getTemplateMode();
$view->setTemplateMode($view::TEMPLATE_MODE_SITE);
// Use the message language
$language = Craft::$app->language;
Craft::$app->language = $message->language;
$settings = App::mailSettings();
$variables = ($message->variables ?: []) + [
'emailKey' => $message->key,
'fromEmail' => Craft::parseEnv($settings->fromEmail),
'fromName' => Craft::parseEnv($settings->fromName),
];
// Render the subject and textBody
$message->setSubject($view->renderString($subjectTemplate, $variables));
$textBody = $view->renderString($textBodyTemplate, $variables);
$message->setTextBody($textBody);
// Is there a custom HTML template set?
if (Craft::$app->getEdition() === Craft::Pro && $this->template) {
$template = $this->template;
} else {
// Default to the _special/email.html template
$view->setTemplateMode($view::TEMPLATE_MODE_CP);
$template = '_special/email';
}
try {
$message->setHtmlBody($view->renderTemplate($template, array_merge($variables, [
'body' => Template::raw(Markdown::process($textBody)),
])));
} catch (\Throwable $e) {
// Just log it and don't worry about the HTML body
Craft::warning('Error rendering email template: ' . $e->getMessage(), __METHOD__);
Craft::$app->getErrorHandler()->logException($e);
}
// Set things back to normal
Craft::$app->language = $language;
$view->setTemplateMode($templateMode);
}
// Set the default sender if there isn't one already
if (!$message->getFrom()) {
$message->setFrom($this->from);
}
// Apply the testToEmailAddress config setting
$testToEmailAddress = Craft::$app->getConfig()->getGeneral()->testToEmailAddress;
if (!empty($testToEmailAddress)) {
$to = [];
foreach ((array)$testToEmailAddress as $emailAddress => $name) {
if (is_numeric($emailAddress)) {
$to[$name] = Craft::t('app', 'Test Recipient');
} else {
$to[$emailAddress] = $name;
}
}
$message->setTo($to);
$message->setCc(null);
$message->setBcc(null);
}
try {
return parent::send($message);
} catch (\Throwable $e) {
$eMessage = $e->getMessage();
// Remove the stack trace to get rid of any sensitive info. Note that Swiftmailer includes a debug
// backlog in the exception message. :-/
$eMessage = substr($eMessage, 0, strpos($eMessage, 'Stack trace:') - 1);
Craft::warning('Error sending email: ' . $eMessage);
$this->afterSend($message, false);
return false;
}
}
}
You can’t perform that action at this time.