Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Update sending attachments.

Both inline and external attachments, as well as mixed sets of
inline and external attachments should work now.  Re-built the internals
of message rendering to remove duplication and redundant code paths.

Fixes #2413
Fixes #2320
  • Loading branch information...
commit 0e4af546d66b4ccd90632be117aee74ef9986e02 1 parent 53598c7
@markstory markstory authored
View
246 lib/Cake/Network/Email/CakeEmail.php
@@ -963,38 +963,9 @@ public function send($content = null) {
}
$this->_textMessage = $this->_htmlMessage = '';
- if ($content !== null) {
- if ($this->_emailFormat === 'text') {
- $this->_textMessage = $content;
- } elseif ($this->_emailFormat === 'html') {
- $this->_htmlMessage = $content;
- } elseif ($this->_emailFormat === 'both') {
- $this->_textMessage = $this->_htmlMessage = $content;
- }
- }
-
$this->_createBoundary();
+ $this->_message = $this->_render($this->_wrap($content));
- $message = $this->_wrap($content);
- // two methods doing similar things seems silly.
- // both handle attachments.
- if (empty($this->_template)) {
- $message = $this->_formatMessage($message);
- } else {
- $message = $this->_render($message);
- }
- $message[] = '';
- $this->_message = $message;
-
- // should be part of a compose method.
- if (!empty($this->_attachments)) {
- $this->_attachFiles();
-
- $this->_message[] = '';
- $this->_message[] = '--' . $this->_boundary . '--';
- $this->_message[] = '';
- }
-
$contents = $this->transportClass()->send($this);
if (!empty($this->_config['log'])) {
$level = LOG_DEBUG;
@@ -1269,142 +1240,179 @@ protected function _createBoundary() {
}
/**
- * Attach files by adding file contents inside boundaries.
+ * Attach non-embedded files by adding file contents inside boundaries.
*
- * @return void
+ * @return array An array of lines to add to the message
*/
protected function _attachFiles() {
+ $msg = array();
foreach ($this->_attachments as $filename => $fileInfo) {
- $handle = fopen($fileInfo['file'], 'rb');
- $data = fread($handle, filesize($fileInfo['file']));
- $data = chunk_split(base64_encode($data)) ;
- fclose($handle);
-
- $this->_message[] = '--' . $this->_boundary;
- $this->_message[] = 'Content-Type: ' . $fileInfo['mimetype'];
- $this->_message[] = 'Content-Transfer-Encoding: base64';
- if (empty($fileInfo['contentId'])) {
- $this->_message[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
- } else {
- $this->_message[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
- $this->_message[] = 'Content-Disposition: inline; filename="' . $filename . '"';
+ if (!empty($fileInfo['contentId'])) {
+ continue;
}
- $this->_message[] = '';
- $this->_message[] = $data;
- $this->_message[] = '';
+ $data = $this->_readFile($fileInfo['file']);
+
+ $msg[] = '--' . $this->_boundary;
+ $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
+ $msg[] = 'Content-Transfer-Encoding: base64';
+ $msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
+ $msg[] = '';
+ $msg[] = $data;
+ $msg[] = '';
}
+ return $msg;
}
/**
- * Format the message by seeing if it has attachments.
+ * Read the file contents and return a base64 version of the file contents.
*
- * @param array $message Message to format
- * @return array
+ * @param string $file The file to read.
+ * @return string File contents in base64 encoding
*/
- protected function _formatMessage($message) {
- if (!empty($this->_attachments)) {
- $prefix = array('--' . $this->_boundary);
- if ($this->_emailFormat === 'text') {
- $prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
- } elseif ($this->_emailFormat === 'html') {
- $prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
- } elseif ($this->_emailFormat === 'both') {
- $prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"';
+ protected function _readFile($file) {
+ $handle = fopen($file, 'rb');
+ $data = fread($handle, filesize($file));
+ $data = chunk_split(base64_encode($data)) ;
+ fclose($handle);
+ return $data;
+ }
+
+/**
+ * Attach inline/embedded files to the message.
+ *
+ * @return array An array of lines to add to the message
+ */
+ protected function _attachInlineFiles() {
+ $msg = array();
+ foreach ($this->_attachments as $filename => $fileInfo) {
+ if (empty($fileInfo['contentId'])) {
+ continue;
}
- $prefix[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
- $prefix[] = '';
- $message = array_merge($prefix, $message);
- }
+ $data = $this->_readFile($fileInfo['file']);
- $tmp = array();
- foreach ($message as $msg) {
- $tmp[] = $this->_encodeString($msg, $this->charset);
+ $msg[] = '--' . $this->_boundary;
+ $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
+ $msg[] = 'Content-Transfer-Encoding: base64';
+ $msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
+ $msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
+ $msg[] = '';
+ $msg[] = $data;
+ $msg[] = '';
}
- $message = $tmp;
-
- return $message;
+ return $msg;
}
/**
- * Render the contents using the current layout and template.
+ * Render the body of the email.
*
* @param string $content Content to render
- * @return array Email ready to be sent
+ * @return array Email body ready to be sent
*/
protected function _render($content) {
$content = implode("\n", $content);
$rendered = $this->_renderTemplates($content);
$msg = array();
- if ($this->_emailFormat === 'both') {
- if (!empty($this->_attachments)) {
- $msg[] = '--' . $this->_boundary;
- $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"';
- $msg[] = '';
- }
- $msg[] = '--alt-' . $this->_boundary;
- $msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
- $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
- $msg[] = '';
- $this->_textMessage = $rendered['text'];
- $content = explode("\n", $this->_textMessage);
- $msg = array_merge($msg, $content);
+ $contentIds = array_filter((array)Set::classicExtract($this->_attachments, '{s}.contentId'));
+ $hasInlineAttachments = count($contentIds) > 0;
+ $hasAttachments = !empty($this->_attachments);
+ $hasMultipleTypes = count($rendered) > 1;
- $msg[] = '';
- $msg[] = '--alt-' . $this->_boundary;
- $msg[] = 'Content-Type: text/html; charset=' . $this->charset;
- $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
- $msg[] = '';
-
- $this->_htmlMessage = $rendered['html'];
- $content = explode("\n", $this->_htmlMessage);
- $msg = array_merge($msg, $content);
+ $boundary = $relBoundary = $textBoundary = $this->_boundary;
+ if ($hasInlineAttachments) {
+ $msg[] = '--' . $boundary;
+ $msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
$msg[] = '';
- $msg[] = '--alt-' . $this->_boundary . '--';
- $msg[] = '';
+ $relBoundary = 'rel-' . $boundary;
+ }
- return $msg;
+ if ($hasMultipleTypes) {
+ $msg[] = '--' . $relBoundary;
+ $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
+ $msg[] = '';
+ $textBoundary = 'alt-' . $boundary;
}
- if (!empty($this->_attachments)) {
- if ($this->_emailFormat === 'html') {
- $msg[] = '';
- $msg[] = '--' . $this->_boundary;
- $msg[] = 'Content-Type: text/html; charset=' . $this->charset;
+ if (isset($rendered['text'])) {
+ if ($textBoundary !== $boundary || $hasAttachments) {
+ $msg[] = '--' . $textBoundary;
+ $msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
$msg[] = '';
- } else {
- $msg[] = '--' . $this->_boundary;
- $msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
+ }
+ $this->_textMessage = $rendered['text'];
+ $content = explode("\n", $this->_textMessage);
+ $msg = array_merge($msg, $content);
+ $msg[] = '';
+ }
+
+ if (isset($rendered['html'])) {
+ if ($textBoundary !== $boundary || $hasAttachments) {
+ $msg[] = '--' . $textBoundary;
+ $msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
$msg[] = '';
}
+ $this->_htmlMessage = $rendered['html'];
+ $content = explode("\n", $this->_htmlMessage);
+ $msg = array_merge($msg, $content);
+ $msg[] = '';
+ }
+
+ if ($hasMultipleTypes) {
+ $msg[] = '--' . $textBoundary . '--';
+ $msg[] = '';
}
- $rendered = $this->_encodeString($rendered[$this->_emailFormat], $this->charset);
- $content = explode("\n", $rendered);
+ if ($hasInlineAttachments) {
+ $attachments = $this->_attachInlineFiles();
+ $msg = array_merge($msg, $attachments);
+ $msg[] = '';
+ $msg[] = '--' . $relBoundary . '--';
+ $msg[] = '';
+ }
- if ($this->_emailFormat === 'html') {
- $this->_htmlMessage = $rendered;
- } else {
- $this->_textMessage = $rendered;
+ if ($hasAttachments) {
+ $attachments = $this->_attachFiles();
+ $msg = array_merge($msg, $attachments);
+ $msg[] = '';
+ $msg[] = '--' . $boundary . '--';
+ $msg[] = '';
}
+ return $msg;
+ }
- return array_merge($msg, $content);
+/**
+ * Gets the text body types that are in this email message
+ *
+ * @return array Array of types. Valid types are 'text' and 'html'
+ */
+ protected function _getTypes() {
+ $types = array($this->_emailFormat);
+ if ($this->_emailFormat == 'both') {
+ $types = array('html', 'text');
+ }
+ return $types;
}
/**
* Build and set all the view properties needed to render the templated emails.
- * Returns false if the email is not templated.
+ * If there is no template set, the $content will be returned in a hash
+ * of the text content types for the email.
*
* @param string $content The content passed in from send() in most cases.
* @return array The rendered content with html and text keys.
*/
- public function _renderTemplates($content) {
+ protected function _renderTemplates($content) {
+ $types = $this->_getTypes();
+ $rendered = array();
if (empty($this->_template)) {
- return false;
+ foreach ($types as $type) {
+ $rendered[$type] = $this->_encodeString($content, $this->charset);
+ }
+ return $rendered;
}
$viewClass = $this->_viewRender;
if ($viewClass !== 'View') {
@@ -1425,12 +1433,6 @@ public function _renderTemplates($content) {
$View->plugin = $layoutPlugin;
}
- $types = array($this->_emailFormat);
- if ($this->_emailFormat == 'both') {
- $types = array('html', 'text');
- }
-
- $rendered = array();
foreach ($types as $type) {
$View->set('content', $content);
$View->hasRendered = false;
@@ -1438,7 +1440,7 @@ public function _renderTemplates($content) {
$render = $View->render($template, $layout);
$render = str_replace(array("\r\n", "\r"), "\n", $render);
- $rendered[$type] = $render;
+ $rendered[$type] = $this->_encodeString($render, $this->charset);
}
return $rendered;
}
View
18 lib/Cake/Test/Case/Network/Email/CakeEmailTest.php
@@ -794,12 +794,25 @@ public function testSendNoTemplateWithAttachmentsAsBoth() {
$this->assertContains('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
$expected = "--$boundary\r\n" .
"Content-Type: multipart/alternative; boundary=\"alt-$boundary\"\r\n" .
+ "\r\n" .
+ "--alt-$boundary\r\n" .
+ "Content-Type: text/plain; charset=UTF-8\r\n" .
"Content-Transfer-Encoding: 8bit\r\n" .
"\r\n" .
"Hello" .
"\r\n" .
"\r\n" .
"\r\n" .
+ "--alt-$boundary\r\n" .
+ "Content-Type: text/html; charset=UTF-8\r\n" .
+ "Content-Transfer-Encoding: 8bit\r\n" .
+ "\r\n" .
+ "Hello" .
+ "\r\n" .
+ "\r\n" .
+ "\r\n" .
+ "--alt-{$boundary}--\r\n" .
+ "\r\n" .
"--$boundary\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n" .
@@ -875,7 +888,7 @@ public function testSendRenderJapanese() {
$this->CakeEmail->charset = 'ISO-2022-JP';
$result = $this->CakeEmail->send();
- $expected = mb_convert_encoding('CakePHP Framework を使って送信したメールです。 http://cakephp.org.','ISO-2022-JP');
+ $expected = mb_convert_encoding('CakePHP Framework を使って送信したメールです。 http://cakephp.org.', 'ISO-2022-JP');
$this->assertContains($expected, $result['message']);
$this->assertContains('Message-ID: ', $result['headers']);
$this->assertContains('To: ', $result['headers']);
@@ -1011,7 +1024,7 @@ public function testSendMultipleMIME() {
$message = $this->CakeEmail->message();
$boundary = $this->CakeEmail->getBoundary();
$this->assertFalse(empty($boundary));
- $this->assertNotContains('--' . $boundary, $message);
+ $this->assertContains('--' . $boundary, $message);
$this->assertNotContains('--' . $boundary . '--', $message);
$this->assertContains('--alt-' . $boundary, $message);
$this->assertContains('--alt-' . $boundary . '--', $message);
@@ -1121,7 +1134,6 @@ public function testMessage() {
// UTF-8 is 8bit
$this->assertTrue($this->checkContentTransferEncoding($message, '8bit'));
-
$this->CakeEmail->charset = 'ISO-2022-JP';
$this->CakeEmail->send();
$message = $this->CakeEmail->message();
Please sign in to comment.
Something went wrong with that request. Please try again.