Adding configuration options to CakeEmail to set the header encoding and body encoding, also translating

all string from the App.encoding value to the configured charset if they differ. This makes possible to
send correctly emails in japanese charset
This commit is contained in:
Jose Lorenzo Rodriguez 2011-10-16 15:41:59 -04:30
parent 3ed712e745
commit b38c0e50a4
4 changed files with 138 additions and 15 deletions

View file

@ -30,6 +30,7 @@
* transport => The name of a supported transport; valid options are as follows: * transport => The name of a supported transport; valid options are as follows:
* Mail - Send using PHP mail function * Mail - Send using PHP mail function
* Smtp - Send using SMTP * Smtp - Send using SMTP
* Debug - Do not send the email, just return the result
* *
* You can add custom transports (or override existing transports) by adding the * You can add custom transports (or override existing transports) by adding the
* appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php',
@ -43,7 +44,9 @@ class EmailConfig {
public $default = array( public $default = array(
'transport' => 'Mail', 'transport' => 'Mail',
'from' => 'you@localhost' 'from' => 'you@localhost',
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
public $smtp = array( public $smtp = array(
@ -54,7 +57,10 @@ class EmailConfig {
'timeout' => 30, 'timeout' => 30,
'username' => 'user', 'username' => 'user',
'password' => 'secret', 'password' => 'secret',
'client' => null 'client' => null,
'log' => false
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
public $fast = array( public $fast = array(
@ -82,7 +88,10 @@ class EmailConfig {
'timeout' => 30, 'timeout' => 30,
'username' => 'user', 'username' => 'user',
'password' => 'secret', 'password' => 'secret',
'client' => null 'client' => null,
'log' => true,
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
} }

View file

@ -30,6 +30,7 @@
* transport => The name of a supported transport; valid options are as follows: * transport => The name of a supported transport; valid options are as follows:
* Mail - Send using PHP mail function * Mail - Send using PHP mail function
* Smtp - Send using SMTP * Smtp - Send using SMTP
* Debug - Do not send the email, just return the result
* *
* You can add custom transports (or override existing transports) by adding the * You can add custom transports (or override existing transports) by adding the
* appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php',
@ -43,7 +44,9 @@ class EmailConfig {
public $default = array( public $default = array(
'transport' => 'Mail', 'transport' => 'Mail',
'from' => 'you@localhost' 'from' => 'you@localhost',
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
public $smtp = array( public $smtp = array(
@ -54,7 +57,10 @@ class EmailConfig {
'timeout' => 30, 'timeout' => 30,
'username' => 'user', 'username' => 'user',
'password' => 'secret', 'password' => 'secret',
'client' => null 'client' => null,
'log' => false
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
public $fast = array( public $fast = array(
@ -82,7 +88,10 @@ class EmailConfig {
'timeout' => 30, 'timeout' => 30,
'username' => 'user', 'username' => 'user',
'password' => 'secret', 'password' => 'secret',
'client' => null 'client' => null,
'log' => true,
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
); );
} }

View file

@ -239,12 +239,26 @@ class CakeEmail {
protected $_transportClass = null; protected $_transportClass = null;
/** /**
* charset the email is sent in * Charset the email body is sent in
*
* *
* @var string * @var string
*/ */
public $charset = 'utf-8'; public $charset = 'utf-8';
/**
* Charset the email header is sent in
* If null, the $charset property will be used as default
* @var string
*/
public $headerCharset = null;
/**
* The application wide charset, used to encode headers and body
* @var string
*/
public $_appCharset = null;
/** /**
* List of files that should be attached to the email. * List of files that should be attached to the email.
* *
@ -274,13 +288,16 @@ class CakeEmail {
* *
*/ */
public function __construct($config = null) { public function __construct($config = null) {
$charset = Configure::read('App.encoding'); $this->_appCharset = Configure::read('App.encoding');
if ($charset !== null) { if ($this->_appCharset !== null) {
$this->charset = $charset; $this->charset = $this->_appCharset;
} }
if ($config) { if ($config) {
$this->config($config); $this->config($config);
} }
if (empty($this->headerCharset)) {
$this->headerCharset = $this->charset;
}
} }
/** /**
@ -994,15 +1011,24 @@ class CakeEmail {
protected function _applyConfig($config) { protected function _applyConfig($config) {
if (is_string($config)) { if (is_string($config)) {
if (!class_exists('EmailConfig') && !config('email')) { if (!class_exists('EmailConfig') && !config('email')) {
throw new SocketException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php')); throw new ConfigureException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php'));
} }
$configs = new EmailConfig(); $configs = new EmailConfig();
if (!isset($configs->{$config})) { if (!isset($configs->{$config})) {
throw new SocketException(__d('cake_dev', 'Unknown email configuration "%s".', $config)); throw new ConfigureException(__d('cake_dev', 'Unknown email configuration "%s".', $config));
} }
$config = $configs->{$config}; $config = $configs->{$config};
} }
$this->_config += $config; $this->_config += $config;
if (!empty($config['charset'])) {
$this->charset = $config['charset'];
}
if (!empty($config['headerCharset'])) {
$this->headerCharset = $config['headerCharset'];
}
if (empty($this->headerCharset)) {
$this->headerCharset = $this->charset;
}
$simpleMethods = array( $simpleMethods = array(
'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc', 'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
'messageId', 'subject', 'viewRender', 'viewVars', 'attachments', 'messageId', 'subject', 'viewRender', 'viewVars', 'attachments',
@ -1073,15 +1099,31 @@ class CakeEmail {
$internalEncoding = function_exists('mb_internal_encoding'); $internalEncoding = function_exists('mb_internal_encoding');
if ($internalEncoding) { if ($internalEncoding) {
$restore = mb_internal_encoding(); $restore = mb_internal_encoding();
mb_internal_encoding($this->charset); mb_internal_encoding($this->_appCharset);
} }
$return = mb_encode_mimeheader($text, $this->charset, 'B'); $text = $this->_encodeString($text, $this->headerCharset);
$return = mb_encode_mimeheader($text, $this->headerCharset, 'B');
if ($internalEncoding) { if ($internalEncoding) {
mb_internal_encoding($restore); mb_internal_encoding($restore);
} }
return $return; return $return;
} }
/**
* Translates a string for one charset to another if the App.encoding value
* differs and the mb_convert_encoding function exists
*
* @param string $text The text to be converted
* @param string $charset the target encoding
* @return string
*/
protected function _encodeString($text, $charset) {
if ($this->_appCharset === $charset || !function_exists('mb_convert_encoding')) {
return $text;
}
return mb_convert_encoding($text, $charset, $this->_appCharset);
}
/** /**
* Wrap the message to follow the RFC 2822 - 2.1.1 * Wrap the message to follow the RFC 2822 - 2.1.1
* *
@ -1322,7 +1364,7 @@ class CakeEmail {
$View->viewPath = $View->layoutPath = 'Emails' . DS . $this->_emailFormat; $View->viewPath = $View->layoutPath = 'Emails' . DS . $this->_emailFormat;
$View->viewVars['content'] = $content; $View->viewVars['content'] = $content;
$rendered = $View->render($template, $layout); $rendered = $this->_encodeString($View->render($template, $layout), $this->charset);
$content = explode("\n", $rendered); $content = explode("\n", $rendered);
if ($this->_emailFormat === 'html') { if ($this->_emailFormat === 'html') {

View file

@ -1120,4 +1120,67 @@ class CakeEmailTest extends CakeTestCase {
$result = $this->CakeEmail->emailFormat('invalid'); $result = $this->CakeEmail->emailFormat('invalid');
} }
/**
* Tests that it is possible to add charset configuration to a CakeEmail object
*
* @return void
*/
public function testConfigCharset() {
$email = new CakeEmail();
$this->assertEquals($email->charset, Configure::read('App.encoding'));
$this->assertEquals($email->headerCharset, Configure::read('App.encoding'));
$email = new CakeEmail(array('charset' => 'iso-2022-jp', 'headerCharset' => 'iso-2022-jp-ms'));
$this->assertEquals($email->charset, 'iso-2022-jp');
$this->assertEquals($email->headerCharset, 'iso-2022-jp-ms');
$email = new CakeEmail(array('charset' => 'iso-2022-jp'));
$this->assertEquals($email->charset, 'iso-2022-jp');
$this->assertEquals($email->headerCharset, 'iso-2022-jp');
$email = new CakeEmail(array('headerCharset' => 'iso-2022-jp-ms'));
$this->assertEquals($email->charset, Configure::read('App.encoding'));
$this->assertEquals($email->headerCharset, 'iso-2022-jp-ms');
}
/**
* Tests that the header is encoded using the configured headerCharset
*
* @return void
*/
public function testHeaderEncoding() {
$this->skipIf(!function_exists('mb_convert_encoding'));
$email = new CakeEmail(array('headerCharset' => 'iso-2022-jp-ms', 'transport' => 'Debug'));
$email->subject('あれ?もしかしての前と');
$headers = $email->getHeaders(array('subject'));
$expected = "?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?=";
$this->assertContains($expected, $headers['Subject']);
$email->to('someone@example.com')->from('someone@example.com');
$result = $email->send('ってテーブルを作ってやってたらう');
$this->assertContains('ってテーブルを作ってやってたらう', $result['message']);
}
/**
* Tests that the body is encoded using the configured charset
*
* @return void
*/
public function testBodyEncoding() {
$this->skipIf(!function_exists('mb_convert_encoding'));
$email = new CakeEmail(array(
'charset' => 'iso-2022-jp',
'headerCharset' => 'iso-2022-jp-ms',
'transport' => 'Debug'
));
$email->subject('あれ?もしかしての前と');
$headers = $email->getHeaders(array('subject'));
$expected = "?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?=";
$this->assertContains($expected, $headers['Subject']);
$email->to('someone@example.com')->from('someone@example.com');
$result = $email->send('ってテーブルを作ってやってたらう');
$this->assertContains('Content-Type: text/plain; charset=iso-2022-jp', $result['headers']);
$this->assertContains('ってテーブルを作ってやってたらう', $result['message']);
}
} }