mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #10494 from creative-commoners/pulls/5/symfony-mailer
NEW Migrate from swiftmailer/swiftmailer to symfony/mailer
This commit is contained in:
commit
919cfcf435
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
Name: emailconfig
|
|
||||||
---
|
|
||||||
SilverStripe\Core\Injector\Injector:
|
|
||||||
Swift_Transport: Swift_MailTransport
|
|
||||||
Swift_Mailer:
|
|
||||||
constructor:
|
|
||||||
- '%$Swift_Transport'
|
|
||||||
SilverStripe\Control\Email\Mailer:
|
|
||||||
class: SilverStripe\Control\Email\SwiftMailer
|
|
||||||
properties:
|
|
||||||
SwiftMailer: '%$Swift_Mailer'
|
|
28
_config/mailer.yml
Normal file
28
_config/mailer.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
Name: mailer
|
||||||
|
---
|
||||||
|
SilverStripe\Core\Injector\Injector:
|
||||||
|
Symfony\Component\Mailer\MailerInterface:
|
||||||
|
class: Symfony\Component\Mailer\Mailer
|
||||||
|
constructor:
|
||||||
|
transport: '%$Symfony\Component\Mailer\Transport\TransportInterface'
|
||||||
|
Symfony\Component\EventDispatcher\EventDispatcherInterface.mailer:
|
||||||
|
class: Symfony\Component\EventDispatcher\EventDispatcher
|
||||||
|
calls:
|
||||||
|
- [addSubscriber, ['%$SilverStripe\Control\Email\MailerSubscriber']]
|
||||||
|
Symfony\Component\Mailer\Transport\TransportInterface:
|
||||||
|
factory: Symfony\Component\Mailer\Transport
|
||||||
|
factory_method: fromDsn
|
||||||
|
constructor:
|
||||||
|
dsn: 'sendmail://default'
|
||||||
|
dispatcher: '%$Symfony\Component\EventDispatcher\EventDispatcherInterface.mailer'
|
||||||
|
---
|
||||||
|
Name: mailer-dsn-env
|
||||||
|
After: '*'
|
||||||
|
Only:
|
||||||
|
envvarset: MAILER_DSN
|
||||||
|
---
|
||||||
|
SilverStripe\Core\Injector\Injector:
|
||||||
|
Symfony\Component\Mailer\Transport\TransportInterface:
|
||||||
|
constructor:
|
||||||
|
dsn: '`MAILER_DSN`'
|
@ -37,10 +37,11 @@
|
|||||||
"silverstripe/assets": "^2",
|
"silverstripe/assets": "^2",
|
||||||
"silverstripe/vendor-plugin": "^2",
|
"silverstripe/vendor-plugin": "^2",
|
||||||
"sminnee/callbacklist": "^0.1.1",
|
"sminnee/callbacklist": "^0.1.1",
|
||||||
"swiftmailer/swiftmailer": "^6.3.0",
|
|
||||||
"symfony/cache": "^6.1",
|
"symfony/cache": "^6.1",
|
||||||
"symfony/config": "^6.1",
|
"symfony/config": "^6.1",
|
||||||
"symfony/filesystem": "^6.1",
|
"symfony/filesystem": "^6.1",
|
||||||
|
"symfony/mailer": "^6.1",
|
||||||
|
"symfony/mime": "^6.1",
|
||||||
"symfony/translation": "^6.1",
|
"symfony/translation": "^6.1",
|
||||||
"symfony/yaml": "^6.1",
|
"symfony/yaml": "^6.1",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
@ -96,9 +97,6 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/includes/constants.php"
|
"src/includes/constants.php"
|
||||||
],
|
|
||||||
"classmap": [
|
|
||||||
"thirdparty/swiftmailer"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include-path": [
|
"include-path": [
|
||||||
|
@ -2,176 +2,131 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Control\Email;
|
namespace SilverStripe\Control\Email;
|
||||||
|
|
||||||
use DateTime;
|
use Exception;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Egulias\EmailValidator\EmailValidator;
|
use Egulias\EmailValidator\EmailValidator;
|
||||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\HTTP;
|
use SilverStripe\Core\Config\Configurable;
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Core\Environment;
|
use SilverStripe\Core\Environment;
|
||||||
|
use SilverStripe\Core\Extensible;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use SilverStripe\View\ThemeResourceLoader;
|
use SilverStripe\View\ThemeResourceLoader;
|
||||||
use SilverStripe\View\ViewableData;
|
use SilverStripe\View\ViewableData;
|
||||||
use Swift_Message;
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
use Swift_Mime_SimpleMessage;
|
use Symfony\Component\Mime\Address;
|
||||||
use Swift_MimePart;
|
use Symfony\Component\Mime\Email as SymfonyEmail;
|
||||||
|
use Symfony\Component\Mime\Part\AbstractPart;
|
||||||
|
|
||||||
/**
|
class Email extends SymfonyEmail
|
||||||
* Class to support sending emails.
|
|
||||||
*/
|
|
||||||
class Email extends ViewableData
|
|
||||||
{
|
{
|
||||||
/**
|
use Configurable;
|
||||||
* @var array
|
use Extensible;
|
||||||
* @config
|
use Injectable;
|
||||||
*/
|
|
||||||
private static $send_all_emails_to = [];
|
private static string|array $send_all_emails_to = [];
|
||||||
|
|
||||||
/**
|
private static string|array $cc_all_emails_to = [];
|
||||||
* @var array
|
|
||||||
* @config
|
private static string|array $bcc_all_emails_to = [];
|
||||||
*/
|
|
||||||
private static $cc_all_emails_to = [];
|
private static string|array $send_all_emails_from = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
* @config
|
|
||||||
*/
|
|
||||||
private static $bcc_all_emails_to = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
* @config
|
|
||||||
*/
|
|
||||||
private static $send_all_emails_from = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The default "from" email address or array of [email => name], or the email address as a string
|
||||||
* This will be set in the config on a site-by-site basis
|
* This will be set in the config on a site-by-site basis
|
||||||
* @see https://docs.silverstripe.org/en/4/developer_guides/email/#administrator-emails
|
* @see https://docs.silverstripe.org/en/4/developer_guides/email/#administrator-emails
|
||||||
*
|
|
||||||
* @config
|
|
||||||
* @var string|array The default administrator email address or array of [email => name]
|
|
||||||
*/
|
*/
|
||||||
private static $admin_email = null;
|
private static string|array $admin_email = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Swift_Message
|
* The name of the HTML template to render the email with (without *.ss extension)
|
||||||
*/
|
*/
|
||||||
private $swiftMessage;
|
private string $HTMLTemplate = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string The name of the HTML template to render the email with (without *.ss extension)
|
* The name of the plain text template to render the plain part of the email with
|
||||||
*/
|
*/
|
||||||
private $HTMLTemplate = null;
|
private string $plainTemplate = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string The name of the plain text template to render the plain part of the email with
|
* Additional data available in a template.
|
||||||
*/
|
|
||||||
private $plainTemplate = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Swift_MimePart
|
|
||||||
*/
|
|
||||||
private $plainPart;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array|ViewableData Additional data available in a template.
|
|
||||||
* Used in the same way than {@link ViewableData->customize()}.
|
* Used in the same way than {@link ViewableData->customize()}.
|
||||||
*/
|
*/
|
||||||
private $data = [];
|
private ViewableData $data;
|
||||||
|
|
||||||
/**
|
private bool $dataHasBeenSet = false;
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $failedRecipients = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for RFC822-valid email format.
|
* Checks for RFC822-valid email format.
|
||||||
*
|
*
|
||||||
* @param string $address
|
|
||||||
* @return boolean
|
|
||||||
*
|
|
||||||
* @copyright Cal Henderson <cal@iamcal.com>
|
* @copyright Cal Henderson <cal@iamcal.com>
|
||||||
* This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
|
* This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
|
||||||
* http://creativecommons.org/licenses/by-sa/2.5/
|
* http://creativecommons.org/licenses/by-sa/2.5/
|
||||||
*/
|
*/
|
||||||
public static function is_valid_address($address)
|
public static function is_valid_address(string $address): bool
|
||||||
{
|
{
|
||||||
$validator = new EmailValidator();
|
$validator = new EmailValidator();
|
||||||
return $validator->isValid($address, new RFCValidation());
|
return $validator->isValid($address, new RFCValidation());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getSendAllEmailsTo(): array
|
||||||
* Get send_all_emails_to
|
|
||||||
*
|
|
||||||
* @return array Keys are addresses, values are names
|
|
||||||
*/
|
|
||||||
public static function getSendAllEmailsTo()
|
|
||||||
{
|
{
|
||||||
return static::mergeConfiguredEmails('send_all_emails_to', 'SS_SEND_ALL_EMAILS_TO');
|
return static::mergeConfiguredAddresses('send_all_emails_to', 'SS_SEND_ALL_EMAILS_TO');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getCCAllEmailsTo(): array
|
||||||
* Get cc_all_emails_to
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getCCAllEmailsTo()
|
|
||||||
{
|
{
|
||||||
return static::mergeConfiguredEmails('cc_all_emails_to', 'SS_CC_ALL_EMAILS_TO');
|
return static::mergeConfiguredAddresses('cc_all_emails_to', 'SS_CC_ALL_EMAILS_TO');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getBCCAllEmailsTo(): array
|
||||||
* Get bcc_all_emails_to
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getBCCAllEmailsTo()
|
|
||||||
{
|
{
|
||||||
return static::mergeConfiguredEmails('bcc_all_emails_to', 'SS_BCC_ALL_EMAILS_TO');
|
return static::mergeConfiguredAddresses('bcc_all_emails_to', 'SS_BCC_ALL_EMAILS_TO');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getSendAllEmailsFrom(): array
|
||||||
* Get send_all_emails_from
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getSendAllEmailsFrom()
|
|
||||||
{
|
{
|
||||||
return static::mergeConfiguredEmails('send_all_emails_from', 'SS_SEND_ALL_EMAILS_FROM');
|
return static::mergeConfiguredAddresses('send_all_emails_from', 'SS_SEND_ALL_EMAILS_FROM');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalise email list from config merged with env vars
|
* Normalise email list from config merged with env vars
|
||||||
*
|
*
|
||||||
* @param string $config Config key
|
* @return Address[]
|
||||||
* @param string $env Env variable key
|
|
||||||
* @return array Array of email addresses
|
|
||||||
*/
|
*/
|
||||||
protected static function mergeConfiguredEmails($config, $env)
|
private static function mergeConfiguredAddresses(string $configKey, string $envKey): array
|
||||||
{
|
{
|
||||||
// Normalise config list
|
$addresses = [];
|
||||||
$normalised = [];
|
$config = (array) static::config()->get($configKey);
|
||||||
$source = (array)static::config()->get($config);
|
$addresses = self::convertConfigToAddreses($config);
|
||||||
foreach ($source as $address => $name) {
|
$env = Environment::getEnv($envKey);
|
||||||
if ($address && !is_numeric($address)) {
|
if ($env) {
|
||||||
$normalised[$address] = $name;
|
$addresses = array_merge($addresses, self::convertConfigToAddreses($env));
|
||||||
} elseif ($name) {
|
}
|
||||||
$normalised[$name] = null;
|
return $addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function convertConfigToAddreses(array|string $config): array
|
||||||
|
{
|
||||||
|
$addresses = [];
|
||||||
|
if (is_array($config)) {
|
||||||
|
foreach ($config as $key => $val) {
|
||||||
|
if (filter_var($key, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$addresses[] = new Address($key, $val);
|
||||||
|
} else {
|
||||||
|
$addresses[] = new Address($val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$addresses[] = new Address($config);
|
||||||
}
|
}
|
||||||
$extra = Environment::getEnv($env);
|
return $addresses;
|
||||||
if ($extra) {
|
|
||||||
$normalised[$extra] = null;
|
|
||||||
}
|
|
||||||
return $normalised;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,23 +134,19 @@ class Email extends ViewableData
|
|||||||
* At the moment only simple string substitutions,
|
* At the moment only simple string substitutions,
|
||||||
* which are not 100% safe from email harvesting.
|
* which are not 100% safe from email harvesting.
|
||||||
*
|
*
|
||||||
* @param string $email Email-address
|
* $method defines the method for obfuscating/encoding the address
|
||||||
* @param string $method Method for obfuscating/encoding the address
|
* - 'direction': Reverse the text and then use CSS to put the text direction back to normal
|
||||||
* - 'direction': Reverse the text and then use CSS to put the text direction back to normal
|
* - 'visible': Simple string substitution ('@' to '[at]', '.' to '[dot], '-' to [dash])
|
||||||
* - 'visible': Simple string substitution ('@' to '[at]', '.' to '[dot], '-' to [dash])
|
* - 'hex': Hexadecimal URL-Encoding - useful for mailto: links
|
||||||
* - 'hex': Hexadecimal URL-Encoding - useful for mailto: links
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public static function obfuscate($email, $method = 'visible')
|
public static function obfuscate(string $email, string $method = 'visible'): string
|
||||||
{
|
{
|
||||||
switch ($method) {
|
switch ($method) {
|
||||||
case 'direction':
|
case 'direction':
|
||||||
Requirements::customCSS('span.codedirection { unicode-bidi: bidi-override; direction: rtl; }', 'codedirectionCSS');
|
Requirements::customCSS('span.codedirection { unicode-bidi: bidi-override; direction: rtl; }', 'codedirectionCSS');
|
||||||
|
|
||||||
return '<span class="codedirection">' . strrev($email) . '</span>';
|
return '<span class="codedirection">' . strrev($email) . '</span>';
|
||||||
case 'visible':
|
case 'visible':
|
||||||
$obfuscated = ['@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '];
|
$obfuscated = ['@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '];
|
||||||
|
|
||||||
return strtr($email, $obfuscated);
|
return strtr($email, $obfuscated);
|
||||||
case 'hex':
|
case 'hex':
|
||||||
$encoded = '';
|
$encoded = '';
|
||||||
@ -203,36 +154,27 @@ class Email extends ViewableData
|
|||||||
for ($x = 0; $x < $emailLength; $x++) {
|
for ($x = 0; $x < $emailLength; $x++) {
|
||||||
$encoded .= '&#x' . bin2hex($email[$x]) . ';';
|
$encoded .= '&#x' . bin2hex($email[$x]) . ';';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $encoded;
|
return $encoded;
|
||||||
default:
|
default:
|
||||||
user_error('Email::obfuscate(): Unknown obfuscation method', E_USER_NOTICE);
|
user_error('Email::obfuscate(): Unknown obfuscation method', E_USER_NOTICE);
|
||||||
|
|
||||||
return $email;
|
return $email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Email constructor.
|
|
||||||
* @param string|array|null $from
|
|
||||||
* @param string|array|null $to
|
|
||||||
* @param string|null $subject
|
|
||||||
* @param string|null $body
|
|
||||||
* @param string|array|null $cc
|
|
||||||
* @param string|array|null $bcc
|
|
||||||
* @param string|null $returnPath
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$from = null,
|
string $from = '',
|
||||||
$to = null,
|
string $to = '',
|
||||||
$subject = null,
|
string $subject = '',
|
||||||
$body = null,
|
string $body = '',
|
||||||
$cc = null,
|
string $cc = '',
|
||||||
$bcc = null,
|
string $bcc = '',
|
||||||
$returnPath = null
|
string $returnPath = ''
|
||||||
) {
|
) {
|
||||||
|
parent::__construct();
|
||||||
if ($from) {
|
if ($from) {
|
||||||
$this->setFrom($from);
|
$this->setFrom($from);
|
||||||
|
} else {
|
||||||
|
$this->setFrom($this->getDefaultFrom());
|
||||||
}
|
}
|
||||||
if ($to) {
|
if ($to) {
|
||||||
$this->setTo($to);
|
$this->setTo($to);
|
||||||
@ -252,52 +194,9 @@ class Email extends ViewableData
|
|||||||
if ($returnPath) {
|
if ($returnPath) {
|
||||||
$this->setReturnPath($returnPath);
|
$this->setReturnPath($returnPath);
|
||||||
}
|
}
|
||||||
|
$this->data = ViewableData::create();
|
||||||
parent::__construct();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* @return Swift_Message
|
|
||||||
*/
|
|
||||||
public function getSwiftMessage()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
if (!$this->swiftMessage) {
|
|
||||||
$message = new Swift_Message(null, null, 'text/html', 'utf-8');
|
|
||||||
// Set priority to fix PHP 8.1 SimpleMessage::getPriority() sscanf() null parameter
|
|
||||||
$message->setPriority(Swift_Mime_SimpleMessage::PRIORITY_NORMAL);
|
|
||||||
$this->setSwiftMessage($message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->swiftMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* @param Swift_Message $swiftMessage
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setSwiftMessage($swiftMessage)
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
$dateTime = new DateTime();
|
|
||||||
$dateTime->setTimestamp(DBDatetime::now()->getTimestamp());
|
|
||||||
$swiftMessage->setDate($dateTime);
|
|
||||||
if (!$swiftMessage->getFrom()) {
|
|
||||||
$swiftMessage->setFrom($this->getDefaultFrom());
|
|
||||||
}
|
|
||||||
$this->swiftMessage = $swiftMessage;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getDefaultFrom(): string
|
private function getDefaultFrom(): string
|
||||||
{
|
{
|
||||||
// admin_email can have a string or an array config
|
// admin_email can have a string or an array config
|
||||||
@ -324,438 +223,178 @@ class Email extends ViewableData
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[]
|
* Passing a string of HTML for $body will have no affect if you also call either setData() or addData()
|
||||||
*/
|
*/
|
||||||
public function getFrom()
|
public function setBody(AbstractPart|string $body = null): static
|
||||||
{
|
{
|
||||||
return $this->getSwiftMessage()->getFrom();
|
if ($body instanceof AbstractPart) {
|
||||||
|
// pass to Symfony\Component\Mime\Message::setBody()
|
||||||
|
return parent::setBody($body);
|
||||||
|
}
|
||||||
|
// Set HTML content directly.
|
||||||
|
return $this->html($body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|array $address
|
* The following arguments combinations are valid
|
||||||
* @return string|array
|
* a) $address = 'my@email.com', $name = 'My name'
|
||||||
|
* b) $address = ['my@email.com' => 'My name', 'other@email.com' => 'My other name']
|
||||||
|
* c) $address = ['my@email.com' => 'My name', 'other@email.com']
|
||||||
*/
|
*/
|
||||||
private function sanitiseAddress($address)
|
private function createAddressArray(string|array $address, $name = ''): array
|
||||||
{
|
{
|
||||||
if (is_array($address)) {
|
if (is_array($address)) {
|
||||||
return array_map('trim', $address ?? []);
|
$ret = [];
|
||||||
|
foreach ($address as $key => $val) {
|
||||||
|
$addr = is_numeric($key) ? $val : $key;
|
||||||
|
$name2 = is_numeric($key) ? '' : $val;
|
||||||
|
$ret[] = new Address($addr, $name2);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
return trim($address ?? '');
|
return [new Address($address, $name)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|array $address
|
* @see createAddressArray()
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function setFrom($address, $name = null)
|
public function setFrom(string|array $address, string $name = ''): static
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->from(...$this->createAddressArray($address, $name));
|
||||||
$this->getSwiftMessage()->setFrom($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|array $address
|
* @see createAddressArray()
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function addFrom($address, $name = null)
|
public function setTo(string|array $address, $name = ''): static
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->to(...$this->createAddressArray($address, $name));
|
||||||
$this->getSwiftMessage()->addFrom($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @see createAddressArray()
|
||||||
*/
|
*/
|
||||||
public function getSender()
|
public function setCC(string|array $address, string $name = ''): static
|
||||||
{
|
{
|
||||||
return $this->getSwiftMessage()->getSender();
|
return $this->cc(...$this->createAddressArray($address, $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $address
|
* @see createAddressArray()
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function setSender($address, $name = null)
|
public function setBCC(string|array $address, string $name = ''): static
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->bcc(...$this->createAddressArray($address, $name));
|
||||||
$this->getSwiftMessage()->setSender($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setSender(string $address, string $name = ''): static
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getReturnPath()
|
|
||||||
{
|
{
|
||||||
return $this->getSwiftMessage()->getReturnPath();
|
return $this->sender(new Address($address, $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setReplyTo(string $address, string $name = ''): static
|
||||||
* The bounce handler address
|
|
||||||
*
|
|
||||||
* @param string $address Email address where bounce notifications should be sent
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setReturnPath($address)
|
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->replyTo(new Address($address, $name));
|
||||||
$this->getSwiftMessage()->setReturnPath($address);
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setSubject(string $subject): static
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getTo()
|
|
||||||
{
|
{
|
||||||
return $this->getSwiftMessage()->getTo();
|
return $this->subject($subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setReturnPath(string $address): static
|
||||||
* Set recipient(s) of the email
|
|
||||||
*
|
|
||||||
* To send to many, pass an array:
|
|
||||||
* ['me@example.com' => 'My Name', 'other@example.com'];
|
|
||||||
*
|
|
||||||
* @param string|array $address The message recipient(s) - if sending to multiple, use an array of address => name
|
|
||||||
* @param string|null $name The name of the recipient (if one)
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setTo($address, $name = null)
|
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->returnPath($address);
|
||||||
$this->getSwiftMessage()->setTo($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setPriority(int $priority): static
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function addTo($address, $name = null)
|
|
||||||
{
|
{
|
||||||
$address = $this->sanitiseAddress($address);
|
return $this->priority($priority);
|
||||||
$this->getSwiftMessage()->addTo($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getCC()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getCc();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setCC($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->setCc($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function addCC($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->addCc($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getBCC()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getBcc();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setBCC($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->setBcc($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function addBCC($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->addBcc($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getReplyTo()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getReplyTo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setReplyTo($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->setReplyTo($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|array $address
|
|
||||||
* @param string|null $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function addReplyTo($address, $name = null)
|
|
||||||
{
|
|
||||||
$address = $this->sanitiseAddress($address);
|
|
||||||
$this->getSwiftMessage()->addReplyTo($address, $name);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getSubject()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getSubject();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $subject The Subject line for the email
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setSubject($subject)
|
|
||||||
{
|
|
||||||
$this->getSwiftMessage()->setSubject($subject);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getPriority()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getPriority();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $priority
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setPriority($priority)
|
|
||||||
{
|
|
||||||
$this->getSwiftMessage()->setPriority($priority);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $path Path to file
|
* @param string $path Path to file
|
||||||
* @param string $alias An override for the name of the file
|
* @param string $alias An override for the name of the file
|
||||||
* @param string $mime The mime type for the attachment
|
* @param string $mime The mime type for the attachment
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function addAttachment($path, $alias = null, $mime = null)
|
public function addAttachment(string $path, ?string $alias = null, ?string $mime = null): static
|
||||||
{
|
{
|
||||||
$attachment = \Swift_Attachment::fromPath($path);
|
return $this->attachFromPath($path, $alias, $mime);
|
||||||
if ($alias) {
|
}
|
||||||
$attachment->setFilename($alias);
|
|
||||||
}
|
|
||||||
if ($mime) {
|
|
||||||
$attachment->setContentType($mime);
|
|
||||||
}
|
|
||||||
$this->getSwiftMessage()->attach($attachment);
|
|
||||||
|
|
||||||
return $this;
|
public function addAttachmentFromData(string $data, string $name, string $mime = null): static
|
||||||
|
{
|
||||||
|
return $this->attach($data, $name, $mime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $data
|
* Get data which is exposed to the template
|
||||||
* @param string $name
|
*
|
||||||
* @param string $mime
|
* The following data is exposed via this method by default:
|
||||||
* @return $this
|
* IsEmail: used to detect if rendering an email template rather than a page template
|
||||||
|
* BaseUrl: used to get the base URL for the email
|
||||||
*/
|
*/
|
||||||
public function addAttachmentFromData($data, $name, $mime = null)
|
public function getData(): ViewableData
|
||||||
{
|
{
|
||||||
$attachment = new \Swift_Attachment($data, $name);
|
$extraData = [
|
||||||
if ($mime) {
|
'IsEmail' => true,
|
||||||
$attachment->setContentType($mime);
|
'BaseURL' => Director::absoluteBaseURL(),
|
||||||
|
];
|
||||||
|
$data = clone $this->data;
|
||||||
|
foreach ($extraData as $key => $value) {
|
||||||
|
if (is_null($data->{$key})) {
|
||||||
|
$data->{$key} = $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->getSwiftMessage()->attach($attachment);
|
$this->extend('updateGetData', $data);
|
||||||
|
return $data;
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array|ViewableData The template data
|
* Set template data
|
||||||
|
*
|
||||||
|
* Calling setData() once means that any content set via text()/html()/setBody() will have no effect
|
||||||
*/
|
*/
|
||||||
public function getData()
|
public function setData(array|ViewableData $data)
|
||||||
{
|
|
||||||
return $this->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array|ViewableData $data The template data to set
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setData($data)
|
|
||||||
{
|
{
|
||||||
|
if (is_array($data)) {
|
||||||
|
$data = ArrayData::create($data);
|
||||||
|
}
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
$this->invalidateBody();
|
$this->dataHasBeenSet = true;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|array $name The data name to add or array to names => value
|
* Add data to be used in the template
|
||||||
* @param string|null $value The value of the data to add
|
*
|
||||||
* @return $this
|
* Calling addData() once means that any content set via text()/html()/setBody() will have no effect
|
||||||
|
*
|
||||||
|
* @param string|array $nameOrData can be either the name to add, or an array of [name => value]
|
||||||
*/
|
*/
|
||||||
public function addData($name, $value = null)
|
public function addData(string|array $nameOrData, mixed $value = null): static
|
||||||
{
|
{
|
||||||
if (is_array($name)) {
|
if (is_array($nameOrData)) {
|
||||||
$this->data = array_merge($this->data, $name);
|
foreach ($nameOrData as $key => $val) {
|
||||||
} elseif (is_array($this->data)) {
|
$this->data->{$key} = $val;
|
||||||
$this->data[$name] = $value;
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->data->$name = $value;
|
$this->data->{$nameOrData} = $value;
|
||||||
}
|
}
|
||||||
|
$this->dataHasBeenSet = true;
|
||||||
$this->invalidateBody();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a datum from the message
|
* Remove a single piece of template data
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function removeData($name)
|
public function removeData(string $name)
|
||||||
{
|
{
|
||||||
if (is_array($this->data)) {
|
$this->data->{$name} = null;
|
||||||
unset($this->data[$name]);
|
|
||||||
} else {
|
|
||||||
$this->data->$name = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->invalidateBody();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getHTMLTemplate(): string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getBody()
|
|
||||||
{
|
|
||||||
return $this->getSwiftMessage()->getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $body The email body
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setBody($body)
|
|
||||||
{
|
|
||||||
$plainPart = $this->findPlainPart();
|
|
||||||
if ($plainPart) {
|
|
||||||
$this->getSwiftMessage()->detach($plainPart);
|
|
||||||
}
|
|
||||||
unset($plainPart);
|
|
||||||
|
|
||||||
$body = HTTP::absoluteURLs($body);
|
|
||||||
$this->getSwiftMessage()->setBody($body);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be replaced with html()
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function invalidateBody()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be replaced with html()');
|
|
||||||
$this->setBody(null);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be replaced with getData()
|
|
||||||
*
|
|
||||||
* @return string The base URL for the email
|
|
||||||
*/
|
|
||||||
public function BaseURL()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be replaced with getData()');
|
|
||||||
return Director::absoluteBaseURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* Debugging help
|
|
||||||
*
|
|
||||||
* @return string Debug info
|
|
||||||
*/
|
|
||||||
public function debug()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
$this->render();
|
|
||||||
|
|
||||||
$class = static::class;
|
|
||||||
return "<h2>Email template {$class}:</h2>\n" . '<pre>' . $this->getSwiftMessage()->toString() . '</pre>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHTMLTemplate()
|
|
||||||
{
|
{
|
||||||
if ($this->HTMLTemplate) {
|
if ($this->HTMLTemplate) {
|
||||||
return $this->HTMLTemplate;
|
return $this->HTMLTemplate;
|
||||||
@ -769,237 +408,123 @@ class Email extends ViewableData
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the template to render the email with
|
* Set the template to render the email with
|
||||||
*
|
|
||||||
* @param string $template
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function setHTMLTemplate($template)
|
public function setHTMLTemplate(string $template): static
|
||||||
{
|
{
|
||||||
if (substr($template ?? '', -3) == '.ss') {
|
if (substr($template ?? '', -3) == '.ss') {
|
||||||
$template = substr($template ?? '', 0, -3);
|
$template = substr($template ?? '', 0, -3);
|
||||||
}
|
}
|
||||||
$this->HTMLTemplate = $template;
|
$this->HTMLTemplate = $template;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the template to render the plain part with
|
* Get the template to render the plain part with
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getPlainTemplate()
|
public function getPlainTemplate(): string
|
||||||
{
|
{
|
||||||
return $this->plainTemplate;
|
return $this->plainTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the template to render the plain part with
|
* Set the template to render the plain part with
|
||||||
*
|
|
||||||
* @param string $template
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function setPlainTemplate($template)
|
public function setPlainTemplate(string $template): static
|
||||||
{
|
{
|
||||||
if (substr($template ?? '', -3) == '.ss') {
|
if (substr($template ?? '', -3) == '.ss') {
|
||||||
$template = substr($template ?? '', 0, -3);
|
$template = substr($template ?? '', 0, -3);
|
||||||
}
|
}
|
||||||
$this->plainTemplate = $template;
|
$this->plainTemplate = $template;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* @param array $recipients
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setFailedRecipients($recipients)
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
$this->failedRecipients = $recipients;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getFailedRecipients()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
return $this->failedRecipients;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be replaced with getData()
|
|
||||||
*
|
|
||||||
* Used by {@link SSViewer} templates to detect if we're rendering an email template rather than a page template
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function IsEmail()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be replaced with getData()');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the message to the recipients
|
* Send the message to the recipients
|
||||||
*
|
|
||||||
* @return bool true if successful or array of failed recipients
|
|
||||||
*/
|
*/
|
||||||
public function send()
|
public function send(): void
|
||||||
{
|
{
|
||||||
if (!$this->getBody()) {
|
$this->updateHtmlAndTextWithRenderedTemplates();
|
||||||
$this->render();
|
Injector::inst()->get(MailerInterface::class)->send($this);
|
||||||
}
|
|
||||||
if (!$this->hasPlainPart()) {
|
|
||||||
$this->generatePlainPartFromBody();
|
|
||||||
}
|
|
||||||
return Injector::inst()->get(Mailer::class)->send($this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array|bool
|
* Send the message to the recipients as plain-only
|
||||||
*/
|
*/
|
||||||
public function sendPlain()
|
public function sendPlain(): void
|
||||||
{
|
{
|
||||||
if (!$this->hasPlainPart()) {
|
$html = $this->getHtmlBody();
|
||||||
$this->render(true);
|
$this->updateHtmlAndTextWithRenderedTemplates(true);
|
||||||
}
|
$this->html(null);
|
||||||
return Injector::inst()->get(Mailer::class)->send($this);
|
Injector::inst()->get(MailerInterface::class)->send($this);
|
||||||
|
$this->html($html);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
* Call html() and/or text() after rendering email templates
|
||||||
|
* If either body html or text were previously explicitly set, those values will not be overwritten
|
||||||
*
|
*
|
||||||
* Render the email
|
* @param bool $plainOnly - if true then do not call html()
|
||||||
* @param bool $plainOnly Only render the message as plain text
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
public function render($plainOnly = false)
|
private function updateHtmlAndTextWithRenderedTemplates(bool $plainOnly = false): void
|
||||||
{
|
{
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
$htmlBody = $this->getHtmlBody();
|
||||||
|
$plainBody = $this->getTextBody();
|
||||||
if ($existingPlainPart = $this->findPlainPart()) {
|
|
||||||
$this->getSwiftMessage()->detach($existingPlainPart);
|
|
||||||
}
|
|
||||||
unset($existingPlainPart);
|
|
||||||
|
|
||||||
// Respect explicitly set body
|
|
||||||
$htmlPart = $plainOnly ? null : $this->getBody();
|
|
||||||
$plainPart = $plainOnly ? $this->getBody() : null;
|
|
||||||
|
|
||||||
// Ensure we can at least render something
|
// Ensure we can at least render something
|
||||||
$htmlTemplate = $this->getHTMLTemplate();
|
$htmlTemplate = $this->getHTMLTemplate();
|
||||||
$plainTemplate = $this->getPlainTemplate();
|
$plainTemplate = $this->getPlainTemplate();
|
||||||
if (!$htmlTemplate && !$plainTemplate && !$plainPart && !$htmlPart) {
|
if (!$htmlTemplate && !$plainTemplate && !$plainBody && !$htmlBody) {
|
||||||
return $this;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$htmlRender = null;
|
||||||
|
$plainRender = null;
|
||||||
|
|
||||||
|
if ($htmlBody && !$this->dataHasBeenSet) {
|
||||||
|
$htmlRender = $htmlBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($plainBody && !$this->dataHasBeenSet) {
|
||||||
|
$plainRender = $plainBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not interfere with emails styles
|
// Do not interfere with emails styles
|
||||||
Requirements::clear();
|
Requirements::clear();
|
||||||
|
|
||||||
// Render plain part
|
// Render plain
|
||||||
if ($plainTemplate && !$plainPart) {
|
if (!$plainRender && $plainTemplate) {
|
||||||
$plainPart = $this->renderWith($plainTemplate, $this->getData())->Plain();
|
$plainRender = $this->getData()->renderWith($plainTemplate)->Plain();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render HTML part, either if sending html email, or a plain part is lacking
|
// Render HTML
|
||||||
if (!$htmlPart && $htmlTemplate && (!$plainOnly || empty($plainPart))) {
|
if (!$htmlRender && $htmlTemplate) {
|
||||||
$htmlPart = $this->renderWith($htmlTemplate, $this->getData());
|
$htmlRender = $this->getData()->renderWith($htmlTemplate)->RAW();
|
||||||
}
|
|
||||||
|
|
||||||
// Plain part fails over to generated from html
|
|
||||||
if (!$plainPart && $htmlPart) {
|
|
||||||
/** @var DBHTMLText $htmlPartObject */
|
|
||||||
$htmlPartObject = DBField::create_field('HTMLFragment', $htmlPart);
|
|
||||||
$plainPart = $htmlPartObject->Plain();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rendering is finished
|
// Rendering is finished
|
||||||
Requirements::restore();
|
Requirements::restore();
|
||||||
|
|
||||||
// Fail if no email to send
|
// Plain render fallbacks to using the html render with html tags removed
|
||||||
if (!$plainPart && !$htmlPart) {
|
if (!$plainRender && $htmlRender) {
|
||||||
return $this;
|
// call html_entity_decode() to ensure any encoded HTML is also stripped inside ->Plain()
|
||||||
|
$dbField = DBField::create_field('HTMLFragment', html_entity_decode($htmlRender));
|
||||||
|
$plainRender = $dbField->Plain();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build HTML / Plain components
|
// Handle edge case where no template was found
|
||||||
if ($htmlPart && !$plainOnly) {
|
if (!$htmlRender && $htmlBody) {
|
||||||
$this->setBody($htmlPart);
|
$htmlRender = $htmlBody;
|
||||||
$this->getSwiftMessage()->setContentType('text/html');
|
|
||||||
$this->getSwiftMessage()->setCharset('utf-8');
|
|
||||||
if ($plainPart) {
|
|
||||||
$this->getSwiftMessage()->addPart($plainPart, 'text/plain', 'utf-8');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($plainPart) {
|
|
||||||
$this->setBody($plainPart);
|
|
||||||
}
|
|
||||||
$this->getSwiftMessage()->setContentType('text/plain');
|
|
||||||
$this->getSwiftMessage()->setCharset('utf-8');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
if (!$plainRender && $plainBody) {
|
||||||
}
|
$plainRender = $plainBody;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* @return Swift_MimePart|false
|
|
||||||
*/
|
|
||||||
public function findPlainPart()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
foreach ($this->getSwiftMessage()->getChildren() as $child) {
|
|
||||||
if ($child instanceof Swift_MimePart && $child->getContentType() == 'text/plain') {
|
|
||||||
return $child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if ($plainRender) {
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
$this->text($plainRender);
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasPlainPart()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
if ($this->getSwiftMessage()->getContentType() === 'text/plain') {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return (bool) $this->findPlainPart();
|
if ($htmlRender && !$plainOnly) {
|
||||||
}
|
$this->html($htmlRender);
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*
|
|
||||||
* Automatically adds a plain part to the email generated from the current Body
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function generatePlainPartFromBody()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
$plainPart = $this->findPlainPart();
|
|
||||||
if ($plainPart) {
|
|
||||||
$this->getSwiftMessage()->detach($plainPart);
|
|
||||||
}
|
}
|
||||||
unset($plainPart);
|
|
||||||
|
|
||||||
$this->getSwiftMessage()->addPart(
|
|
||||||
Convert::xml2raw($this->getBody()),
|
|
||||||
'text/plain',
|
|
||||||
'utf-8'
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
src/Control/Email/MailerSubscriber.php
Normal file
100
src/Control/Email/MailerSubscriber.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Control\Email;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use SilverStripe\Control\HTTP;
|
||||||
|
use SilverStripe\Core\Extensible;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This subscriber is registered in BaseKernel->bootEmail()
|
||||||
|
*
|
||||||
|
* See https://symfony.com/doc/current/mailer.html#mailer-events for further info
|
||||||
|
*/
|
||||||
|
class MailerSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
use Injectable;
|
||||||
|
use Extensible;
|
||||||
|
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
MessageEvent::class => 'onMessage',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(MessageEvent $event): void
|
||||||
|
{
|
||||||
|
$email = $event->getMessage();
|
||||||
|
if (!($email instanceof Email)) {
|
||||||
|
throw new InvalidArgumentException('Message is not a ' . Email::class);
|
||||||
|
}
|
||||||
|
$this->applyConfig($email);
|
||||||
|
$this->updateUrls($email);
|
||||||
|
$this->extend('updateOnMessage', $email, $event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyConfig(Email $email): void
|
||||||
|
{
|
||||||
|
$sendAllTo = Email::getSendAllEmailsTo();
|
||||||
|
if (!empty($sendAllTo)) {
|
||||||
|
$this->setTo($email, $sendAllTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ccAllTo = Email::getCCAllEmailsTo();
|
||||||
|
if (!empty($ccAllTo)) {
|
||||||
|
$email->addCc(...$ccAllTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bccAllTo = Email::getBCCAllEmailsTo();
|
||||||
|
if (!empty($bccAllTo)) {
|
||||||
|
$email->addBcc(...$bccAllTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sendAllFrom = Email::getSendAllEmailsFrom();
|
||||||
|
if (!empty($sendAllFrom)) {
|
||||||
|
$this->setFrom($email, $sendAllFrom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setTo(Email $email, array $sendAllTo): void
|
||||||
|
{
|
||||||
|
$headers = $email->getHeaders();
|
||||||
|
// store the old data as X-Original-* Headers for debugging
|
||||||
|
if (!empty($email->getTo())) {
|
||||||
|
$headers->addMailboxListHeader('X-Original-To', $email->getTo());
|
||||||
|
}
|
||||||
|
if (!empty($email->getCc())) {
|
||||||
|
$headers->addMailboxListHeader('X-Original-Cc', $email->getCc());
|
||||||
|
}
|
||||||
|
if (!empty($email->getBcc())) {
|
||||||
|
$headers->addMailboxListHeader('X-Original-Bcc', $email->getBcc());
|
||||||
|
}
|
||||||
|
// set default recipient and remove all other recipients
|
||||||
|
$email->to(...$sendAllTo);
|
||||||
|
$email->cc(...[]);
|
||||||
|
$email->bcc(...[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setFrom(Email $email, array $sendAllFrom): void
|
||||||
|
{
|
||||||
|
$headers = $email->getHeaders();
|
||||||
|
if (!empty($email->getFrom())) {
|
||||||
|
$headers->addMailboxListHeader('X-Original-From', $email->getFrom());
|
||||||
|
}
|
||||||
|
$email->from(...$sendAllFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateUrls(Email $email): void
|
||||||
|
{
|
||||||
|
if ($email->getHtmlBody()) {
|
||||||
|
$email->html(HTTP::absoluteURLs($email->getHtmlBody()));
|
||||||
|
}
|
||||||
|
if ($email->getTextBody()) {
|
||||||
|
$email->text(HTTP::absoluteURLs($email->getTextBody()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Control\Email;
|
|
||||||
|
|
||||||
use SilverStripe\Core\Config\Configurable;
|
|
||||||
use SilverStripe\Core\Injector\Injectable;
|
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
use Swift_Mailer;
|
|
||||||
use Swift_Message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be replaced with symfony/mailer
|
|
||||||
*
|
|
||||||
* Mailer objects are responsible for actually sending emails.
|
|
||||||
* The default Mailer class will use PHP's mail() function.
|
|
||||||
*/
|
|
||||||
class SwiftMailer implements Mailer
|
|
||||||
{
|
|
||||||
|
|
||||||
use Configurable;
|
|
||||||
use Injectable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
* @config
|
|
||||||
*/
|
|
||||||
private static $swift_plugins = [
|
|
||||||
SwiftPlugin::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Swift_Mailer
|
|
||||||
*/
|
|
||||||
private $swift;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Email $message
|
|
||||||
* @return bool Whether the sending was "successful" or not
|
|
||||||
*/
|
|
||||||
public function send($message)
|
|
||||||
{
|
|
||||||
$swiftMessage = $message->getSwiftMessage();
|
|
||||||
$failedRecipients = [];
|
|
||||||
$result = $this->sendSwift($swiftMessage, $failedRecipients);
|
|
||||||
$message->setFailedRecipients($failedRecipients);
|
|
||||||
|
|
||||||
return $result != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Swift_Message $message
|
|
||||||
* @param array $failedRecipients
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function sendSwift($message, &$failedRecipients = null)
|
|
||||||
{
|
|
||||||
return $this->getSwiftMailer()->send($message, $failedRecipients);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Swift_Mailer
|
|
||||||
*/
|
|
||||||
public function getSwiftMailer()
|
|
||||||
{
|
|
||||||
return $this->swift;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Swift_Mailer $swift
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setSwiftMailer($swift)
|
|
||||||
{
|
|
||||||
// register any required plugins
|
|
||||||
foreach ($this->config()->get('swift_plugins') as $plugin) {
|
|
||||||
$swift->registerPlugin(Injector::inst()->create($plugin));
|
|
||||||
}
|
|
||||||
$this->swift = $swift;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Control\Email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Will be replaced with symfony/mailer
|
|
||||||
*/
|
|
||||||
class SwiftPlugin implements \Swift_Events_SendListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Before sending a message make sure all our overrides are taken into account
|
|
||||||
*
|
|
||||||
* @param \Swift_Events_SendEvent $evt
|
|
||||||
*/
|
|
||||||
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
|
|
||||||
{
|
|
||||||
/** @var \Swift_Message $message */
|
|
||||||
$message = $evt->getMessage();
|
|
||||||
|
|
||||||
$sendAllTo = Email::getSendAllEmailsTo();
|
|
||||||
if (!empty($sendAllTo)) {
|
|
||||||
$this->setTo($message, $sendAllTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ccAllTo = Email::getCCAllEmailsTo();
|
|
||||||
if (!empty($ccAllTo)) {
|
|
||||||
foreach ($ccAllTo as $address => $name) {
|
|
||||||
$message->addCc($address, $name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$bccAllTo = Email::getBCCAllEmailsTo();
|
|
||||||
if (!empty($bccAllTo)) {
|
|
||||||
foreach ($bccAllTo as $address => $name) {
|
|
||||||
$message->addBcc($address, $name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sendAllFrom = Email::getSendAllEmailsFrom();
|
|
||||||
if (!empty($sendAllFrom)) {
|
|
||||||
$this->setFrom($message, $sendAllFrom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Swift_Message $message
|
|
||||||
* @param array|string $to
|
|
||||||
*/
|
|
||||||
protected function setTo($message, $to)
|
|
||||||
{
|
|
||||||
$headers = $message->getHeaders();
|
|
||||||
$origTo = $message->getTo();
|
|
||||||
$cc = $message->getCc();
|
|
||||||
$bcc = $message->getBcc();
|
|
||||||
|
|
||||||
// set default recipient and remove all other recipients
|
|
||||||
$message->setTo($to);
|
|
||||||
$headers->removeAll('Cc');
|
|
||||||
$headers->removeAll('Bcc');
|
|
||||||
|
|
||||||
// store the old data as X-Original-* Headers for debugging
|
|
||||||
$headers->addMailboxHeader('X-Original-To', $origTo);
|
|
||||||
$headers->addMailboxHeader('X-Original-Cc', $cc);
|
|
||||||
$headers->addMailboxHeader('X-Original-Bcc', $bcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \Swift_Message $message
|
|
||||||
* @param array|string $from
|
|
||||||
*/
|
|
||||||
protected function setFrom($message, $from)
|
|
||||||
{
|
|
||||||
$headers = $message->getHeaders();
|
|
||||||
$origFrom = $message->getFrom();
|
|
||||||
$headers->addMailboxHeader('X-Original-From', $origFrom);
|
|
||||||
$message->setFrom($from);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sendPerformed(\Swift_Events_SendEvent $evt)
|
|
||||||
{
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,6 @@ use SilverStripe\Control\Controller;
|
|||||||
use SilverStripe\Control\Cookie;
|
use SilverStripe\Control\Cookie;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use SilverStripe\Control\Email\Mailer;
|
|
||||||
use SilverStripe\Control\HTTPApplication;
|
use SilverStripe\Control\HTTPApplication;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
@ -39,6 +38,10 @@ use SilverStripe\Security\Member;
|
|||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mailer\Transport\NullTransport;
|
||||||
|
use Symfony\Component\Messenger\MessageBusInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case class for the Silverstripe framework.
|
* Test case class for the Silverstripe framework.
|
||||||
@ -317,20 +320,17 @@ abstract class SapphireTest extends TestCase implements TestOnly
|
|||||||
SSViewer::config()->update('source_file_comments', false);
|
SSViewer::config()->update('source_file_comments', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the test mailer
|
// Set up the test mailer and register it as a service
|
||||||
if (class_exists(TestMailer::class)) {
|
$dispatcher = Injector::inst()->get(EventDispatcherInterface::class . '.mailer');
|
||||||
Injector::inst()->registerService(new TestMailer(), Mailer::class);
|
$transport = new NullTransport($dispatcher);
|
||||||
}
|
$testMailer = new TestMailer($transport, $dispatcher);
|
||||||
|
Injector::inst()->registerService($testMailer, MailerInterface::class);
|
||||||
if (class_exists(Email::class)) {
|
Email::config()->remove('send_all_emails_to');
|
||||||
Email::config()->remove('send_all_emails_to');
|
Email::config()->remove('send_all_emails_from');
|
||||||
Email::config()->remove('send_all_emails_from');
|
Email::config()->remove('cc_all_emails_to');
|
||||||
Email::config()->remove('cc_all_emails_to');
|
Email::config()->remove('bcc_all_emails_to');
|
||||||
Email::config()->remove('bcc_all_emails_to');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to determine if the current test should enable a test database
|
* Helper method to determine if the current test should enable a test database
|
||||||
*
|
*
|
||||||
@ -611,8 +611,8 @@ abstract class SapphireTest extends TestCase implements TestOnly
|
|||||||
*/
|
*/
|
||||||
public function clearEmails()
|
public function clearEmails()
|
||||||
{
|
{
|
||||||
/** @var Mailer $mailer */
|
/** @var MailerInterface $mailer */
|
||||||
$mailer = Injector::inst()->get(Mailer::class);
|
$mailer = Injector::inst()->get(MailerInterface::class);
|
||||||
if ($mailer instanceof TestMailer) {
|
if ($mailer instanceof TestMailer) {
|
||||||
$mailer->clearEmails();
|
$mailer->clearEmails();
|
||||||
return true;
|
return true;
|
||||||
@ -632,8 +632,8 @@ abstract class SapphireTest extends TestCase implements TestOnly
|
|||||||
*/
|
*/
|
||||||
public static function findEmail($to, $from = null, $subject = null, $content = null)
|
public static function findEmail($to, $from = null, $subject = null, $content = null)
|
||||||
{
|
{
|
||||||
/** @var Mailer $mailer */
|
/** @var MailerInterface $mailer */
|
||||||
$mailer = Injector::inst()->get(Mailer::class);
|
$mailer = Injector::inst()->get(MailerInterface::class);
|
||||||
if ($mailer instanceof TestMailer) {
|
if ($mailer instanceof TestMailer) {
|
||||||
return $mailer->findEmail($to, $from, $subject, $content);
|
return $mailer->findEmail($to, $from, $subject, $content);
|
||||||
}
|
}
|
||||||
|
@ -2,98 +2,72 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Dev;
|
namespace SilverStripe\Dev;
|
||||||
|
|
||||||
use SilverStripe\Control\Email\Mailer;
|
use Exception;
|
||||||
use Swift_Attachment;
|
use InvalidArgumentException;
|
||||||
|
use SilverStripe\Control\Email\Email;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\Mailer\Envelope;
|
||||||
|
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mailer\Messenger\SendEmailMessage;
|
||||||
|
use Symfony\Component\Mime\RawMessage;
|
||||||
|
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||||
|
use Symfony\Component\Messenger\Event\SendMessageToTransportsEvent;
|
||||||
|
use Symfony\Component\Messenger\MessageBusInterface;
|
||||||
|
use Symfony\Component\Messenger\Envelope as MessagerEnvelope;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Part\DataPart;
|
||||||
|
|
||||||
class TestMailer implements Mailer
|
class TestMailer implements MailerInterface
|
||||||
{
|
{
|
||||||
/**
|
private array $emailsSent = [];
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $emailsSent = [];
|
|
||||||
|
|
||||||
public function send($email)
|
private TransportInterface $transport;
|
||||||
|
private EventDispatcherInterface $dispatcher;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
TransportInterface $transport,
|
||||||
|
EventDispatcherInterface $dispatcher
|
||||||
|
) {
|
||||||
|
$this->transport = $transport;
|
||||||
|
$this->dispatcher = $dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(RawMessage $message, Envelope $envelope = null): void
|
||||||
{
|
{
|
||||||
// Detect body type
|
if (!is_a($message, Email::class)) {
|
||||||
$htmlContent = null;
|
throw new InvalidArgumentException('$message must be a ' . Email::class);
|
||||||
$plainContent = null;
|
|
||||||
if ($email->getSwiftMessage()->getContentType() === 'text/plain') {
|
|
||||||
$type = 'plain';
|
|
||||||
$plainContent = $email->getBody();
|
|
||||||
} else {
|
|
||||||
$type = 'html';
|
|
||||||
$htmlContent = $email->getBody();
|
|
||||||
$plainPart = $email->findPlainPart();
|
|
||||||
if ($plainPart) {
|
|
||||||
$plainContent = $plainPart->getBody();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/** @var Email $email */
|
||||||
// Get attachments
|
$email = $message;
|
||||||
$attachedFiles = [];
|
$this->dispatchEvent($email, $envelope);
|
||||||
foreach ($email->getSwiftMessage()->getChildren() as $child) {
|
$this->emailsSent[] = [
|
||||||
if ($child instanceof Swift_Attachment) {
|
'Type' => $email->getHtmlBody() ? 'html' : 'plain',
|
||||||
$attachedFiles[] = [
|
'To' => $this->convertAddressesToString($email->getTo()),
|
||||||
'contents' => $child->getBody(),
|
'From' => $this->convertAddressesToString($email->getFrom()),
|
||||||
'filename' => $child->getFilename(),
|
|
||||||
'mimetype' => $child->getContentType(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialise email
|
|
||||||
$serialised = [
|
|
||||||
'Type' => $type,
|
|
||||||
'To' => implode(';', array_keys($email->getTo() ?: [])),
|
|
||||||
'From' => implode(';', array_keys($email->getFrom() ?: [])),
|
|
||||||
'Subject' => $email->getSubject(),
|
'Subject' => $email->getSubject(),
|
||||||
'Content' => $email->getBody(),
|
'Content' => $email->getHtmlBody() ?: $email->getTextBody(),
|
||||||
'AttachedFiles' => $attachedFiles,
|
'Headers' => $email->getHeaders(),
|
||||||
'Headers' => $email->getSwiftMessage()->getHeaders(),
|
'PlainContent' => $email->getTextBody(),
|
||||||
|
'HtmlContent' => $email->getHtmlBody(),
|
||||||
|
'AttachedFiles' => array_map(fn(DataPart $attachment) => [
|
||||||
|
'contents' => $attachment->getBody(),
|
||||||
|
'filename' => $attachment->getFilename(),
|
||||||
|
'mimetype' => $attachment->getContentType()
|
||||||
|
], $email->getAttachments()),
|
||||||
];
|
];
|
||||||
if ($plainContent) {
|
|
||||||
$serialised['PlainContent'] = $plainContent;
|
|
||||||
}
|
|
||||||
if ($htmlContent) {
|
|
||||||
$serialised['HtmlContent'] = $htmlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->saveEmail($serialised);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save a single email to the log
|
|
||||||
*
|
|
||||||
* @param array $data A map of information about the email
|
|
||||||
*/
|
|
||||||
protected function saveEmail($data)
|
|
||||||
{
|
|
||||||
$this->emailsSent[] = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the log of emails sent
|
|
||||||
*/
|
|
||||||
public function clearEmails()
|
|
||||||
{
|
|
||||||
$this->emailsSent = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for an email that was sent.
|
* Search for an email that was sent.
|
||||||
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
||||||
*
|
|
||||||
* @param string $to
|
|
||||||
* @param string $from
|
|
||||||
* @param string $subject
|
|
||||||
* @param string $content
|
|
||||||
* @return array|null Contains keys: 'Type', 'To', 'From', 'Subject', 'Content', 'PlainContent', 'AttachedFiles',
|
|
||||||
* 'HtmlContent'
|
|
||||||
*/
|
*/
|
||||||
public function findEmail($to, $from = null, $subject = null, $content = null)
|
public function findEmail(
|
||||||
{
|
string $to,
|
||||||
|
?string $from = null,
|
||||||
|
?string $subject = null,
|
||||||
|
?string $content = null
|
||||||
|
): ?array {
|
||||||
$compare = [
|
$compare = [
|
||||||
'To' => $to,
|
'To' => $to,
|
||||||
'From' => $from,
|
'From' => $from,
|
||||||
@ -131,9 +105,28 @@ class TestMailer implements Mailer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $value
|
* Clear the log of emails sent
|
||||||
*/
|
*/
|
||||||
private function normaliseSpaces(string $value)
|
public function clearEmails(): void
|
||||||
|
{
|
||||||
|
$this->emailsSent = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertAddressesToString(array $addresses): string
|
||||||
|
{
|
||||||
|
return implode(',', array_map(fn(Address $address) => $address->getAddress(), $addresses));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dispatchEvent(Email $email, Envelope $envelope = null): void
|
||||||
|
{
|
||||||
|
$sender = $email->getSender()[0] ?? $email->getFrom()[0] ?? new Address('test.sender@example.com');
|
||||||
|
$recipients = empty($email->getTo()) ? [new Address('test.recipient@example.com')] : $email->getTo();
|
||||||
|
$envelope ??= new Envelope($sender, $recipients);
|
||||||
|
$event = new MessageEvent($email, $envelope, $this->transport);
|
||||||
|
$this->dispatcher->dispatch($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normaliseSpaces(string $value): string
|
||||||
{
|
{
|
||||||
return str_replace([', ', '; '], [',', ';'], $value ?? '');
|
return str_replace([', ', '; '], [',', ';'], $value ?? '');
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ use SilverStripe\CMS\Controllers\CMSMain;
|
|||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use SilverStripe\Control\Email\Mailer;
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
@ -36,6 +35,7 @@ use SilverStripe\ORM\SS_List;
|
|||||||
use SilverStripe\ORM\UnsavedRelationList;
|
use SilverStripe\ORM\UnsavedRelationList;
|
||||||
use SilverStripe\ORM\ValidationException;
|
use SilverStripe\ORM\ValidationException;
|
||||||
use SilverStripe\ORM\ValidationResult;
|
use SilverStripe\ORM\ValidationResult;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The member class which represents the users of the system
|
* The member class which represents the users of the system
|
||||||
@ -907,7 +907,7 @@ class Member extends DataObject
|
|||||||
// We don't send emails out on dev/tests sites to prevent accidentally spamming users.
|
// We don't send emails out on dev/tests sites to prevent accidentally spamming users.
|
||||||
// However, if TestMailer is in use this isn't a risk.
|
// However, if TestMailer is in use this isn't a risk.
|
||||||
// @todo some developers use external tools, so emailing might be a good idea anyway
|
// @todo some developers use external tools, so emailing might be a good idea anyway
|
||||||
if ((Director::isLive() || Injector::inst()->get(Mailer::class) instanceof TestMailer)
|
if ((Director::isLive() || Injector::inst()->get(MailerInterface::class) instanceof TestMailer)
|
||||||
&& $this->isChanged('Password')
|
&& $this->isChanged('Password')
|
||||||
&& $this->record['Password']
|
&& $this->record['Password']
|
||||||
&& $this->Email
|
&& $this->Email
|
||||||
|
@ -2,73 +2,74 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Control\Tests\Email;
|
namespace SilverStripe\Control\Tests\Email;
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use SilverStripe\Control\Email\Mailer;
|
|
||||||
use SilverStripe\Control\Email\SwiftMailer;
|
|
||||||
use SilverStripe\Control\Tests\Email\EmailTest\EmailSubClass;
|
use SilverStripe\Control\Tests\Email\EmailTest\EmailSubClass;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\Dev\TestMailer;
|
use SilverStripe\Dev\TestMailer;
|
||||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use Swift_Attachment;
|
use SilverStripe\View\ViewableData;
|
||||||
use Swift_Mailer;
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
use Swift_Message;
|
use Symfony\Component\Mime\Address;
|
||||||
use Swift_NullTransport;
|
use Symfony\Component\Mime\Part\DataPart;
|
||||||
use Swift_RfcComplianceException;
|
use Symfony\Component\Mime\Part\AbstractPart;
|
||||||
|
|
||||||
class EmailTest extends SapphireTest
|
class EmailTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
private array $origThemes = [];
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
|
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
|
||||||
|
$this->origThemes = SSViewer::get_themes();
|
||||||
|
SSViewer::set_themes([
|
||||||
|
'silverstripe/framework:/tests/php/Control/Email/EmailTest',
|
||||||
|
'$default',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddAttachment()
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
parent::tearDown();
|
||||||
|
SSViewer::set_themes($this->origThemes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAttachment(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
|
|
||||||
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||||
|
$attachments = $email->getAttachments();
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
$this->assertCount(1, $attachments);
|
||||||
$this->assertCount(1, $children);
|
$attachment = $this->getFirstAttachment($attachments);
|
||||||
|
$this->assertSame('text/plain', $attachment->getContentType());
|
||||||
/** @var Swift_Attachment $child */
|
$this->assertSame('attachment.txt', $attachment->getFilename());
|
||||||
$child = reset($children);
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Swift_Attachment::class, $child);
|
|
||||||
$this->assertEquals('text/plain', $child->getContentType());
|
|
||||||
$this->assertEquals('attachment.txt', $child->getFilename());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddAttachmentFromData()
|
public function testAddAttachmentFromData(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
|
|
||||||
$email->addAttachmentFromData('foo bar', 'foo.txt', 'text/plain');
|
$email->addAttachmentFromData('foo bar', 'foo.txt', 'text/plain');
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
$attachments = $email->getAttachments();
|
||||||
|
$this->assertCount(1, $attachments);
|
||||||
|
$attachment = $this->getFirstAttachment($attachments);
|
||||||
|
$this->assertSame('text/plain', $attachment->getContentType());
|
||||||
|
$this->assertSame('foo.txt', $attachment->getFilename());
|
||||||
|
$this->assertSame('foo bar', $attachment->getBody());
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertCount(1, $children);
|
private function getFirstAttachment(array $attachments): DataPart
|
||||||
|
{
|
||||||
/** @var Swift_Attachment $child */
|
return $attachments[0];
|
||||||
$child = reset($children);
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Swift_Attachment::class, $child);
|
|
||||||
$this->assertEquals('foo bar', $child->getBody());
|
|
||||||
$this->assertEquals('text/plain', $child->getContentType());
|
|
||||||
$this->assertEquals('foo.txt', $child->getFilename());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideValidEmailAddresses
|
* @dataProvider provideValidEmailAddresses
|
||||||
*/
|
*/
|
||||||
public function testValidEmailAddress($email)
|
public function testValidEmailAddress($email): void
|
||||||
{
|
{
|
||||||
$this->assertTrue(Email::is_valid_address($email));
|
$this->assertTrue(Email::is_valid_address($email));
|
||||||
}
|
}
|
||||||
@ -76,26 +77,26 @@ class EmailTest extends SapphireTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideInvalidEmailAddresses
|
* @dataProvider provideInvalidEmailAddresses
|
||||||
*/
|
*/
|
||||||
public function testInvalidEmailAddress($email)
|
public function testInvalidEmailAddress($email): void
|
||||||
{
|
{
|
||||||
$this->assertFalse(Email::is_valid_address($email));
|
$this->assertFalse(Email::is_valid_address($email));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideValidEmailAddresses()
|
public function provideValidEmailAddresses(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['test@example.com', 'test-123@sub.example.com'],
|
['test@example.com', 'test-123@sub.example.com'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideInvalidEmailAddresses()
|
public function provideInvalidEmailAddresses(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['foo.bar@', '@example.com', 'foo@'],
|
['foo.bar@', '@example.com', 'foo@'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testObfuscate()
|
public function testObfuscate(): void
|
||||||
{
|
{
|
||||||
$emailAddress = 'test-1@example.com';
|
$emailAddress = 'test-1@example.com';
|
||||||
|
|
||||||
@ -111,392 +112,272 @@ class EmailTest extends SapphireTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSendPlain()
|
private function getTemplateClass(string $templateName): string
|
||||||
{
|
{
|
||||||
$email = $this->makeEmailMock('Test send plain');
|
return implode('\\', ['SilverStripe', 'Control', 'Tests', 'Email', 'EmailTest', $templateName]);
|
||||||
|
|
||||||
// email should not call render if a body is supplied
|
|
||||||
$email->expects($this->never())->method('renderWith');
|
|
||||||
$successful = $email->sendPlain();
|
|
||||||
|
|
||||||
$this->assertTrue($successful);
|
|
||||||
$this->assertEmpty($email->getFailedRecipients());
|
|
||||||
|
|
||||||
/** @var TestMailer $mailer */
|
|
||||||
$mailer = Injector::inst()->get(Mailer::class);
|
|
||||||
$sentMail = $mailer->findEmail('to@example.com');
|
|
||||||
|
|
||||||
$this->assertTrue(is_array($sentMail));
|
|
||||||
|
|
||||||
$this->assertEquals('to@example.com', $sentMail['To']);
|
|
||||||
$this->assertEquals('from@example.com', $sentMail['From']);
|
|
||||||
$this->assertEquals('Test send plain', $sentMail['Subject']);
|
|
||||||
$this->assertEquals('Body for Test send plain', $sentMail['Content']);
|
|
||||||
|
|
||||||
$this->assertCount(1, $sentMail['AttachedFiles']);
|
|
||||||
$child = reset($sentMail['AttachedFiles']);
|
|
||||||
$this->assertEquals('text/plain', $child['mimetype']);
|
|
||||||
$this->assertEquals('attachment.txt', $child['filename']);
|
|
||||||
$this->assertEquals('Hello, I\'m a text document.', $child['contents']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSend()
|
private function getMailer(): TestMailer
|
||||||
{
|
{
|
||||||
/** @var Email|MockObject $email */
|
return Injector::inst()->get(MailerInterface::class);
|
||||||
$email = $this->makeEmailMock('Test send HTML');
|
|
||||||
|
|
||||||
// email should not call render if a body is supplied
|
|
||||||
$email->expects($this->never())->method('renderWith');
|
|
||||||
$successful = $email->send();
|
|
||||||
|
|
||||||
$this->assertTrue($successful);
|
|
||||||
$this->assertEmpty($email->getFailedRecipients());
|
|
||||||
|
|
||||||
/** @var TestMailer $mailer */
|
|
||||||
$mailer = Injector::inst()->get(Mailer::class);
|
|
||||||
$sentMail = $mailer->findEmail('to@example.com');
|
|
||||||
|
|
||||||
$this->assertTrue(is_array($sentMail));
|
|
||||||
|
|
||||||
$this->assertEquals('to@example.com', $sentMail['To']);
|
|
||||||
$this->assertEquals('from@example.com', $sentMail['From']);
|
|
||||||
$this->assertEquals('Test send HTML', $sentMail['Subject']);
|
|
||||||
$this->assertEquals('Body for Test send HTML', $sentMail['Content']);
|
|
||||||
|
|
||||||
$this->assertCount(1, $sentMail['AttachedFiles']);
|
|
||||||
$child = reset($sentMail['AttachedFiles']);
|
|
||||||
$this->assertEquals('text/plain', $child['mimetype']);
|
|
||||||
$this->assertEquals('attachment.txt', $child['filename']);
|
|
||||||
$this->assertEquals('Hello, I\'m a text document.', $child['contents']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRenderedSend()
|
private function createTestEmail(string $subject = 'My subject', $setPlain = true): Email
|
||||||
{
|
{
|
||||||
/** @var Email|MockObject $email */
|
|
||||||
$email = $this->getMockBuilder(Email::class)
|
|
||||||
->enableProxyingToOriginalMethods()
|
|
||||||
->getMock();
|
|
||||||
$email->setFrom('from@example.com');
|
|
||||||
$email->setTo('to@example.com');
|
|
||||||
$email->setData([
|
|
||||||
'EmailContent' => 'test',
|
|
||||||
]);
|
|
||||||
$this->assertFalse($email->hasPlainPart());
|
|
||||||
$this->assertEmpty($email->getBody());
|
|
||||||
// these seem to fail for some reason :/
|
|
||||||
//$email->expects($this->once())->method('render');
|
|
||||||
//$email->expects($this->once())->method('generatePlainPartFromBody');
|
|
||||||
$email->send();
|
|
||||||
$this->assertTrue($email->hasPlainPart());
|
|
||||||
$this->assertNotEmpty($email->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRenderedSendSubclass()
|
|
||||||
{
|
|
||||||
// Include dev theme
|
|
||||||
SSViewer::set_themes([
|
|
||||||
'silverstripe/framework:/tests/php/Control/Email/EmailTest',
|
|
||||||
'$default',
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** @var Email|MockObject $email */
|
|
||||||
$email = $this->getMockBuilder(EmailSubClass::class)
|
|
||||||
->enableProxyingToOriginalMethods()
|
|
||||||
->getMock();
|
|
||||||
$email->setFrom('from@example.com');
|
|
||||||
$email->setTo('to@example.com');
|
|
||||||
$email->setData([
|
|
||||||
'EmailContent' => 'test',
|
|
||||||
]);
|
|
||||||
$this->assertFalse($email->hasPlainPart());
|
|
||||||
$this->assertEmpty($email->getBody());
|
|
||||||
$email->send();
|
|
||||||
$this->assertTrue($email->hasPlainPart());
|
|
||||||
$this->assertNotEmpty($email->getBody());
|
|
||||||
$this->assertStringContainsString('<h1>Email Sub-class</h1>', $email->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testConsturctor()
|
|
||||||
{
|
|
||||||
$email = new Email(
|
|
||||||
'from@example.com',
|
|
||||||
'to@example.com',
|
|
||||||
'subject',
|
|
||||||
'body',
|
|
||||||
'cc@example.com',
|
|
||||||
'bcc@example.com',
|
|
||||||
'bounce@example.com'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
$this->assertCount(1, $email->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
$this->assertEquals('subject', $email->getSubject());
|
|
||||||
$this->assertEquals('body', $email->getBody());
|
|
||||||
$this->assertCount(1, $email->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
$this->assertCount(1, $email->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
$this->assertEquals('bounce@example.com', $email->getReturnPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetSwiftMessage()
|
|
||||||
{
|
|
||||||
$email = new Email(
|
|
||||||
'from@example.com',
|
|
||||||
'to@example.com',
|
|
||||||
'subject',
|
|
||||||
'body',
|
|
||||||
'cc@example.com',
|
|
||||||
'bcc@example.com',
|
|
||||||
'bounce@example.com'
|
|
||||||
);
|
|
||||||
$swiftMessage = $email->getSwiftMessage();
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Swift_Message::class, $swiftMessage);
|
|
||||||
|
|
||||||
$this->assertCount(1, $swiftMessage->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($swiftMessage->getFrom() ?? []));
|
|
||||||
$this->assertCount(1, $swiftMessage->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($swiftMessage->getTo() ?? []));
|
|
||||||
$this->assertEquals('subject', $swiftMessage->getSubject());
|
|
||||||
$this->assertEquals('body', $swiftMessage->getBody());
|
|
||||||
$this->assertCount(1, $swiftMessage->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($swiftMessage->getCc() ?? []));
|
|
||||||
$this->assertCount(1, $swiftMessage->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($swiftMessage->getBcc() ?? []));
|
|
||||||
$this->assertEquals('bounce@example.com', $swiftMessage->getReturnPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetSwiftMessage()
|
|
||||||
{
|
|
||||||
Email::config()->update('admin_email', 'admin@example.com');
|
|
||||||
DBDatetime::set_mock_now('2017-01-01 07:00:00');
|
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$swiftMessage = new Swift_Message();
|
$email->setFrom('from@example.com');
|
||||||
$email->setSwiftMessage($swiftMessage);
|
$email->setTo('to@example.com');
|
||||||
$dateTime = new DateTime();
|
$email->setSubject($subject);
|
||||||
$dateTime->setTimestamp(DBDatetime::now()->getTimestamp());
|
if ($setPlain) {
|
||||||
$email->getSwiftMessage()->setDate($dateTime);
|
$email->text("Plain body for $subject");
|
||||||
$this->assertCount(1, $email->getFrom());
|
}
|
||||||
$this->assertContains('admin@example.com', array_keys($swiftMessage->getFrom() ?? []));
|
$email->html("<p>HTML body for $subject</p>");
|
||||||
$this->assertEquals(strtotime('2017-01-01 07:00:00'), $swiftMessage->getDate()->getTimestamp());
|
$email->setCC('cc@example.com');
|
||||||
$this->assertEquals($swiftMessage, $email->getSwiftMessage());
|
$email->setBCC('bcc@example.com');
|
||||||
|
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||||
|
return $email;
|
||||||
|
}
|
||||||
|
|
||||||
// check from field is retained
|
public function testSendPlain(): void
|
||||||
$swiftMessage = new Swift_Message();
|
{
|
||||||
$swiftMessage->setFrom('from@example.com');
|
$email = $this->createTestEmail('Test send plain');
|
||||||
$email->setSwiftMessage($swiftMessage);
|
$email->sendPlain();
|
||||||
|
$this->assertStringNotContainsString($email->getTextBody(), 'My Plain Template');
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
|
||||||
|
$this->assertSame('to@example.com', $sentMail['To']);
|
||||||
|
$this->assertSame('from@example.com', $sentMail['From']);
|
||||||
|
$this->assertSame('Test send plain', $sentMail['Subject']);
|
||||||
|
$this->assertStringContainsString('Plain body for Test send plain', $sentMail['Content']);
|
||||||
|
|
||||||
|
$this->assertCount(1, $sentMail['AttachedFiles']);
|
||||||
|
$child = reset($sentMail['AttachedFiles']);
|
||||||
|
$this->assertSame('text/plain', $child['mimetype']);
|
||||||
|
$this->assertSame('attachment.txt', $child['filename']);
|
||||||
|
$this->assertSame('Hello, I\'m a text document.', $child['contents']);
|
||||||
|
|
||||||
|
// assert MIME types
|
||||||
|
// explicitly setting $email->html(null) because sendPlain() will itself set $this->html(null), and then
|
||||||
|
// revert it to its previous AFTER sending the email. For testing purposes, we need to manuall set it
|
||||||
|
// to null in order to test the MIME types for what would have been sent in practice
|
||||||
|
$email->html(null);
|
||||||
|
$this->assertSame([
|
||||||
|
'text/plain charset: utf-8',
|
||||||
|
'text/plain disposition: attachment filename: attachment.txt'
|
||||||
|
], array_map(fn(AbstractPart $part) => $part->asDebugString(), $email->getBody()->getParts()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendPlainFallback(): void
|
||||||
|
{
|
||||||
|
$email = $this->createTestEmail('Test send plain', false);
|
||||||
|
$email->sendPlain();
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
// assert that it has HTML body with HTML tags removed
|
||||||
|
$this->assertSame('HTML body for Test send plain', $sentMail['Content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendPlainThenNormalWithSetData(): void
|
||||||
|
{
|
||||||
|
$email = $this->createTestEmail('Test send plain', false);
|
||||||
|
$email->setData([
|
||||||
|
'EmailContent' => 'This is the content of the email',
|
||||||
|
]);
|
||||||
|
$email->sendPlain();
|
||||||
|
$email->send();
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
$this->assertSame('This is the content of the email', $sentMail['Content']);
|
||||||
|
$email->to('to2@example.com');
|
||||||
|
$email->send();
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to2@example.com');
|
||||||
|
$this->assertStringContainsString('This is the content of the email', $sentMail['Content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSend(): void
|
||||||
|
{
|
||||||
|
$email = $this->createTestEmail('Test send HTML');
|
||||||
|
|
||||||
|
// email should not call render if a body is supplied
|
||||||
|
$email->setHTMLTemplate($this->getTemplateClass('HtmlTemplate'));
|
||||||
|
$email->send();
|
||||||
|
$this->assertStringNotContainsString($email->getHtmlBody(), 'My HTML Template');
|
||||||
|
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
|
||||||
|
$this->assertSame('to@example.com', $sentMail['To']);
|
||||||
|
$this->assertSame('from@example.com', $sentMail['From']);
|
||||||
|
$this->assertSame('Test send HTML', $sentMail['Subject']);
|
||||||
|
$this->assertStringContainsString('<p>HTML body for Test send HTML</p>', $sentMail['Content']);
|
||||||
|
|
||||||
|
$this->assertCount(1, $sentMail['AttachedFiles']);
|
||||||
|
$child = reset($sentMail['AttachedFiles']);
|
||||||
|
$this->assertSame('text/plain', $child['mimetype']);
|
||||||
|
$this->assertSame('attachment.txt', $child['filename']);
|
||||||
|
$this->assertSame('Hello, I\'m a text document.', $child['contents']);
|
||||||
|
|
||||||
|
// assert MIME types
|
||||||
|
$this->assertSame([
|
||||||
|
implode("\n └ ", [
|
||||||
|
'multipart/alternative',
|
||||||
|
'text/plain charset: utf-8',
|
||||||
|
'text/html charset: utf-8'
|
||||||
|
]),
|
||||||
|
'text/plain disposition: attachment filename: attachment.txt'
|
||||||
|
], array_map(fn(AbstractPart $part) => $part->asDebugString(), $email->getBody()->getParts()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRenderedSend(): void
|
||||||
|
{
|
||||||
|
$email = new Email(to: 'to@example.com');
|
||||||
|
$email->setHTMLTemplate($this->getTemplateClass('HtmlTemplate'));
|
||||||
|
$email->setData([
|
||||||
|
'EmailContent' => '<p>test</p>',
|
||||||
|
]);
|
||||||
|
$email->send();
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
$this->assertStringContainsString('My HTML Template', $sentMail['Content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRenderedSendSubclass(): void
|
||||||
|
{
|
||||||
|
$email = new EmailSubClass(to: 'to@example.com');
|
||||||
|
$email->setData([
|
||||||
|
'EmailContent' => 'test',
|
||||||
|
]);
|
||||||
|
$email->send();
|
||||||
|
$sentMail = $this->getMailer()->findEmail('to@example.com');
|
||||||
|
$this->assertStringContainsString('<h1>Email Sub-class</h1>', $sentMail['Content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructor(): void
|
||||||
|
{
|
||||||
|
$email = new Email(
|
||||||
|
'from@example.com',
|
||||||
|
'to@example.com',
|
||||||
|
'subject',
|
||||||
|
'<p>body</p>',
|
||||||
|
'cc@example.com',
|
||||||
|
'bcc@example.com',
|
||||||
|
'bounce@example.com'
|
||||||
|
);
|
||||||
$this->assertCount(1, $email->getFrom());
|
$this->assertCount(1, $email->getFrom());
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
$this->assertSame('from@example.com', $email->getFrom()[0]->getAddress());
|
||||||
|
$this->assertCount(1, $email->getTo());
|
||||||
|
$this->assertSame('to@example.com', $email->getTo()[0]->getAddress());
|
||||||
|
$this->assertEquals('subject', $email->getSubject());
|
||||||
|
$this->assertEquals('<p>body</p>', $email->getHtmlBody());
|
||||||
|
$this->assertCount(1, $email->getCC());
|
||||||
|
$this->assertEquals('cc@example.com', $email->getCC()[0]->getAddress());
|
||||||
|
$this->assertCount(1, $email->getBCC());
|
||||||
|
$this->assertEquals('bcc@example.com', $email->getBcc()[0]->getAddress());
|
||||||
|
$this->assertEquals('bounce@example.com', $email->getReturnPath()->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetBody(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setBody('<p>body</p>');
|
||||||
|
$this->assertSame('<p>body</p>', $email->getHtmlBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetFrom(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setFrom('from@example.com');
|
||||||
|
$this->assertCount(1, $email->getFrom());
|
||||||
|
$this->assertSame('from@example.com', $email->getFrom()[0]->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSender(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setSender('sender@example.com');
|
||||||
|
$this->assertSame('sender@example.com', $email->getSender()->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetTo(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setTo('to@example.com');
|
||||||
|
$this->assertCount(1, $email->getTo());
|
||||||
|
$this->assertSame('to@example.com', $email->getTo()[0]->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetReplyTo(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setReplyTo('reply-to@example.com');
|
||||||
|
$this->assertCount(1, $email->getReplyTo());
|
||||||
|
$this->assertSame('reply-to@example.com', $email->getReplyTo()[0]->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetSubject(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setSubject('my subject');
|
||||||
|
$this->assertSame('my subject', $email->getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetReturnPath(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$email->setReturnPath('return-path@example.com');
|
||||||
|
$this->assertSame('return-path@example.com', $email->getReturnPath()->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetPriority(): void
|
||||||
|
{
|
||||||
|
$email = new Email();
|
||||||
|
// Intentionally set above 5 to test that Symfony\Component\Mime\Email->priority() is being called
|
||||||
|
$email->setPriority(7);
|
||||||
|
$this->assertSame(5, $email->getPriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAdminEmailApplied()
|
public function testAdminEmailApplied()
|
||||||
{
|
{
|
||||||
Email::config()->update('admin_email', 'admin@example.com');
|
Email::config()->update('admin_email', 'admin@example.com');
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
$this->assertCount(1, $email->getFrom());
|
||||||
$this->assertContains('admin@example.com', array_keys($email->getFrom() ?? []));
|
$this->assertSame('admin@example.com', $email->getFrom()[0]->getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFrom()
|
public function testDataWithArray(): void
|
||||||
{
|
|
||||||
$email = new Email('from@example.com');
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetFrom()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com');
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
$email->setFrom('new-from@example.com');
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('new-from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddFrom()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com');
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
$email->addFrom('new-from@example.com');
|
|
||||||
$this->assertCount(2, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
$this->assertContains('new-from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetGetSender()
|
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$this->assertEmpty($email->getSender());
|
$this->assertSame(true, $email->getData()->IsEmail);
|
||||||
$email->setSender('sender@example.com', 'Silver Stripe');
|
$this->assertSame(Director::absoluteBaseURL(), $email->getData()->BaseURL);
|
||||||
$this->assertEquals(['sender@example.com' => 'Silver Stripe'], $email->getSender());
|
$email->setData(['Lorem' => 'Ipsum']);
|
||||||
}
|
$this->assertSame(true, $email->getData()->IsEmail);
|
||||||
|
$this->assertSame(Director::absoluteBaseURL(), $email->getData()->BaseURL);
|
||||||
public function testSetGetReturnPath()
|
$this->assertSame('Ipsum', $email->getData()->Lorem);
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$this->assertEmpty($email->getReturnPath());
|
|
||||||
$email->setReturnPath('return@example.com');
|
|
||||||
$this->assertEquals('return@example.com', $email->getReturnPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetGetTo()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com', 'to@example.com');
|
|
||||||
$this->assertCount(1, $email->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
$email->setTo('new-to@example.com', 'Silver Stripe');
|
|
||||||
$this->assertEquals(['new-to@example.com' => 'Silver Stripe'], $email->getTo());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddTo()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com', 'to@example.com');
|
|
||||||
$this->assertCount(1, $email->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
$email->addTo('new-to@example.com');
|
|
||||||
$this->assertCount(2, $email->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
$this->assertContains('new-to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetGetCC()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com', 'to@example.com', 'subject', 'body', 'cc@example.com');
|
|
||||||
$this->assertCount(1, $email->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
$email->setCC('new-cc@example.com', 'Silver Stripe');
|
|
||||||
$this->assertEquals(['new-cc@example.com' => 'Silver Stripe'], $email->getCC());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddCC()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com', 'to@example.com', 'subject', 'body', 'cc@example.com');
|
|
||||||
$this->assertCount(1, $email->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
$email->addCC('new-cc@example.com', 'Silver Stripe');
|
|
||||||
$this->assertCount(2, $email->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
$this->assertContains('new-cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetGetBCC()
|
|
||||||
{
|
|
||||||
$email = new Email(
|
|
||||||
'from@example.com',
|
|
||||||
'to@example.com',
|
|
||||||
'subject',
|
|
||||||
'body',
|
|
||||||
'cc@example.com',
|
|
||||||
'bcc@example.com'
|
|
||||||
);
|
|
||||||
$this->assertCount(1, $email->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
$email->setBCC('new-bcc@example.com', 'Silver Stripe');
|
|
||||||
$this->assertEquals(['new-bcc@example.com' => 'Silver Stripe'], $email->getBCC());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddBCC()
|
|
||||||
{
|
|
||||||
$email = new Email(
|
|
||||||
'from@example.com',
|
|
||||||
'to@example.com',
|
|
||||||
'subject',
|
|
||||||
'body',
|
|
||||||
'cc@example.com',
|
|
||||||
'bcc@example.com'
|
|
||||||
);
|
|
||||||
$this->assertCount(1, $email->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
$email->addBCC('new-bcc@example.com', 'Silver Stripe');
|
|
||||||
$this->assertCount(2, $email->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
$this->assertContains('new-bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReplyTo()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$this->assertEmpty($email->getReplyTo());
|
|
||||||
$email->setReplyTo('reply-to@example.com', 'Silver Stripe');
|
|
||||||
$this->assertEquals(['reply-to@example.com' => 'Silver Stripe'], $email->getReplyTo());
|
|
||||||
$email->addReplyTo('new-reply-to@example.com');
|
|
||||||
$this->assertCount(2, $email->getReplyTo());
|
|
||||||
$this->assertContains('reply-to@example.com', array_keys($email->getReplyTo() ?? []));
|
|
||||||
$this->assertContains('new-reply-to@example.com', array_keys($email->getReplyTo() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSubject()
|
|
||||||
{
|
|
||||||
$email = new Email('from@example.com', 'to@example.com', 'subject');
|
|
||||||
$this->assertEquals('subject', $email->getSubject());
|
|
||||||
$email->setSubject('new subject');
|
|
||||||
$this->assertEquals('new subject', $email->getSubject());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPriority()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$this->assertEquals(3, $email->getPriority());
|
|
||||||
$email->setPriority(5);
|
|
||||||
$this->assertEquals(5, $email->getPriority());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testData()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$this->assertEmpty($email->getData());
|
|
||||||
$email->setData([
|
|
||||||
'Title' => 'My Title',
|
|
||||||
]);
|
|
||||||
$this->assertCount(1, $email->getData());
|
|
||||||
$this->assertEquals(['Title' => 'My Title'], $email->getData());
|
|
||||||
|
|
||||||
$email->addData('Content', 'My content');
|
$email->addData('Content', 'My content');
|
||||||
$this->assertCount(2, $email->getData());
|
$this->assertSame(true, $email->getData()->IsEmail);
|
||||||
$this->assertEquals([
|
$this->assertSame(Director::absoluteBaseURL(), $email->getData()->BaseURL);
|
||||||
'Title' => 'My Title',
|
$this->assertSame('Ipsum', $email->getData()->Lorem);
|
||||||
'Content' => 'My content',
|
$this->assertSame('My content', $email->getData()->Content);
|
||||||
], $email->getData());
|
|
||||||
$email->removeData('Title');
|
|
||||||
$this->assertEquals(['Content' => 'My content'], $email->getData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDataWithViewableData()
|
public function testDataWithViewableData(): void
|
||||||
{
|
{
|
||||||
|
$email = new Email();
|
||||||
|
$viewableData = new ViewableData();
|
||||||
|
$viewableData->ABC = 'XYZ';
|
||||||
|
$email->setData($viewableData);
|
||||||
|
$data = $email->getData();
|
||||||
|
$this->assertSame('XYZ', $data->ABC);
|
||||||
|
$this->assertSame(true, $data->IsEmail);
|
||||||
|
$this->assertSame(Director::absoluteBaseURL(), $data->BaseURL);
|
||||||
$member = new Member();
|
$member = new Member();
|
||||||
$member->FirstName = 'First Name';
|
$member->FirstName = 'First Name';
|
||||||
$email = new Email();
|
|
||||||
$this->assertEmpty($email->getData());
|
|
||||||
$email->setData($member);
|
$email->setData($member);
|
||||||
$this->assertEquals($member, $email->getData());
|
$this->assertSame($member->FirstName, $email->getData()->FirstName);
|
||||||
$email->addData('Test', 'Test value');
|
$email->addData('Test', 'Test value');
|
||||||
$this->assertEquals('Test value', $email->getData()->Test);
|
$this->assertEquals('Test value', $email->getData()->Test);
|
||||||
$email->removeData('Test');
|
$email->removeData('Test');
|
||||||
$this->assertNull($email->getData()->Test);
|
$this->assertNull($email->getData()->Test);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBody()
|
public function testHTMLTemplate(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
|
||||||
$this->assertEmpty($email->getBody());
|
|
||||||
$email->setBody('<h1>Title</h1>');
|
|
||||||
$this->assertEquals('<h1>Title</h1>', $email->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHTMLTemplate()
|
|
||||||
{
|
|
||||||
// Include dev theme
|
|
||||||
SSViewer::set_themes([
|
|
||||||
'silverstripe/framework:/tests/php/Control/Email/EmailTest',
|
|
||||||
'$default',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Find template on disk
|
// Find template on disk
|
||||||
$emailTemplate = ModuleResourceLoader::singleton()->resolveResource(
|
$emailTemplate = ModuleResourceLoader::singleton()->resolveResource(
|
||||||
'silverstripe/framework:templates/SilverStripe/Control/Email/Email.ss'
|
'silverstripe/framework:templates/SilverStripe/Control/Email/Email.ss'
|
||||||
@ -522,7 +403,7 @@ class EmailTest extends SapphireTest
|
|||||||
$this->assertEquals('MyTemplate', $email->getHTMLTemplate());
|
$this->assertEquals('MyTemplate', $email->getHTMLTemplate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPlainTemplate()
|
public function testPlainTemplate(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$this->assertEmpty($email->getPlainTemplate());
|
$this->assertEmpty($email->getPlainTemplate());
|
||||||
@ -530,145 +411,64 @@ class EmailTest extends SapphireTest
|
|||||||
$this->assertEquals('MyTemplate', $email->getPlainTemplate());
|
$this->assertEquals('MyTemplate', $email->getPlainTemplate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFailedRecipients()
|
public function testRerender(): void
|
||||||
{
|
|
||||||
$mailer = new SwiftMailer();
|
|
||||||
/** @var Swift_NullTransport|MockObject $transport */
|
|
||||||
$transport = $this->getMockBuilder(Swift_NullTransport::class)->getMock();
|
|
||||||
$transport->expects($this->once())
|
|
||||||
->method('send')
|
|
||||||
->willThrowException(new Swift_RfcComplianceException('Bad email'));
|
|
||||||
$mailer->setSwiftMailer(new Swift_Mailer($transport));
|
|
||||||
$email = new Email();
|
|
||||||
$email->setTo('to@example.com');
|
|
||||||
$email->setFrom('from@example.com');
|
|
||||||
$mailer->send($email);
|
|
||||||
$this->assertCount(1, $email->getFailedRecipients());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testIsEmail()
|
|
||||||
{
|
|
||||||
$this->assertTrue((new Email)->IsEmail());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRenderAgain()
|
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
|
$email->setPlainTemplate($this->getTemplateClass('PlainTemplate'));
|
||||||
$email->setData([
|
$email->setData([
|
||||||
'EmailContent' => 'my content',
|
'EmailContent' => '<p>my content</p>',
|
||||||
]);
|
]);
|
||||||
$email->render();
|
$email->send();
|
||||||
$this->assertStringContainsString('my content', $email->getBody());
|
$this->assertStringContainsString('<p>my content</p>', $email->getHtmlBody());
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
|
||||||
$this->assertCount(1, $children);
|
|
||||||
$plainPart = reset($children);
|
|
||||||
$this->assertEquals('my content', $plainPart->getBody());
|
|
||||||
|
|
||||||
// ensure repeat renders don't add multiple plain parts
|
// Ensure setting data causes html() to be updated
|
||||||
$email->render();
|
|
||||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRerender()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$email->setData([
|
$email->setData([
|
||||||
'EmailContent' => 'my content',
|
'EmailContent' => '<p>your content</p>'
|
||||||
]);
|
]);
|
||||||
$email->render();
|
$email->send();
|
||||||
$this->assertStringContainsString('my content', $email->getBody());
|
$this->assertStringContainsString('<p>your content</p>', $email->getHtmlBody());
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
|
||||||
$this->assertCount(1, $children);
|
|
||||||
$plainPart = reset($children);
|
|
||||||
$this->assertEquals('my content', $plainPart->getBody());
|
|
||||||
|
|
||||||
// Ensure setting data causes a rerender
|
// Ensure removing data causes html() to be updated
|
||||||
$email->setData([
|
|
||||||
'EmailContent' => 'your content'
|
|
||||||
]);
|
|
||||||
$email->render();
|
|
||||||
$this->assertStringContainsString('your content', $email->getBody());
|
|
||||||
|
|
||||||
// Ensure removing data causes a rerender
|
|
||||||
$email->removeData('EmailContent');
|
$email->removeData('EmailContent');
|
||||||
$email->render();
|
$email->send();
|
||||||
$this->assertStringNotContainsString('your content', $email->getBody());
|
$this->assertStringNotContainsString('<p>your content</p>', $email->getHtmlBody());
|
||||||
|
|
||||||
// Ensure adding data causes a rerender
|
// Ensure adding data causes html() to be updated
|
||||||
$email->addData([
|
$email->addData([
|
||||||
'EmailContent' => 'their content'
|
'EmailContent' => '<p>their content</p>'
|
||||||
]);
|
]);
|
||||||
$email->render();
|
$email->send();
|
||||||
$this->assertStringContainsString('their content', $email->getBody());
|
$this->assertStringContainsString('<p>their content</p>', $email->getHtmlBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRenderPlainOnly()
|
public function testRenderPlainOnly(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$email->setData([
|
$email->setData([
|
||||||
'EmailContent' => 'test content',
|
'EmailContent' => 'test content',
|
||||||
]);
|
]);
|
||||||
$email->render(true);
|
$email->sendPlain();
|
||||||
$this->assertEquals('text/plain', $email->getSwiftMessage()->getContentType());
|
$this->assertSame('test content', $email->getTextBody());
|
||||||
$this->assertEmpty($email->getSwiftMessage()->getChildren());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasPlainPart()
|
public function testMultipleEmailSends(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email(to: 'to@example.com');
|
||||||
$email->setData([
|
$email->setData([
|
||||||
'EmailContent' => 'test',
|
'EmailContent' => '<p>Test</p>',
|
||||||
]);
|
]);
|
||||||
//emails are assumed to be HTML by default
|
$this->assertSame(null, $email->getHtmlBody());
|
||||||
$this->assertFalse($email->hasPlainPart());
|
$this->assertSame(null, $email->getTextBody());
|
||||||
//make sure plain attachments aren't picked up as a plain part
|
|
||||||
$email->addAttachmentFromData('data', 'attachent.txt', 'text/plain');
|
|
||||||
$this->assertFalse($email->hasPlainPart());
|
|
||||||
$email->getSwiftMessage()->addPart('plain', 'text/plain');
|
|
||||||
$this->assertTrue($email->hasPlainPart());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGeneratePlainPartFromBody()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$email->setBody('<h1>Test</h1>');
|
|
||||||
$this->assertEmpty($email->getSwiftMessage()->getChildren());
|
|
||||||
$email->generatePlainPartFromBody();
|
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
|
||||||
$this->assertCount(1, $children);
|
|
||||||
$plainPart = reset($children);
|
|
||||||
$this->assertStringContainsString('Test', $plainPart->getBody());
|
|
||||||
$this->assertStringNotContainsString('<h1>Test</h1>', $plainPart->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMultipleEmailSends()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$email->setData([
|
|
||||||
'EmailContent' => 'Test',
|
|
||||||
]);
|
|
||||||
$this->assertEmpty($email->getBody());
|
|
||||||
$this->assertEmpty($email->getSwiftMessage()->getChildren());
|
|
||||||
$email->send();
|
$email->send();
|
||||||
$this->assertStringContainsString('Test', $email->getBody());
|
$this->assertStringContainsString('<p>Test</p>', $email->getHtmlBody());
|
||||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
$this->assertSame('Test', $email->getTextBody());
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
|
||||||
/** @var \Swift_MimePart $plainPart */
|
|
||||||
$plainPart = reset($children);
|
|
||||||
$this->assertStringContainsString('Test', $plainPart->getBody());
|
|
||||||
|
|
||||||
|
|
||||||
//send again
|
//send again
|
||||||
$email->send();
|
$email->send();
|
||||||
$this->assertStringContainsString('Test', $email->getBody());
|
$this->assertStringContainsString('<p>Test</p>', $email->getHtmlBody());
|
||||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
$this->assertSame('Test', $email->getTextBody());
|
||||||
$children = $email->getSwiftMessage()->getChildren();
|
|
||||||
/** @var \Swift_MimePart $plainPart */
|
|
||||||
$plainPart = reset($children);
|
|
||||||
$this->assertStringContainsString('Test', $plainPart->getBody());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDefaultFrom()
|
public function testGetDefaultFrom(): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$class = new \ReflectionClass(Email::class);
|
$class = new \ReflectionClass(Email::class);
|
||||||
@ -695,22 +495,43 @@ class EmailTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return MockObject|Email
|
* @dataProvider provideCreateAddressArray
|
||||||
*/
|
*/
|
||||||
protected function makeEmailMock($subject)
|
public function testCreateAddressArray(string|array $address, string $name, array $expected): void
|
||||||
{
|
{
|
||||||
/** @var Email|MockObject $email */
|
$method = new \ReflectionMethod(Email::class, 'createAddressArray');
|
||||||
$email = $this->getMockBuilder(Email::class)
|
$method->setAccessible(true);
|
||||||
->enableProxyingToOriginalMethods()
|
$obj = new Email();
|
||||||
->getMock();
|
$actual = $method->invoke($obj, $address, $name);
|
||||||
|
for ($i = 0; $i < count($expected); $i++) {
|
||||||
|
$this->assertSame($expected[$i]->getAddress(), $actual[$i]->getAddress());
|
||||||
|
$this->assertSame($expected[$i]->getName(), $actual[$i]->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$email->setFrom('from@example.com');
|
public function provideCreateAddressArray(): array
|
||||||
$email->setTo('to@example.com');
|
{
|
||||||
$email->setSubject($subject);
|
return [
|
||||||
$email->setBody("Body for {$subject}");
|
[
|
||||||
$email->setCC('cc@example.com');
|
'my@email.com',
|
||||||
$email->setBCC('bcc@example.com');
|
'My name',
|
||||||
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
[
|
||||||
return $email;
|
new Address('my@email.com', 'My name'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'my@email.com' => 'My name',
|
||||||
|
'other@email.com' => 'My other name',
|
||||||
|
'no-name@email.com'
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
new Address('my@email.com', 'My name'),
|
||||||
|
new Address('other@email.com', 'My other name'),
|
||||||
|
new Address('no-name@email.com', ''),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<% base_tag %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<h1>My HTML Template</h1>
|
||||||
|
$EmailContent
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,3 @@
|
|||||||
|
# My Plain Template
|
||||||
|
|
||||||
|
$EmailContent
|
100
tests/php/Control/Email/MailerSubscriberTest.php
Normal file
100
tests/php/Control/Email/MailerSubscriberTest.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Control\Tests\Email;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Email\Email;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Dev\TestMailer;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
|
||||||
|
class MailerSubscriberTest extends SapphireTest
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
Email::config()->remove('send_all_emails_to');
|
||||||
|
Email::config()->remove('cc_all_emails_to');
|
||||||
|
Email::config()->remove('bcc_all_emails_to');
|
||||||
|
Email::config()->remove('send_all_emails_from');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEmail(): Email
|
||||||
|
{
|
||||||
|
return (new Email())
|
||||||
|
->setTo('original-to@example.com')
|
||||||
|
->setCC('original-cc@example.com')
|
||||||
|
->setBCC('original-bcc@example.com')
|
||||||
|
->setFrom('original-from@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMailer(): TestMailer
|
||||||
|
{
|
||||||
|
return Injector::inst()->get(MailerInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getHeaderValue(Email $email, string $headerName): ?string
|
||||||
|
{
|
||||||
|
$headers = $email->getHeaders();
|
||||||
|
if (!$headers->has($headerName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $headers->getHeaderBody($headerName)[0]->getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendAllEmailsTo(): void
|
||||||
|
{
|
||||||
|
Email::config()->update('send_all_emails_to', 'to@example.com');
|
||||||
|
$email = $this->getEmail();
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$this->assertCount(1, $email->getTo());
|
||||||
|
$this->assertSame('to@example.com', $email->getTo()[0]->getAddress());
|
||||||
|
$this->assertCount(1, $email->getFrom());
|
||||||
|
$this->assertSame('original-from@example.com', $email->getFrom()[0]->getAddress());
|
||||||
|
|
||||||
|
$this->assertSame('original-to@example.com', $this->getHeaderValue($email, 'X-Original-To'));
|
||||||
|
$this->assertSame('original-cc@example.com', $this->getHeaderValue($email, 'X-Original-Cc'));
|
||||||
|
$this->assertSame('original-bcc@example.com', $this->getHeaderValue($email, 'X-Original-Bcc'));
|
||||||
|
$this->assertSame(null, $this->getHeaderValue($email, 'X-Original-From'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendAllEmailsFrom(): void
|
||||||
|
{
|
||||||
|
Email::config()->update('send_all_emails_from', 'from@example.com');
|
||||||
|
$email = $this->getEmail();
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$this->assertCount(1, $email->getTo());
|
||||||
|
$this->assertSame('original-to@example.com', $email->getTo()[0]->getAddress());
|
||||||
|
$this->assertCount(1, $email->getFrom());
|
||||||
|
$this->assertSame('from@example.com', $email->getFrom()[0]->getAddress());
|
||||||
|
|
||||||
|
$this->assertSame(null, $this->getHeaderValue($email, 'X-Original-To'));
|
||||||
|
$this->assertSame(null, $this->getHeaderValue($email, 'X-Original-Cc'));
|
||||||
|
$this->assertSame(null, $this->getHeaderValue($email, 'X-Original-Bcc'));
|
||||||
|
$this->assertSame('original-from@example.com', $this->getHeaderValue($email, 'X-Original-From'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCCAllEmailsTo(): void
|
||||||
|
{
|
||||||
|
Email::config()->update('cc_all_emails_to', 'cc@example.com');
|
||||||
|
$email = $this->getEmail();
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$this->assertCount(2, $email->getCc());
|
||||||
|
$this->assertSame('original-cc@example.com', $email->getCc()[0]->getAddress());
|
||||||
|
$this->assertSame('cc@example.com', $email->getCc()[1]->getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBCCAllEmailsTo(): void
|
||||||
|
{
|
||||||
|
Email::config()->update('bcc_all_emails_to', 'bcc@example.com');
|
||||||
|
$email = $this->getEmail();
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$this->assertCount(2, $email->getBcc());
|
||||||
|
$this->assertSame('original-bcc@example.com', $email->getBcc()[0]->getAddress());
|
||||||
|
$this->assertSame('bcc@example.com', $email->getBcc()[1]->getAddress());
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Control\Tests\Email;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Email\Email;
|
|
||||||
use SilverStripe\Control\Email\SwiftMailer;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use Swift_Mailer;
|
|
||||||
use Swift_MailTransport;
|
|
||||||
use Swift_Message;
|
|
||||||
use Swift_NullTransport;
|
|
||||||
use Swift_Plugins_AntiFloodPlugin;
|
|
||||||
|
|
||||||
class SwiftMailerTest extends SapphireTest
|
|
||||||
{
|
|
||||||
public function testSwiftMailer()
|
|
||||||
{
|
|
||||||
$mailer = new SwiftMailer();
|
|
||||||
$mailer->setSwiftMailer($swift = new Swift_Mailer(new Swift_NullTransport()));
|
|
||||||
|
|
||||||
$this->assertEquals($swift, $mailer->getSwiftMailer());
|
|
||||||
|
|
||||||
SwiftMailer::config()->remove('swift_plugins');
|
|
||||||
SwiftMailer::config()->update('swift_plugins', [Swift_Plugins_AntiFloodPlugin::class]);
|
|
||||||
|
|
||||||
/** @var Swift_MailTransport $transport */
|
|
||||||
$transport = $this->getMockBuilder(Swift_MailTransport::class)->getMock();
|
|
||||||
$transport
|
|
||||||
->expects($this->once())
|
|
||||||
->method('registerPlugin')
|
|
||||||
->with(
|
|
||||||
$this->isInstanceOf(Swift_Plugins_AntiFloodPlugin::class)
|
|
||||||
);
|
|
||||||
|
|
||||||
/** @var Swift_Mailer $swift */
|
|
||||||
$swift = $this->getMockBuilder(Swift_Mailer::class)->disableOriginalConstructor()->getMock();
|
|
||||||
$swift
|
|
||||||
->expects($this->once())
|
|
||||||
->method('registerPlugin')
|
|
||||||
->willReturnCallback(function ($plugin) use ($transport) {
|
|
||||||
$transport->registerPlugin($plugin);
|
|
||||||
});
|
|
||||||
|
|
||||||
$mailer->setSwiftMailer($swift);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSend()
|
|
||||||
{
|
|
||||||
$email = new Email();
|
|
||||||
$email->setTo('to@example.com');
|
|
||||||
$email->setFrom('from@example.com');
|
|
||||||
$email->setSubject('Subject');
|
|
||||||
|
|
||||||
$mailer = $this->getMockBuilder(SwiftMailer::class)
|
|
||||||
->setMethods(['sendSwift'])
|
|
||||||
->getMock();
|
|
||||||
$mailer->expects($this->once())->method('sendSwift')->with(
|
|
||||||
$this->isInstanceOf(Swift_Message::class)
|
|
||||||
);
|
|
||||||
|
|
||||||
$mailer->send($email);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSendSwift()
|
|
||||||
{
|
|
||||||
$mailer = new SwiftMailer();
|
|
||||||
$sendSwiftMethod = new \ReflectionMethod($mailer, 'sendSwift');
|
|
||||||
$sendSwiftMethod->setAccessible(true);
|
|
||||||
$transport = $this->getMockBuilder(Swift_NullTransport::class)->getMock();
|
|
||||||
$transport->expects($this->once())
|
|
||||||
->method('send');
|
|
||||||
$mailer->setSwiftMailer(new Swift_Mailer($transport));
|
|
||||||
$swiftMessage = new Swift_Message('Test', 'Body');
|
|
||||||
$swiftMessage->setTo('to@example.com');
|
|
||||||
$swiftMessage->setFrom('from@example.com');
|
|
||||||
$sendSwiftMethod->invoke($mailer, $swiftMessage);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\Control\Tests\Email;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Email\Email;
|
|
||||||
use SilverStripe\Control\Email\SwiftPlugin;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
|
|
||||||
class SwiftPluginTest extends SapphireTest
|
|
||||||
{
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
//clean the config
|
|
||||||
Email::config()->remove('send_all_emails_to');
|
|
||||||
Email::config()->remove('cc_all_emails_to');
|
|
||||||
Email::config()->remove('bcc_all_emails_to');
|
|
||||||
Email::config()->remove('send_all_emails_from');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getEmail()
|
|
||||||
{
|
|
||||||
return (new Email())
|
|
||||||
->setTo('original-to@example.com')
|
|
||||||
->setCC('original-cc@example.com')
|
|
||||||
->setBCC('original-bcc@example.com')
|
|
||||||
->setFrom('original-from@example.com');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMailer()
|
|
||||||
{
|
|
||||||
$mailer = new \Swift_Mailer(new \Swift_NullTransport());
|
|
||||||
$mailer->registerPlugin(new SwiftPlugin());
|
|
||||||
|
|
||||||
return $mailer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSendAllEmailsTo()
|
|
||||||
{
|
|
||||||
Email::config()->update('send_all_emails_to', 'to@example.com');
|
|
||||||
$email = $this->getEmail();
|
|
||||||
$this->getMailer()->send($email->getSwiftMessage());
|
|
||||||
$headers = $email->getSwiftMessage()->getHeaders();
|
|
||||||
|
|
||||||
$this->assertCount(1, $email->getTo());
|
|
||||||
$this->assertContains('to@example.com', array_keys($email->getTo() ?? []));
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('original-from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
|
|
||||||
$this->assertTrue($headers->has('X-Original-To'));
|
|
||||||
$this->assertTrue($headers->has('X-Original-Cc'));
|
|
||||||
$this->assertTrue($headers->has('X-Original-Bcc'));
|
|
||||||
$this->assertFalse($headers->has('X-Original-From'));
|
|
||||||
|
|
||||||
$originalTo = array_keys($headers->get('X-Original-To')->getFieldBodyModel() ?? []);
|
|
||||||
$originalCc = array_keys($headers->get('X-Original-Cc')->getFieldBodyModel() ?? []);
|
|
||||||
$originalBcc = array_keys($headers->get('X-Original-Bcc')->getFieldBodyModel() ?? []);
|
|
||||||
|
|
||||||
$this->assertCount(1, $originalTo);
|
|
||||||
$this->assertContains('original-to@example.com', $originalTo);
|
|
||||||
$this->assertCount(1, $originalCc);
|
|
||||||
$this->assertContains('original-cc@example.com', $originalCc);
|
|
||||||
$this->assertCount(1, $originalBcc);
|
|
||||||
$this->assertContains('original-bcc@example.com', $originalBcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSendAllEmailsFrom()
|
|
||||||
{
|
|
||||||
Email::config()->update('send_all_emails_from', 'from@example.com');
|
|
||||||
$email = $this->getEmail();
|
|
||||||
$this->getMailer()->send($email->getSwiftMessage());
|
|
||||||
|
|
||||||
$headers = $email->getSwiftMessage()->getHeaders();
|
|
||||||
|
|
||||||
$this->assertFalse($headers->has('X-Original-To'));
|
|
||||||
$this->assertFalse($headers->has('X-Original-Cc'));
|
|
||||||
$this->assertFalse($headers->has('X-Original-Bcc'));
|
|
||||||
$this->assertTrue($headers->has('X-Original-From'));
|
|
||||||
|
|
||||||
$this->assertCount(1, $email->getFrom());
|
|
||||||
$this->assertContains('from@example.com', array_keys($email->getFrom() ?? []));
|
|
||||||
|
|
||||||
$this->assertCount(1, $headers->get('X-Original-From')->getFieldBodyModel());
|
|
||||||
$this->assertContains('original-from@example.com', array_keys($headers->get('X-Original-From')->getFieldBodyModel() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCCAllEmailsTo()
|
|
||||||
{
|
|
||||||
Email::config()->update('cc_all_emails_to', 'cc@example.com');
|
|
||||||
$email = $this->getEmail();
|
|
||||||
$this->getMailer()->send($email->getSwiftMessage());
|
|
||||||
|
|
||||||
$this->assertCount(2, $email->getCC());
|
|
||||||
$this->assertContains('cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
$this->assertContains('original-cc@example.com', array_keys($email->getCC() ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testBCCAllEmailsTo()
|
|
||||||
{
|
|
||||||
Email::config()->update('bcc_all_emails_to', 'bcc@example.com');
|
|
||||||
$email = $this->getEmail();
|
|
||||||
$this->getMailer()->send($email->getSwiftMessage());
|
|
||||||
|
|
||||||
$this->assertCount(2, $email->getBCC());
|
|
||||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
$this->assertContains('original-bcc@example.com', array_keys($email->getBCC() ?? []));
|
|
||||||
}
|
|
||||||
}
|
|
75
thirdparty/swiftmailer/Swift/MailTransport.php
vendored
75
thirdparty/swiftmailer/Swift/MailTransport.php
vendored
@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was copied in from swiftmailer/swiftmailer v5.4.12 after it was removed from switftmailer v6
|
|
||||||
* It has been slightly modified to meet phpcs standards and initialise Swift_DependencyContainer
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of SwiftMailer.
|
|
||||||
* (c) 2004-2009 Chris Corbyn
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE file (MIT)
|
|
||||||
* https://github.com/swiftmailer/swiftmailer/blob/181b89f18a90f8925ef805f950d47a7190e9b950/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends Messages using the mail() function.
|
|
||||||
*
|
|
||||||
* @author Chris Corbyn
|
|
||||||
*
|
|
||||||
* at deprecated since 5.4.5 (to be removed in 6.0)
|
|
||||||
*/
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// ignore missing namespace
|
|
||||||
class Swift_MailTransport extends Swift_Transport_MailTransport
|
|
||||||
// @codingStandardsIgnoreEnd
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new MailTransport, optionally specifying $extraParams.
|
|
||||||
*
|
|
||||||
* @param string $extraParams
|
|
||||||
*/
|
|
||||||
public function __construct($extraParams = '-f%s')
|
|
||||||
{
|
|
||||||
call_user_func_array(
|
|
||||||
[$this, 'Swift_Transport_MailTransport::__construct'],
|
|
||||||
$this->getDependencies() ?? []
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->setExtraParams($extraParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new MailTransport instance.
|
|
||||||
*
|
|
||||||
* @param string $extraParams To be passed to mail()
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public static function newInstance($extraParams = '-f%s')
|
|
||||||
{
|
|
||||||
return new self($extraParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add in deps for MailTransport which was removed as part of SwiftMailer v6
|
|
||||||
* @see transport_deps.php
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getDependencies(): array
|
|
||||||
{
|
|
||||||
$deps = Swift_DependencyContainer::getInstance()->createDependenciesFor('transport.mail');
|
|
||||||
if (empty($deps)) {
|
|
||||||
Swift_DependencyContainer::getInstance()
|
|
||||||
->register('transport.mail')
|
|
||||||
->asNewInstanceOf('Swift_Transport_MailTransport')
|
|
||||||
->withDependencies(['transport.mailinvoker', 'transport.eventdispatcher'])
|
|
||||||
->register('transport.mailinvoker')
|
|
||||||
->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker');
|
|
||||||
$deps = Swift_DependencyContainer::getInstance()->createDependenciesFor('transport.mail');
|
|
||||||
}
|
|
||||||
return $deps;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was copied in from swiftmailer/swiftmailer v5.4.12 after it was removed from switftmailer v6
|
|
||||||
* It has been slightly modified to meet phpcs standards
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of SwiftMailer.
|
|
||||||
* (c) 2004-2009 Chris Corbyn
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE file (MIT)
|
|
||||||
* https://github.com/swiftmailer/swiftmailer/blob/181b89f18a90f8925ef805f950d47a7190e9b950/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface intercepts calls to the mail() function.
|
|
||||||
*
|
|
||||||
* @author Chris Corbyn
|
|
||||||
*/
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// ignore missing namespace
|
|
||||||
interface Swift_Transport_MailInvoker
|
|
||||||
// @codingStandardsIgnoreEnd
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send mail via the mail() function.
|
|
||||||
*
|
|
||||||
* This method takes the same arguments as PHP mail().
|
|
||||||
*
|
|
||||||
* @param string $to
|
|
||||||
* @param string $subject
|
|
||||||
* @param string $body
|
|
||||||
* @param string $headers
|
|
||||||
* @param string $extraParams
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function mail($to, $subject, $body, $headers = null, $extraParams = null);
|
|
||||||
}
|
|
@ -1,313 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was copied in from swiftmailer/swiftmailer v5.4.12 after it was removed from switftmailer v6
|
|
||||||
* It has been slightly modified to meet phpcs standards and to update method signatures to match the swiftmailer v6
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of SwiftMailer.
|
|
||||||
* (c) 2004-2009 Chris Corbyn
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE file (MIT)
|
|
||||||
* https://github.com/swiftmailer/swiftmailer/blob/181b89f18a90f8925ef805f950d47a7190e9b950/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends Messages using the mail() function.
|
|
||||||
*
|
|
||||||
* It is advised that users do not use this transport if at all possible
|
|
||||||
* since a number of plugin features cannot be used in conjunction with this
|
|
||||||
* transport due to the internal interface in PHP itself.
|
|
||||||
*
|
|
||||||
* The level of error reporting with this transport is incredibly weak, again
|
|
||||||
* due to limitations of PHP's internal mail() function. You'll get an
|
|
||||||
* all-or-nothing result from sending.
|
|
||||||
*
|
|
||||||
* @author Chris Corbyn
|
|
||||||
*
|
|
||||||
* at deprecated since 5.4.5 (to be removed in 6.0)
|
|
||||||
*/
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// ignore missing namespace
|
|
||||||
class Swift_Transport_MailTransport implements Swift_Transport
|
|
||||||
// @codingStandardsIgnoreEnd
|
|
||||||
{
|
|
||||||
/** Additional parameters to pass to mail() */
|
|
||||||
private $_extraParams = '-f%s';
|
|
||||||
|
|
||||||
/** The event dispatcher from the plugin API */
|
|
||||||
private $_eventDispatcher;
|
|
||||||
|
|
||||||
/** An invoker that calls the mail() function */
|
|
||||||
private $_invoker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new MailTransport with the $log.
|
|
||||||
*
|
|
||||||
* @param Swift_Transport_MailInvoker $invoker
|
|
||||||
* @param Swift_Events_EventDispatcher $eventDispatcher
|
|
||||||
*/
|
|
||||||
public function __construct(Swift_Transport_MailInvoker $invoker, Swift_Events_EventDispatcher $eventDispatcher)
|
|
||||||
{
|
|
||||||
// @trigger_error(sprintf('The %s class is deprecated since version 5.4.5 and will be removed in 6.0. Use the Sendmail or SMTP transport instead.', __CLASS__), E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
$this->_invoker = $invoker;
|
|
||||||
$this->_eventDispatcher = $eventDispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not used.
|
|
||||||
*/
|
|
||||||
public function isStarted()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not used.
|
|
||||||
*/
|
|
||||||
public function start()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not used.
|
|
||||||
*/
|
|
||||||
public function stop()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the additional parameters used on the mail() function.
|
|
||||||
*
|
|
||||||
* This string is formatted for sprintf() where %s is the sender address.
|
|
||||||
*
|
|
||||||
* @param string $params
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setExtraParams($params)
|
|
||||||
{
|
|
||||||
$this->_extraParams = $params;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the additional parameters used on the mail() function.
|
|
||||||
*
|
|
||||||
* This string is formatted for sprintf() where %s is the sender address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getExtraParams()
|
|
||||||
{
|
|
||||||
return $this->_extraParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the given Message.
|
|
||||||
*
|
|
||||||
* Recipient/sender data will be retrieved from the Message API.
|
|
||||||
* The return value is the number of recipients who were accepted for delivery.
|
|
||||||
*
|
|
||||||
* @param Swift_Mime_Message $message
|
|
||||||
* @param string[] $failedRecipients An array of failures by-reference
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
|
|
||||||
{
|
|
||||||
$failedRecipients = (array) $failedRecipients;
|
|
||||||
|
|
||||||
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
|
|
||||||
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
|
|
||||||
if ($evt->bubbleCancelled()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$count = (
|
|
||||||
count((array) $message->getTo())
|
|
||||||
+ count((array) $message->getCc())
|
|
||||||
+ count((array) $message->getBcc())
|
|
||||||
);
|
|
||||||
|
|
||||||
$toHeader = $message->getHeaders()->get('To');
|
|
||||||
$subjectHeader = $message->getHeaders()->get('Subject');
|
|
||||||
|
|
||||||
if (0 === $count) {
|
|
||||||
$this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
|
|
||||||
}
|
|
||||||
$to = $toHeader ? $toHeader->getFieldBody() : '';
|
|
||||||
$subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
|
|
||||||
|
|
||||||
$reversePath = $this->_getReversePath($message);
|
|
||||||
|
|
||||||
// Remove headers that would otherwise be duplicated
|
|
||||||
$message->getHeaders()->remove('To');
|
|
||||||
$message->getHeaders()->remove('Subject');
|
|
||||||
|
|
||||||
$messageStr = $message->toString();
|
|
||||||
|
|
||||||
if ($toHeader) {
|
|
||||||
$message->getHeaders()->set($toHeader);
|
|
||||||
}
|
|
||||||
$message->getHeaders()->set($subjectHeader);
|
|
||||||
|
|
||||||
// Separate headers from body
|
|
||||||
if (false !== $endHeaders = strpos($messageStr ?? '', "\r\n\r\n")) {
|
|
||||||
$headers = substr($messageStr ?? '', 0, $endHeaders) . "\r\n"; //Keep last EOL
|
|
||||||
$body = substr($messageStr ?? '', $endHeaders + 4);
|
|
||||||
} else {
|
|
||||||
$headers = $messageStr . "\r\n";
|
|
||||||
$body = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($messageStr);
|
|
||||||
|
|
||||||
if ("\r\n" != PHP_EOL) {
|
|
||||||
// Non-windows (not using SMTP)
|
|
||||||
$headers = str_replace("\r\n", PHP_EOL, $headers ?? '');
|
|
||||||
$subject = str_replace("\r\n", PHP_EOL, $subject ?? '');
|
|
||||||
$body = str_replace("\r\n", PHP_EOL, $body ?? '');
|
|
||||||
$to = str_replace("\r\n", PHP_EOL, $to ?? '');
|
|
||||||
} else {
|
|
||||||
// Windows, using SMTP
|
|
||||||
$headers = str_replace("\r\n.", "\r\n..", $headers ?? '');
|
|
||||||
$subject = str_replace("\r\n.", "\r\n..", $subject ?? '');
|
|
||||||
$body = str_replace("\r\n.", "\r\n..", $body ?? '');
|
|
||||||
$to = str_replace("\r\n.", "\r\n..", $to ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_invoker->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) {
|
|
||||||
if ($evt) {
|
|
||||||
$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
|
|
||||||
$evt->setFailedRecipients($failedRecipients);
|
|
||||||
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$failedRecipients = array_merge(
|
|
||||||
$failedRecipients,
|
|
||||||
array_keys((array) $message->getTo()),
|
|
||||||
array_keys((array) $message->getCc()),
|
|
||||||
array_keys((array) $message->getBcc())
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($evt) {
|
|
||||||
$evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
|
|
||||||
$evt->setFailedRecipients($failedRecipients);
|
|
||||||
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
|
|
||||||
}
|
|
||||||
|
|
||||||
$message->generateId();
|
|
||||||
|
|
||||||
$count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a plugin.
|
|
||||||
*
|
|
||||||
* @param Swift_Events_EventListener $plugin
|
|
||||||
*/
|
|
||||||
public function registerPlugin(Swift_Events_EventListener $plugin)
|
|
||||||
{
|
|
||||||
$this->_eventDispatcher->bindEventListener($plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Throw a TransportException, first sending it to any listeners */
|
|
||||||
protected function _throwException(Swift_TransportException $e)
|
|
||||||
{
|
|
||||||
if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
|
|
||||||
$this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
|
|
||||||
if (!$evt->bubbleCancelled()) {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Determine the best-use reverse path for this message */
|
|
||||||
private function _getReversePath(Swift_Message $message)
|
|
||||||
{
|
|
||||||
$return = $message->getReturnPath();
|
|
||||||
// casting to array to fixed incorrect PHPDOC in Swift_Mime_SimpleMessage which specifies @string
|
|
||||||
$sender = (array) $message->getSender();
|
|
||||||
$from = $message->getFrom();
|
|
||||||
$path = null;
|
|
||||||
if (!empty($return)) {
|
|
||||||
$path = $return;
|
|
||||||
} elseif (!empty($sender)) {
|
|
||||||
$keys = array_keys($sender ?? []);
|
|
||||||
$path = array_shift($keys);
|
|
||||||
} elseif (!empty($from)) {
|
|
||||||
$keys = array_keys($from ?? []);
|
|
||||||
$path = array_shift($keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fix CVE-2016-10074 by disallowing potentially unsafe shell characters.
|
|
||||||
*
|
|
||||||
* Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
|
|
||||||
*
|
|
||||||
* @param string $string The string to be validated
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function _isShellSafe($string)
|
|
||||||
{
|
|
||||||
// Future-proof
|
|
||||||
if (escapeshellcmd($string ?? '') !== $string || !in_array(escapeshellarg($string ?? ''), ["'$string'", "\"$string\""])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$length = strlen($string ?? '');
|
|
||||||
for ($i = 0; $i < $length; ++$i) {
|
|
||||||
$c = $string[$i];
|
|
||||||
// All other characters have a special meaning in at least one common shell, including = and +.
|
|
||||||
// Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
|
|
||||||
// Note that this does permit non-Latin alphanumeric characters based on the current locale.
|
|
||||||
if (!ctype_alnum($c) && strpos('@_-.', $c ?? '') === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return php mail extra params to use for invoker->mail.
|
|
||||||
*
|
|
||||||
* @param $extraParams
|
|
||||||
* @param $reversePath
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
private function _formatExtraParams($extraParams, $reversePath)
|
|
||||||
{
|
|
||||||
if (false !== strpos($extraParams ?? '', '-f%s')) {
|
|
||||||
if (empty($reversePath) || false === $this->_isShellSafe($reversePath)) {
|
|
||||||
$extraParams = str_replace('-f%s', '', $extraParams ?? '');
|
|
||||||
} else {
|
|
||||||
$extraParams = sprintf($extraParams ?? '', $reversePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !empty($extraParams) ? $extraParams : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function ping()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was copied in from swiftmailer/swiftmailer v5.4.12 after it was removed from switftmailer v6
|
|
||||||
* It has been slightly modified to meet phpcs standards
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of SwiftMailer.
|
|
||||||
* (c) 2004-2009 Chris Corbyn
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE file (MIT)
|
|
||||||
* https://github.com/swiftmailer/swiftmailer/blob/181b89f18a90f8925ef805f950d47a7190e9b950/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the implementation class for {@link Swift_Transport_MailInvoker}.
|
|
||||||
*
|
|
||||||
* @author Chris Corbyn
|
|
||||||
*/
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// ignore missing namespace
|
|
||||||
class Swift_Transport_SimpleMailInvoker implements Swift_Transport_MailInvoker
|
|
||||||
// @codingStandardsIgnoreEnd* It has been slightly modified to meet phpcs standards
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send mail via the mail() function.
|
|
||||||
*
|
|
||||||
* This method takes the same arguments as PHP mail().
|
|
||||||
*
|
|
||||||
* @param string $to
|
|
||||||
* @param string $subject
|
|
||||||
* @param string $body
|
|
||||||
* @param string $headers
|
|
||||||
* @param string $extraParams
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function mail($to, $subject, $body, $headers = null, $extraParams = null)
|
|
||||||
{
|
|
||||||
if (!ini_get('safe_mode')) {
|
|
||||||
return @mail($to ?? '', $subject ?? '', $body ?? '', $headers ?? '', $extraParams ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return @mail($to ?? '', $subject ?? '', $body ?? '', $headers ?? '');
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user