mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
API Mailer can be configured to use different encoding mechanisms, and added support for unicode quoted-string encoding
API Mailer bounce email can now be configured API Mailer no longer calls Convert::xml2raw on all email subjects API Deprecate dead Mailer code and refactored duplicate or mis-documented code.
This commit is contained in:
parent
29e3347562
commit
e47800917a
@ -14,6 +14,7 @@
|
|||||||
tables actually exist.
|
tables actually exist.
|
||||||
* `SS_Filterable`, `SS_Limitable` and `SS_Sortable` now explicitly extend `SS_List`
|
* `SS_Filterable`, `SS_Limitable` and `SS_Sortable` now explicitly extend `SS_List`
|
||||||
* `Convert::html2raw` no longer wraps text by default and can decode single quotes.
|
* `Convert::html2raw` no longer wraps text by default and can decode single quotes.
|
||||||
|
* `Mailer` no longer calls `xml2raw` on all email subject line, and now must be passed in via plain text.
|
||||||
|
|
||||||
#### Deprecated classes/methods removed
|
#### Deprecated classes/methods removed
|
||||||
|
|
||||||
|
@ -116,6 +116,14 @@ See [Wikipedia E-mail Message header](http://en.wikipedia.org/wiki/E-mail#Messag
|
|||||||
|
|
||||||
The [newsletter module](http://silverstripe.org/newsletter-module) provides a UI and logic to send batch emails.
|
The [newsletter module](http://silverstripe.org/newsletter-module) provides a UI and logic to send batch emails.
|
||||||
|
|
||||||
|
### 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');`
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
|
||||||
`[api:Email]`
|
`[api:Email]`
|
||||||
|
@ -62,17 +62,11 @@ class Email extends ViewableData {
|
|||||||
protected $bcc;
|
protected $bcc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Mailer $mailer Instance of a {@link Mailer} class.
|
* @deprecated since version 3.3
|
||||||
*/
|
|
||||||
protected static $mailer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This can be used to provide a mailer class other than the default, e.g. for testing.
|
|
||||||
*
|
|
||||||
* @param Mailer $mailer
|
|
||||||
*/
|
*/
|
||||||
public static function set_mailer(Mailer $mailer) {
|
public static function set_mailer(Mailer $mailer) {
|
||||||
self::$mailer = $mailer;
|
Deprecation::notice('3.3.0', 'Use Injector to override the Mailer service');
|
||||||
|
Injector::inst()->registerService($mailer, 'Mailer');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,8 +75,7 @@ class Email extends ViewableData {
|
|||||||
* @return Mailer
|
* @return Mailer
|
||||||
*/
|
*/
|
||||||
public static function mailer() {
|
public static function mailer() {
|
||||||
if(!self::$mailer) self::$mailer = new Mailer();
|
return Injector::inst()->get('Mailer');
|
||||||
return self::$mailer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
550
email/Mailer.php
550
email/Mailer.php
@ -10,45 +10,247 @@
|
|||||||
class Mailer extends Object {
|
class Mailer extends Object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a plain-text email.
|
* Default encoding type for messages. Available options are:
|
||||||
|
* - quoted-printable
|
||||||
|
* - base64
|
||||||
*
|
*
|
||||||
* @param string $to
|
* @var string
|
||||||
* @param string $from
|
* @config
|
||||||
* @param string §subject
|
|
||||||
* @param string $plainContent
|
|
||||||
* @param bool $attachedFiles
|
|
||||||
* @param array $customheaders
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customheaders = false) {
|
private static $default_message_encoding = 'quoted-printable';
|
||||||
// Not ensurely where this is supposed to be set, but defined it false for now to remove php notices
|
|
||||||
$plainEncoding = false;
|
|
||||||
|
|
||||||
if ($customheaders && is_array($customheaders) == false) {
|
/**
|
||||||
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
|
* Encoding type currently set
|
||||||
dieprintr($customheaders);
|
*
|
||||||
|
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the subject line contains extended characters, we must encode it
|
/**
|
||||||
$subject = Convert::xml2raw($subject);
|
* Set the email used for bounces
|
||||||
$subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
|
*
|
||||||
|
* @param string $email
|
||||||
|
*/
|
||||||
|
public function setBounceEmail($email) {
|
||||||
|
$this->bounceEmail = $email;
|
||||||
|
}
|
||||||
|
|
||||||
// Make the plain text part
|
/**
|
||||||
|
* 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-Type"] = "text/plain; charset=utf-8";
|
||||||
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
|
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();
|
||||||
|
|
||||||
$plainContent = ($plainEncoding == "base64")
|
// Send prepared message
|
||||||
? chunk_split(base64_encode($plainContent),60)
|
return $this->sendPreparedMessage($to, $from, $subject, $attachedFiles, $customHeaders, $fullBody, $headers);
|
||||||
: $this->QuotedPrintable_encode($plainContent);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
// Messages with attachments are handled differently
|
||||||
if($attachedFiles) {
|
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
|
// The first part is the message itself
|
||||||
$fullMessage = $this->processHeaders($headers, $plainContent);
|
$fullMessage = $this->processHeaders($headers, $body);
|
||||||
$messageParts = array($fullMessage);
|
$messageParts = array($fullMessage);
|
||||||
|
|
||||||
// Include any specified attachments as additional parts
|
// Include any specified attachments as additional parts
|
||||||
foreach($attachedFiles as $file) {
|
foreach($attachments as $file) {
|
||||||
if(isset($file['tmp_name']) && isset($file['name'])) {
|
if(isset($file['tmp_name']) && isset($file['name'])) {
|
||||||
$messageParts[] = $this->encodeFileForEmail($file['tmp_name'], $file['name']);
|
$messageParts[] = $this->encodeFileForEmail($file['tmp_name'], $file['name']);
|
||||||
} else {
|
} else {
|
||||||
@ -57,94 +259,38 @@ class Mailer extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We further wrap all of this into another multipart block
|
// We further wrap all of this into another multipart block
|
||||||
list($fullBody, $headers) = $this->encodeMultipart($messageParts, "multipart/mixed");
|
return $this->encodeMultipart($messageParts, "multipart/mixed");
|
||||||
|
|
||||||
// Messages without attachments do not require such treatment
|
|
||||||
} else {
|
|
||||||
$fullBody = $plainContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email headers
|
|
||||||
$headers["From"] = $this->validEmailAddr($from);
|
|
||||||
|
|
||||||
// Messages with the X-SilverStripeMessageID header can be tracked
|
|
||||||
if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
|
|
||||||
$bounceAddress = BOUNCE_EMAIL;
|
|
||||||
// Get the human name from the from address, if there is one
|
|
||||||
if(preg_match('/^([^<>]+)<([^<>])> *$/', $from, $parts))
|
|
||||||
$bounceAddress = "$parts[1]<$bounceAddress>";
|
|
||||||
} else {
|
|
||||||
$bounceAddress = $from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// $headers["Sender"] = $from;
|
|
||||||
$headers["X-Mailer"] = X_MAILER;
|
|
||||||
if(!isset($customheaders["X-Priority"])) {
|
|
||||||
$headers["X-Priority"] = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
$headers = array_merge((array)$headers, (array)$customheaders);
|
|
||||||
|
|
||||||
// the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
|
|
||||||
if (isset($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset($headers['CC']); }
|
|
||||||
if (isset($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset($headers['cc']); }
|
|
||||||
|
|
||||||
// Send the email
|
|
||||||
$headers = $this->processHeaders($headers);
|
|
||||||
$to = $this->validEmailAddr($to);
|
|
||||||
|
|
||||||
// Try it without the -f option if it fails
|
|
||||||
if(!$result = @mail($to, $subject, $fullBody, $headers, "-f$bounceAddress"))
|
|
||||||
$result = mail($to, $subject, $fullBody, $headers);
|
|
||||||
|
|
||||||
if($result)
|
|
||||||
return array($to,$subject,$fullBody,$headers);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an email as a both HTML and plaintext
|
* Generate the plainPart of a html message
|
||||||
*
|
*
|
||||||
* $attachedFiles should be an array of file names
|
* @param string $plainContent Plain body
|
||||||
* - if you pass the entire $_FILES entry, the user-uploaded filename will be preserved
|
* @param string $htmlContent HTML message
|
||||||
* use $plainContent to override default plain-content generation
|
* @return string Encoded headers / message in a single block
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false,
|
protected function preparePlainSubmessage($plainContent, $htmlContent) {
|
||||||
$plainContent = false) {
|
$plainEncoding = $this->getMessageEncoding();
|
||||||
|
|
||||||
if ($customheaders && is_array($customheaders) == false) {
|
// Generate plain text version if not explicitly given
|
||||||
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
|
if(!$plainContent) $plainContent = Convert::xml2raw($htmlContent);
|
||||||
dieprintr($customheaders);
|
|
||||||
}
|
|
||||||
|
|
||||||
$bodyIsUnicode = (strpos($htmlContent,"&#") !== false);
|
|
||||||
$plainEncoding = "";
|
|
||||||
|
|
||||||
// We generate plaintext content by default, but you can pass custom stuff
|
|
||||||
$plainEncoding = '';
|
|
||||||
if(!$plainContent) {
|
|
||||||
$plainContent = Convert::xml2raw($htmlContent);
|
|
||||||
if(isset($bodyIsUnicode) && $bodyIsUnicode) $plainEncoding = "base64";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the subject line contains extended characters, we must encode the
|
|
||||||
$subject = Convert::xml2raw($subject);
|
|
||||||
$subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
|
|
||||||
|
|
||||||
// Make the plain text part
|
// Make the plain text part
|
||||||
$headers["Content-Type"] = "text/plain; charset=utf-8";
|
$headers["Content-Type"] = "text/plain; charset=utf-8";
|
||||||
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
|
$headers["Content-Transfer-Encoding"] = $plainEncoding;
|
||||||
|
$plainContentEncoded = $this->encodeMessage($plainContent, $plainEncoding);
|
||||||
|
|
||||||
$plainPart = $this->processHeaders($headers, ($plainEncoding == "base64")
|
// Merge with headers
|
||||||
? chunk_split(base64_encode($plainContent),60)
|
return $this->processHeaders($headers, $plainContentEncoded);
|
||||||
: wordwrap($this->QuotedPrintable_encode($plainContent),75));
|
}
|
||||||
|
|
||||||
// Make the HTML part
|
|
||||||
$headers["Content-Type"] = "text/html; charset=utf-8";
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
// Add basic wrapper tags if the body tag hasn't been given
|
||||||
if(stripos($htmlContent, '<body') === false) {
|
if(stripos($htmlContent, '<body') === false) {
|
||||||
$htmlContent =
|
$htmlContent =
|
||||||
@ -159,83 +305,24 @@ class Mailer extends Object {
|
|||||||
"</HTML>";
|
"</HTML>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$headers["Content-Transfer-Encoding"] = "quoted-printable";
|
// Make the HTML part
|
||||||
$htmlPart = $this->processHeaders($headers, wordwrap($this->QuotedPrintable_encode($htmlContent),75));
|
$headers["Content-Type"] = "text/html; charset=utf-8";
|
||||||
|
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();
|
||||||
|
$htmlContentEncoded = $this->encodeMessage($htmlContent, $this->getMessageEncoding());
|
||||||
|
|
||||||
list($messageBody, $messageHeaders) = $this->encodeMultipart(
|
// Merge with headers
|
||||||
array($plainPart,$htmlPart),
|
return $this->processHeaders($headers, $htmlContentEncoded);
|
||||||
"multipart/alternative"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Messages with attachments are handled differently
|
|
||||||
if($attachedFiles && is_array($attachedFiles)) {
|
|
||||||
|
|
||||||
// The first part is the message itself
|
|
||||||
$fullMessage = $this->processHeaders($messageHeaders, $messageBody);
|
|
||||||
$messageParts = array($fullMessage);
|
|
||||||
|
|
||||||
// Include any specified attachments as additional parts
|
|
||||||
foreach($attachedFiles 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
|
|
||||||
list($fullBody, $headers) = $this->encodeMultipart($messageParts, "multipart/mixed");
|
|
||||||
|
|
||||||
// Messages without attachments do not require such treatment
|
|
||||||
} else {
|
|
||||||
$headers = $messageHeaders;
|
|
||||||
$fullBody = $messageBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email headers
|
|
||||||
$headers["From"] = $this->validEmailAddr($from);
|
|
||||||
|
|
||||||
// Messages with the X-SilverStripeMessageID header can be tracked
|
|
||||||
if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
|
|
||||||
$bounceAddress = BOUNCE_EMAIL;
|
|
||||||
} else {
|
|
||||||
$bounceAddress = $from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip the human name from the bounce address
|
|
||||||
if(preg_match('/^([^<>]*)<([^<>]+)> *$/', $bounceAddress, $parts)) $bounceAddress = $parts[2];
|
|
||||||
|
|
||||||
// $headers["Sender"] = $from;
|
|
||||||
$headers["X-Mailer"] = X_MAILER;
|
|
||||||
if (!isset($customheaders["X-Priority"])) $headers["X-Priority"] = 3;
|
|
||||||
|
|
||||||
$headers = array_merge((array)$headers, (array)$customheaders);
|
|
||||||
|
|
||||||
// the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
|
|
||||||
if (isset($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset($headers['CC']); }
|
|
||||||
if (isset($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset($headers['cc']); }
|
|
||||||
|
|
||||||
// the carbon copy header has to be 'Bcc', not 'BCC' or 'bcc' -- ensure this.
|
|
||||||
if (isset($headers['BCC'])) {$headers['Bcc']=$headers['BCC']; unset($headers['BCC']); }
|
|
||||||
if (isset($headers['bcc'])) {$headers['Bcc']=$headers['bcc']; unset($headers['bcc']); }
|
|
||||||
|
|
||||||
// Send the email
|
|
||||||
$headers = $this->processHeaders($headers);
|
|
||||||
$to = $this->validEmailAddr($to);
|
|
||||||
|
|
||||||
// Try it without the -f option if it fails
|
|
||||||
if(!$bounceAddress ||
|
|
||||||
!($result = @mail($to, $subject, $fullBody, $headers, escapeshellarg("-f$bounceAddress")))) {
|
|
||||||
$result = mail($to, $subject, $fullBody, $headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Make visibility protected in 3.2
|
* 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
|
||||||
*/
|
*/
|
||||||
function encodeMultipart($parts, $contentType, $headers = false) {
|
protected function encodeMultipart($parts, $contentType, $headers = array()) {
|
||||||
$separator = "----=_NextPart_" . preg_replace('/[^0-9]/', '', rand() * 10000000000);
|
$separator = "----=_NextPart_" . preg_replace('/[^0-9]/', '', rand() * 10000000000);
|
||||||
|
|
||||||
$headers["MIME-Version"] = "1.0";
|
$headers["MIME-Version"] = "1.0";
|
||||||
@ -259,19 +346,22 @@ class Mailer extends Object {
|
|||||||
return array($body, $headers);
|
return array($body, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo Make visibility protected in 3.2
|
|
||||||
*/
|
|
||||||
function processHeaders($headers, $body = false) {
|
|
||||||
$res = '';
|
|
||||||
if(is_array($headers)) {
|
|
||||||
while(list($k, $v) = each($headers)) {
|
|
||||||
$res .= "$k: $v\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if($body) $res .= "\n$body";
|
|
||||||
|
|
||||||
return $res;
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -313,10 +403,8 @@ class Mailer extends Object {
|
|||||||
* 'mimetype' => 'image/gif',
|
* 'mimetype' => 'image/gif',
|
||||||
* 'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif"
|
* 'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif"
|
||||||
* );
|
* );
|
||||||
*
|
|
||||||
* @todo Make visibility protected in 3.2
|
|
||||||
*/
|
*/
|
||||||
function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "") {
|
protected function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "") {
|
||||||
if(!$file) {
|
if(!$file) {
|
||||||
user_error("encodeFileForEmail: not passed a filename and/or data", E_USER_WARNING);
|
user_error("encodeFileForEmail: not passed a filename and/or data", E_USER_WARNING);
|
||||||
return;
|
return;
|
||||||
@ -348,7 +436,7 @@ class Mailer extends Object {
|
|||||||
// This mime type is needed, otherwise some clients will show it as an inline attachment
|
// This mime type is needed, otherwise some clients will show it as an inline attachment
|
||||||
$mimeType = 'application/octet-stream';
|
$mimeType = 'application/octet-stream';
|
||||||
$encoding = "quoted-printable";
|
$encoding = "quoted-printable";
|
||||||
$file['contents'] = $this->QuotedPrintable_encode($file['contents']);
|
$file['contents'] = quoted_printable_encode($file['contents']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
|
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
|
||||||
@ -364,76 +452,42 @@ class Mailer extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Make visibility protected in 3.2
|
* Cleans up emails which may be in 'Name <email@silverstripe.com>' format
|
||||||
|
*
|
||||||
|
* @param string $emailAddress
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
function QuotedPrintable_encode($quotprint) {
|
protected function validEmailAddress($emailAddress) {
|
||||||
$quotprint = (string)str_replace('\r\n',chr(13).chr(10),$quotprint);
|
$emailAddress = trim($emailAddress);
|
||||||
$quotprint = (string)str_replace('\n', chr(13).chr(10),$quotprint);
|
$openBracket = strpos($emailAddress, '<');
|
||||||
$quotprint = (string)preg_replace_callback("~([\x01-\x1F\x3D\x7F-\xFF])~", function($matches) {
|
$closeBracket = strpos($emailAddress, '>');
|
||||||
return sprintf('=%02X', ord($matches[1]));
|
|
||||||
}, $quotprint);
|
// Unwrap email contained by braces
|
||||||
//$quotprint = (string)str_replace('\=0D=0A',"=0D=0A",$quotprint);
|
if($openBracket === 0 && $closeBracket !== false) {
|
||||||
$quotprint = (string)str_replace('=0D=0A',"\n",$quotprint);
|
return substr($emailAddress, 1, $closeBracket - 1);
|
||||||
$quotprint = (string)str_replace('=0A=0D',"\n",$quotprint);
|
|
||||||
$quotprint = (string)str_replace('=0D',"\n",$quotprint);
|
|
||||||
$quotprint = (string)str_replace('=0A',"\n",$quotprint);
|
|
||||||
return (string) $quotprint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Ensure name component cannot be mistaken for an email address
|
||||||
* @todo Make visibility protected in 3.2
|
if($openBracket) {
|
||||||
*/
|
$emailAddress = str_replace('@', '', substr($emailAddress, 0, $openBracket))
|
||||||
function validEmailAddr($emailAddress) {
|
. substr($emailAddress, $openBracket);
|
||||||
$emailAddress = trim($emailAddress);
|
|
||||||
$angBrack = strpos($emailAddress, '<');
|
|
||||||
|
|
||||||
if($angBrack === 0) {
|
|
||||||
$emailAddress = substr($emailAddress, 1, strpos($emailAddress,'>')-1);
|
|
||||||
|
|
||||||
} else if($angBrack) {
|
|
||||||
$emailAddress = str_replace('@', '', substr($emailAddress, 0, $angBrack))
|
|
||||||
. substr($emailAddress, $angBrack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $emailAddress;
|
return $emailAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Return a multipart/related e-mail chunk for the given HTML message and its linked images
|
* @deprecated since version 3.2.0
|
||||||
* Decodes absolute URLs, accessing the appropriate local images
|
|
||||||
*/
|
*/
|
||||||
function wrapImagesInline($htmlContent) {
|
public function wrapImagesInline($htmlContent) {
|
||||||
global $_INLINED_IMAGES;
|
Deprecation::notice('3.2.0', 'wrapImagesInline is deprecated');
|
||||||
$_INLINED_IMAGES = null;
|
|
||||||
|
|
||||||
$replacedContent = imageRewriter($htmlContent, 'wrapImagesInline_rewriter($URL)');
|
|
||||||
|
|
||||||
// Make the HTML part
|
|
||||||
$headers["Content-Type"] = "text/html; charset=utf-8";
|
|
||||||
$headers["Content-Transfer-Encoding"] = "quoted-printable";
|
|
||||||
$multiparts[] = processHeaders($headers, QuotedPrintable_encode($replacedContent));
|
|
||||||
|
|
||||||
// Make all the image parts
|
|
||||||
global $_INLINED_IMAGES;
|
|
||||||
foreach($_INLINED_IMAGES as $url => $cid) {
|
|
||||||
$multiparts[] = encodeFileForEmail($url, false, "inline", "Content-ID: <$cid>\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge together in a multipart
|
/**
|
||||||
list($body, $headers) = encodeMultipart($multiparts, "multipart/related");
|
* @deprecated since version 3.2.0
|
||||||
return processHeaders($headers, $body);
|
*/
|
||||||
}
|
public function wrapImagesInline_rewriter($url) {
|
||||||
|
Deprecation::notice('3.2.0', 'wrapImagesInline_rewriter is deprecated');
|
||||||
function wrapImagesInline_rewriter($url) {
|
|
||||||
$url = relativiseURL($url);
|
|
||||||
|
|
||||||
global $_INLINED_IMAGES;
|
|
||||||
if(!$_INLINED_IMAGES[$url]) {
|
|
||||||
$identifier = "automatedmessage." . rand(1000,1000000000) . "@silverstripe.com";
|
|
||||||
$_INLINED_IMAGES[$url] = $identifier;
|
|
||||||
}
|
|
||||||
return "cid:" . $_INLINED_IMAGES[$url];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
243
tests/email/MailerTest.php
Normal file
243
tests/email/MailerTest.php
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks the sending of emails without actually sending anything
|
||||||
|
*/
|
||||||
|
class MailerTest_MockMailer extends Mailer implements TestOnly {
|
||||||
|
protected function email($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress) {
|
||||||
|
return array($to, $subjectEncoded, $fullBody, $headersEncoded, $bounceAddress);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user