mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-01-19 11:06:15 +00:00
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
This commit is contained in:
parent
53598c722c
commit
0e4af546d6
2 changed files with 145 additions and 131 deletions
|
@ -963,37 +963,8 @@ class CakeEmail {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_textMessage = $this->_htmlMessage = '';
|
$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->_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);
|
$contents = $this->transportClass()->send($this);
|
||||||
if (!empty($this->_config['log'])) {
|
if (!empty($this->_config['log'])) {
|
||||||
|
@ -1269,142 +1240,179 @@ class CakeEmail {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() {
|
protected function _attachFiles() {
|
||||||
|
$msg = array();
|
||||||
foreach ($this->_attachments as $filename => $fileInfo) {
|
foreach ($this->_attachments as $filename => $fileInfo) {
|
||||||
$handle = fopen($fileInfo['file'], 'rb');
|
if (!empty($fileInfo['contentId'])) {
|
||||||
$data = fread($handle, filesize($fileInfo['file']));
|
continue;
|
||||||
$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 . '"';
|
|
||||||
}
|
}
|
||||||
$this->_message[] = '';
|
$data = $this->_readFile($fileInfo['file']);
|
||||||
$this->_message[] = $data;
|
|
||||||
$this->_message[] = '';
|
$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
|
* @param string $file The file to read.
|
||||||
* @return array
|
* @return string File contents in base64 encoding
|
||||||
*/
|
*/
|
||||||
protected function _formatMessage($message) {
|
protected function _readFile($file) {
|
||||||
if (!empty($this->_attachments)) {
|
$handle = fopen($file, 'rb');
|
||||||
$prefix = array('--' . $this->_boundary);
|
$data = fread($handle, filesize($file));
|
||||||
if ($this->_emailFormat === 'text') {
|
$data = chunk_split(base64_encode($data)) ;
|
||||||
$prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
|
fclose($handle);
|
||||||
} elseif ($this->_emailFormat === 'html') {
|
return $data;
|
||||||
$prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
|
|
||||||
} elseif ($this->_emailFormat === 'both') {
|
|
||||||
$prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"';
|
|
||||||
}
|
|
||||||
$prefix[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
|
|
||||||
$prefix[] = '';
|
|
||||||
$message = array_merge($prefix, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp = array();
|
|
||||||
foreach ($message as $msg) {
|
|
||||||
$tmp[] = $this->_encodeString($msg, $this->charset);
|
|
||||||
}
|
|
||||||
$message = $tmp;
|
|
||||||
|
|
||||||
return $message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the contents using the current layout and template.
|
* 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;
|
||||||
|
}
|
||||||
|
$data = $this->_readFile($fileInfo['file']);
|
||||||
|
|
||||||
|
$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[] = '';
|
||||||
|
}
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the body of the email.
|
||||||
*
|
*
|
||||||
* @param string $content Content to render
|
* @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) {
|
protected function _render($content) {
|
||||||
$content = implode("\n", $content);
|
$content = implode("\n", $content);
|
||||||
$rendered = $this->_renderTemplates($content);
|
$rendered = $this->_renderTemplates($content);
|
||||||
|
|
||||||
$msg = array();
|
$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'];
|
$contentIds = array_filter((array)Set::classicExtract($this->_attachments, '{s}.contentId'));
|
||||||
$content = explode("\n", $this->_textMessage);
|
$hasInlineAttachments = count($contentIds) > 0;
|
||||||
$msg = array_merge($msg, $content);
|
$hasAttachments = !empty($this->_attachments);
|
||||||
|
$hasMultipleTypes = count($rendered) > 1;
|
||||||
|
|
||||||
$msg[] = '';
|
$boundary = $relBoundary = $textBoundary = $this->_boundary;
|
||||||
$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);
|
|
||||||
|
|
||||||
|
if ($hasInlineAttachments) {
|
||||||
|
$msg[] = '--' . $boundary;
|
||||||
|
$msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
|
||||||
$msg[] = '';
|
$msg[] = '';
|
||||||
$msg[] = '--alt-' . $this->_boundary . '--';
|
$relBoundary = 'rel-' . $boundary;
|
||||||
$msg[] = '';
|
|
||||||
|
|
||||||
return $msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->_attachments)) {
|
if ($hasMultipleTypes) {
|
||||||
if ($this->_emailFormat === 'html') {
|
$msg[] = '--' . $relBoundary;
|
||||||
$msg[] = '';
|
$msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
|
||||||
$msg[] = '--' . $this->_boundary;
|
$msg[] = '';
|
||||||
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
|
$textBoundary = 'alt-' . $boundary;
|
||||||
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
|
}
|
||||||
$msg[] = '';
|
|
||||||
} else {
|
if (isset($rendered['text'])) {
|
||||||
$msg[] = '--' . $this->_boundary;
|
if ($textBoundary !== $boundary || $hasAttachments) {
|
||||||
|
$msg[] = '--' . $textBoundary;
|
||||||
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
|
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
|
||||||
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
|
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
|
||||||
$msg[] = '';
|
$msg[] = '';
|
||||||
}
|
}
|
||||||
|
$this->_textMessage = $rendered['text'];
|
||||||
|
$content = explode("\n", $this->_textMessage);
|
||||||
|
$msg = array_merge($msg, $content);
|
||||||
|
$msg[] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$rendered = $this->_encodeString($rendered[$this->_emailFormat], $this->charset);
|
if (isset($rendered['html'])) {
|
||||||
$content = explode("\n", $rendered);
|
if ($textBoundary !== $boundary || $hasAttachments) {
|
||||||
|
$msg[] = '--' . $textBoundary;
|
||||||
if ($this->_emailFormat === 'html') {
|
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
|
||||||
$this->_htmlMessage = $rendered;
|
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
|
||||||
} else {
|
$msg[] = '';
|
||||||
$this->_textMessage = $rendered;
|
}
|
||||||
|
$this->_htmlMessage = $rendered['html'];
|
||||||
|
$content = explode("\n", $this->_htmlMessage);
|
||||||
|
$msg = array_merge($msg, $content);
|
||||||
|
$msg[] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge($msg, $content);
|
if ($hasMultipleTypes) {
|
||||||
|
$msg[] = '--' . $textBoundary . '--';
|
||||||
|
$msg[] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasInlineAttachments) {
|
||||||
|
$attachments = $this->_attachInlineFiles();
|
||||||
|
$msg = array_merge($msg, $attachments);
|
||||||
|
$msg[] = '';
|
||||||
|
$msg[] = '--' . $relBoundary . '--';
|
||||||
|
$msg[] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasAttachments) {
|
||||||
|
$attachments = $this->_attachFiles();
|
||||||
|
$msg = array_merge($msg, $attachments);
|
||||||
|
$msg[] = '';
|
||||||
|
$msg[] = '--' . $boundary . '--';
|
||||||
|
$msg[] = '';
|
||||||
|
}
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* 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.
|
* @param string $content The content passed in from send() in most cases.
|
||||||
* @return array The rendered content with html and text keys.
|
* @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)) {
|
if (empty($this->_template)) {
|
||||||
return false;
|
foreach ($types as $type) {
|
||||||
|
$rendered[$type] = $this->_encodeString($content, $this->charset);
|
||||||
|
}
|
||||||
|
return $rendered;
|
||||||
}
|
}
|
||||||
$viewClass = $this->_viewRender;
|
$viewClass = $this->_viewRender;
|
||||||
if ($viewClass !== 'View') {
|
if ($viewClass !== 'View') {
|
||||||
|
@ -1425,12 +1433,6 @@ class CakeEmail {
|
||||||
$View->plugin = $layoutPlugin;
|
$View->plugin = $layoutPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
$types = array($this->_emailFormat);
|
|
||||||
if ($this->_emailFormat == 'both') {
|
|
||||||
$types = array('html', 'text');
|
|
||||||
}
|
|
||||||
|
|
||||||
$rendered = array();
|
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
$View->set('content', $content);
|
$View->set('content', $content);
|
||||||
$View->hasRendered = false;
|
$View->hasRendered = false;
|
||||||
|
@ -1438,7 +1440,7 @@ class CakeEmail {
|
||||||
|
|
||||||
$render = $View->render($template, $layout);
|
$render = $View->render($template, $layout);
|
||||||
$render = str_replace(array("\r\n", "\r"), "\n", $render);
|
$render = str_replace(array("\r\n", "\r"), "\n", $render);
|
||||||
$rendered[$type] = $render;
|
$rendered[$type] = $this->_encodeString($render, $this->charset);
|
||||||
}
|
}
|
||||||
return $rendered;
|
return $rendered;
|
||||||
}
|
}
|
||||||
|
|
|
@ -794,12 +794,25 @@ class CakeEmailTest extends CakeTestCase {
|
||||||
$this->assertContains('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
|
$this->assertContains('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
|
||||||
$expected = "--$boundary\r\n" .
|
$expected = "--$boundary\r\n" .
|
||||||
"Content-Type: multipart/alternative; boundary=\"alt-$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" .
|
"Content-Transfer-Encoding: 8bit\r\n" .
|
||||||
"\r\n" .
|
"\r\n" .
|
||||||
"Hello" .
|
"Hello" .
|
||||||
"\r\n" .
|
"\r\n" .
|
||||||
"\r\n" .
|
"\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" .
|
"--$boundary\r\n" .
|
||||||
"Content-Type: application/octet-stream\r\n" .
|
"Content-Type: application/octet-stream\r\n" .
|
||||||
"Content-Transfer-Encoding: base64\r\n" .
|
"Content-Transfer-Encoding: base64\r\n" .
|
||||||
|
@ -875,7 +888,7 @@ class CakeEmailTest extends CakeTestCase {
|
||||||
$this->CakeEmail->charset = 'ISO-2022-JP';
|
$this->CakeEmail->charset = 'ISO-2022-JP';
|
||||||
$result = $this->CakeEmail->send();
|
$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($expected, $result['message']);
|
||||||
$this->assertContains('Message-ID: ', $result['headers']);
|
$this->assertContains('Message-ID: ', $result['headers']);
|
||||||
$this->assertContains('To: ', $result['headers']);
|
$this->assertContains('To: ', $result['headers']);
|
||||||
|
@ -1011,7 +1024,7 @@ class CakeEmailTest extends CakeTestCase {
|
||||||
$message = $this->CakeEmail->message();
|
$message = $this->CakeEmail->message();
|
||||||
$boundary = $this->CakeEmail->getBoundary();
|
$boundary = $this->CakeEmail->getBoundary();
|
||||||
$this->assertFalse(empty($boundary));
|
$this->assertFalse(empty($boundary));
|
||||||
$this->assertNotContains('--' . $boundary, $message);
|
$this->assertContains('--' . $boundary, $message);
|
||||||
$this->assertNotContains('--' . $boundary . '--', $message);
|
$this->assertNotContains('--' . $boundary . '--', $message);
|
||||||
$this->assertContains('--alt-' . $boundary, $message);
|
$this->assertContains('--alt-' . $boundary, $message);
|
||||||
$this->assertContains('--alt-' . $boundary . '--', $message);
|
$this->assertContains('--alt-' . $boundary . '--', $message);
|
||||||
|
@ -1121,7 +1134,6 @@ class CakeEmailTest extends CakeTestCase {
|
||||||
// UTF-8 is 8bit
|
// UTF-8 is 8bit
|
||||||
$this->assertTrue($this->checkContentTransferEncoding($message, '8bit'));
|
$this->assertTrue($this->checkContentTransferEncoding($message, '8bit'));
|
||||||
|
|
||||||
|
|
||||||
$this->CakeEmail->charset = 'ISO-2022-JP';
|
$this->CakeEmail->charset = 'ISO-2022-JP';
|
||||||
$this->CakeEmail->send();
|
$this->CakeEmail->send();
|
||||||
$message = $this->CakeEmail->message();
|
$message = $this->CakeEmail->message();
|
||||||
|
|
Loading…
Add table
Reference in a new issue