Merge branch '2.1' of github.com:cakephp/cakephp into 2.1

This commit is contained in:
Jose Lorenzo Rodriguez 2012-01-05 19:58:15 -04:30
commit 7c0297a92c
40 changed files with 789 additions and 429 deletions

View file

@ -268,7 +268,7 @@
* 'password' => 'password', //plaintext password (xcache.admin.pass)
* ));
*
* Memcache (http://www.danga.com/memcached/)
* Memcache (http://memcached.org/)
*
* Cache::config('default', array(
* 'engine' => 'Memcache', //[required]

View file

@ -24,7 +24,7 @@ App::uses('Debugger', 'Utility');
<p>For updates and important announcements, visit http://cakefest.org</p>
</iframe>
<h2><?php echo __d('cake_dev', 'Release Notes for CakePHP %s.', Configure::version()); ?></h2>
<a href="http://cakephp.org/changelogs/2.0.4"><?php __d('cake_dev', 'Read the changelog'); ?> </a>
<a href="http://cakephp.org/changelogs/2.0.5"><?php echo __d('cake_dev', 'Read the changelog'); ?> </a>
<?php
if (Configure::read('debug') > 0):
Debugger::checkSecurityKeys();

View file

@ -17,4 +17,4 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
$versionFile = file(CAKE . 'VERSION.txt');
return $config['Cake.version'] = array_pop($versionFile);
return $config['Cake.version'] = trim(array_pop($versionFile));

View file

@ -545,6 +545,18 @@ class Controller extends Object implements CakeEventListener {
$pluginDot = $this->plugin . '.';
}
if ($pluginController && $this->plugin != null) {
$merge = array('components', 'helpers');
$appVars = get_class_vars($pluginController);
if (
($this->uses !== null || $this->uses !== false) &&
is_array($this->uses) && !empty($appVars['uses'])
) {
$this->uses = array_merge($this->uses, array_diff($appVars['uses'], $this->uses));
}
$this->_mergeVars($merge, $pluginController);
}
if (is_subclass_of($this, $this->_mergeParent) || !empty($pluginController)) {
$appVars = get_class_vars($this->_mergeParent);
$uses = $appVars['uses'];
@ -567,18 +579,6 @@ class Controller extends Object implements CakeEventListener {
}
$this->_mergeVars($merge, $this->_mergeParent, true);
}
if ($pluginController && $this->plugin != null) {
$merge = array('components', 'helpers');
$appVars = get_class_vars($pluginController);
if (
($this->uses !== null || $this->uses !== false) &&
is_array($this->uses) && !empty($appVars['uses'])
) {
$this->uses = array_merge($this->uses, array_diff($appVars['uses'], $this->uses));
}
$this->_mergeVars($merge, $pluginController);
}
}
/**

View file

@ -212,7 +212,7 @@ class App {
*
* @param string $type type of path
* @param string $plugin name of plugin
* @return string array
* @return array
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::path
*/
public static function path($type, $plugin = null) {
@ -387,7 +387,7 @@ class App {
* `App::core('Cache/Engine'); will return the full path to the cache engines package`
*
* @param string $type
* @return string full path to package
* @return array full path to package
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::core
*/
public static function core($type) {
@ -580,7 +580,7 @@ class App {
* based on Inflector::underscore($name) . ".$ext";
* @param array $search paths to search for files, array('path 1', 'path 2', 'path 3');
* @param string $file full name of the file to search for including extension
* @param boolean $return, return the loaded file, the file must have a return
* @param boolean $return Return the loaded file, the file must have a return
* statement in it to work: return $variable;
* @return boolean true if Class is already in memory or if file is found and loaded, false if not
*/

View file

@ -224,7 +224,7 @@ class CakeEventManager {
*/
public function dispatch($event) {
if (is_string($event)) {
$Event = new CakeEvent($event);
$event = new CakeEvent($event);
}
if (!$this->_isGlobal) {

View file

@ -185,6 +185,18 @@ class I18n {
if (is_array($trans)) {
if (isset($trans[$plurals])) {
$trans = $trans[$plurals];
} else {
trigger_error(
__d('cake_dev',
'Missing plural form translation for "%s" in "%s" domain, "%s" locale. ' .
' Check your po file for correct plurals and valid Plural-Forms header.',
$singular,
$domain,
$_this->_lang
),
E_USER_WARNING
);
$trans = $trans[0];
}
}
if (strlen($trans)) {

View file

@ -982,7 +982,7 @@ class Multibyte {
$parts = array();
$maxchars = floor(($length * 3) / 4);
while (strlen($string) > $maxchars) {
$i = $maxchars;
$i = (int)$maxchars;
$test = ord($string[$i]);
while ($test >= 128 && $test <= 191) {
$i--;

View file

@ -369,7 +369,10 @@ class TranslateBehavior extends ModelBehavior {
/**
* Bind translation for fields, optionally with hasMany association for
* fake field
* fake field.
*
* *Note* You should avoid binding translations that overlap existing model properties.
* This can cause un-expected and un-desirable behavior.
*
* @param Model $model instance of model
* @param string|array $fields string with field or array(field1, field2=>AssocName, field3)
@ -392,6 +395,11 @@ class TranslateBehavior extends ModelBehavior {
$field = $key;
$association = $value;
}
if ($association === 'name') {
throw new CakeException(
__d('cake_dev', 'You cannot bind a translation named "name".')
);
}
if (array_key_exists($field, $this->settings[$model->alias])) {
unset($this->settings[$model->alias][$field]);

View file

@ -70,13 +70,6 @@ class CakeSession {
*/
public static $lastError = null;
/**
* 'Security.level' setting, "high", "medium", or "low".
*
* @var string
*/
public static $security = null;
/**
* Start time for this session.
*
@ -121,7 +114,7 @@ class CakeSession {
/**
* Number of requests that can occur during a session time without the session being renewed.
* This feature is only used when `Session.harden` is set to true.
* This feature is only used when `Session.autoRegenerate` is set to true.
*
* @var integer
* @see CakeSession::_checkValid()

View file

@ -413,7 +413,7 @@ class DataSource extends Object {
* @access public
*/
public function getSchemaName() {
return false;
return null;
}
/**

View file

@ -723,7 +723,7 @@ class Postgres extends DboSource {
break;
case 'binary':
case 'bytea':
$resultRow[$table][$column] = stream_get_contents($row[$index]);
$resultRow[$table][$column] = is_null($row[$index]) ? null : stream_get_contents($row[$index]);
break;
default:
$resultRow[$table][$column] = $row[$index];

View file

@ -484,7 +484,7 @@ class DboSource extends DataSource {
if ($this->hasResult()) {
return $this->_result->rowCount();
}
return null;
return 0;
}
/**
@ -930,14 +930,14 @@ class DboSource extends DataSource {
}
if ($quote) {
if ($schema && isset($schemaName)) {
if ($schema && !empty($schemaName)) {
if (false == strstr($table, '.')) {
return $this->name($schemaName) . '.' . $this->name($table);
}
}
return $this->name($table);
}
if ($schema && isset($schemaName)) {
if ($schema && !empty($schemaName)) {
if (false == strstr($table, '.')) {
return $schemaName . '.' . $table;
}

View file

@ -1203,7 +1203,17 @@ class Model extends Object implements CakeEventListener {
$timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');
$date = array();
if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) {
if (isset($data['meridian']) && empty($data['meridian'])) {
return null;
}
if (
isset($data['hour']) &&
isset($data['meridian']) &&
!empty($data['hour']) &&
$data['hour'] != 12 &&
'pm' == $data['meridian']
) {
$data['hour'] = $data['hour'] + 12;
}
if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) {
@ -1213,9 +1223,7 @@ class Model extends Object implements CakeEventListener {
foreach ($timeFields as $key => $val) {
if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
$data[$val] = '00';
} elseif ($data[$val] === '') {
$data[$val] = '';
} else {
} elseif ($data[$val] !== '') {
$data[$val] = sprintf('%02d', $data[$val]);
}
if (!empty($data[$val])) {
@ -2810,6 +2818,13 @@ class Model extends Object implements CakeEventListener {
foreach ($results as $result) {
$result['children'] = array();
$id = $result[$this->alias][$this->primaryKey];
if (!isset($result[$this->alias]['parent_id'])) {
trigger_error(
__d('cake_dev', 'You cannot use find("threaded") on models without a "parent_id" field.'),
E_USER_WARNING
);
return $return;
}
$parentId = $result[$this->alias]['parent_id'];
if (isset($idMap[$id]['children'])) {
$idMap[$id] = array_merge($result, (array)$idMap[$id]);

View file

@ -107,7 +107,7 @@ class CakeRequest implements ArrayAccess {
'Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone',
'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource',
'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser',
'webOS', 'Windows CE', 'Xiino'
'webOS', 'Windows CE', 'Windows Phone OS', 'Xiino'
)),
'requested' => array('param' => 'requested', 'value' => 1)
);

View file

@ -662,14 +662,12 @@ class CakeEmail {
}
$headers['MIME-Version'] = '1.0';
if (!empty($this->_attachments)) {
if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
$headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
} elseif ($this->_emailFormat === 'text') {
$headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
} elseif ($this->_emailFormat === 'html') {
$headers['Content-Type'] = 'text/html; charset=' . $this->charset;
} elseif ($this->_emailFormat === 'both') {
$headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->_boundary . '"';
}
$headers['Content-Transfer-Encoding'] = $this->_getContentTransferEncoding();
@ -835,10 +833,37 @@ class CakeEmail {
}
/**
* Attachments
* Add attachments to the email message
*
* Attachments can be defined in a few forms depending on how much control you need:
*
* Attach a single file:
*
* {{{
* $email->attachments('path/to/file');
* }}}
*
* Attach a file with a different filename:
*
* {{{
* $email->attachments(array('custom_name.txt' => 'path/to/file.txt'));
* }}}
*
* Attach a file and specify additional properties:
*
* {{{
* $email->attachments(array('custom_name.png' => array(
* 'file' => 'path/to/file',
* 'mimetype' => 'image/png',
* 'contentId' => 'abc123'
* ));
* }}}
*
* The `contentId` key allows you to specify an inline attachment. In your email text, you
* can use `<img src="cid:abc123" />` to display the image inline.
*
* @param mixed $attachments String with the filename or array with filenames
* @return mixed
* @return mixed Either the array of attachments when getting or $this when setting.
* @throws SocketException
*/
public function attachments($attachments = null) {
@ -936,34 +961,9 @@ class CakeEmail {
}
$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);
if (empty($this->_template)) {
$message = $this->_formatMessage($message);
} else {
$message = $this->_render($message);
}
$message[] = '';
$this->_message = $message;
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;
@ -1213,6 +1213,7 @@ class CakeEmail {
}
$formatted[] = trim(substr($tmpLine, 0, $lastSpace));
$tmpLine = substr($tmpLine, $lastSpace + 1);
$tmpLineLength = strlen($tmpLine);
}
}
@ -1237,71 +1238,183 @@ 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() {
$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 . '"';
}
$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;
protected function _readFile($file) {
$handle = fopen($file, 'rb');
$data = fread($handle, filesize($file));
$data = chunk_split(base64_encode($data)) ;
fclose($handle);
return $data;
}
/**
* 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
* @return array Email ready to be sent
* @return array Email body ready to be sent
*/
protected function _render($content) {
$viewClass = $this->_viewRender;
$content = implode("\n", $content);
$rendered = $this->_renderTemplates($content);
$msg = array();
$contentIds = array_filter((array)Set::classicExtract($this->_attachments, '{s}.contentId'));
$hasInlineAttachments = count($contentIds) > 0;
$hasAttachments = !empty($this->_attachments);
$hasMultipleTypes = count($rendered) > 1;
$boundary = $relBoundary = $textBoundary = $this->_boundary;
if ($hasInlineAttachments) {
$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
$msg[] = '';
$relBoundary = 'rel-' . $boundary;
}
if ($hasMultipleTypes) {
$msg[] = '--' . $relBoundary;
$msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
$msg[] = '';
$textBoundary = 'alt-' . $boundary;
}
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[] = '';
}
$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[] = '';
}
if ($hasInlineAttachments) {
$attachments = $this->_attachInlineFiles();
$msg = array_merge($msg, $attachments);
$msg[] = '';
$msg[] = '--' . $relBoundary . '--';
$msg[] = '';
}
if ($hasAttachments) {
$attachments = $this->_attachFiles();
$msg = array_merge($msg, $attachments);
}
if ($hasAttachments || $hasMultipleTypes) {
$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.
* 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.
*/
protected function _renderTemplates($content) {
$types = $this->_getTypes();
$rendered = array();
if (empty($this->_template)) {
foreach ($types as $type) {
$rendered[$type] = $this->_encodeString($content, $this->charset);
}
return $rendered;
}
$viewClass = $this->_viewRender;
if ($viewClass !== 'View') {
list($plugin, $viewClass) = pluginSplit($viewClass, true);
$viewClass .= 'View';
@ -1311,7 +1424,6 @@ class CakeEmail {
$View = new $viewClass(null);
$View->viewVars = $this->_viewVars;
$View->helpers = $this->_helpers;
$msg = array();
list($templatePlugin, $template) = pluginSplit($this->_template);
list($layoutPlugin, $layout) = pluginSplit($this->_layout);
@ -1321,73 +1433,16 @@ class CakeEmail {
$View->plugin = $layoutPlugin;
}
$content = implode("\n", $content);
if ($this->_emailFormat === 'both') {
$originalContent = $content;
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[] = '';
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'text';
$View->viewVars['content'] = $originalContent;
$this->_textMessage = str_replace(array("\r\n", "\r"), "\n", $View->render($template, $layout));
$content = explode("\n", $this->_textMessage);
$msg = array_merge($msg, $content);
$msg[] = '';
$msg[] = '--alt-' . $this->_boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
$msg[] = '';
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'html';
$View->viewVars['content'] = $originalContent;
foreach ($types as $type) {
$View->set('content', $content);
$View->hasRendered = false;
$this->_htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->render($template, $layout));
$content = explode("\n", $this->_htmlMessage);
$msg = array_merge($msg, $content);
$msg[] = '';
$msg[] = '--alt-' . $this->_boundary . '--';
$msg[] = '';
return $msg;
$View->viewPath = $View->layoutPath = 'Emails' . DS . $type;
$render = $View->render($template, $layout);
$render = str_replace(array("\r\n", "\r"), "\n", $render);
$rendered[$type] = $this->_encodeString($render, $this->charset);
}
if (!empty($this->_attachments)) {
if ($this->_emailFormat === 'html') {
$msg[] = '';
$msg[] = '--' . $this->_boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
$msg[] = '';
} else {
$msg[] = '--' . $this->_boundary;
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
$msg[] = '';
}
}
$View->viewPath = $View->layoutPath = 'Emails' . DS . $this->_emailFormat;
$View->viewVars['content'] = $content;
$rendered = $this->_encodeString($View->render($template, $layout), $this->charset);
$content = explode("\n", $rendered);
if ($this->_emailFormat === 'html') {
$this->_htmlMessage = $rendered;
} else {
$this->_textMessage = $rendered;
}
return array_merge($msg, $content);
return $rendered;
}
/**

View file

@ -174,6 +174,20 @@ class Router {
return self::$_namedExpressions;
}
/**
* Resource map getter & setter.
*
* @param array $resourceMap Resource map
* @return mixed
* @see Router::$_resourceMap
*/
public static function resourceMap($resourceMap = null) {
if ($resourceMap === null) {
return self::$_resourceMap;
}
self::$_resourceMap = $resourceMap;
}
/**
* Connects a new Route in the router.
*

View file

@ -228,12 +228,6 @@ MSGBLOC;
$expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message);
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertEquals(DebugCompTransport::$lastEmail, $this->__osFix($expect));
// TODO: better test for format of message sent?
$this->Controller->EmailTest->sendAs = 'both';
$expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-"', $message);
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertEquals(preg_replace('/alt-[a-z0-9]{32}/i', 'alt-', DebugCompTransport::$lastEmail), $this->__osFix($expect));
}
/**
@ -307,13 +301,29 @@ HTMLBLOC;
$this->assertEquals(DebugCompTransport::$lastEmail, $this->__osFix($expect));
$this->Controller->EmailTest->sendAs = 'both';
$expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-"', $header);
$expect .= '--alt-' . "\n" . 'Content-Type: text/plain; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 8bit' . "\n\n" . $text . "\n\n";
$expect .= '--alt-' . "\n" . 'Content-Type: text/html; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 8bit' . "\n\n" . $html . "\n\n";
$expect = '<pre>' . $expect . '--alt---' . "\n\n" . '</pre>';
$expect = str_replace('{CONTENTTYPE}', 'multipart/mixed; boundary="{boundary}"', $header);
$expect .= "--{boundary}\n" .
'Content-Type: multipart/alternative; boundary="alt-{boundary}"' . "\n\n" .
'--alt-{boundary}' . "\n" .
'Content-Type: text/plain; charset=UTF-8' . "\n" .
'Content-Transfer-Encoding: 8bit' . "\n\n" .
$text .
"\n\n" .
'--alt-{boundary}' . "\n" .
'Content-Type: text/html; charset=UTF-8' . "\n" .
'Content-Transfer-Encoding: 8bit' . "\n\n" .
$html .
"\n\n" .
'--alt-{boundary}--' . "\n\n\n" .
'--{boundary}--' . "\n";
$expect = '<pre>' . $expect . '</pre>';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertEquals(preg_replace('/alt-[a-z0-9]{32}/i', 'alt-', DebugCompTransport::$lastEmail), $this->__osFix($expect));
$this->assertEquals(
$this->__osFix($expect),
preg_replace('/[a-z0-9]{32}/i', '{boundary}', DebugCompTransport::$lastEmail)
);
$html = <<<HTMLBLOC
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
@ -441,7 +451,7 @@ HTMLBLOC;
$this->Controller->EmailTest->delivery = 'DebugComp';
$text = $html = 'This is the body of the message';
$text = $html = "This is the body of the message\n";
$this->Controller->EmailTest->sendAs = 'both';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
@ -740,8 +750,8 @@ HTMLBLOC;
$this->assertTrue($this->Controller->EmailTest->send($body));
$msg = DebugCompTransport::$lastEmail;
$this->assertNotRegExp('/text\/plain/', $msg);
$this->assertNotRegExp('/text\/html/', $msg);
$this->assertRegExp('/text\/plain/', $msg);
$this->assertRegExp('/text\/html/', $msg);
$this->assertRegExp('/multipart\/alternative/', $msg);
}

View file

@ -210,6 +210,22 @@ class CakeEventManagerTest extends CakeTestCase {
$manager->dispatch($event);
}
/**
* Tests event dispatching using event key name
*
* @return void
*/
public function testDispatchWithKeyName() {
$manager = new CakeEventManager;
$listener = new CakeEventTestListener;
$manager->attach(array($listener, 'listenerFunction'), 'fake.event');
$event = 'fake.event';
$manager->dispatch($event);
$expected = array('listenerFunction');
$this->assertEquals($expected, $listener->callStack);
}
/**
* Tests event dispatching with a return value
*

View file

@ -868,4 +868,16 @@ class TranslateBehaviorTest extends CakeTestCase {
$this->assertFalse($result);
}
/**
* Test that an exception is raised when you try to over-write the name attribute.
*
* @expectedException CakeException
* @return void
*/
public function testExceptionOnNameTranslation() {
$this->loadFixtures('Translate', 'TranslatedItem');
$TestModel = new TranslatedItem();
$TestModel->bindTranslation(array('name' => 'name'));
}
}

View file

@ -759,7 +759,7 @@ class PostgresTest extends CakeTestCase {
*/
function testVirtualFieldAsAConstant() {
$this->loadFixtures('Article', 'Comment');
$Article =& ClassRegistry::init('Article');
$Article = ClassRegistry::init('Article');
$Article->virtualFields = array(
'empty' => "NULL",
'number' => 43,

View file

@ -708,6 +708,22 @@ class DboSourceTest extends CakeTestCase {
$Article->tablePrefix = '';
$result = $this->testDb->fullTableName($Article, true, false);
$this->assertEquals($result, '`with spaces`');
$this->loadFixtures('Article');
$Article->useTable = $Article->table = 'articles';
$Article->setDataSource('test');
$testdb = $Article->getDataSource();
$result = $testdb->fullTableName($Article, false, true);
$this->assertEquals($testdb->getSchemaName() . '.articles', $result);
// tests for empty schemaName
$noschema = ConnectionManager::create('noschema', array(
'datasource' => 'DboTestSource'
));
$Article->setDataSource('noschema');
$Article->schemaName = null;
$result = $noschema->fullTableName($Article, false, true);
$this->assertEquals('articles', $result);
}
/**

View file

@ -814,91 +814,85 @@ class ModelIntegrationTest extends BaseModelTest {
}
/**
* test deconstruct() with time fields.
* data provider for time tests.
*
* @return array
*/
public static function timeProvider() {
$db = ConnectionManager::getDataSource('test');
$now = $db->expression('NOW()');
return array(
// blank
array(
array('hour' => '', 'min' => '', 'meridian' => ''),
''
),
// missing hour
array(
array('hour' => '', 'min' => '00', 'meridian' => 'pm'),
''
),
// all blank
array(
array('hour' => '', 'min' => '', 'sec' => ''),
''
),
// set and empty merdian
array(
array('hour' => '1', 'min' => '00', 'meridian' => ''),
''
),
// midnight
array(
array('hour' => '12', 'min' => '0', 'meridian' => 'am'),
'00:00:00'
),
array(
array('hour' => '00', 'min' => '00'),
'00:00:00'
),
// 3am
array(
array('hour' => '03', 'min' => '04', 'sec' => '04'),
'03:04:04'
),
array(
array('hour' => '3', 'min' => '4', 'sec' => '4'),
'03:04:04'
),
array(
array('hour' => '03', 'min' => '4', 'sec' => '4'),
'03:04:04'
),
array(
$now,
$now
)
);
}
/**
* test deconstruct with time fields.
*
* @dataProvider timeProvider
* @return void
*/
public function testDeconstructFieldsTime() {
public function testDeconstructFieldsTime($input, $result) {
$this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.');
$this->loadFixtures('Apple');
$TestModel = new Apple();
$data = array();
$data['Apple']['mytime']['hour'] = '';
$data['Apple']['mytime']['min'] = '';
$data['Apple']['mytime']['sec'] = '';
$data = array(
'Apple' => array(
'mytime' => $input
)
);
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => ''));
$expected = array('Apple' => array('mytime' => $result));
$this->assertEquals($TestModel->data, $expected);
$data = array();
$data['Apple']['mytime']['hour'] = '';
$data['Apple']['mytime']['min'] = '';
$data['Apple']['mytime']['meridan'] = '';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => ''));
$this->assertEquals($TestModel->data, $expected, 'Empty values are not returning properly. %s');
$data = array();
$data['Apple']['mytime']['hour'] = '12';
$data['Apple']['mytime']['min'] = '0';
$data['Apple']['mytime']['meridian'] = 'am';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => '00:00:00'));
$this->assertEquals($TestModel->data, $expected, 'Midnight is not returning proper values. %s');
$data = array();
$data['Apple']['mytime']['hour'] = '00';
$data['Apple']['mytime']['min'] = '00';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => '00:00:00'));
$this->assertEquals($TestModel->data, $expected, 'Midnight is not returning proper values. %s');
$data = array();
$data['Apple']['mytime']['hour'] = '03';
$data['Apple']['mytime']['min'] = '04';
$data['Apple']['mytime']['sec'] = '04';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => '03:04:04'));
$this->assertEquals($TestModel->data, $expected);
$data = array();
$data['Apple']['mytime']['hour'] = '3';
$data['Apple']['mytime']['min'] = '4';
$data['Apple']['mytime']['sec'] = '4';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => '03:04:04'));
$this->assertEquals($TestModel->data, $expected);
$data = array();
$data['Apple']['mytime']['hour'] = '03';
$data['Apple']['mytime']['min'] = '4';
$data['Apple']['mytime']['sec'] = '4';
$TestModel->data = null;
$TestModel->set($data);
$expected = array('Apple' => array('mytime' => '03:04:04'));
$this->assertEquals($TestModel->data, $expected);
$db = ConnectionManager::getDataSource('test');
$data = array();
$data['Apple']['mytime'] = $db->expression('NOW()');
$TestModel->data = null;
$TestModel->set($data);
$this->assertEquals($TestModel->data, $data);
}
/**

View file

@ -2987,13 +2987,21 @@ class ModelReadTest extends BaseModelTest {
$noAfterFindData = $noAfterFindModel->find('all');
$this->assertFalse($afterFindModel == $noAfterFindModel);
// Limitation of PHP 4 and PHP 5 > 5.1.6 when comparing recursive objects
if (PHP_VERSION === '5.1.6') {
$this->assertFalse($afterFindModel != $duplicateModel);
}
$this->assertEquals($afterFindData, $noAfterFindData);
}
/**
* find(threaded) should trigger errors whne there is no parent_id field.
*
* @expectedException PHPUnit_Framework_Error_Warning
* @return void
*/
public function testFindThreadedError() {
$this->loadFixtures('Apple', 'Sample');
$Apple = new Apple();
$Apple->find('threaded');
}
/**
* testFindAllThreaded method
*

View file

@ -2770,7 +2770,7 @@ class ModelWriteTest extends BaseModelTest {
$this->assertEquals(count($result['Tag']), 2);
$this->assertEquals($result['Tag'][0]['tag'], 'tag1');
$this->assertEquals(count($result['Comment']), 1);
$this->assertEquals(count($result['Comment'][0]['comment']['Article comment']), 1);
$this->assertEquals(count($result['Comment'][0]['comment']), 1);
}
/**
@ -4098,7 +4098,7 @@ class ModelWriteTest extends BaseModelTest {
$this->assertEquals(count($result['Tag']), 2);
$this->assertEquals($result['Tag'][0]['tag'], 'tag1');
$this->assertEquals(count($result['Comment']), 1);
$this->assertEquals(count($result['Comment'][0]['comment']['Article comment']), 1);
$this->assertEquals(count($result['Comment'][0]['comment']), 1);
}
/**

View file

@ -636,6 +636,10 @@ class CakeRequestTest extends CakeTestCase {
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 5.1; rv:2.0b6pre) Gecko/20100902 Firefox/4.0b6pre Fennec/2.0b1pre';
$this->assertTrue($request->is('mobile'));
$this->assertTrue($request->isMobile());
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; OMNIA7)';
$this->assertTrue($request->is('mobile'));
$this->assertTrue($request->isMobile());
}
/**

View file

@ -745,6 +745,136 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->send("Forgot to set To");
}
/**
* Test send() with no template.
*
* @return void
*/
public function testSendNoTemplateWithAttachments() {
$this->CakeEmail->transport('debug');
$this->CakeEmail->from('cake@cakephp.org');
$this->CakeEmail->to('cake@cakephp.org');
$this->CakeEmail->subject('My title');
$this->CakeEmail->emailFormat('text');
$this->CakeEmail->attachments(array(CAKE . 'basics.php'));
$result = $this->CakeEmail->send('Hello');
$boundary = $this->CakeEmail->getBoundary();
$this->assertContains('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
$expected = "--$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" .
"--$boundary\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n" .
"Content-Disposition: attachment; filename=\"basics.php\"\r\n\r\n";
$this->assertContains($expected, $result['message']);
}
/**
* Test send() with no template as both
*
* @return void
*/
public function testSendNoTemplateWithAttachmentsAsBoth() {
$this->CakeEmail->transport('debug');
$this->CakeEmail->from('cake@cakephp.org');
$this->CakeEmail->to('cake@cakephp.org');
$this->CakeEmail->subject('My title');
$this->CakeEmail->emailFormat('both');
$this->CakeEmail->attachments(array(CAKE . 'VERSION.txt'));
$result = $this->CakeEmail->send('Hello');
$boundary = $this->CakeEmail->getBoundary();
$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" .
"Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n\r\n";
$this->assertContains($expected, $result['message']);
}
/**
* Test setting inline attachments and messages.
*
* @return void
*/
public function testSendWithInlineAttachments() {
$this->CakeEmail->transport('debug');
$this->CakeEmail->from('cake@cakephp.org');
$this->CakeEmail->to('cake@cakephp.org');
$this->CakeEmail->subject('My title');
$this->CakeEmail->emailFormat('both');
$this->CakeEmail->attachments(array(
'cake.png' => array(
'file' => CAKE . 'VERSION.txt',
'contentId' => 'abc123'
)
));
$result = $this->CakeEmail->send('Hello');
$boundary = $this->CakeEmail->getBoundary();
$this->assertContains('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
$expected = "--$boundary\r\n" .
"Content-Type: multipart/related; boundary=\"rel-$boundary\"\r\n" .
"\r\n" .
"--rel-$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" .
"Content-ID: <abc123>\r\n" .
"Content-Disposition: inline; filename=\"cake.png\"\r\n\r\n";
$this->assertContains($expected, $result['message']);
$this->assertContains('--rel-' . $boundary . '--', $result['message']);
$this->assertContains('--' . $boundary . '--', $result['message']);
}
/**
* testSendWithLog method
*
@ -789,9 +919,9 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->template('default', 'default');
$result = $this->CakeEmail->send();
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the CakePHP Framework'));
$this->assertTrue((bool)strpos($result['headers'], 'Message-ID: '));
$this->assertTrue((bool)strpos($result['headers'], 'To: '));
$this->assertContains('This email was sent using the CakePHP Framework', $result['message']);
$this->assertContains('Message-ID: ', $result['headers']);
$this->assertContains('To: ', $result['headers']);
}
/**
@ -813,10 +943,10 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->charset = 'ISO-2022-JP';
$result = $this->CakeEmail->send();
$expected = mb_convert_encoding('CakePHP Framework を使って送信したメールです。 http://cakephp.org.','ISO-2022-JP');
$this->assertTrue((bool)strpos($result['message'], $expected));
$this->assertTrue((bool)strpos($result['headers'], 'Message-ID: '));
$this->assertTrue((bool)strpos($result['headers'], 'To: '));
$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']);
}
/**
@ -836,7 +966,7 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->viewVars(array('value' => 12345));
$result = $this->CakeEmail->send();
$this->assertTrue((bool)strpos($result['message'], 'Here is your value: 12345'));
$this->assertContains('Here is your value: 12345', $result['message']);
}
/**
@ -908,21 +1038,21 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->config(array('empty'));
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.'));
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the CakePHP Framework'));
$this->assertContains('Into TestPlugin.', $result['message']);
$this->assertContains('This email was sent using the CakePHP Framework', $result['message']);
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'TestPlugin.plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.'));
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.'));
$this->assertContains('Into TestPlugin.', $result['message']);
$this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.'));
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.'));
$this->assertContains('Into TestPlugin.', $result['message']);
$this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$this->CakeEmail->viewVars(array('value' => 12345));
$result = $this->CakeEmail->template('custom', 'TestPlugin.plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Here is your value: 12345'));
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.'));
$this->assertContains('Here is your value: 12345', $result['message']);
$this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$this->setExpectedException('MissingViewException');
$this->CakeEmail->template('test_plugin_tpl', 'plug_default')->send();
@ -949,10 +1079,10 @@ class CakeEmailTest extends CakeTestCase {
$message = $this->CakeEmail->message();
$boundary = $this->CakeEmail->getBoundary();
$this->assertFalse(empty($boundary));
$this->assertFalse(in_array('--' . $boundary, $message));
$this->assertFalse(in_array('--' . $boundary . '--', $message));
$this->assertTrue(in_array('--alt-' . $boundary, $message));
$this->assertTrue(in_array('--alt-' . $boundary . '--', $message));
$this->assertContains('--' . $boundary, $message);
$this->assertContains('--' . $boundary . '--', $message);
$this->assertContains('--alt-' . $boundary, $message);
$this->assertContains('--alt-' . $boundary . '--', $message);
$this->CakeEmail->attachments(array('fake.php' => __FILE__));
$this->CakeEmail->send();
@ -960,10 +1090,10 @@ class CakeEmailTest extends CakeTestCase {
$message = $this->CakeEmail->message();
$boundary = $this->CakeEmail->getBoundary();
$this->assertFalse(empty($boundary));
$this->assertTrue(in_array('--' . $boundary, $message));
$this->assertTrue(in_array('--' . $boundary . '--', $message));
$this->assertTrue(in_array('--alt-' . $boundary, $message));
$this->assertTrue(in_array('--alt-' . $boundary . '--', $message));
$this->assertContains('--' . $boundary, $message);
$this->assertContains('--' . $boundary . '--', $message);
$this->assertContains('--alt-' . $boundary, $message);
$this->assertContains('--alt-' . $boundary . '--', $message);
}
/**
@ -980,19 +1110,19 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->config(array());
$this->CakeEmail->attachments(array(CAKE . 'basics.php'));
$result = $this->CakeEmail->send('body');
$this->assertTrue((bool)strpos($result['message'], "Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"basics.php\""));
$this->assertContains("Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"basics.php\"", $result['message']);
$this->CakeEmail->attachments(array('my.file.txt' => CAKE . 'basics.php'));
$result = $this->CakeEmail->send('body');
$this->assertTrue((bool)strpos($result['message'], "Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"my.file.txt\""));
$this->assertContains("Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"my.file.txt\"", $result['message']);
$this->CakeEmail->attachments(array('file.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain')));
$result = $this->CakeEmail->send('body');
$this->assertTrue((bool)strpos($result['message'], "Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"file.txt\""));
$this->assertContains("Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"file.txt\"", $result['message']);
$this->CakeEmail->attachments(array('file2.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain', 'contentId' => 'a1b1c1')));
$result = $this->CakeEmail->send('body');
$this->assertTrue((bool)strpos($result['message'], "Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-ID: <a1b1c1>\r\nContent-Disposition: inline; filename=\"file2.txt\""));
$this->assertContains("Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-ID: <a1b1c1>\r\nContent-Disposition: inline; filename=\"file2.txt\"", $result['message']);
}
/**
@ -1047,24 +1177,23 @@ class CakeEmailTest extends CakeTestCase {
$result = $this->CakeEmail->send();
$expected = '<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>';
$this->assertTrue((bool)strpos($this->CakeEmail->message(CakeEmail::MESSAGE_HTML), $expected));
$this->assertContains($expected, $this->CakeEmail->message(CakeEmail::MESSAGE_HTML));
$expected = 'This email was sent using the CakePHP Framework, http://cakephp.org.';
$this->assertTrue((bool)strpos($this->CakeEmail->message(CakeEmail::MESSAGE_TEXT), $expected));
$this->assertContains($expected, $this->CakeEmail->message(CakeEmail::MESSAGE_TEXT));
$message = $this->CakeEmail->message();
$this->assertTrue(in_array('Content-Type: text/plain; charset=UTF-8', $message));
$this->assertTrue(in_array('Content-Type: text/html; charset=UTF-8', $message));
$this->assertContains('Content-Type: text/plain; charset=UTF-8', $message);
$this->assertContains('Content-Type: text/html; charset=UTF-8', $message);
// UTF-8 is 8bit
$this->assertTrue($this->checkContentTransferEncoding($message, '8bit'));
$this->CakeEmail->charset = 'ISO-2022-JP';
$this->CakeEmail->send();
$message = $this->CakeEmail->message();
$this->assertTrue(in_array('Content-Type: text/plain; charset=ISO-2022-JP', $message));
$this->assertTrue(in_array('Content-Type: text/html; charset=ISO-2022-JP', $message));
$this->assertContains('Content-Type: text/plain; charset=ISO-2022-JP', $message);
$this->assertContains('Content-Type: text/html; charset=ISO-2022-JP', $message);
// ISO-2022-JP is 7bit
$this->assertTrue($this->checkContentTransferEncoding($message, '7bit'));

View file

@ -214,6 +214,7 @@ class SmtpTransportTest extends CakeTestCase {
$this->getMock('CakeEmail', array('message'), array(), 'SmtpCakeEmail');
$email = new SmtpCakeEmail();
$email->from('noreply@cakephp.org', 'CakePHP Test');
$email->returnPath('pleasereply@cakephp.org', 'CakePHP Return');
$email->to('cake@cakephp.org', 'CakePHP');
$email->cc(array('mark@cakephp.org' => 'Mark Story', 'juan@cakephp.org' => 'Juan Basso'));
$email->bcc('phpnut@cakephp.org');
@ -222,6 +223,7 @@ class SmtpTransportTest extends CakeTestCase {
$email->expects($this->any())->method('message')->will($this->returnValue(array('First Line', 'Second Line', '')));
$data = "From: CakePHP Test <noreply@cakephp.org>\r\n";
$data .= "Return-Path: CakePHP Return <pleasereply@cakephp.org>\r\n";
$data .= "To: CakePHP <cake@cakephp.org>\r\n";
$data .= "Cc: Mark Story <mark@cakephp.org>, Juan Basso <juan@cakephp.org>\r\n";
$data .= "X-Mailer: CakePHP Email\r\n";

View file

@ -2454,6 +2454,37 @@ class RouterTest extends CakeTestCase {
$this->assertFalse($result);
}
/**
* Tests resourceMap as getter and setter.
*
* @return void
*/
public function testResourceMap() {
$default = Router::resourceMap();
$exepcted = array(
array('action' => 'index', 'method' => 'GET', 'id' => false),
array('action' => 'view', 'method' => 'GET', 'id' => true),
array('action' => 'add', 'method' => 'POST', 'id' => false),
array('action' => 'edit', 'method' => 'PUT', 'id' => true),
array('action' => 'delete', 'method' => 'DELETE', 'id' => true),
array('action' => 'edit', 'method' => 'POST', 'id' => true)
);
$this->assertEquals($default, $exepcted);
$custom = array(
array('action' => 'index', 'method' => 'GET', 'id' => false),
array('action' => 'view', 'method' => 'GET', 'id' => true),
array('action' => 'add', 'method' => 'POST', 'id' => false),
array('action' => 'edit', 'method' => 'PUT', 'id' => true),
array('action' => 'delete', 'method' => 'DELETE', 'id' => true),
array('action' => 'update', 'method' => 'POST', 'id' => true)
);
Router::resourceMap($custom);
$this->assertEquals($custom, Router::resourceMap());
Router::resourceMap($default);
}
/**
* test setting redirect routes
*

View file

@ -1562,6 +1562,10 @@ class HtmlHelperTest extends CakeTestCase {
}
$this->assertEquals($helper->parseAttributes(array('compact')), ' compact="compact"');
$attrs = array('class' => array('foo', 'bar'));
$expected = ' class="foo bar"';
$this->assertEquals(' class="foo bar"', $helper->parseAttributes($attrs));
$helper = new Html5TestHelper($this->View);
$expected = ' require';
$this->assertEquals($helper->parseAttributes(array('require')), $expected);

View file

@ -60,7 +60,7 @@ class TextHelperTest extends CakeTestCase {
$text5 = '0<b>1<i>2<span class="myclass">3</span>4<u>5</u>6</i>7</b>8<b>9</b>0';
$text6 = '<p><strong>Extra dates have been announced for this year\'s tour.</strong></p><p>Tickets for the new shows in</p>';
$text7 = 'El moño está en el lugar correcto. Eso fue lo que dijo la niña, ¿habrá dicho la verdad?';
$text8 = 'Vive la R'.chr(195).chr(169).'publique de France';
$text8 = 'Vive la R' . chr(195) . chr(169) . 'publique de France';
$text9 = 'НОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
$this->assertSame($this->Text->truncate($text1, 15), 'The quick br...');
@ -86,6 +86,51 @@ class TextHelperTest extends CakeTestCase {
$this->assertSame($this->Text->truncate($text7, 15), 'El moño está...');
$this->assertSame($this->Text->truncate($text8, 15), 'Vive la R'.chr(195).chr(169).'pu...');
$this->assertSame($this->Text->truncate($text9, 10), 'НОПРСТУ...');
$text = '<p><span style="font-size: medium;"><a>Iamatestwithnospacesandhtml</a></span></p>';
$result = $this->Text->truncate($text, 10, array(
'ending' => '...',
'exact' => false,
'html' => true
));
$expected = '<p><span style="font-size: medium;"><a>...</a></span></p>';
$this->assertEquals($expected, $result);
$text = '<p><span style="font-size: medium;">El biógrafo de Steve Jobs, Walter
Isaacson, explica porqué Jobs le pidió que le hiciera su biografía en
este artículo de El País.</span></p>
<p><span style="font-size: medium;"><span style="font-size:
large;">Por qué Steve era distinto.</span></span></p>
<p><span style="font-size: medium;"><a href="http://www.elpais.com/
articulo/primer/plano/Steve/era/distinto/elpepueconeg/
20111009elpneglse_4/Tes">http://www.elpais.com/articulo/primer/plano/
Steve/era/distinto/elpepueconeg/20111009elpneglse_4/Tes</a></span></p>
<p><span style="font-size: medium;">Ya se ha publicado la biografía de
Steve Jobs escrita por Walter Isaacson "<strong>Steve Jobs by Walter
Isaacson</strong>", aquí os dejamos la dirección de amazon donde
podeís adquirirla.</span></p>
<p><span style="font-size: medium;"><a>http://www.amazon.com/Steve-
Jobs-Walter-Isaacson/dp/1451648537</a></span></p>';
$result = $this->Text->truncate($text, 500, array(
'ending' => '... ',
'exact' => false,
'html' => true
));
$expected = '<p><span style="font-size: medium;">El biógrafo de Steve Jobs, Walter
Isaacson, explica porqué Jobs le pidió que le hiciera su biografía en
este artículo de El País.</span></p>
<p><span style="font-size: medium;"><span style="font-size:
large;">Por qué Steve era distinto.</span></span></p>
<p><span style="font-size: medium;"><a href="http://www.elpais.com/
articulo/primer/plano/Steve/era/distinto/elpepueconeg/
20111009elpneglse_4/Tes">http://www.elpais.com/articulo/primer/plano/
Steve/era/distinto/elpepueconeg/20111009elpneglse_4/Tes</a></span></p>
<p><span style="font-size: medium;">Ya se ha publicado la biografía de
Steve Jobs escrita por Walter Isaacson "<strong>Steve Jobs by Walter
Isaacson</strong>", aquí os dejamos la dirección de amazon donde
podeís adquirirla.</span></p>
<p><span style="font-size: medium;"><a>... </a></span></p>';
$this->assertEquals($expected, $result);
}
/**

View file

@ -671,7 +671,10 @@ class Debugger {
$data += $defaults;
$files = $this->trace(array('start' => $data['start'], 'format' => 'points'));
$code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
$code = '';
if (isset($files[0]['file'])) {
$code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
}
$trace = $this->trace(array('start' => $data['start'], 'depth' => '20'));
$insertOpts = array('before' => '{:', 'after' => '}');
$context = array();

View file

@ -109,7 +109,8 @@ abstract class ObjectCollection {
$options[$opt] = $event->{$opt};
}
}
$callback = array_pop(explode('.', $event->name()));
$parts = explode('.', $event->name());
$callback = array_pop($parts);
}
$options = array_merge(
array(

View file

@ -671,7 +671,7 @@ class Set {
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
$key = intval($key);
}
if ($i === $count - 1) {
if ($i === $count - 1 && is_array($_list)) {
$_list[$key] = $data;
} else {
if (!isset($_list[$key])) {
@ -679,6 +679,9 @@ class Set {
}
$_list =& $_list[$key];
}
if (!is_array($_list)) {
return array();
}
}
return $list;
}

View file

@ -356,7 +356,7 @@ class Helper extends Object {
* @param string $insertBefore String to be inserted before options.
* @param string $insertAfter String to be inserted after options.
* @return string Composed attributes.
* @deprecated This method has been moved to HtmlHelper
* @deprecated This method will be moved to HtmlHelper in 3.0
*/
protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
if (!is_string($options)) {
@ -390,12 +390,12 @@ class Helper extends Object {
* @param string $value The value of the attribute to create.
* @param boolean $escape Define if the value must be escaped
* @return string The composed attribute.
* @deprecated This method has been moved to HtmlHelper
* @deprecated This method will be moved to HtmlHelper in 3.0
*/
protected function _formatAttribute($key, $value, $escape = true) {
$attribute = '';
if (is_array($value)) {
$value = '';
$value = implode(' ' , $value);
}
if (is_numeric($key)) {
@ -672,9 +672,6 @@ class Helper extends Object {
$options = $this->_name($options);
$options = $this->value($options);
$options = $this->domId($options);
if ($this->tagIsInvalid() !== false) {
$options = $this->addClass($options, 'form-error');
}
return $options;
}

View file

@ -737,8 +737,48 @@ class FormHelper extends AppHelper {
* Returns a formatted LABEL element for HTML FORMs. Will automatically generate
* a for attribute if one is not provided.
*
* ### Options
*
* - `for` - Set the for attribute, if its not defined the for attribute
* will be generated from the $fieldName parameter using
* FormHelper::domId().
*
* Examples:
*
* The text and for attribute are generated off of the fieldname
*
* {{{
* echo $this->Form->label('Post.published');
* <label for="PostPublished">Published</label>
* }}}
*
* Custom text:
*
* {{{
* echo $this->Form->label('Post.published', 'Publish');
* <label for="PostPublished">Publish</label>
* }}}
*
* Custom class name:
*
* {{{
* echo $this->Form->label('Post.published', 'Publish', 'required');
* <label for="PostPublished" class="required">Publish</label>
* }}}
*
* Custom attributes:
*
* {{{
* echo $this->Form->label('Post.published', 'Publish', array(
* 'for' => 'post-publish'
* ));
* <label for="post-publish">Publish</label>
* }}}
*
* @param string $fieldName This should be "Modelname.fieldname"
* @param string $text Text that will appear in the label field.
* @param string $text Text that will appear in the label field. If
* $text is left undefined the text will be inflected from the
* fieldName.
* @param mixed $options An array of HTML attributes, or a string, to be used as a class name.
* @return string The formatted LABEL element
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::label
@ -2534,6 +2574,9 @@ class FormHelper extends AppHelper {
}
$result = parent::_initInputField($field, $options);
if ($this->tagIsInvalid() !== false) {
$result = $this->addClass($result, 'form-error');
}
if (!empty($result['disabled']) || $secure === self::SECURE_SKIP) {
return $result;
}

View file

@ -1071,93 +1071,4 @@ class HtmlHelper extends AppHelper {
return $configs;
}
/**
* Returns a space-delimited string with items of the $options array. If a
* key of $options array happens to be one of:
*
* - 'compact'
* - 'checked'
* - 'declare'
* - 'readonly'
* - 'disabled'
* - 'selected'
* - 'defer'
* - 'ismap'
* - 'nohref'
* - 'noshade'
* - 'nowrap'
* - 'multiple'
* - 'noresize'
*
* And its value is one of:
*
* - '1' (string)
* - 1 (integer)
* - true (boolean)
* - 'true' (string)
*
* Then the value will be reset to be identical with key's name.
* If the value is not one of these 3, the parameter is not output.
*
* 'escape' is a special option in that it controls the conversion of
* attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
*
* If value for any option key is set to `null` or `false`, that option will be excluded from output.
*
* @param array $options Array of options.
* @param array $exclude Array of options to be excluded, the options here will not be part of the return.
* @param string $insertBefore String to be inserted before options.
* @param string $insertAfter String to be inserted after options.
* @return string Composed attributes.
*/
protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
if (is_array($options)) {
$options = array_merge(array('escape' => true), $options);
if (!is_array($exclude)) {
$exclude = array();
}
$filtered = array_diff_key($options, array_merge(array_flip($exclude), array('escape' => true)));
$escape = $options['escape'];
$attributes = array();
foreach ($filtered as $key => $value) {
if ($value !== false && $value !== null) {
$attributes[] = $this->_formatAttribute($key, $value, $escape);
}
}
$out = implode(' ', $attributes);
} else {
$out = $options;
}
return $out ? $insertBefore . $out . $insertAfter : '';
}
/**
* Formats an individual attribute, and returns the string value of the composed attribute.
* Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
*
* @param string $key The name of the attribute to create
* @param string $value The value of the attribute to create.
* @param boolean $escape Define if the value must be escaped
* @return string The composed attribute.
*/
protected function _formatAttribute($key, $value, $escape = true) {
$attribute = '';
if (is_array($value)) {
$value = '';
}
if (is_numeric($key)) {
$attribute = sprintf($this->_minimizedAttributeFormat, $value, $value);
} elseif (in_array($key, $this->_minimizedAttributes)) {
if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
$attribute = sprintf($this->_minimizedAttributeFormat, $key, $key);
}
} else {
$attribute = sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
}
return $attribute;
}
}

View file

@ -323,20 +323,32 @@ class TextHelper extends AppHelper {
}
if (!$exact) {
$spacepos = mb_strrpos($truncate, ' ');
if (isset($spacepos)) {
if ($html) {
$bits = mb_substr($truncate, $spacepos);
preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
if (!empty($droppedTags)) {
if ($html) {
$truncateCheck = mb_substr($truncate, 0, $spacepos);
$lastOpenTag = mb_strrpos($truncateCheck, '<');
$lastCloseTag = mb_strrpos($truncateCheck, '>');
if ($lastOpenTag > $lastCloseTag) {
preg_match_all('/<[\w]+[^>]*>/s', $truncate, $lastTagMatches);
$lastTag = array_pop($lastTagMatches[0]);
$spacepos = mb_strrpos($truncate, $lastTag) + mb_strlen($lastTag);
}
$bits = mb_substr($truncate, $spacepos);
preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
if (!empty($droppedTags)) {
if (!empty($openTags)) {
foreach ($droppedTags as $closingTag) {
if (!in_array($closingTag[1], $openTags)) {
array_unshift($openTags, $closingTag[1]);
}
}
} else {
foreach ($droppedTags as $closingTag) {
array_push($openTags, $closingTag[1]);
}
}
}
$truncate = mb_substr($truncate, 0, $spacepos);
}
$truncate = mb_substr($truncate, 0, $spacepos);
}
$truncate .= $ending;

View file

@ -465,7 +465,7 @@ class View extends Object {
* the 'meta', 'css', and 'script' blocks. They are appended in that order.
*
* Deprecated features:
*
*
* - `$scripts_for_layout` is deprecated and will be removed in CakePHP 3.0.
* Use the block features instead. `meta`, `css` and `script` will be populated
* by the matching methods on HtmlHelper.
@ -493,7 +493,7 @@ class View extends Object {
$this->getEventManager()->dispatch(new CakeEvent('View.beforeLayout', $this, array($layoutFileName)));
$scripts = implode("\n\t", $this->_scripts);
$scripts .= $this->get('meta') . $this->get('css') . $this->get('script');
$scripts .= $this->Blocks->get('meta') . $this->Blocks->get('css') . $this->Blocks->get('script');
$this->viewVars = array_merge($this->viewVars, array(
'content_for_layout' => $content,
@ -600,7 +600,7 @@ class View extends Object {
}
/**
* Append to an existing or new block. Appending to a new
* Append to an existing or new block. Appending to a new
* block will create the block.
*
* @param string $name Name of the block
@ -650,7 +650,7 @@ class View extends Object {
}
/**
* Provides view or element extension/inheritance. Views can extends a
* Provides view or element extension/inheritance. Views can extends a
* parent view and populate blocks in the parent template.
*
* @param string $name The view or element to 'extend' the current one with.
@ -668,7 +668,7 @@ class View extends Object {
case self::TYPE_LAYOUT:
$parent = $this->_getLayoutFileName($name);
break;
}
if ($parent == $this->_current) {
throw new LogicException(__d('cake_dev', 'You cannot have views extend themselves.'));
@ -775,7 +775,7 @@ class View extends Object {
/**
* Magic accessor for deprecated attributes.
*
*
* @param string $name Name of the attribute to set.
* @param string $value Value of the attribute to set.
* @return mixed
@ -789,6 +789,16 @@ class View extends Object {
}
}
/**
* Magic isset check for deprecated attributes.
*
* @param string $name Name of the attribute to check.
* @return boolean
*/
public function __isset($name) {
return isset($this->name);
}
/**
* Interact with the HelperCollection to load all the helpers.
*
@ -817,12 +827,10 @@ class View extends Object {
$data = $this->viewVars;
}
$this->_current = $viewFile;
$initialBlocks = count($this->Blocks->unclosed());
$this->getEventManager()->dispatch(new CakeEvent('View.beforeRenderFile', $this, array($viewFile)));
$content = $this->_evaluate($viewFile, $data);
if ($this->Blocks->active()) {
throw new CakeException(__d('cake_dev', 'The "%s" block was left open.', $this->Blocks->active()));
}
$afterEvent = new CakeEvent('View.afterRenderFile', $this, array($viewFile, $content));
//TODO: For BC puporses, set extra info in the event object. Remove when appropriate
$afterEvent->modParams = 1;
@ -833,11 +841,16 @@ class View extends Object {
$this->_stack[] = $this->fetch('content');
$this->assign('content', $content);
$content = $this->_render($this->_parents[$viewFile], $data);
$content = $this->_render($this->_parents[$viewFile]);
$this->assign('content', array_pop($this->_stack));
}
$remainingBlocks = count($this->Blocks->unclosed());
if ($initialBlocks !== $remainingBlocks) {
throw new CakeException(__d('cake_dev', 'The "%s" block was left open. Blocks are not allowed to cross files.', $this->Blocks->active()));
}
return $content;
}
@ -845,7 +858,7 @@ class View extends Object {
* Sandbox method to evaluate a template / view script in.
*
* @param string $___viewFn Filename of the view
* @param array $___dataForView Data to include in rendered view.
* @param array $___dataForView Data to include in rendered view.
* If empty the current View::$viewVars will be used.
* @return string Rendered output
*/

View file

@ -145,4 +145,13 @@ class ViewBlock {
public function active() {
return end($this->_active);
}
/**
* Get the names of the unclosed/active blocks.
*
* @return array An array of unclosed blocks.
*/
public function unclosed() {
return $this->_active;
}
}