mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW Move to SwiftMailer powered Emails (#6466)
* API Replace existing Email and Mailer classes with SwiftMailer powered email system * DOCS New Email docs * Initial feedback from Damian * Making Mailer an interface * Templates relocated * Round of feedback * More robust approach to plain parts * Revert changes to TestMailer
This commit is contained in:
parent
e6ae532998
commit
3ea5015f8b
@ -1067,7 +1067,6 @@ mappings:
|
||||
EmailTest: SilverStripe\Control\Tests\Email\EmailTest
|
||||
EmailTest_Mailer: SilverStripe\Control\Tests\Email\EmailTest\TestMailer
|
||||
MailerTest: SilverStripe\Control\Tests\Email\MailerTest
|
||||
MailerTest_MockMailer: SilverStripe\Control\Tests\Email\MailerTest\MockMailer
|
||||
ErrorControlChainTest_Chain: SilverStripe\Core\Tests\Startup\ErrorControlChainTest\ErrorControlChainTest_Chain
|
||||
ErrorControlChainTest: SilverStripe\Core\Tests\Startup\ErrorControlChainTest
|
||||
ParameterConfirmationTokenTest_Token: SilverStripe\Core\Tests\Startup\ParameterConfirmationTokenTest\ParameterConfirmationTokenTest_Token
|
||||
|
12
_config/email.yml
Normal file
12
_config/email.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
Name: coreconfig
|
||||
---
|
||||
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'
|
@ -21,7 +21,8 @@
|
||||
"monolog/monolog": "~1.11",
|
||||
"league/flysystem": "~1.0.12",
|
||||
"symfony/yaml": "~2.7",
|
||||
"embed/embed": "^2.6"
|
||||
"embed/embed": "^2.6",
|
||||
"swiftmailer/swiftmailer": "~5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~4.8",
|
||||
|
@ -7,10 +7,16 @@ covers how to create an `Email` instance, customise it with a HTML template, the
|
||||
|
||||
## Configuration
|
||||
|
||||
Out of the box, SilverStripe will use the built-in PHP `mail()` command. If you are not running an SMTP server, you
|
||||
will need to either configure PHP's SMTP settings (see [PHP documentation](http://php.net/mail) to include your mail
|
||||
server configuration or use one of the third party SMTP services like [SparkPost](https://github.com/lekoala/silverstripe-sparkpost)
|
||||
and [Postmark](https://github.com/fullscreeninteractive/silverstripe-postmarkmailer).
|
||||
SilverStripe provides an API over the top of the [SwiftMailer](http://swiftmailer.org/) PHP library which comes with an
|
||||
extensive list of "transports" for sending mail via different services.
|
||||
|
||||
Out of the box, SilverStripe will use the built-in PHP `mail()` command via the `Swift_MailTransport` class. If you'd
|
||||
like to use a more robust transport to send mail you can swap out the transport used by the `Mailer` via config:
|
||||
|
||||
```yml
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
Swift_Transport: Swift_SendmailTransport
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@ -31,8 +37,8 @@ to `*text*`).
|
||||
$email->send();
|
||||
|
||||
<div class="info" markdown="1">
|
||||
The default HTML template for emails is named `GenericEmail` and is located in `framework/templates/email/`. To
|
||||
customise this template, copy it to the `mysite/templates/Email/` folder or use `setTemplate` when you create the
|
||||
The default HTML template for emails is named `GenericEmail` and is located in `framework/templates/SilverStripe/Email/`.
|
||||
To customise this template, copy it to the `mysite/templates/Email/` folder or use `setTemplate` when you create the
|
||||
`Email` instance.
|
||||
</div>
|
||||
|
||||
@ -40,7 +46,7 @@ customise this template, copy it to the `mysite/templates/Email/` folder or use
|
||||
### Templates
|
||||
|
||||
HTML emails can use custom templates using the same template language as your website template. You can also pass the
|
||||
email object additional information using the `populateTemplate` method.
|
||||
email object additional information using the `setData` and `addData` methods.
|
||||
|
||||
**mysite/templates/Email/MyCustomEmail.ss**
|
||||
|
||||
@ -50,64 +56,29 @@ email object additional information using the `populateTemplate` method.
|
||||
|
||||
The PHP Logic..
|
||||
|
||||
:::php
|
||||
$email = new Email();
|
||||
$email
|
||||
```php
|
||||
$email = SilverStripe\Control\Email\Email::create()
|
||||
->setTemplate('Email\\MyCustomEmail')
|
||||
->setData(array(
|
||||
'Member' => Member::currentUser(),
|
||||
'Link'=> $link,
|
||||
))
|
||||
->setFrom($from)
|
||||
->setTo($to)
|
||||
->setSubject($subject)
|
||||
->setTemplate('MyCustomEmail')
|
||||
->populateTemplate(new ArrayData(array(
|
||||
'Member' => Member::currentUser(),
|
||||
'Link' => $link
|
||||
)));
|
||||
->setSubject($subject);
|
||||
|
||||
$email->send();
|
||||
if ($email->send()) {
|
||||
//email sent successfully
|
||||
} else {
|
||||
// there may have been 1 or more failures
|
||||
}
|
||||
```
|
||||
|
||||
<div class="alert" markdown="1">
|
||||
As we've added a new template file (`MyCustomEmail`) make sure you clear the SilverStripe cache for your changes to
|
||||
take affect.
|
||||
</div>
|
||||
|
||||
## Sub classing
|
||||
|
||||
To keep your application code clean and your internal API clear, a better approach to generating an email is to create
|
||||
a new subclass of `Email` which takes the required dependencies and handles setting the properties itself.
|
||||
|
||||
**mysite/code/MyCustomEmail.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class MyEmail extends Email {
|
||||
|
||||
protected $ss_template = "MyEmail";
|
||||
|
||||
public function __construct($member) {
|
||||
$from = 'no-reply@mysite.com';
|
||||
$to = $member->Email;
|
||||
$subject = "Welcome to our site.";
|
||||
$link = Director::absoluteBaseUrl();
|
||||
|
||||
parent::__construct($from, $to, $subject);
|
||||
|
||||
$this->populateTemplate(new ArrayData(array(
|
||||
'Member' => $member->Email,
|
||||
'Link' => $link
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Then within your application, usage of the email is much clearer to follow.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
$member = Member::currentUser();
|
||||
|
||||
$email = new MyEmail($member);
|
||||
$email->send();
|
||||
|
||||
|
||||
## Administrator Emails
|
||||
|
||||
You can set the default sender address of emails through the `Email.admin_email` [configuration setting](/developer_guides/configuration).
|
||||
@ -115,7 +86,7 @@ You can set the default sender address of emails through the `Email.admin_email`
|
||||
**mysite/_config/app.yml**
|
||||
|
||||
:::yaml
|
||||
Email:
|
||||
SilverStripe\Control\Email\Email:
|
||||
admin_email: support@silverstripe.org
|
||||
|
||||
|
||||
@ -128,10 +99,12 @@ email marked as spam. If you want to send from another address think about using
|
||||
|
||||
There are several other [configuration settings](/developer_guides/configuration) to manipulate the email server.
|
||||
|
||||
* `Email.send_all_emails_to` will redirect all emails sent to the given address. This is useful for testing and staging
|
||||
servers where you do not wish to send emails out.
|
||||
* `Email.cc_all_emails_to` and `Email.bcc_all_emails_to` will add an additional recipient in the BCC / CC header.
|
||||
These are good for monitoring system-generated correspondence on the live systems.
|
||||
* `SilverStripe\Control\Email\Email.send_all_emails_to` will redirect all emails sent to the given address.
|
||||
All recipients will be removed (including CC and BCC addresses). This is useful for testing and staging servers where
|
||||
you do not wish to send emails out. For debugging the original addresses are added as `X-Original-*` headers on the email.
|
||||
* `SilverStripe\Control\Email\Email.cc_all_emails_to` and `SilverStripe\Control\Email\Email.bcc_all_emails_to` will add
|
||||
an additional recipient in the BCC / CC header. These are good for monitoring system-generated correspondence on the
|
||||
live systems.
|
||||
|
||||
Configuration of those properties looks like the following:
|
||||
|
||||
@ -146,7 +119,10 @@ Configuration of those properties looks like the following:
|
||||
|
||||
### Setting custom "Reply To" email address.
|
||||
|
||||
For email messages that should have an email address which is replied to that actually differs from the original "from" email, do the following. This is encouraged especially when the domain responsible for sending the message isn't necessarily the same which should be used for return correspondence and should help prevent your message from being marked as spam.
|
||||
For email messages that should have an email address which is replied to that actually differs from the original "from"
|
||||
email, do the following. This is encouraged especially when the domain responsible for sending the message isn't
|
||||
necessarily the same which should be used for return correspondence and should help prevent your message from being
|
||||
marked as spam.
|
||||
|
||||
:::php
|
||||
$email = new Email(..);
|
||||
@ -154,74 +130,21 @@ For email messages that should have an email address which is replied to that ac
|
||||
|
||||
### Setting Custom Headers
|
||||
|
||||
For email headers which do not have getters or setters (like setTo(), setFrom()) you can use **addCustomHeader($header,
|
||||
$value)**
|
||||
For email headers which do not have getters or setters (like setTo(), setFrom()) you can manipulate the underlying
|
||||
`Swift_Message` that we provide a wrapper for.
|
||||
|
||||
:::php
|
||||
$email = new Email(...);
|
||||
$email->addCustomHeader('HeaderName', 'HeaderValue');
|
||||
$email->getSwiftMessage()->getHeaders()->addTextHeader('HeaderName', 'HeaderValue');
|
||||
..
|
||||
|
||||
<div class="info" markdown="1">
|
||||
See this [Wikipedia](http://en.wikipedia.org/wiki/E-mail#Message_header) entry for a list of header names.
|
||||
</div>
|
||||
|
||||
## Newsletters
|
||||
## SwiftMailer Documentation
|
||||
|
||||
The [newsletter module](http://silverstripe.org/newsletter-module) provides a UI and logic to send batch emails.
|
||||
|
||||
## Custom Mailers
|
||||
|
||||
SilverStripe supports changing out the underlying web server SMTP mailer service through the `Email::set_mailer()`
|
||||
function. A `Mailer` subclass will commonly override the `sendPlain` and `sendHTML` methods to send emails through curl
|
||||
or some other process that isn't the built in `mail()` command.
|
||||
|
||||
<div class="info" markdown="1">
|
||||
There are a number of custom mailer add-ons available like [Mandrill](https://github.com/lekoala/silverstripe-mandrill)
|
||||
and [Postmark](https://github.com/fullscreeninteractive/silverstripe-postmarkmailer).
|
||||
</div>
|
||||
|
||||
In this example, `LocalMailer` will take any email's going while the site is in Development mode and save it to the
|
||||
assets folder instead.
|
||||
|
||||
**mysite/code/LocalMailer.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class LocalMailer extends Mailer {
|
||||
|
||||
function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
|
||||
$file = ASSETS_PATH . '/_mail_'. urlencode(sprintf("%s_%s", $subject, $to));
|
||||
|
||||
file_put_contents($file, $htmlContent);
|
||||
}
|
||||
|
||||
|
||||
function sendPlain($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
|
||||
$file = ASSETS_PATH . '/_mail_'. urlencode(sprintf("%s_%s", $subject, $to));
|
||||
|
||||
file_put_contents($file, $htmlContent);
|
||||
}
|
||||
}
|
||||
|
||||
**mysite/_config.php**
|
||||
|
||||
:::php
|
||||
if(Director::isLive()) {
|
||||
Email::set_mailer(new PostmarkMailer());
|
||||
} else {
|
||||
Email::set_mailer(new LocalMailer());
|
||||
}
|
||||
|
||||
|
||||
### Setting bounce handler
|
||||
|
||||
A bounce handler email can be specified one of a few ways:
|
||||
|
||||
* Via config by setting the `Mailer.default_bounce_email` config to the desired email address.
|
||||
* Via _ss_environment.php by setting the `BOUNCE_EMAIL` definition.
|
||||
* Via PHP by calling `Email::mailer()->setBounceEmail('bounce@mycompany.com');`
|
||||
For further information on SwiftMailer, consult their docs: http://swiftmailer.org/docs/introduction.html
|
||||
|
||||
## API Documentation
|
||||
|
||||
|
@ -4,25 +4,13 @@ namespace SilverStripe\Control\Email;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTP;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\View\Requirements;
|
||||
use SilverStripe\View\ViewableData;
|
||||
use SilverStripe\View\ViewableData_Customised;
|
||||
|
||||
if (isset($_SERVER['SERVER_NAME'])) {
|
||||
/**
|
||||
* X-Mailer header value on emails sent
|
||||
*/
|
||||
define('X_MAILER', 'SilverStripe Mailer - version 2006.06.21 (Sent from "'.$_SERVER['SERVER_NAME'].'")');
|
||||
} else {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
define('X_MAILER', 'SilverStripe Mailer - version 2006.06.21');
|
||||
}
|
||||
use Swift_Message;
|
||||
use Swift_MimePart;
|
||||
|
||||
/**
|
||||
* Class to support sending emails.
|
||||
@ -31,70 +19,28 @@ class Email extends ViewableData
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string $from Email-Address
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
protected $from;
|
||||
private static $send_all_emails_to = array();
|
||||
|
||||
/**
|
||||
* @var string $to Email-Address. Use comma-separation to pass multiple email-addresses.
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
protected $to;
|
||||
private static $cc_all_emails_to = array();
|
||||
|
||||
/**
|
||||
* @var string $subject Subject of the email
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
protected $subject;
|
||||
private static $bcc_all_emails_to = array();
|
||||
|
||||
/**
|
||||
* Passed straight into {@link $ss_template} as $Body variable.
|
||||
*
|
||||
* @var string $body HTML content of the email.
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* If not set, defaults to converting the HTML-body with {@link Convert::xml2raw()}.
|
||||
*
|
||||
* @var string $plaintext_body Optional string for plaintext emails.
|
||||
*/
|
||||
protected $plaintext_body;
|
||||
|
||||
/**
|
||||
* @var string $cc
|
||||
*/
|
||||
protected $cc;
|
||||
|
||||
/**
|
||||
* @var string $bcc
|
||||
*/
|
||||
protected $bcc;
|
||||
|
||||
/**
|
||||
* @var array $customHeaders A map of header-name -> header-value
|
||||
*/
|
||||
protected $customHeaders = array();
|
||||
|
||||
/**
|
||||
* @var array $attachments Internal, use {@link attachFileFromString()} or {@link attachFile()}
|
||||
*/
|
||||
protected $attachments = array();
|
||||
|
||||
/**
|
||||
* @var boolean $parseVariables_done
|
||||
*/
|
||||
protected $parseVariables_done = false;
|
||||
|
||||
/**
|
||||
* @var string $ss_template The name of the used template (without *.ss extension)
|
||||
*/
|
||||
protected $ss_template = 'GenericEmail';
|
||||
|
||||
/**
|
||||
* Used in the same way than {@link ViewableData->customize()}.
|
||||
*
|
||||
* @var ViewableData_Customised $template_data Additional data available in a template.
|
||||
*/
|
||||
protected $template_data;
|
||||
private static $send_all_emails_from = array();
|
||||
|
||||
/**
|
||||
* This will be set in the config on a site-by-site basis
|
||||
@ -102,637 +48,58 @@ class Email extends ViewableData
|
||||
* @config
|
||||
* @var string The default administrator email address.
|
||||
*/
|
||||
private static $admin_email = '';
|
||||
private static $admin_email = null;
|
||||
|
||||
/**
|
||||
* Send every email generated by the Email class to the given address.
|
||||
* @var Swift_Message
|
||||
*/
|
||||
private $swiftMessage;
|
||||
|
||||
/**
|
||||
* @var string The name of the HTML template to render the email with (without *.ss extension)
|
||||
*/
|
||||
private $HTMLTemplate = self::class;
|
||||
|
||||
/**
|
||||
* @var string The name of the plain text template to render the plain part of the email with
|
||||
*/
|
||||
private $plainTemplate = '';
|
||||
|
||||
/**
|
||||
* @var Swift_MimePart
|
||||
*/
|
||||
private $plainPart;
|
||||
|
||||
/**
|
||||
* @var array|ViewableData Additional data available in a template.
|
||||
* Used in the same way than {@link ViewableData->customize()}.
|
||||
*/
|
||||
private $data = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $failedRecipients = array();
|
||||
|
||||
/**
|
||||
* Checks for RFC822-valid email format.
|
||||
*
|
||||
* It will also add " [addressed to (email), cc to (email), bcc to (email)]" to the end of the subject line
|
||||
*
|
||||
* To set this, set Email.send_all_emails_to in your yml config file.
|
||||
* It can also be set in _ss_environment.php with SS_SEND_ALL_EMAILS_TO.
|
||||
*
|
||||
* @config
|
||||
* @var string $send_all_emails_to Email-Address
|
||||
*/
|
||||
private static $send_all_emails_to;
|
||||
|
||||
/**
|
||||
* Send every email generated by the Email class *from* the given address.
|
||||
* It will also add " [, from to (email)]" to the end of the subject line
|
||||
*
|
||||
* To set this, set Email.send_all_emails_from in your yml config file.
|
||||
* It can also be set in _ss_environment.php with SS_SEND_ALL_EMAILS_FROM.
|
||||
*
|
||||
* @config
|
||||
* @var string $send_all_emails_from Email-Address
|
||||
*/
|
||||
private static $send_all_emails_from;
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var string BCC every email generated by the Email class to the given address.
|
||||
*/
|
||||
private static $bcc_all_emails_to;
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var string CC every email generated by the Email class to the given address.
|
||||
*/
|
||||
private static $cc_all_emails_to;
|
||||
|
||||
/**
|
||||
* Create a new email.
|
||||
*
|
||||
* @param string|null $from
|
||||
* @param string|null $to
|
||||
* @param string|null $subject
|
||||
* @param string|null $body
|
||||
* @param string|null $bounceHandlerURL
|
||||
* @param string|null $cc
|
||||
* @param string|null $bcc
|
||||
*/
|
||||
public function __construct(
|
||||
$from = null,
|
||||
$to = null,
|
||||
$subject = null,
|
||||
$body = null,
|
||||
$bounceHandlerURL = null,
|
||||
$cc = null,
|
||||
$bcc = null
|
||||
) {
|
||||
|
||||
if ($from !== null) {
|
||||
$this->from = $from;
|
||||
}
|
||||
if ($to !== null) {
|
||||
$this->to = $to;
|
||||
}
|
||||
if ($subject !== null) {
|
||||
$this->subject = $subject;
|
||||
}
|
||||
if ($body !== null) {
|
||||
$this->body = $body;
|
||||
}
|
||||
if ($cc !== null) {
|
||||
$this->cc = $cc;
|
||||
}
|
||||
if ($bcc !== null) {
|
||||
$this->bcc = $bcc;
|
||||
}
|
||||
|
||||
if ($bounceHandlerURL !== null) {
|
||||
Deprecation::notice('4.0', 'Use "emailbouncehandler" module');
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mailer.
|
||||
*
|
||||
* @return Mailer
|
||||
*/
|
||||
public static function mailer()
|
||||
{
|
||||
return Injector::inst()->get('SilverStripe\\Control\\Email\\Mailer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a file based on provided raw data.
|
||||
*
|
||||
* @param string $data The raw file data (not encoded).
|
||||
* @param string $attachedFilename Name of the file that should appear once it's sent as a separate attachment.
|
||||
* @param string|null $mimeType MIME type to use when attaching file. If not provided, will attempt to infer via HTTP::get_mime_type().
|
||||
* @return $this
|
||||
*/
|
||||
public function attachFileFromString($data, $attachedFilename, $mimeType = null)
|
||||
{
|
||||
$this->attachments[] = array(
|
||||
'contents' => $data,
|
||||
'filename' => $attachedFilename,
|
||||
'mimetype' => $mimeType,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the specified file to this email message.
|
||||
*
|
||||
* @param string $filename Relative or full path to file you wish to attach to this email message.
|
||||
* @param string|null $attachedFilename Name of the file that should appear once it's sent as a separate attachment.
|
||||
* @param string|null $mimeType MIME type to use when attaching file. If not provided, will attempt to infer via HTTP::get_mime_type().
|
||||
* @return $this
|
||||
*/
|
||||
public function attachFile($filename, $attachedFilename = null, $mimeType = null)
|
||||
{
|
||||
if (!$attachedFilename) {
|
||||
$attachedFilename = basename($filename);
|
||||
}
|
||||
$absoluteFileName = Director::getAbsFile($filename);
|
||||
if (file_exists($absoluteFileName)) {
|
||||
$this->attachFileFromString(file_get_contents($absoluteFileName), $attachedFilename, $mimeType);
|
||||
} else {
|
||||
user_error("Could not attach '$absoluteFileName' to email. File does not exist.", E_USER_NOTICE);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function Subject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function Body()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function To()
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function From()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function Cc()
|
||||
{
|
||||
return $this->cc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function Bcc()
|
||||
{
|
||||
return $this->bcc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setSubject($val)
|
||||
{
|
||||
$this->subject = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setBody($val)
|
||||
{
|
||||
$this->body = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setTo($val)
|
||||
{
|
||||
$this->to = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setFrom($val)
|
||||
{
|
||||
$this->from = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setCc($val)
|
||||
{
|
||||
$this->cc = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setBcc($val)
|
||||
{
|
||||
$this->bcc = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "Reply-To" header with an email address.
|
||||
*
|
||||
* @param string $val
|
||||
* @return $this
|
||||
*/
|
||||
public function setReplyTo($val)
|
||||
{
|
||||
$this->addCustomHeader('Reply-To', $val);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom header to this email message. Useful for implementing all those cool features that we didn't think of.
|
||||
*
|
||||
* IMPORTANT: If the specified header already exists, the provided value will be appended!
|
||||
*
|
||||
* @todo Should there be an option to replace instead of append? Or maybe a new method ->setCustomHeader()?
|
||||
*
|
||||
* @param string $headerName
|
||||
* @param string $headerValue
|
||||
* @return $this
|
||||
*/
|
||||
public function addCustomHeader($headerName, $headerValue)
|
||||
{
|
||||
if ($headerName == 'Cc') {
|
||||
$this->cc = $headerValue;
|
||||
} elseif ($headerName == 'Bcc') {
|
||||
$this->bcc = $headerValue;
|
||||
} else {
|
||||
// Append value instead of replacing.
|
||||
if (isset($this->customHeaders[$headerName])) {
|
||||
$this->customHeaders[$headerName] .= ", " . $headerValue;
|
||||
} else {
|
||||
$this->customHeaders[$headerName] = $headerValue;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function BaseURL()
|
||||
{
|
||||
return Director::absoluteBaseURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an HTML string for debugging purposes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function debug()
|
||||
{
|
||||
$this->parseVariables();
|
||||
|
||||
return "<h2>Email template $this->class</h2>\n" .
|
||||
"<p><b>From:</b> $this->from\n" .
|
||||
"<b>To:</b> $this->to\n" .
|
||||
"<b>Cc:</b> $this->cc\n" .
|
||||
"<b>Bcc:</b> $this->bcc\n" .
|
||||
"<b>Subject:</b> $this->subject</p>" .
|
||||
$this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set template name (without *.ss extension).
|
||||
*
|
||||
* @param string $template
|
||||
* @return $this
|
||||
*/
|
||||
public function setTemplate($template)
|
||||
{
|
||||
$this->ss_template = $template;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->ss_template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Email|ViewableData_Customised
|
||||
*/
|
||||
protected function templateData()
|
||||
{
|
||||
if ($this->template_data) {
|
||||
return $this->template_data->customise(array(
|
||||
"To" => $this->to,
|
||||
"Cc" => $this->cc,
|
||||
"Bcc" => $this->bcc,
|
||||
"From" => $this->from,
|
||||
"Subject" => $this->subject,
|
||||
"Body" => $this->body,
|
||||
"BaseURL" => $this->BaseURL(),
|
||||
"IsEmail" => true,
|
||||
));
|
||||
} else {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link SSViewer} templates to detect if we're rendering an email template rather than a page template
|
||||
*/
|
||||
public function IsEmail()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate this email template with values. This may be called many times.
|
||||
*
|
||||
* @param array|ViewableData $data
|
||||
* @return $this
|
||||
*/
|
||||
public function populateTemplate($data)
|
||||
{
|
||||
if ($this->template_data) {
|
||||
$this->template_data = $this->template_data->customise($data);
|
||||
} else {
|
||||
if (is_array($data)) {
|
||||
$data = new ArrayData($data);
|
||||
}
|
||||
$this->template_data = $this->customise($data);
|
||||
}
|
||||
$this->parseVariables_done = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all the template variables into the internal variables, including
|
||||
* the template into body. Called before send() or debugSend()
|
||||
* $isPlain=true will cause the template to be ignored, otherwise the GenericEmail template will be used
|
||||
* and it won't be plain email :)
|
||||
*
|
||||
* @param bool $isPlain
|
||||
* @return $this
|
||||
*/
|
||||
protected function parseVariables($isPlain = false)
|
||||
{
|
||||
$origState = SSViewer::config()->get('source_file_comments');
|
||||
SSViewer::config()->update('source_file_comments', false);
|
||||
|
||||
if (!$this->parseVariables_done) {
|
||||
$this->parseVariables_done = true;
|
||||
|
||||
// Parse $ variables in the base parameters
|
||||
$this->templateData();
|
||||
|
||||
// Process a .SS template file
|
||||
$fullBody = $this->body;
|
||||
if ($this->ss_template && !$isPlain) {
|
||||
// Requery data so that updated versions of To, From, Subject, etc are included
|
||||
$data = $this->templateData();
|
||||
$candidateTemplates = [
|
||||
$this->ss_template,
|
||||
[ 'type' => 'email', $this->ss_template ]
|
||||
];
|
||||
$template = new SSViewer($candidateTemplates);
|
||||
if ($template->exists()) {
|
||||
$fullBody = $template->process($data);
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite relative URLs
|
||||
$this->body = HTTP::absoluteURLs($fullBody);
|
||||
}
|
||||
SSViewer::config()->update('source_file_comments', $origState);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the email in plaintext.
|
||||
*
|
||||
* @see send() for sending emails with HTML content.
|
||||
* @uses Mailer->sendPlain()
|
||||
*
|
||||
* @param string $messageID Optional message ID so the message can be identified in bounces etc.
|
||||
* @return mixed Success of the sending operation from an MTA perspective. Doesn't actually give any indication if
|
||||
* the mail has been delivered to the recipient properly). See Mailer->sendPlain() for return type details.
|
||||
*/
|
||||
public function sendPlain($messageID = null)
|
||||
{
|
||||
Requirements::clear();
|
||||
|
||||
$this->parseVariables(true);
|
||||
|
||||
if (empty($this->from)) {
|
||||
$this->from = Email::config()->admin_email;
|
||||
}
|
||||
|
||||
$headers = $this->customHeaders;
|
||||
|
||||
if ($messageID) {
|
||||
$headers['X-SilverStripeMessageID'] = project() . '.' . $messageID;
|
||||
}
|
||||
|
||||
if (project()) {
|
||||
$headers['X-SilverStripeSite'] = project();
|
||||
}
|
||||
|
||||
$to = $this->to;
|
||||
$from = $this->from;
|
||||
$subject = $this->subject;
|
||||
if ($sendAllTo = $this->config()->send_all_emails_to) {
|
||||
$subject .= " [addressed to $to";
|
||||
$to = $sendAllTo;
|
||||
if ($this->cc) {
|
||||
$subject .= ", cc to $this->cc";
|
||||
}
|
||||
if ($this->bcc) {
|
||||
$subject .= ", bcc to $this->bcc";
|
||||
}
|
||||
$subject .= ']';
|
||||
unset($headers['Cc']);
|
||||
unset($headers['Bcc']);
|
||||
} else {
|
||||
if ($this->cc) {
|
||||
$headers['Cc'] = $this->cc;
|
||||
}
|
||||
if ($this->bcc) {
|
||||
$headers['Bcc'] = $this->bcc;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ccAllTo = $this->config()->cc_all_emails_to) {
|
||||
if (!empty($headers['Cc']) && trim($headers['Cc'])) {
|
||||
$headers['Cc'] .= ', ' . $ccAllTo;
|
||||
} else {
|
||||
$headers['Cc'] = $ccAllTo;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bccAllTo = $this->config()->bcc_all_emails_to) {
|
||||
if (!empty($headers['Bcc']) && trim($headers['Bcc'])) {
|
||||
$headers['Bcc'] .= ', ' . $bccAllTo;
|
||||
} else {
|
||||
$headers['Bcc'] = $bccAllTo;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sendAllfrom = $this->config()->send_all_emails_from) {
|
||||
if ($from) {
|
||||
$subject .= " [from $from]";
|
||||
}
|
||||
$from = $sendAllfrom;
|
||||
}
|
||||
|
||||
Requirements::restore();
|
||||
|
||||
return self::mailer()->sendPlain($to, $from, $subject, $this->body, $this->attachments, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email with HTML content.
|
||||
*
|
||||
* @see sendPlain() for sending plaintext emails only.
|
||||
* @uses Mailer->sendHTML()
|
||||
*
|
||||
* @param string $messageID Optional message ID so the message can be identified in bounces etc.
|
||||
* @return mixed Success of the sending operation from an MTA perspective. Doesn't actually give any indication if
|
||||
* the mail has been delivered to the recipient properly). See Mailer->sendPlain() for return type details.
|
||||
*/
|
||||
public function send($messageID = null)
|
||||
{
|
||||
Requirements::clear();
|
||||
|
||||
$this->parseVariables();
|
||||
|
||||
if (empty($this->from)) {
|
||||
$this->from = Email::config()->admin_email;
|
||||
}
|
||||
|
||||
$headers = $this->customHeaders;
|
||||
|
||||
if ($messageID) {
|
||||
$headers['X-SilverStripeMessageID'] = project() . '.' . $messageID;
|
||||
}
|
||||
|
||||
if (project()) {
|
||||
$headers['X-SilverStripeSite'] = project();
|
||||
}
|
||||
|
||||
|
||||
$to = $this->to;
|
||||
$from = $this->from;
|
||||
$subject = $this->subject;
|
||||
if ($sendAllTo = $this->config()->send_all_emails_to) {
|
||||
$subject .= " [addressed to $to";
|
||||
$to = $sendAllTo;
|
||||
if ($this->cc) {
|
||||
$subject .= ", cc to $this->cc";
|
||||
}
|
||||
if ($this->bcc) {
|
||||
$subject .= ", bcc to $this->bcc";
|
||||
}
|
||||
$subject .= ']';
|
||||
unset($headers['Cc']);
|
||||
unset($headers['Bcc']);
|
||||
} else {
|
||||
if ($this->cc) {
|
||||
$headers['Cc'] = $this->cc;
|
||||
}
|
||||
if ($this->bcc) {
|
||||
$headers['Bcc'] = $this->bcc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($ccAllTo = $this->config()->cc_all_emails_to) {
|
||||
if (!empty($headers['Cc']) && trim($headers['Cc'])) {
|
||||
$headers['Cc'] .= ', ' . $ccAllTo;
|
||||
} else {
|
||||
$headers['Cc'] = $ccAllTo;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bccAllTo = $this->config()->bcc_all_emails_to) {
|
||||
if (!empty($headers['Bcc']) && trim($headers['Bcc'])) {
|
||||
$headers['Bcc'] .= ', ' . $bccAllTo;
|
||||
} else {
|
||||
$headers['Bcc'] = $bccAllTo;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sendAllfrom = $this->config()->send_all_emails_from) {
|
||||
if ($from) {
|
||||
$subject .= " [from $from]";
|
||||
}
|
||||
$from = $sendAllfrom;
|
||||
}
|
||||
|
||||
Requirements::restore();
|
||||
|
||||
return self::mailer()->sendHTML(
|
||||
$to,
|
||||
$from,
|
||||
$subject,
|
||||
$this->body,
|
||||
$this->attachments,
|
||||
$headers,
|
||||
$this->plaintext_body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the email address to get as close to RFC 822 compliant as possible.
|
||||
*
|
||||
* @param string $email
|
||||
* @return bool
|
||||
* @param string $address
|
||||
* @return boolean
|
||||
*
|
||||
* @copyright Cal Henderson <cal@iamcal.com>
|
||||
* This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
|
||||
* http://creativecommons.org/licenses/by-sa/2.5/
|
||||
*/
|
||||
public static function is_valid_address($email)
|
||||
public static function is_valid_address($address)
|
||||
{
|
||||
$qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
|
||||
$dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
|
||||
$atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c'.
|
||||
'\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
|
||||
$quoted_pair = '\\x5c[\\x00-\\x7f]';
|
||||
$domain_literal = "\\x5b($dtext|$quoted_pair)*\\x5d";
|
||||
$quoted_string = "\\x22($qtext|$quoted_pair)*\\x22";
|
||||
$domain_ref = $atom;
|
||||
$sub_domain = "($domain_ref|$domain_literal)";
|
||||
$word = "($atom|$quoted_string)";
|
||||
$domain = "$sub_domain(\\x2e$sub_domain)*";
|
||||
$local_part = "$word(\\x2e$word)*";
|
||||
$addr_spec = "$local_part\\x40$domain";
|
||||
|
||||
return preg_match("!^$addr_spec$!", $email) === 1;
|
||||
return \Swift_Validate::email($address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an email-address to help protect it from spam bots. At the moment only simple string substitutions, which
|
||||
* are not 100% safe from email harvesting.
|
||||
*
|
||||
* @todo Integrate javascript-based solution
|
||||
* Encode an email-address to protect it from spambots.
|
||||
* At the moment only simple string substitutions,
|
||||
* which are not 100% safe from email harvesting.
|
||||
*
|
||||
* @param string $email Email-address
|
||||
* @param string $method Method for obfuscating/encoding the address
|
||||
@ -744,25 +111,659 @@ class Email extends ViewableData
|
||||
public static function obfuscate($email, $method = 'visible')
|
||||
{
|
||||
switch ($method) {
|
||||
case 'direction':
|
||||
Requirements::customCSS(
|
||||
'span.codedirection { unicode-bidi: bidi-override; direction: rtl; }',
|
||||
'codedirectionCSS'
|
||||
);
|
||||
case 'direction' :
|
||||
Requirements::customCSS('span.codedirection { unicode-bidi: bidi-override; direction: rtl; }', 'codedirectionCSS');
|
||||
|
||||
return '<span class="codedirection">' . strrev($email) . '</span>';
|
||||
case 'visible':
|
||||
case 'visible' :
|
||||
$obfuscated = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
|
||||
|
||||
return strtr($email, $obfuscated);
|
||||
case 'hex':
|
||||
case 'hex' :
|
||||
$encoded = '';
|
||||
for ($x=0; $x < strlen($email);
|
||||
$x++) {
|
||||
$encoded .= '&#x' . bin2hex($email{$x}).';';
|
||||
for ($x = 0; $x < strlen($email); $x++) {
|
||||
$encoded .= '&#x' . bin2hex($email{$x}) . ';';
|
||||
}
|
||||
|
||||
return $encoded;
|
||||
default:
|
||||
user_error('Email::obfuscate(): Unknown obfuscation method', E_USER_NOTICE);
|
||||
|
||||
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(
|
||||
$from = null,
|
||||
$to = null,
|
||||
$subject = null,
|
||||
$body = null,
|
||||
$cc = null,
|
||||
$bcc = null,
|
||||
$returnPath = null
|
||||
) {
|
||||
if ($from) {
|
||||
$this->setFrom($from);
|
||||
}
|
||||
if ($to) {
|
||||
$this->setTo($to);
|
||||
}
|
||||
if ($subject) {
|
||||
$this->setSubject($subject);
|
||||
}
|
||||
if ($body) {
|
||||
$this->setBody($body);
|
||||
}
|
||||
if ($cc) {
|
||||
$this->setCC($cc);
|
||||
}
|
||||
if ($bcc) {
|
||||
$this->setBCC($bcc);
|
||||
}
|
||||
if ($returnPath) {
|
||||
$this->setReturnPath($returnPath);
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Swift_Message
|
||||
*/
|
||||
public function getSwiftMessage()
|
||||
{
|
||||
if (!$this->swiftMessage) {
|
||||
$this->setSwiftMessage(new Swift_Message(null, null, 'text/html', 'utf-8'));
|
||||
}
|
||||
|
||||
return $this->swiftMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Swift_Message $swiftMessage
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSwiftMessage($swiftMessage)
|
||||
{
|
||||
$swiftMessage->setDate(DBDatetime::now()->Format('U'));
|
||||
if (!$swiftMessage->getFrom() && ($defaultFrom = $this->config()->admin_email)) {
|
||||
$swiftMessage->setFrom($defaultFrom);
|
||||
}
|
||||
$this->swiftMessage = $swiftMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->getSwiftMessage()->getFrom();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setFrom($address, $name = null)
|
||||
{
|
||||
$this->getSwiftMessage()->setFrom($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addFrom($address, $name = null)
|
||||
{
|
||||
$this->getSwiftMessage()->addFrom($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSender()
|
||||
{
|
||||
return $this->getSwiftMessage()->getSender();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setSender($address, $name = null)
|
||||
{
|
||||
$this->getSwiftMessage()->setSender($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getReturnPath()
|
||||
{
|
||||
return $this->getSwiftMessage()->getReturnPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* The bounce handler address
|
||||
*
|
||||
* @param string $address Email address where bounce notifications should be sent
|
||||
* @return $this
|
||||
*/
|
||||
public function setReturnPath($address)
|
||||
{
|
||||
$this->getSwiftMessage()->setReturnPath($address);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTo()
|
||||
{
|
||||
return $this->getSwiftMessage()->getTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recipient(s) of the email
|
||||
*
|
||||
* To send to many, pass an array:
|
||||
* 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)
|
||||
{
|
||||
$this->getSwiftMessage()->setTo($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addTo($address, $name = null)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$this->getSwiftMessage()->setCc($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addCC($address, $name = null)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$this->getSwiftMessage()->setBcc($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addBCC($address, $name = null)
|
||||
{
|
||||
$this->getSwiftMessage()->addBcc($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReplyTo()
|
||||
{
|
||||
return $this->getSwiftMessage()->getReplyTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setReplyTo($address, $name = null)
|
||||
{
|
||||
$this->getSwiftMessage()->setReplyTo($address, $name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $address
|
||||
* @param string|null $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addReplyTo($address, $name = null)
|
||||
{
|
||||
$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 $alias An override for the name of the file
|
||||
* @param string $mime The mime type for the attachment
|
||||
* @return $this
|
||||
*/
|
||||
public function addAttachment($path, $alias = null, $mime = null)
|
||||
{
|
||||
$attachment = \Swift_Attachment::fromPath($path);
|
||||
if ($alias) {
|
||||
$attachment->setFilename($alias);
|
||||
}
|
||||
if ($mime) {
|
||||
$attachment->setContentType($mime);
|
||||
}
|
||||
$this->getSwiftMessage()->attach($attachment);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param string $name
|
||||
* @param string $mime
|
||||
* @return $this
|
||||
*/
|
||||
public function addAttachmentFromData($data, $name, $mime = null)
|
||||
{
|
||||
$attachment = new \Swift_Attachment($data, $name);
|
||||
if ($mime) {
|
||||
$attachment->setContentType($mime);
|
||||
}
|
||||
$this->getSwiftMessage()->attach($attachment);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|ViewableData The template data
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|ViewableData $data The template data to set
|
||||
* @return $this
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $name The data name to add or array to names => value
|
||||
* @param string|null $value The value of the data to add
|
||||
* @return $this
|
||||
*/
|
||||
public function addData($name, $value = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->data = array_merge($this->data, $name);
|
||||
} elseif (is_array($this->data)) {
|
||||
$this->data[$name] = $value;
|
||||
} else {
|
||||
$this->data->$name = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a datum from the message
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function removeData($name)
|
||||
{
|
||||
if (is_array($this->data)) {
|
||||
unset($this->data[$name]);
|
||||
} else {
|
||||
$this->data->$name = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->getSwiftMessage()->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body The email body
|
||||
* @return $this
|
||||
*/
|
||||
public function setBody($body)
|
||||
{
|
||||
$body = HTTP::absoluteURLs($body);
|
||||
$this->getSwiftMessage()->setBody($body);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The base URL for the email
|
||||
*/
|
||||
public function BaseURL()
|
||||
{
|
||||
return Director::absoluteBaseURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging help
|
||||
*
|
||||
* @return string Debug info
|
||||
*/
|
||||
public function debug()
|
||||
{
|
||||
$this->render();
|
||||
|
||||
return "<h2>Email template {$this->class}:</h2>\n" . '<pre>' . $this->getSwiftMessage()->toString() . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHTMLTemplate()
|
||||
{
|
||||
return $this->HTMLTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template to render the email with
|
||||
*
|
||||
* @param string $template
|
||||
* @return $this
|
||||
*/
|
||||
public function setHTMLTemplate($template)
|
||||
{
|
||||
if (substr($template, -3) == '.ss') {
|
||||
$template = substr($template, 0, -3);
|
||||
}
|
||||
$this->HTMLTemplate = $template;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template to render the plain part with
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPlainTemplate()
|
||||
{
|
||||
return $this->plainTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template to render the plain part with
|
||||
*
|
||||
* @param string $template
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlainTemplate($template)
|
||||
{
|
||||
if (substr($template, -3) == '.ss') {
|
||||
$template = substr($template, 0, -3);
|
||||
}
|
||||
$this->plainTemplate = $template;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $recipients
|
||||
* @return $this
|
||||
*/
|
||||
public function setFailedRecipients($recipients)
|
||||
{
|
||||
$this->failedRecipients = $recipients;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFailedRecipients()
|
||||
{
|
||||
return $this->failedRecipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link SSViewer} templates to detect if we're rendering an email template rather than a page template
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsEmail()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message to the recipients
|
||||
*
|
||||
* @return bool true if successful or array of failed recipients
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
if (!$this->getBody()) {
|
||||
$this->render();
|
||||
}
|
||||
if (!$this->hasPlainPart()) {
|
||||
$this->generatePlainPartFromBody();
|
||||
}
|
||||
return Injector::inst()->get(Mailer::class)->send($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|bool
|
||||
*/
|
||||
public function sendPlain()
|
||||
{
|
||||
if (!$this->hasPlainPart()) {
|
||||
$this->render(true);
|
||||
}
|
||||
return Injector::inst()->get(Mailer::class)->send($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the email
|
||||
* @param bool $plainOnly Only render the message as plain text
|
||||
* @return $this
|
||||
*/
|
||||
public function render($plainOnly = false)
|
||||
{
|
||||
if ($existingPlainPart = $this->findPlainPart()) {
|
||||
$this->getSwiftMessage()->detach($existingPlainPart);
|
||||
}
|
||||
unset($existingPlainPart);
|
||||
if (!$this->getHTMLTemplate() && !$this->getPlainTemplate()) {
|
||||
return $this;
|
||||
}
|
||||
$HTMLPart = '';
|
||||
$plainPart = '';
|
||||
|
||||
if ($this->getHTMLTemplate()) {
|
||||
$HTMLPart = $this->renderWith($this->getHTMLTemplate(), $this->getData());
|
||||
}
|
||||
|
||||
if ($this->getPlainTemplate()) {
|
||||
$plainPart = $this->renderWith($this->getPlainTemplate(), $this->getData());
|
||||
} elseif ($HTMLPart) {
|
||||
$plainPart = Convert::xml2raw($HTMLPart);
|
||||
}
|
||||
|
||||
if ($HTMLPart && !$plainOnly) {
|
||||
$this->setBody($HTMLPart);
|
||||
$this->getSwiftMessage()->setContentType('text/html');
|
||||
$this->getSwiftMessage()->setCharset('utf-8');
|
||||
if ($plainPart) {
|
||||
$this->getSwiftMessage()->addPart($plainPart, 'text/plain', 'utf-8');
|
||||
}
|
||||
} elseif ($plainPart || $plainOnly) {
|
||||
if ($plainPart) {
|
||||
$this->setBody($plainPart);
|
||||
}
|
||||
$this->getSwiftMessage()->setContentType('text/plain');
|
||||
$this->getSwiftMessage()->setCharset('utf-8');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Swift_MimePart|false
|
||||
*/
|
||||
public function findPlainPart()
|
||||
{
|
||||
foreach ($this->getSwiftMessage()->getChildren() as $child) {
|
||||
if ($child instanceof Swift_MimePart && $child->getContentType() == 'text/plain') {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPlainPart()
|
||||
{
|
||||
if ($this->getSwiftMessage()->getContentType() == 'text/plain') {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->findPlainPart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically adds a plain part to the email generated from the current Body
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function generatePlainPartFromBody()
|
||||
{
|
||||
$this->getSwiftMessage()->addPart(
|
||||
Convert::xml2raw($this->getBody()),
|
||||
'text/plain',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -2,526 +2,12 @@
|
||||
|
||||
namespace SilverStripe\Control\Email;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Control\HTTP;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Object;
|
||||
|
||||
/**
|
||||
* Mailer objects are responsible for actually sending emails.
|
||||
* The default Mailer class will use PHP's mail() function.
|
||||
*/
|
||||
class Mailer extends Object
|
||||
interface Mailer
|
||||
{
|
||||
|
||||
/**
|
||||
* Default encoding type for messages. Available options are:
|
||||
* - quoted-printable
|
||||
* - base64
|
||||
*
|
||||
* @var string
|
||||
* @config
|
||||
* @param Email $email
|
||||
* @return bool
|
||||
*/
|
||||
private static $default_message_encoding = 'quoted-printable';
|
||||
|
||||
/**
|
||||
* Encoding type currently set
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $messageEncoding = null;
|
||||
|
||||
/**
|
||||
* Email used for bounces
|
||||
*
|
||||
* @var string
|
||||
* @config
|
||||
*/
|
||||
private static $default_bounce_email = null;
|
||||
|
||||
/**
|
||||
* Email used for bounces
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bounceEmail = null;
|
||||
|
||||
/**
|
||||
* Email used for bounces
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBounceEmail()
|
||||
{
|
||||
return $this->bounceEmail
|
||||
?: (defined('BOUNCE_EMAIL') ? BOUNCE_EMAIL : null)
|
||||
?: self::config()->default_bounce_email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the email used for bounces
|
||||
*
|
||||
* @param string $email
|
||||
*/
|
||||
public function setBounceEmail($email)
|
||||
{
|
||||
$this->bounceEmail = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding type used for plain text messages
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessageEncoding()
|
||||
{
|
||||
return $this->messageEncoding ?: static::config()->default_message_encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets encoding type for messages. Available options are:
|
||||
* - quoted-printable
|
||||
* - base64
|
||||
*
|
||||
* @param string $encoding
|
||||
*/
|
||||
public function setMessageEncoding($encoding)
|
||||
{
|
||||
$this->messageEncoding = $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a message using the given encoding mechanism
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $encoding
|
||||
* @return string Encoded $message
|
||||
*/
|
||||
protected function encodeMessage($message, $encoding)
|
||||
{
|
||||
switch ($encoding) {
|
||||
case 'base64':
|
||||
return chunk_split(base64_encode($message), 60);
|
||||
case 'quoted-printable':
|
||||
return quoted_printable_encode($message);
|
||||
default:
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge custom headers with default ones
|
||||
*
|
||||
* @param array $headers Default headers
|
||||
* @param array $customHeaders Custom headers
|
||||
* @return array Resulting message headers
|
||||
*/
|
||||
protected function mergeCustomHeaders($headers, $customHeaders)
|
||||
{
|
||||
$headers["X-Mailer"] = X_MAILER;
|
||||
if (!isset($customHeaders["X-Priority"])) {
|
||||
$headers["X-Priority"] = 3;
|
||||
}
|
||||
|
||||
// Merge!
|
||||
$headers = array_merge($headers, $customHeaders);
|
||||
|
||||
// Headers 'Cc' and 'Bcc' need to have the correct case
|
||||
foreach (array('Bcc', 'Cc') as $correctKey) {
|
||||
foreach ($headers as $key => $value) {
|
||||
if (strcmp($key, $correctKey) !== 0 && strcasecmp($key, $correctKey) === 0) {
|
||||
$headers[$correctKey] = $value;
|
||||
unset($headers[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a plain-text email.
|
||||
*
|
||||
* @param string $to Email recipient
|
||||
* @param string $from Email from
|
||||
* @param string $subject Subject text
|
||||
* @param string $plainContent Plain text content
|
||||
* @param array $attachedFiles List of attached files
|
||||
* @param array $customHeaders List of custom headers
|
||||
* @return mixed Return false if failure, or list of arguments if success
|
||||
*/
|
||||
public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = array(), $customHeaders = array())
|
||||
{
|
||||
// Prepare plain text body
|
||||
$fullBody = $this->encodeMessage($plainContent, $this->getMessageEncoding());
|
||||
$headers["Content-Type"] = "text/plain; charset=utf-8";
|
||||
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();
|
||||
|
||||
// Send prepared message
|
||||
return $this->sendPreparedMessage($to, $from, $subject, $attachedFiles, $customHeaders, $fullBody, $headers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends an email as a both HTML and plaintext
|
||||
*
|
||||
* @param string $to Email recipient
|
||||
* @param string $from Email from
|
||||
* @param string $subject Subject text
|
||||
* @param string $htmlContent HTML Content
|
||||
* @param array $attachedFiles List of attachments
|
||||
* @param array $customHeaders User specified headers
|
||||
* @param string $plainContent Plain text content. If omitted, will be generated from $htmlContent
|
||||
* @return mixed Return false if failure, or list of arguments if success
|
||||
*/
|
||||
public function sendHTML(
|
||||
$to,
|
||||
$from,
|
||||
$subject,
|
||||
$htmlContent,
|
||||
$attachedFiles = array(),
|
||||
$customHeaders = array(),
|
||||
$plainContent = ''
|
||||
) {
|
||||
// Prepare both Plain and HTML components and merge
|
||||
$plainPart = $this->preparePlainSubmessage($plainContent, $htmlContent);
|
||||
$htmlPart = $this->prepareHTMLSubmessage($htmlContent);
|
||||
list($fullBody, $headers) = $this->encodeMultipart(
|
||||
array($plainPart, $htmlPart),
|
||||
"multipart/alternative"
|
||||
);
|
||||
|
||||
// Send prepared message
|
||||
return $this->sendPreparedMessage($to, $from, $subject, $attachedFiles, $customHeaders, $fullBody, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email of an arbitrary format
|
||||
*
|
||||
* @param string $to To
|
||||
* @param string $from From
|
||||
* @param string $subject Subject
|
||||
* @param array $attachedFiles List of attachments
|
||||
* @param array $customHeaders User specified headers
|
||||
* @param string $fullBody Prepared message
|
||||
* @param array $headers Prepared headers
|
||||
* @return mixed Return false if failure, or list of arguments if success
|
||||
*/
|
||||
protected function sendPreparedMessage($to, $from, $subject, $attachedFiles, $customHeaders, $fullBody, $headers)
|
||||
{
|
||||
// If the subject line contains extended characters, we must encode the
|
||||
$subjectEncoded = "=?UTF-8?B?" . base64_encode($subject) . "?=";
|
||||
$to = $this->validEmailAddress($to);
|
||||
$from = $this->validEmailAddress($from);
|
||||
|
||||
// Messages with attachments are handled differently
|
||||
if ($attachedFiles) {
|
||||
list($fullBody, $headers) = $this->encodeAttachments($attachedFiles, $headers, $fullBody);
|
||||
}
|
||||
|
||||
// Get bounce email
|
||||
$bounceAddress = $this->getBounceEmail() ?: $from;
|
||||
if (preg_match('/^([^<>]*)<([^<>]+)> *$/', $bounceAddress, $parts)) {
|
||||
$bounceAddress = $parts[2];
|
||||
}
|
||||
|
||||
// Get headers
|
||||
$headers["From"] = $from;
|
||||
$headers = $this->mergeCustomHeaders($headers, $customHeaders);
|
||||
$headersEncoded = $this->processHeaders($headers);
|
||||
|
||||
return $this->email($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the actual email
|
||||
*
|
||||
* @param string $to
|
||||
* @param string $subjectEncoded
|
||||
* @param string $fullBody
|
||||
* @param string $headersEncoded
|
||||
* @param string $bounceAddress
|
||||
* @return mixed Return false if failure, or list of arguments if success
|
||||
*/
|
||||
protected function email($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress)
|
||||
{
|
||||
// Try it without the -f option if it fails
|
||||
$result = @mail($to, $subjectEncoded, $fullBody, $headersEncoded, escapeshellarg("-f$bounceAddress"));
|
||||
if (!$result) {
|
||||
$result = mail($to, $subjectEncoded, $fullBody, $headersEncoded);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
return array($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode attachments into a message
|
||||
*
|
||||
* @param array $attachments
|
||||
* @param array $headers
|
||||
* @param string $body
|
||||
* @return array Array containing completed body followed by headers
|
||||
*/
|
||||
protected function encodeAttachments($attachments, $headers, $body)
|
||||
{
|
||||
// The first part is the message itself
|
||||
$fullMessage = $this->processHeaders($headers, $body);
|
||||
$messageParts = array($fullMessage);
|
||||
|
||||
// Include any specified attachments as additional parts
|
||||
foreach ($attachments as $file) {
|
||||
if (isset($file['tmp_name']) && isset($file['name'])) {
|
||||
$messageParts[] = $this->encodeFileForEmail($file['tmp_name'], $file['name']);
|
||||
} else {
|
||||
$messageParts[] = $this->encodeFileForEmail($file);
|
||||
}
|
||||
}
|
||||
|
||||
// We further wrap all of this into another multipart block
|
||||
return $this->encodeMultipart($messageParts, "multipart/mixed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the plainPart of a html message
|
||||
*
|
||||
* @param string $plainContent Plain body
|
||||
* @param string $htmlContent HTML message
|
||||
* @return string Encoded headers / message in a single block
|
||||
*/
|
||||
protected function preparePlainSubmessage($plainContent, $htmlContent)
|
||||
{
|
||||
$plainEncoding = $this->getMessageEncoding();
|
||||
|
||||
// Generate plain text version if not explicitly given
|
||||
if (!$plainContent) {
|
||||
$plainContent = Convert::xml2raw($htmlContent);
|
||||
}
|
||||
|
||||
// Make the plain text part
|
||||
$headers["Content-Type"] = "text/plain; charset=utf-8";
|
||||
$headers["Content-Transfer-Encoding"] = $plainEncoding;
|
||||
$plainContentEncoded = $this->encodeMessage($plainContent, $plainEncoding);
|
||||
|
||||
// Merge with headers
|
||||
return $this->processHeaders($headers, $plainContentEncoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the html part of a html message
|
||||
*
|
||||
* @param string $htmlContent HTML message
|
||||
* @return string Encoded headers / message in a single block
|
||||
*/
|
||||
protected function prepareHTMLSubmessage($htmlContent)
|
||||
{
|
||||
// Add basic wrapper tags if the body tag hasn't been given
|
||||
if (stripos($htmlContent, '<body') === false) {
|
||||
$htmlContent =
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" .
|
||||
"<HTML><HEAD>\n" .
|
||||
"<META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" .
|
||||
"<STYLE type=\"text/css\"></STYLE>\n\n".
|
||||
"</HEAD>\n" .
|
||||
"<BODY bgColor=\"#ffffff\">\n" .
|
||||
$htmlContent .
|
||||
"\n</BODY>\n" .
|
||||
"</HTML>";
|
||||
}
|
||||
|
||||
// Make the HTML part
|
||||
$headers["Content-Type"] = "text/html; charset=utf-8";
|
||||
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();
|
||||
$htmlContentEncoded = $this->encodeMessage($htmlContent, $this->getMessageEncoding());
|
||||
|
||||
// Merge with headers
|
||||
return $this->processHeaders($headers, $htmlContentEncoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an array of parts using multipart
|
||||
*
|
||||
* @param array $parts List of parts
|
||||
* @param string $contentType Content-type of parts
|
||||
* @param array $headers Existing headers to include in response
|
||||
* @return array Array with two items, the body followed by headers
|
||||
*/
|
||||
protected function encodeMultipart($parts, $contentType, $headers = array())
|
||||
{
|
||||
$separator = "----=_NextPart_" . preg_replace('/[^0-9]/', '', rand() * 10000000000);
|
||||
|
||||
$headers["MIME-Version"] = "1.0";
|
||||
$headers["Content-Type"] = "$contentType; boundary=\"$separator\"";
|
||||
$headers["Content-Transfer-Encoding"] = "7bit";
|
||||
|
||||
if ($contentType == "multipart/alternative") {
|
||||
// $baseMessage = "This is an encoded HTML message. There are two parts: a plain text and an HTML message,
|
||||
// open whatever suits you better.";
|
||||
$baseMessage = "\nThis is a multi-part message in MIME format.";
|
||||
} else {
|
||||
// $baseMessage = "This is a message containing attachments. The e-mail body is contained in the first
|
||||
// attachment";
|
||||
$baseMessage = "\nThis is a multi-part message in MIME format.";
|
||||
}
|
||||
|
||||
$separator = "\n--$separator\n";
|
||||
$body = "$baseMessage\n" .
|
||||
$separator . implode("\n".$separator, $parts) . "\n" . trim($separator) . "--";
|
||||
|
||||
return array($body, $headers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add headers to the start of the message
|
||||
*
|
||||
* @param array $headers
|
||||
* @param string $body
|
||||
* @return string Resulting message body
|
||||
*/
|
||||
protected function processHeaders($headers, $body = '')
|
||||
{
|
||||
$result = '';
|
||||
foreach ($headers as $key => $value) {
|
||||
$result .= "$key: $value\n";
|
||||
}
|
||||
if ($body) {
|
||||
$result .= "\n$body";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the contents of a file for emailing, including headers
|
||||
*
|
||||
* $file can be an array, in which case it expects these members:
|
||||
* 'filename' - the filename of the file
|
||||
* 'contents' - the raw binary contents of the file as a string
|
||||
* and can optionally include these members:
|
||||
* 'mimetype' - the mimetype of the file (calculated from filename if missing)
|
||||
* 'contentLocation' - the 'Content-Location' header value for the file
|
||||
*
|
||||
* $file can also be a string, in which case it is assumed to be the filename
|
||||
*
|
||||
* h5. contentLocation
|
||||
*
|
||||
* Content Location is one of the two methods allowed for embedding images into an html email.
|
||||
* It's also the simplest, and best supported.
|
||||
*
|
||||
* Assume we have an email with this in the body:
|
||||
*
|
||||
* <img src="http://example.com/image.gif" />
|
||||
*
|
||||
* To display the image, an email viewer would have to download the image from the web every time
|
||||
* it is displayed. Due to privacy issues, most viewers will not display any images unless
|
||||
* the user clicks 'Show images in this email'. Not optimal.
|
||||
*
|
||||
* However, we can also include a copy of this image as an attached file in the email.
|
||||
* By giving it a contentLocation of "http://example.com/image.gif" most email viewers
|
||||
* will use this attached copy instead of downloading it. Better,
|
||||
* most viewers will show it without a 'Show images in this email' conformation.
|
||||
*
|
||||
* Here is an example of passing this information through Email.php:
|
||||
*
|
||||
* $email = new Email();
|
||||
* $email->attachments[] = array(
|
||||
* 'filename' => BASE_PATH . "/themes/mytheme/images/header.gif",
|
||||
* 'contents' => file_get_contents(BASE_PATH . "/themes/mytheme/images/header.gif"),
|
||||
* 'mimetype' => 'image/gif',
|
||||
* 'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif"
|
||||
* );
|
||||
*
|
||||
* @param array|string $file
|
||||
* @param bool $destFileName
|
||||
* @param string $disposition
|
||||
* @param string $extraHeaders
|
||||
* @return string
|
||||
*/
|
||||
protected function encodeFileForEmail($file, $destFileName = false, $disposition = null, $extraHeaders = "")
|
||||
{
|
||||
if (!$file) {
|
||||
throw new InvalidArgumentException("Not passed a filename and/or data");
|
||||
}
|
||||
|
||||
if (is_string($file)) {
|
||||
$file = array('filename' => $file);
|
||||
$fh = fopen($file['filename'], "rb");
|
||||
if ($fh) {
|
||||
$file['contents'] = "";
|
||||
while (!feof($fh)) {
|
||||
$file['contents'] .= fread($fh, 10000);
|
||||
}
|
||||
fclose($fh);
|
||||
}
|
||||
}
|
||||
|
||||
// Build headers, including content type
|
||||
if (!$destFileName) {
|
||||
$base = basename($file['filename']);
|
||||
} else {
|
||||
$base = $destFileName;
|
||||
}
|
||||
|
||||
$mimeType = !empty($file['mimetype']) ? $file['mimetype'] : HTTP::get_mime_type($file['filename']);
|
||||
if (!$mimeType) {
|
||||
$mimeType = "application/unknown";
|
||||
}
|
||||
if (empty($disposition)) {
|
||||
$disposition = isset($file['contentLocation']) ? 'inline' : 'attachment';
|
||||
}
|
||||
|
||||
// Encode for emailing
|
||||
if (substr($mimeType, 0, 4) != 'text') {
|
||||
$encoding = "base64";
|
||||
$file['contents'] = chunk_split(base64_encode($file['contents']));
|
||||
} else {
|
||||
// This mime type is needed, otherwise some clients will show it as an inline attachment
|
||||
$mimeType = 'application/octet-stream';
|
||||
$encoding = "quoted-printable";
|
||||
$file['contents'] = quoted_printable_encode($file['contents']);
|
||||
}
|
||||
|
||||
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
|
||||
"Content-Transfer-Encoding: $encoding\n".
|
||||
"Content-Disposition: $disposition;\n\tfilename=\"$base\"\n";
|
||||
|
||||
if (isset($file['contentLocation'])) {
|
||||
$headers .= 'Content-Location: ' . $file['contentLocation'] . "\n" ;
|
||||
}
|
||||
|
||||
$headers .= $extraHeaders . "\n";
|
||||
|
||||
// Return completed packet
|
||||
return $headers . $file['contents'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up emails which may be in 'Name <email@silverstripe.com>' format
|
||||
*
|
||||
* @param string $emailAddress
|
||||
* @return string
|
||||
*/
|
||||
protected function validEmailAddress($emailAddress)
|
||||
{
|
||||
$emailAddress = trim($emailAddress);
|
||||
$openBracket = strpos($emailAddress, '<');
|
||||
$closeBracket = strpos($emailAddress, '>');
|
||||
|
||||
// Unwrap email contained by braces
|
||||
if ($openBracket === 0 && $closeBracket !== false) {
|
||||
return substr($emailAddress, 1, $closeBracket - 1);
|
||||
}
|
||||
|
||||
// Ensure name component cannot be mistaken for an email address
|
||||
if ($openBracket) {
|
||||
$emailAddress = str_replace('@', '', substr($emailAddress, 0, $openBracket))
|
||||
. substr($emailAddress, $openBracket);
|
||||
}
|
||||
|
||||
return $emailAddress;
|
||||
}
|
||||
public function send($email);
|
||||
}
|
||||
|
80
src/Control/Email/SwiftMailer.php
Normal file
80
src/Control/Email/SwiftMailer.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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 = array(
|
||||
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 = array();
|
||||
$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;
|
||||
}
|
||||
}
|
87
src/Control/Email/SwiftPlugin.php
Normal file
87
src/Control/Email/SwiftPlugin.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Email;
|
||||
|
||||
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::config()->send_all_emails_to;
|
||||
$ccAllTo = Email::config()->cc_all_emails_to;
|
||||
$bccAllTo = Email::config()->bcc_all_emails_to;
|
||||
$sendAllFrom = Email::config()->send_all_emails_from;
|
||||
|
||||
if (!empty($sendAllTo)) {
|
||||
$this->setTo($message, $sendAllTo);
|
||||
}
|
||||
|
||||
if (!empty($ccAllTo)) {
|
||||
if (!is_array($ccAllTo)) {
|
||||
$ccAllTo = array($ccAllTo => null);
|
||||
}
|
||||
foreach ($ccAllTo as $address => $name) {
|
||||
$message->addCc($address, $name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($bccAllTo)) {
|
||||
if (!is_array($bccAllTo)) {
|
||||
$bccAllTo = array($bccAllTo => null);
|
||||
}
|
||||
foreach ($bccAllTo as $address => $name) {
|
||||
$message->addBcc($address, $name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($sendAllFrom)) {
|
||||
$this->setFrom($message, $sendAllFrom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Swift_Mime_Message $message
|
||||
* @param 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_Mime_Message $message
|
||||
* @param 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
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ use SilverStripe\CMS\Controllers\RootURLController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Control\Email\Email;
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
@ -307,10 +308,13 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
// Clear requirements
|
||||
Requirements::clear();
|
||||
|
||||
// Set up email
|
||||
// Set up the test mailer
|
||||
$this->mailer = new TestMailer();
|
||||
Injector::inst()->registerService($this->mailer, 'SilverStripe\\Control\\Email\\Mailer');
|
||||
Injector::inst()->registerService($this->mailer, Mailer::class);
|
||||
Email::config()->remove('send_all_emails_to');
|
||||
Email::config()->remove('send_all_emails_from');
|
||||
Email::config()->remove('cc_all_emails_to');
|
||||
Email::config()->remove('bcc_all_emails_to');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,87 +3,69 @@
|
||||
namespace SilverStripe\Dev;
|
||||
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
use Swift_Attachment;
|
||||
|
||||
class TestMailer extends Mailer
|
||||
class TestMailer implements Mailer
|
||||
{
|
||||
protected $emailsSent = array();
|
||||
|
||||
/**
|
||||
* Send a plain-text email.
|
||||
* TestMailer will merely record that the email was asked to be sent, without sending anything.
|
||||
*
|
||||
* @param string $to
|
||||
* @param string $from
|
||||
* @param string $subject
|
||||
* @param string $plainContent
|
||||
* @param bool $attachedFiles
|
||||
* @param bool $customHeaders
|
||||
* @return bool|mixed
|
||||
* @var array
|
||||
*/
|
||||
public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customHeaders = false)
|
||||
protected $emailsSent = [];
|
||||
|
||||
public function send($email)
|
||||
{
|
||||
$this->saveEmail([
|
||||
'Type' => 'plain',
|
||||
'To' => $to,
|
||||
'From' => $from,
|
||||
'Subject' => $subject,
|
||||
|
||||
'Content' => $plainContent,
|
||||
'PlainContent' => $plainContent,
|
||||
|
||||
'AttachedFiles' => $attachedFiles,
|
||||
'CustomHeaders' => $customHeaders,
|
||||
]);
|
||||
|
||||
return true;
|
||||
// Detect body type
|
||||
$htmlContent = null;
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a multi-part HTML email
|
||||
* TestMailer will merely record that the email was asked to be sent, without sending anything.
|
||||
*
|
||||
* @param string $to
|
||||
* @param string $from
|
||||
* @param string $subject
|
||||
* @param string $htmlContent
|
||||
* @param bool $attachedFiles
|
||||
* @param bool $customHeaders
|
||||
* @param bool $plainContent
|
||||
* @param bool $inlineImages
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function sendHTML(
|
||||
$to,
|
||||
$from,
|
||||
$subject,
|
||||
$htmlContent,
|
||||
$attachedFiles = false,
|
||||
$customHeaders = false,
|
||||
$plainContent = false,
|
||||
$inlineImages = false
|
||||
) {
|
||||
// Get attachments
|
||||
$attachedFiles = [];
|
||||
foreach ($email->getSwiftMessage()->getChildren() as $child) {
|
||||
if ($child instanceof Swift_Attachment) {
|
||||
$attachedFiles[] = [
|
||||
'contents' => $child->getBody(),
|
||||
'filename' => $child->getFilename(),
|
||||
'mimetype' => $child->getContentType(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->saveEmail([
|
||||
'Type' => 'html',
|
||||
'To' => $to,
|
||||
'From' => $from,
|
||||
'Subject' => $subject,
|
||||
// Serialise email
|
||||
$serialised = [
|
||||
'Type' => $type,
|
||||
'To' => implode(';', array_keys($email->getTo() ?: [])),
|
||||
'From' => implode(';', array_keys($email->getFrom() ?: [])),
|
||||
'Subject' => $email->getSubject(),
|
||||
'Content' => $email->getBody(),
|
||||
'AttachedFiles' => $attachedFiles
|
||||
];
|
||||
if ($plainContent) {
|
||||
$serialised['PlainContent'] = $plainContent;
|
||||
}
|
||||
if ($htmlContent) {
|
||||
$serialised['HtmlContent'] = $htmlContent;
|
||||
}
|
||||
|
||||
'Content' => $htmlContent,
|
||||
'PlainContent' => $plainContent,
|
||||
'HtmlContent' => $htmlContent,
|
||||
|
||||
'AttachedFiles' => $attachedFiles,
|
||||
'CustomHeaders' => $customHeaders,
|
||||
'InlineImages' => $inlineImages,
|
||||
]);
|
||||
$this->saveEmail($serialised);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a single email to the log
|
||||
* @param $data A map of information about the email
|
||||
*
|
||||
* @param array $data A map of information about the email
|
||||
*/
|
||||
protected function saveEmail($data)
|
||||
{
|
||||
@ -138,5 +120,6 @@ class TestMailer extends Mailer
|
||||
return $email;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\Email\Email;
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
@ -953,18 +954,17 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
|
||||
// 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.
|
||||
if ((Director::isLive() || Email::mailer() instanceof TestMailer)
|
||||
if ((Director::isLive() || Injector::inst()->get(Mailer::class) instanceof TestMailer)
|
||||
&& $this->isChanged('Password')
|
||||
&& $this->record['Password']
|
||||
&& $this->config()->notify_password_change
|
||||
) {
|
||||
/** @var Email $e */
|
||||
$e = Email::create();
|
||||
$e->setSubject(_t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject'));
|
||||
$e->setTemplate('ChangePasswordEmail');
|
||||
$e->populateTemplate($this);
|
||||
$e->setTo($this->Email);
|
||||
$e->send();
|
||||
Email::create()
|
||||
->setHTMLTemplate('SilverStripe\\Control\\Email\\ChangePasswordEmail')
|
||||
->setData($this)
|
||||
->setTo($this->Email)
|
||||
->setSubject(_t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject'))
|
||||
->send();
|
||||
}
|
||||
|
||||
// The test on $this->ID is used for when records are initially created.
|
||||
@ -1796,6 +1796,7 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
$this->write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HtmlEditorConfig for this user to be used in the CMS.
|
||||
* This is set by the group. If multiple configurations are set,
|
||||
|
@ -363,16 +363,13 @@ JS;
|
||||
if ($member) {
|
||||
$token = $member->generateAutologinTokenAndStoreHash();
|
||||
|
||||
/** @var Email $e */
|
||||
$e = Email::create();
|
||||
$e->setSubject(_t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject'));
|
||||
$e->setTemplate('ForgotPasswordEmail');
|
||||
$e->populateTemplate($member);
|
||||
$e->populateTemplate(array(
|
||||
'PasswordResetLink' => Security::getPasswordResetLink($member, $token)
|
||||
));
|
||||
$e->setTo($member->Email);
|
||||
$e->send();
|
||||
Email::create()
|
||||
->setHTMLTemplate('SilverStripe\\Control\\Email\\ForgotPasswordEmail')
|
||||
->setData($member)
|
||||
->setSubject(_t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject'))
|
||||
->addData('PasswordResetLink', Security::getPasswordResetLink($member, $token))
|
||||
->setTo($member->Email)
|
||||
->send();
|
||||
|
||||
return $this->controller->redirect('Security/passwordsent/' . urlencode($data['Email']));
|
||||
} elseif ($data['Email']) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
$Body
|
||||
$EmailContent
|
||||
</div>
|
||||
|
||||
</body>
|
@ -2,59 +2,54 @@
|
||||
|
||||
namespace SilverStripe\Control\Tests\Email;
|
||||
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use PHPUnit_Framework_MockObject_MockObject;
|
||||
use SilverStripe\Control\Email\Email;
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
use SilverStripe\Control\Email\SwiftMailer;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\Security\Member;
|
||||
use Swift_Attachment;
|
||||
use Swift_Mailer;
|
||||
use Swift_Message;
|
||||
use Swift_NullTransport;
|
||||
use Swift_RfcComplianceException;
|
||||
|
||||
class EmailTest extends SapphireTest
|
||||
{
|
||||
|
||||
public function testAttachFiles()
|
||||
public function testAddAttachment()
|
||||
{
|
||||
$email = new Email();
|
||||
|
||||
$email->attachFileFromString('foo bar', 'foo.txt', 'text/plain');
|
||||
$email->attachFile(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
|
||||
$this->assertEquals(
|
||||
array('contents'=>'foo bar', 'filename'=>'foo.txt', 'mimetype'=>'text/plain'),
|
||||
$email->attachments[0],
|
||||
'File is attached correctly from string'
|
||||
);
|
||||
$children = $email->getSwiftMessage()->getChildren();
|
||||
$this->assertCount(1, $children);
|
||||
|
||||
$this->assertEquals(
|
||||
array('contents'=>'Hello, I\'m a text document.', 'filename'=>'attachment.txt', 'mimetype'=>'text/plain'),
|
||||
$email->attachments[1],
|
||||
'File is attached correctly from file'
|
||||
);
|
||||
/** @var Swift_Attachment $child */
|
||||
$child = reset($children);
|
||||
|
||||
$this->assertInstanceOf(Swift_Attachment::class, $child);
|
||||
$this->assertEquals('text/plain', $child->getContentType());
|
||||
$this->assertEquals('attachment.txt', $child->getFilename());
|
||||
}
|
||||
|
||||
public function testCustomHeaders()
|
||||
public function testAddAttachmentFromData()
|
||||
{
|
||||
$email = new Email();
|
||||
|
||||
$email->addCustomHeader('Cc', 'test1@example.com');
|
||||
$email->addCustomHeader('Bcc', 'test2@example.com');
|
||||
$email->addAttachmentFromData('foo bar', 'foo.txt', 'text/plain');
|
||||
$children = $email->getSwiftMessage()->getChildren();
|
||||
|
||||
$this->assertEmpty(
|
||||
$email->customHeaders,
|
||||
'addCustomHeader() doesn\'t add Cc and Bcc headers'
|
||||
);
|
||||
$this->assertCount(1, $children);
|
||||
|
||||
$email->addCustomHeader('Reply-To', 'test1@example.com');
|
||||
$this->assertEquals(
|
||||
array('Reply-To' => 'test1@example.com'),
|
||||
$email->customHeaders,
|
||||
'addCustomHeader() adds headers'
|
||||
);
|
||||
/** @var Swift_Attachment $child */
|
||||
$child = reset($children);
|
||||
|
||||
$email->addCustomHeader('Reply-To', 'test2@example.com');
|
||||
$this->assertEquals(
|
||||
array('Reply-To' => 'test1@example.com, test2@example.com'),
|
||||
$email->customHeaders,
|
||||
'addCustomHeader() appends data to existing headers'
|
||||
);
|
||||
$this->assertInstanceOf(Swift_Attachment::class, $child);
|
||||
$this->assertEquals('foo bar', $child->getBody());
|
||||
$this->assertEquals('text/plain', $child->getContentType());
|
||||
$this->assertEquals('foo.txt', $child->getFilename());
|
||||
}
|
||||
|
||||
public function testValidEmailAddress()
|
||||
@ -63,17 +58,11 @@ class EmailTest extends SapphireTest
|
||||
$invalidEmails = array('foo.bar@', '@example.com', 'foo@');
|
||||
|
||||
foreach ($validEmails as $email) {
|
||||
$this->assertTrue(
|
||||
Email::is_valid_address($email),
|
||||
'is_valid_address() returns true for a valid email address'
|
||||
);
|
||||
$this->assertTrue(Email::is_valid_address($email));
|
||||
}
|
||||
|
||||
foreach ($invalidEmails as $email) {
|
||||
$this->assertFalse(
|
||||
Email::is_valid_address($email),
|
||||
'is_valid_address() returns false for an invalid email address'
|
||||
);
|
||||
$this->assertFalse(Email::is_valid_address($email));
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,122 +74,508 @@ class EmailTest extends SapphireTest
|
||||
$visible = Email::obfuscate($emailAddress, 'visible');
|
||||
$hex = Email::obfuscate($emailAddress, 'hex');
|
||||
|
||||
$this->assertEquals('<span class="codedirection">moc.elpmaxe@1-tset</span>', $direction);
|
||||
$this->assertEquals('test [dash] 1 [at] example [dot] com', $visible);
|
||||
$this->assertEquals(
|
||||
'<span class="codedirection">moc.elpmaxe@1-tset</span>',
|
||||
$direction,
|
||||
'obfuscate() correctly reverses the email direction'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'test [dash] 1 [at] example [dot] com',
|
||||
$visible,
|
||||
'obfuscate() correctly obfuscates email characters'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'test-1@examp'
|
||||
. 'le.com',
|
||||
$hex,
|
||||
'obfuscate() correctly returns hex representation of email'
|
||||
'test-1@example.com',
|
||||
$hex
|
||||
);
|
||||
}
|
||||
|
||||
public function testSendPlain()
|
||||
{
|
||||
// Set custom $project - used in email headers
|
||||
global $project;
|
||||
$oldProject = $project;
|
||||
$project = 'emailtest';
|
||||
|
||||
Injector::inst()->registerService(new EmailTest\TestMailer(), Mailer::class);
|
||||
$email = new Email(
|
||||
/** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
|
||||
$email = $this->getMockBuilder(Email::class)
|
||||
->enableProxyingToOriginalMethods()
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs(array(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
'Test send plain',
|
||||
'Testing Email->sendPlain()',
|
||||
null,
|
||||
'cc@example.com',
|
||||
'bcc@example.com'
|
||||
);
|
||||
$email->attachFile(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
$email->addCustomHeader('foo', 'bar');
|
||||
$sent = $email->sendPlain(123);
|
||||
'bcc@example.com',
|
||||
))
|
||||
->getMock();
|
||||
|
||||
// Restore old project name after sending
|
||||
$project = $oldProject;
|
||||
// email should not call render if a body is supplied
|
||||
$email->expects($this->never())->method('render');
|
||||
|
||||
$this->assertEquals('to@example.com', $sent['to']);
|
||||
$this->assertEquals('from@example.com', $sent['from']);
|
||||
$this->assertEquals('Test send plain', $sent['subject']);
|
||||
$this->assertEquals('Testing Email->sendPlain()', $sent['content']);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
0 => array(
|
||||
'contents'=>'Hello, I\'m a text document.',
|
||||
'filename'=>'attachment.txt',
|
||||
'mimetype'=>'text/plain'
|
||||
)
|
||||
),
|
||||
$sent['files']
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'foo' => 'bar',
|
||||
'X-SilverStripeMessageID' => 'emailtest.123',
|
||||
'X-SilverStripeSite' => 'emailtest',
|
||||
'Cc' => 'cc@example.com',
|
||||
'Bcc' => 'bcc@example.com'
|
||||
),
|
||||
$sent['customheaders']
|
||||
);
|
||||
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
$successful = $email->sendPlain();
|
||||
|
||||
$this->assertTrue($successful);
|
||||
$this->assertEmpty($email->getFailedRecipients());
|
||||
|
||||
$sentMail = $this->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('Testing Email->sendPlain()', $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 testSendHTML()
|
||||
public function testSend()
|
||||
{
|
||||
// Set custom $project - used in email headers
|
||||
global $project;
|
||||
$oldProject = $project;
|
||||
$project = 'emailtest';
|
||||
/** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
|
||||
$email = $this->getMockBuilder(Email::class)
|
||||
->enableProxyingToOriginalMethods()
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs(array(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
'Test send HTML',
|
||||
'Testing Email->send()',
|
||||
'cc@example.com',
|
||||
'bcc@example.com',
|
||||
))
|
||||
->getMock();
|
||||
|
||||
Injector::inst()->registerService(new EmailTest\TestMailer(), Mailer::class);
|
||||
// email should not call render if a body is supplied
|
||||
$email->expects($this->never())->method('render');
|
||||
|
||||
$email->addAttachment(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
$successful = $email->send();
|
||||
|
||||
$this->assertTrue($successful);
|
||||
$this->assertEmpty($email->getFailedRecipients());
|
||||
|
||||
$sentMail = $this->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('Testing Email->send()', $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()
|
||||
{
|
||||
/** @var Email|PHPUnit_Framework_MockObject_MockObject $email */
|
||||
$email = $this->getMockBuilder(Email::class)
|
||||
->enableProxyingToOriginalMethods()
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs(array(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
))
|
||||
->getMock();
|
||||
$email->setData(array(
|
||||
'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 testConsturctor()
|
||||
{
|
||||
$email = new Email(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
'Test send plain',
|
||||
'Testing Email->send()',
|
||||
null,
|
||||
'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();
|
||||
$swiftMessage = new Swift_Message();
|
||||
$email->setSwiftMessage($swiftMessage);
|
||||
$this->assertCount(1, $email->getFrom());
|
||||
$this->assertContains('admin@example.com', array_keys($swiftMessage->getFrom()));
|
||||
$this->assertEquals(strtotime('2017-01-01 07:00:00'), $swiftMessage->getDate());
|
||||
$this->assertEquals($swiftMessage, $email->getSwiftMessage());
|
||||
|
||||
// check from field is retained
|
||||
$swiftMessage = new Swift_Message();
|
||||
$swiftMessage->setFrom('from@example.com');
|
||||
$email->setSwiftMessage($swiftMessage);
|
||||
$this->assertCount(1, $email->getFrom());
|
||||
$this->assertContains('from@example.com', array_keys($email->getFrom()));
|
||||
}
|
||||
|
||||
public function testAdminEmailApplied()
|
||||
{
|
||||
Email::config()->update('admin_email', 'admin@example.com');
|
||||
$email = new Email();
|
||||
|
||||
$this->assertCount(1, $email->getFrom());
|
||||
$this->assertContains('admin@example.com', array_keys($email->getFrom()));
|
||||
}
|
||||
|
||||
public function testGetFrom()
|
||||
{
|
||||
$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();
|
||||
$this->assertEmpty($email->getSender());
|
||||
$email->setSender('sender@example.com', 'Silver Stripe');
|
||||
$this->assertEquals(array('sender@example.com' => 'Silver Stripe'), $email->getSender());
|
||||
}
|
||||
|
||||
public function testSetGetReturnPath()
|
||||
{
|
||||
$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(array('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(array('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'
|
||||
);
|
||||
$email->attachFile(__DIR__ . '/EmailTest/attachment.txt', null, 'text/plain');
|
||||
$email->addCustomHeader('foo', 'bar');
|
||||
$sent = $email->send(123);
|
||||
$this->assertCount(1, $email->getBCC());
|
||||
$this->assertContains('bcc@example.com', array_keys($email->getBCC()));
|
||||
$email->setBCC('new-bcc@example.com', 'Silver Stripe');
|
||||
$this->assertEquals(array('new-bcc@example.com' => 'Silver Stripe'), $email->getBCC());
|
||||
}
|
||||
|
||||
// Restore old project name after sending
|
||||
$project = $oldProject;
|
||||
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()));
|
||||
}
|
||||
|
||||
$this->assertEquals('to@example.com', $sent['to']);
|
||||
$this->assertEquals('from@example.com', $sent['from']);
|
||||
$this->assertEquals('Test send plain', $sent['subject']);
|
||||
$this->assertContains('Testing Email->send()', $sent['content']);
|
||||
$this->assertNull($sent['plaincontent']);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
0 => array(
|
||||
'contents'=>'Hello, I\'m a text document.',
|
||||
'filename'=>'attachment.txt',
|
||||
'mimetype'=>'text/plain'
|
||||
)
|
||||
),
|
||||
$sent['files']
|
||||
);
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'foo' => 'bar',
|
||||
'X-SilverStripeMessageID' => 'emailtest.123',
|
||||
'X-SilverStripeSite' => 'emailtest',
|
||||
'Cc' => 'cc@example.com',
|
||||
'Bcc' => 'bcc@example.com'
|
||||
),
|
||||
$sent['customheaders']
|
||||
);
|
||||
public function testReplyTo()
|
||||
{
|
||||
$email = new Email();
|
||||
$this->assertEmpty($email->getReplyTo());
|
||||
$email->setReplyTo('reply-to@example.com', 'Silver Stripe');
|
||||
$this->assertEquals(array('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(array(
|
||||
'Title' => 'My Title',
|
||||
));
|
||||
$this->assertCount(1, $email->getData());
|
||||
$this->assertEquals(array('Title' => 'My Title'), $email->getData());
|
||||
|
||||
$email->addData('Content', 'My content');
|
||||
$this->assertCount(2, $email->getData());
|
||||
$this->assertEquals(array(
|
||||
'Title' => 'My Title',
|
||||
'Content' => 'My content',
|
||||
), $email->getData());
|
||||
$email->removeData('Title');
|
||||
$this->assertEquals(array('Content' => 'My content'), $email->getData());
|
||||
}
|
||||
|
||||
public function testDataWithViewableData()
|
||||
{
|
||||
$member = new Member();
|
||||
$member->FirstName = 'First Name';
|
||||
$email = new Email();
|
||||
$this->assertEmpty($email->getData());
|
||||
$email->setData($member);
|
||||
$this->assertEquals($member, $email->getData());
|
||||
$email->addData('Test', 'Test value');
|
||||
$this->assertEquals('Test value', $email->getData()->Test);
|
||||
$email->removeData('Test');
|
||||
$this->assertNull($email->getData()->Test);
|
||||
}
|
||||
|
||||
public function testBody()
|
||||
{
|
||||
$email = new Email();
|
||||
$this->assertEmpty($email->getBody());
|
||||
$email->setBody('<h1>Title</h1>');
|
||||
$this->assertEquals('<h1>Title</h1>', $email->getBody());
|
||||
}
|
||||
|
||||
public function testHTMLTemplate()
|
||||
{
|
||||
$email = new Email();
|
||||
$this->assertEquals(Email::class, $email->getHTMLTemplate());
|
||||
$email->setHTMLTemplate('MyTemplate');
|
||||
$this->assertEquals('MyTemplate', $email->getHTMLTemplate());
|
||||
}
|
||||
|
||||
public function testPlainTemplate()
|
||||
{
|
||||
$email = new Email();
|
||||
$this->assertEmpty($email->getPlainTemplate());
|
||||
$email->setPlainTemplate('MyTemplate');
|
||||
$this->assertEquals('MyTemplate', $email->getPlainTemplate());
|
||||
}
|
||||
|
||||
public function testGetFailedRecipients()
|
||||
{
|
||||
$mailer = new SwiftMailer();
|
||||
/** @var Swift_NullTransport|PHPUnit_Framework_MockObject_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 testRender()
|
||||
{
|
||||
$email = new Email();
|
||||
$email->setData(array(
|
||||
'EmailContent' => 'my content',
|
||||
));
|
||||
$email->render();
|
||||
$this->assertContains('my content', $email->getBody());
|
||||
$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
|
||||
$email->render();
|
||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
||||
}
|
||||
|
||||
public function testRenderPlainOnly()
|
||||
{
|
||||
$email = new Email();
|
||||
$email->setData(array(
|
||||
'EmailContent' => 'test content',
|
||||
));
|
||||
$email->render(true);
|
||||
$this->assertEquals('text/plain', $email->getSwiftMessage()->getContentType());
|
||||
$this->assertEmpty($email->getSwiftMessage()->getChildren());
|
||||
}
|
||||
|
||||
public function testHasPlainPart()
|
||||
{
|
||||
$email = new Email();
|
||||
$email->setData(array(
|
||||
'EmailContent' => 'test',
|
||||
));
|
||||
//emails are assumed to be HTML by default
|
||||
$this->assertFalse($email->hasPlainPart());
|
||||
//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->assertContains('Test', $plainPart->getBody());
|
||||
$this->assertNotContains('<h1>Test</h1>', $plainPart->getBody());
|
||||
}
|
||||
|
||||
public function testMultipleEmailSends()
|
||||
{
|
||||
$email = new Email();
|
||||
$email->setData(array(
|
||||
'EmailContent' => 'Test',
|
||||
));
|
||||
$this->assertEmpty($email->getBody());
|
||||
$this->assertEmpty($email->getSwiftMessage()->getChildren());
|
||||
$email->send();
|
||||
$this->assertContains('Test', $email->getBody());
|
||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
||||
$children = $email->getSwiftMessage()->getChildren();
|
||||
/** @var \Swift_MimePart $plainPart */
|
||||
$plainPart = reset($children);
|
||||
$this->assertContains('Test', $plainPart->getBody());
|
||||
|
||||
|
||||
//send again
|
||||
$email->send();
|
||||
$this->assertContains('Test', $email->getBody());
|
||||
$this->assertCount(1, $email->getSwiftMessage()->getChildren());
|
||||
$children = $email->getSwiftMessage()->getChildren();
|
||||
/** @var \Swift_MimePart $plainPart */
|
||||
$plainPart = reset($children);
|
||||
$this->assertContains('Test', $plainPart->getBody());
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Email\EmailTest;
|
||||
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
|
||||
class TestMailer extends Mailer
|
||||
{
|
||||
|
||||
public function sendHTML(
|
||||
$to,
|
||||
$from,
|
||||
$subject,
|
||||
$htmlContent,
|
||||
$attachedFiles = false,
|
||||
$customheaders = false,
|
||||
$plainContent = false
|
||||
) {
|
||||
return array(
|
||||
'to' => $to,
|
||||
'from' => $from,
|
||||
'subject' => $subject,
|
||||
'content' => $htmlContent,
|
||||
'files' => $attachedFiles,
|
||||
'customheaders' => $customheaders,
|
||||
'plaincontent' => $plainContent
|
||||
);
|
||||
}
|
||||
|
||||
public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customheaders = false)
|
||||
{
|
||||
return array(
|
||||
'to' => $to,
|
||||
'from' => $from,
|
||||
'subject' => $subject,
|
||||
'content' => $plainContent,
|
||||
'files' => $attachedFiles,
|
||||
'customheaders' => $customheaders
|
||||
);
|
||||
}
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Email;
|
||||
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class MailerTest extends SapphireTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Replaces ----=_NextPart_214491627619 placeholders with ----=_NextPart_000000000000
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
protected function normaliseDivisions($input)
|
||||
{
|
||||
return preg_replace('/----=_NextPart_\d+/m', '----=_NextPart_000000000000', $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test plain text messages
|
||||
*/
|
||||
public function testSendPlain()
|
||||
{
|
||||
$mailer = new MailerTest\MockMailer();
|
||||
|
||||
// Test with default encoding
|
||||
$testMessage = "The majority of the answers so far are saying that private methods are implementation details ".
|
||||
"which don't (or at least shouldn't) matter so long as the public interface is well-tested and ".
|
||||
"working. That's absolutely correct if your only purpose for testing is to guarantee that the ".
|
||||
"public interface works.";
|
||||
list($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress) = $mailer->sendPlain(
|
||||
'<email@silverstripe.com>',
|
||||
'tom@jones <tom@silverstripe.com>',
|
||||
"What is the <purpose> of testing?",
|
||||
$testMessage,
|
||||
null,
|
||||
array('CC' => 'admin@silverstripe.com', 'bcc' => 'andrew@thing.com')
|
||||
);
|
||||
|
||||
$this->assertEquals('email@silverstripe.com', $to);
|
||||
$this->assertEquals('=?UTF-8?B?V2hhdCBpcyB0aGUgPHB1cnBvc2U+IG9mIHRlc3Rpbmc/?=', $subjectEncoded);
|
||||
$this->assertEquals('=?UTF-8?B?'. base64_encode('What is the <purpose> of testing?').'?=', $subjectEncoded);
|
||||
|
||||
$this->assertEquals(
|
||||
<<<PHP
|
||||
The majority of the answers so far are saying that private methods are impl=
|
||||
ementation details which don't (or at least shouldn't) matter so long as th=
|
||||
e public interface is well-tested and working. That's absolutely correct if=
|
||||
your only purpose for testing is to guarantee that the public interface wo=
|
||||
rks.
|
||||
PHP
|
||||
,
|
||||
Convert::nl2os($fullBody)
|
||||
);
|
||||
$this->assertEquals($testMessage, quoted_printable_decode($fullBody));
|
||||
$this->assertEquals(
|
||||
<<<PHP
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
From: tomjones <tom@silverstripe.com>
|
||||
X-Mailer: SilverStripe Mailer - version 2006.06.21
|
||||
X-Priority: 3
|
||||
Bcc: andrew@thing.com
|
||||
Cc: admin@silverstripe.com
|
||||
|
||||
PHP
|
||||
,
|
||||
Convert::nl2os($headersEncoded)
|
||||
);
|
||||
$this->assertEquals('tom@silverstripe.com', $bounceAddress);
|
||||
|
||||
// Test override bounce email and alternate encoding
|
||||
$mailer->setBounceEmail('bounce@silverstripe.com');
|
||||
$mailer->setMessageEncoding('base64');
|
||||
list($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress) = $mailer->sendPlain(
|
||||
'<email@silverstripe.com>',
|
||||
'tom@jones <tom@silverstripe.com>',
|
||||
"What is the <purpose> of testing?",
|
||||
$testMessage,
|
||||
null,
|
||||
array('CC' => 'admin@silverstripe.com', 'bcc' => 'andrew@thing.com')
|
||||
);
|
||||
|
||||
$this->assertEquals('bounce@silverstripe.com', $bounceAddress);
|
||||
$this->assertEquals(
|
||||
<<<PHP
|
||||
VGhlIG1ham9yaXR5IG9mIHRoZSBhbnN3ZXJzIHNvIGZhciBhcmUgc2F5aW5n
|
||||
IHRoYXQgcHJpdmF0ZSBtZXRob2RzIGFyZSBpbXBsZW1lbnRhdGlvbiBkZXRh
|
||||
aWxzIHdoaWNoIGRvbid0IChvciBhdCBsZWFzdCBzaG91bGRuJ3QpIG1hdHRl
|
||||
ciBzbyBsb25nIGFzIHRoZSBwdWJsaWMgaW50ZXJmYWNlIGlzIHdlbGwtdGVz
|
||||
dGVkIGFuZCB3b3JraW5nLiBUaGF0J3MgYWJzb2x1dGVseSBjb3JyZWN0IGlm
|
||||
IHlvdXIgb25seSBwdXJwb3NlIGZvciB0ZXN0aW5nIGlzIHRvIGd1YXJhbnRl
|
||||
ZSB0aGF0IHRoZSBwdWJsaWMgaW50ZXJmYWNlIHdvcmtzLg==
|
||||
|
||||
PHP
|
||||
,
|
||||
Convert::nl2os($fullBody)
|
||||
);
|
||||
$this->assertEquals($testMessage, base64_decode($fullBody));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test HTML messages
|
||||
*/
|
||||
public function testSendHTML()
|
||||
{
|
||||
$mailer = new MailerTest\MockMailer();
|
||||
|
||||
// Test with default encoding
|
||||
$testMessageHTML = "<p>The majority of the <i>answers</i> so far are saying that private methods are " .
|
||||
"implementation details which don't (<a href=\"http://www.google.com\">or at least shouldn't</a>) ".
|
||||
"matter so long as the public interface is well-tested & working</p> ".
|
||||
"<p>That's absolutely correct if your only purpose for testing is to guarantee that the ".
|
||||
"public interface works.</p>";
|
||||
$testMessagePlain = Convert::xml2raw($testMessageHTML);
|
||||
$this->assertTrue(stripos($testMessagePlain, '&#') === false);
|
||||
list($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress) = $mailer->sendHTML(
|
||||
'<email@silverstripe.com>',
|
||||
'tom@jones <tom@silverstripe.com>',
|
||||
"What is the <purpose> of testing?",
|
||||
$testMessageHTML,
|
||||
null,
|
||||
array('CC' => 'admin@silverstripe.com', 'bcc' => 'andrew@thing.com')
|
||||
);
|
||||
|
||||
$this->assertEquals('email@silverstripe.com', $to);
|
||||
$this->assertEquals('=?UTF-8?B?V2hhdCBpcyB0aGUgPHB1cnBvc2U+IG9mIHRlc3Rpbmc/?=', $subjectEncoded);
|
||||
$this->assertEquals('=?UTF-8?B?'. base64_encode('What is the <purpose> of testing?').'?=', $subjectEncoded);
|
||||
|
||||
$this->assertEquals(
|
||||
Convert::nl2os(
|
||||
<<<PHP
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
|
||||
------=_NextPart_000000000000
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
The majority of the answers so far are saying that private methods are impl=
|
||||
ementation details which don't (or at least shouldn't[http://www.google.com=
|
||||
]) matter so long as the public interface is well-tested & working=0A=0A=0A=
|
||||
=0AThat's absolutely correct if your only purpose for testing is to guarant=
|
||||
ee that the public interface works.
|
||||
|
||||
------=_NextPart_000000000000
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">=0A<HTML><HEA=
|
||||
D>=0A<META http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-=
|
||||
8">=0A<STYLE type=3D"text/css"></STYLE>=0A=0A</HEAD>=0A<BODY bgColor=3D"#ff=
|
||||
ffff">=0A<p>The majority of the <i>answers</i> so far are saying that priva=
|
||||
te methods are implementation details which don't (<a href=3D"http://ww=
|
||||
w.google.com">or at least shouldn't</a>) matter so long as the public i=
|
||||
nterface is well-tested & working</p> <p>That's absolutely correct =
|
||||
if your only purpose for testing is to guarantee that the public interface =
|
||||
works.</p>=0A</BODY>=0A</HTML>
|
||||
------=_NextPart_000000000000--
|
||||
PHP
|
||||
),
|
||||
Convert::nl2os($this->normaliseDivisions($fullBody))
|
||||
);
|
||||
// Check that the messages exist in the output
|
||||
$this->assertTrue(stripos($fullBody, quoted_printable_encode($testMessagePlain)) !== false);
|
||||
$this->assertEquals(
|
||||
<<<PHP
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/alternative; boundary="----=_NextPart_000000000000"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
From: tomjones <tom@silverstripe.com>
|
||||
X-Mailer: SilverStripe Mailer - version 2006.06.21
|
||||
X-Priority: 3
|
||||
Bcc: andrew@thing.com
|
||||
Cc: admin@silverstripe.com
|
||||
|
||||
PHP
|
||||
,
|
||||
Convert::nl2os($this->normaliseDivisions($headersEncoded))
|
||||
);
|
||||
$this->assertEquals('tom@silverstripe.com', $bounceAddress);
|
||||
|
||||
// Test override bounce email and alternate encoding
|
||||
$mailer->setBounceEmail('bounce@silverstripe.com');
|
||||
$mailer->setMessageEncoding('base64');
|
||||
list($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress) = $mailer->sendHTML(
|
||||
'<email@silverstripe.com>',
|
||||
'tom@jones <tom@silverstripe.com>',
|
||||
"What is the <purpose> of testing?",
|
||||
$testMessageHTML,
|
||||
null,
|
||||
array('CC' => 'admin@silverstripe.com', 'bcc' => 'andrew@thing.com')
|
||||
);
|
||||
|
||||
$this->assertEquals('bounce@silverstripe.com', $bounceAddress);
|
||||
$this->assertEquals(
|
||||
<<<PHP
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
|
||||
------=_NextPart_000000000000
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
VGhlIG1ham9yaXR5IG9mIHRoZSBhbnN3ZXJzIHNvIGZhciBhcmUgc2F5aW5n
|
||||
IHRoYXQgcHJpdmF0ZSBtZXRob2RzIGFyZSBpbXBsZW1lbnRhdGlvbiBkZXRh
|
||||
aWxzIHdoaWNoIGRvbid0IChvciBhdCBsZWFzdCBzaG91bGRuJ3RbaHR0cDov
|
||||
L3d3dy5nb29nbGUuY29tXSkgbWF0dGVyIHNvIGxvbmcgYXMgdGhlIHB1Ymxp
|
||||
YyBpbnRlcmZhY2UgaXMgd2VsbC10ZXN0ZWQgJiB3b3JraW5nCgoKClRoYXQn
|
||||
cyBhYnNvbHV0ZWx5IGNvcnJlY3QgaWYgeW91ciBvbmx5IHB1cnBvc2UgZm9y
|
||||
IHRlc3RpbmcgaXMgdG8gZ3VhcmFudGVlIHRoYXQgdGhlIHB1YmxpYyBpbnRl
|
||||
cmZhY2Ugd29ya3Mu
|
||||
|
||||
|
||||
------=_NextPart_000000000000
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMCBU
|
||||
cmFuc2l0aW9uYWwvL0VOIj4KPEhUTUw+PEhFQUQ+CjxNRVRBIGh0dHAtZXF1
|
||||
aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0
|
||||
PXV0Zi04Ij4KPFNUWUxFIHR5cGU9InRleHQvY3NzIj48L1NUWUxFPgoKPC9I
|
||||
RUFEPgo8Qk9EWSBiZ0NvbG9yPSIjZmZmZmZmIj4KPHA+VGhlIG1ham9yaXR5
|
||||
IG9mIHRoZSA8aT5hbnN3ZXJzPC9pPiBzbyBmYXIgYXJlIHNheWluZyB0aGF0
|
||||
IHByaXZhdGUgbWV0aG9kcyBhcmUgaW1wbGVtZW50YXRpb24gZGV0YWlscyB3
|
||||
aGljaCBkb24mIzM5O3QgKDxhIGhyZWY9Imh0dHA6Ly93d3cuZ29vZ2xlLmNv
|
||||
bSI+b3IgYXQgbGVhc3Qgc2hvdWxkbiYjMzk7dDwvYT4pIG1hdHRlciBzbyBs
|
||||
b25nIGFzIHRoZSBwdWJsaWMgaW50ZXJmYWNlIGlzIHdlbGwtdGVzdGVkICZh
|
||||
bXA7IHdvcmtpbmc8L3A+IDxwPlRoYXQmIzM5O3MgYWJzb2x1dGVseSBjb3Jy
|
||||
ZWN0IGlmIHlvdXIgb25seSBwdXJwb3NlIGZvciB0ZXN0aW5nIGlzIHRvIGd1
|
||||
YXJhbnRlZSB0aGF0IHRoZSBwdWJsaWMgaW50ZXJmYWNlIHdvcmtzLjwvcD4K
|
||||
PC9CT0RZPgo8L0hUTUw+
|
||||
|
||||
------=_NextPart_000000000000--
|
||||
PHP
|
||||
,
|
||||
Convert::nl2os($this->normaliseDivisions($fullBody))
|
||||
);
|
||||
|
||||
// Check that the text message version is somewhere in there
|
||||
$this->assertTrue(stripos($fullBody, chunk_split(base64_encode($testMessagePlain), 60)) !== false);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Email\MailerTest;
|
||||
|
||||
use SilverStripe\Control\Email\Mailer;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
/**
|
||||
* Mocks the sending of emails without actually sending anything
|
||||
*/
|
||||
class MockMailer extends Mailer implements TestOnly
|
||||
{
|
||||
protected function email($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress)
|
||||
{
|
||||
return array($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress);
|
||||
}
|
||||
}
|
76
tests/php/Control/Email/SwiftMailerTest.php
Normal file
76
tests/php/Control/Email/SwiftMailerTest.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?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', array(Swift_Plugins_AntiFloodPlugin::class));
|
||||
|
||||
/** @var Swift_MailTransport $transport */
|
||||
$transport = $this->getMockBuilder(Swift_MailTransport::class)->getMock();
|
||||
$transport
|
||||
->expects($this->once())
|
||||
->method('registerPlugin')
|
||||
->willReturnCallback(function ($plugin) {
|
||||
$this->assertInstanceOf(Swift_Plugins_AntiFloodPlugin::class, $plugin);
|
||||
});
|
||||
|
||||
/** @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->getMock(SwiftMailer::class, array('sendSwift'));
|
||||
$mailer->expects($this->once())->method('sendSwift')->willReturnCallback(function ($message) {
|
||||
$this->assertInstanceOf(Swift_Message::class, $message);
|
||||
});
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
110
tests/php/Control/Email/SwiftPluginTest.php
Normal file
110
tests/php/Control/Email/SwiftPluginTest.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Email;
|
||||
|
||||
use SilverStripe\Control\Email\Email;
|
||||
use SilverStripe\Control\Email\SwiftPlugin;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class SwiftPluginTest extends SapphireTest
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
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()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user