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

View file

@ -24,7 +24,7 @@ App::uses('Debugger', 'Utility');
<p>For updates and important announcements, visit http://cakefest.org</p> <p>For updates and important announcements, visit http://cakefest.org</p>
</iframe> </iframe>
<h2><?php echo __d('cake_dev', 'Release Notes for CakePHP %s.', Configure::version()); ?></h2> <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 <?php
if (Configure::read('debug') > 0): if (Configure::read('debug') > 0):
Debugger::checkSecurityKeys(); Debugger::checkSecurityKeys();

View file

@ -17,4 +17,4 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
$versionFile = file(CAKE . 'VERSION.txt'); $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 . '.'; $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)) { if (is_subclass_of($this, $this->_mergeParent) || !empty($pluginController)) {
$appVars = get_class_vars($this->_mergeParent); $appVars = get_class_vars($this->_mergeParent);
$uses = $appVars['uses']; $uses = $appVars['uses'];
@ -567,18 +579,6 @@ class Controller extends Object implements CakeEventListener {
} }
$this->_mergeVars($merge, $this->_mergeParent, true); $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 $type type of path
* @param string $plugin name of plugin * @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 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::path
*/ */
public static function path($type, $plugin = null) { 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` * `App::core('Cache/Engine'); will return the full path to the cache engines package`
* *
* @param string $type * @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 * @link http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::core
*/ */
public static function core($type) { public static function core($type) {
@ -580,7 +580,7 @@ class App {
* based on Inflector::underscore($name) . ".$ext"; * based on Inflector::underscore($name) . ".$ext";
* @param array $search paths to search for files, array('path 1', 'path 2', 'path 3'); * @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 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; * 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 * @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) { public function dispatch($event) {
if (is_string($event)) { if (is_string($event)) {
$Event = new CakeEvent($event); $event = new CakeEvent($event);
} }
if (!$this->_isGlobal) { if (!$this->_isGlobal) {

View file

@ -185,6 +185,18 @@ class I18n {
if (is_array($trans)) { if (is_array($trans)) {
if (isset($trans[$plurals])) { if (isset($trans[$plurals])) {
$trans = $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)) { if (strlen($trans)) {

View file

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

View file

@ -369,7 +369,10 @@ class TranslateBehavior extends ModelBehavior {
/** /**
* Bind translation for fields, optionally with hasMany association for * 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 Model $model instance of model
* @param string|array $fields string with field or array(field1, field2=>AssocName, field3) * @param string|array $fields string with field or array(field1, field2=>AssocName, field3)
@ -392,6 +395,11 @@ class TranslateBehavior extends ModelBehavior {
$field = $key; $field = $key;
$association = $value; $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])) { if (array_key_exists($field, $this->settings[$model->alias])) {
unset($this->settings[$model->alias][$field]); unset($this->settings[$model->alias][$field]);

View file

@ -70,13 +70,6 @@ class CakeSession {
*/ */
public static $lastError = null; public static $lastError = null;
/**
* 'Security.level' setting, "high", "medium", or "low".
*
* @var string
*/
public static $security = null;
/** /**
* Start time for this session. * 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. * 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 * @var integer
* @see CakeSession::_checkValid() * @see CakeSession::_checkValid()

View file

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

View file

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

View file

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

View file

@ -1203,7 +1203,17 @@ class Model extends Object implements CakeEventListener {
$timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec'); $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');
$date = array(); $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; $data['hour'] = $data['hour'] + 12;
} }
if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) { 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) { foreach ($timeFields as $key => $val) {
if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
$data[$val] = '00'; $data[$val] = '00';
} elseif ($data[$val] === '') { } elseif ($data[$val] !== '') {
$data[$val] = '';
} else {
$data[$val] = sprintf('%02d', $data[$val]); $data[$val] = sprintf('%02d', $data[$val]);
} }
if (!empty($data[$val])) { if (!empty($data[$val])) {
@ -2810,6 +2818,13 @@ class Model extends Object implements CakeEventListener {
foreach ($results as $result) { foreach ($results as $result) {
$result['children'] = array(); $result['children'] = array();
$id = $result[$this->alias][$this->primaryKey]; $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']; $parentId = $result[$this->alias]['parent_id'];
if (isset($idMap[$id]['children'])) { if (isset($idMap[$id]['children'])) {
$idMap[$id] = array_merge($result, (array)$idMap[$id]); $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', 'Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone',
'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource', 'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource',
'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser', 'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser',
'webOS', 'Windows CE', 'Xiino' 'webOS', 'Windows CE', 'Windows Phone OS', 'Xiino'
)), )),
'requested' => array('param' => 'requested', 'value' => 1) 'requested' => array('param' => 'requested', 'value' => 1)
); );

View file

@ -662,14 +662,12 @@ class CakeEmail {
} }
$headers['MIME-Version'] = '1.0'; $headers['MIME-Version'] = '1.0';
if (!empty($this->_attachments)) { if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
$headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"'; $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
} elseif ($this->_emailFormat === 'text') { } elseif ($this->_emailFormat === 'text') {
$headers['Content-Type'] = 'text/plain; charset=' . $this->charset; $headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
} elseif ($this->_emailFormat === 'html') { } elseif ($this->_emailFormat === 'html') {
$headers['Content-Type'] = 'text/html; charset=' . $this->charset; $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(); $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 * @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 * @throws SocketException
*/ */
public function attachments($attachments = null) { public function attachments($attachments = null) {
@ -936,34 +961,9 @@ 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);
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); $contents = $this->transportClass()->send($this);
if (!empty($this->_config['log'])) { if (!empty($this->_config['log'])) {
$level = LOG_DEBUG; $level = LOG_DEBUG;
@ -1213,6 +1213,7 @@ class CakeEmail {
} }
$formatted[] = trim(substr($tmpLine, 0, $lastSpace)); $formatted[] = trim(substr($tmpLine, 0, $lastSpace));
$tmpLine = substr($tmpLine, $lastSpace + 1); $tmpLine = substr($tmpLine, $lastSpace + 1);
$tmpLineLength = strlen($tmpLine); $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() { 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 = $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;
}
/**
* Read the file contents and return a base64 version of the file contents.
*
* @param string $file The file to read.
* @return string File contents in base64 encoding
*/
protected function _readFile($file) {
$handle = fopen($file, 'rb');
$data = fread($handle, filesize($file));
$data = chunk_split(base64_encode($data)) ; $data = chunk_split(base64_encode($data)) ;
fclose($handle); fclose($handle);
return $data;
$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[] = '';
$this->_message[] = $data;
$this->_message[] = '';
}
} }
/** /**
* Format the message by seeing if it has attachments. * Attach inline/embedded files to the message.
* *
* @param array $message Message to format * @return array An array of lines to add to the message
* @return array
*/ */
protected function _formatMessage($message) { protected function _attachInlineFiles() {
if (!empty($this->_attachments)) { $msg = array();
$prefix = array('--' . $this->_boundary); foreach ($this->_attachments as $filename => $fileInfo) {
if ($this->_emailFormat === 'text') { if (empty($fileInfo['contentId'])) {
$prefix[] = 'Content-Type: text/plain; charset=' . $this->charset; continue;
} 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);
} }
$data = $this->_readFile($fileInfo['file']);
$tmp = array(); $msg[] = '--' . $this->_boundary;
foreach ($message as $msg) { $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
$tmp[] = $this->_encodeString($msg, $this->charset); $msg[] = 'Content-Transfer-Encoding: base64';
$msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
$msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
$msg[] = '';
$msg[] = $data;
$msg[] = '';
} }
$message = $tmp; return $msg;
return $message;
} }
/** /**
* Render the contents using the current layout and template. * 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) {
$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') { if ($viewClass !== 'View') {
list($plugin, $viewClass) = pluginSplit($viewClass, true); list($plugin, $viewClass) = pluginSplit($viewClass, true);
$viewClass .= 'View'; $viewClass .= 'View';
@ -1311,7 +1424,6 @@ class CakeEmail {
$View = new $viewClass(null); $View = new $viewClass(null);
$View->viewVars = $this->_viewVars; $View->viewVars = $this->_viewVars;
$View->helpers = $this->_helpers; $View->helpers = $this->_helpers;
$msg = array();
list($templatePlugin, $template) = pluginSplit($this->_template); list($templatePlugin, $template) = pluginSplit($this->_template);
list($layoutPlugin, $layout) = pluginSplit($this->_layout); list($layoutPlugin, $layout) = pluginSplit($this->_layout);
@ -1321,73 +1433,16 @@ class CakeEmail {
$View->plugin = $layoutPlugin; $View->plugin = $layoutPlugin;
} }
$content = implode("\n", $content); foreach ($types as $type) {
$View->set('content', $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;
$View->hasRendered = false; $View->hasRendered = false;
$this->_htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->render($template, $layout)); $View->viewPath = $View->layoutPath = 'Emails' . DS . $type;
$content = explode("\n", $this->_htmlMessage);
$msg = array_merge($msg, $content);
$msg[] = ''; $render = $View->render($template, $layout);
$msg[] = '--alt-' . $this->_boundary . '--'; $render = str_replace(array("\r\n", "\r"), "\n", $render);
$msg[] = ''; $rendered[$type] = $this->_encodeString($render, $this->charset);
return $msg;
} }
return $rendered;
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);
} }
/** /**

View file

@ -174,6 +174,20 @@ class Router {
return self::$_namedExpressions; 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. * Connects a new Route in the router.
* *

View file

@ -228,12 +228,6 @@ MSGBLOC;
$expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message); $expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message);
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertEquals(DebugCompTransport::$lastEmail, $this->__osFix($expect)); $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->assertEquals(DebugCompTransport::$lastEmail, $this->__osFix($expect));
$this->Controller->EmailTest->sendAs = 'both'; $this->Controller->EmailTest->sendAs = 'both';
$expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-"', $header); $expect = str_replace('{CONTENTTYPE}', 'multipart/mixed; boundary="{boundary}"', $header);
$expect .= '--alt-' . "\n" . 'Content-Type: text/plain; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 8bit' . "\n\n" . $text . "\n\n"; $expect .= "--{boundary}\n" .
$expect .= '--alt-' . "\n" . 'Content-Type: text/html; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 8bit' . "\n\n" . $html . "\n\n"; 'Content-Type: multipart/alternative; boundary="alt-{boundary}"' . "\n\n" .
$expect = '<pre>' . $expect . '--alt---' . "\n\n" . '</pre>'; '--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->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 $html = <<<HTMLBLOC
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
@ -441,7 +451,7 @@ HTMLBLOC;
$this->Controller->EmailTest->delivery = 'DebugComp'; $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->Controller->EmailTest->sendAs = 'both';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $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)); $this->assertTrue($this->Controller->EmailTest->send($body));
$msg = DebugCompTransport::$lastEmail; $msg = DebugCompTransport::$lastEmail;
$this->assertNotRegExp('/text\/plain/', $msg); $this->assertRegExp('/text\/plain/', $msg);
$this->assertNotRegExp('/text\/html/', $msg); $this->assertRegExp('/text\/html/', $msg);
$this->assertRegExp('/multipart\/alternative/', $msg); $this->assertRegExp('/multipart\/alternative/', $msg);
} }

View file

@ -210,6 +210,22 @@ class CakeEventManagerTest extends CakeTestCase {
$manager->dispatch($event); $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 * Tests event dispatching with a return value
* *

View file

@ -868,4 +868,16 @@ class TranslateBehaviorTest extends CakeTestCase {
$this->assertFalse($result); $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() { function testVirtualFieldAsAConstant() {
$this->loadFixtures('Article', 'Comment'); $this->loadFixtures('Article', 'Comment');
$Article =& ClassRegistry::init('Article'); $Article = ClassRegistry::init('Article');
$Article->virtualFields = array( $Article->virtualFields = array(
'empty' => "NULL", 'empty' => "NULL",
'number' => 43, 'number' => 43,

View file

@ -708,6 +708,22 @@ class DboSourceTest extends CakeTestCase {
$Article->tablePrefix = ''; $Article->tablePrefix = '';
$result = $this->testDb->fullTableName($Article, true, false); $result = $this->testDb->fullTableName($Article, true, false);
$this->assertEquals($result, '`with spaces`'); $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 * @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->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.');
$this->loadFixtures('Apple'); $this->loadFixtures('Apple');
$TestModel = new Apple(); $TestModel = new Apple();
$data = array(); $data = array(
$data['Apple']['mytime']['hour'] = ''; 'Apple' => array(
$data['Apple']['mytime']['min'] = ''; 'mytime' => $input
$data['Apple']['mytime']['sec'] = ''; )
);
$TestModel->data = null; $TestModel->data = null;
$TestModel->set($data); $TestModel->set($data);
$expected = array('Apple' => array('mytime' => '')); $expected = array('Apple' => array('mytime' => $result));
$this->assertEquals($TestModel->data, $expected); $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'); $noAfterFindData = $noAfterFindModel->find('all');
$this->assertFalse($afterFindModel == $noAfterFindModel); $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); $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 * testFindAllThreaded method
* *

View file

@ -2770,7 +2770,7 @@ class ModelWriteTest extends BaseModelTest {
$this->assertEquals(count($result['Tag']), 2); $this->assertEquals(count($result['Tag']), 2);
$this->assertEquals($result['Tag'][0]['tag'], 'tag1'); $this->assertEquals($result['Tag'][0]['tag'], 'tag1');
$this->assertEquals(count($result['Comment']), 1); $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(count($result['Tag']), 2);
$this->assertEquals($result['Tag'][0]['tag'], 'tag1'); $this->assertEquals($result['Tag'][0]['tag'], 'tag1');
$this->assertEquals(count($result['Comment']), 1); $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'; $_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->is('mobile'));
$this->assertTrue($request->isMobile()); $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"); $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 * testSendWithLog method
* *
@ -789,9 +919,9 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->template('default', 'default'); $this->CakeEmail->template('default', 'default');
$result = $this->CakeEmail->send(); $result = $this->CakeEmail->send();
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the CakePHP Framework')); $this->assertContains('This email was sent using the CakePHP Framework', $result['message']);
$this->assertTrue((bool)strpos($result['headers'], 'Message-ID: ')); $this->assertContains('Message-ID: ', $result['headers']);
$this->assertTrue((bool)strpos($result['headers'], 'To: ')); $this->assertContains('To: ', $result['headers']);
} }
/** /**
@ -814,9 +944,9 @@ class CakeEmailTest extends CakeTestCase {
$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->assertTrue((bool)strpos($result['message'], $expected)); $this->assertContains($expected, $result['message']);
$this->assertTrue((bool)strpos($result['headers'], 'Message-ID: ')); $this->assertContains('Message-ID: ', $result['headers']);
$this->assertTrue((bool)strpos($result['headers'], 'To: ')); $this->assertContains('To: ', $result['headers']);
} }
/** /**
@ -836,7 +966,7 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->viewVars(array('value' => 12345)); $this->CakeEmail->viewVars(array('value' => 12345));
$result = $this->CakeEmail->send(); $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')); $this->CakeEmail->config(array('empty'));
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'default')->send(); $result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.')); $this->assertContains('Into TestPlugin.', $result['message']);
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the CakePHP Framework')); $this->assertContains('This email was sent using the CakePHP Framework', $result['message']);
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'TestPlugin.plug_default')->send(); $result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'TestPlugin.plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.')); $this->assertContains('Into TestPlugin.', $result['message']);
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.')); $this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'plug_default')->send(); $result = $this->CakeEmail->template('TestPlugin.test_plugin_tpl', 'plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Into TestPlugin.')); $this->assertContains('Into TestPlugin.', $result['message']);
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.')); $this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$this->CakeEmail->viewVars(array('value' => 12345)); $this->CakeEmail->viewVars(array('value' => 12345));
$result = $this->CakeEmail->template('custom', 'TestPlugin.plug_default')->send(); $result = $this->CakeEmail->template('custom', 'TestPlugin.plug_default')->send();
$this->assertTrue((bool)strpos($result['message'], 'Here is your value: 12345')); $this->assertContains('Here is your value: 12345', $result['message']);
$this->assertTrue((bool)strpos($result['message'], 'This email was sent using the TestPlugin.')); $this->assertContains('This email was sent using the TestPlugin.', $result['message']);
$this->setExpectedException('MissingViewException'); $this->setExpectedException('MissingViewException');
$this->CakeEmail->template('test_plugin_tpl', 'plug_default')->send(); $this->CakeEmail->template('test_plugin_tpl', 'plug_default')->send();
@ -949,10 +1079,10 @@ 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->assertFalse(in_array('--' . $boundary, $message)); $this->assertContains('--' . $boundary, $message);
$this->assertFalse(in_array('--' . $boundary . '--', $message)); $this->assertContains('--' . $boundary . '--', $message);
$this->assertTrue(in_array('--alt-' . $boundary, $message)); $this->assertContains('--alt-' . $boundary, $message);
$this->assertTrue(in_array('--alt-' . $boundary . '--', $message)); $this->assertContains('--alt-' . $boundary . '--', $message);
$this->CakeEmail->attachments(array('fake.php' => __FILE__)); $this->CakeEmail->attachments(array('fake.php' => __FILE__));
$this->CakeEmail->send(); $this->CakeEmail->send();
@ -960,10 +1090,10 @@ 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->assertTrue(in_array('--' . $boundary, $message)); $this->assertContains('--' . $boundary, $message);
$this->assertTrue(in_array('--' . $boundary . '--', $message)); $this->assertContains('--' . $boundary . '--', $message);
$this->assertTrue(in_array('--alt-' . $boundary, $message)); $this->assertContains('--alt-' . $boundary, $message);
$this->assertTrue(in_array('--alt-' . $boundary . '--', $message)); $this->assertContains('--alt-' . $boundary . '--', $message);
} }
/** /**
@ -980,19 +1110,19 @@ class CakeEmailTest extends CakeTestCase {
$this->CakeEmail->config(array()); $this->CakeEmail->config(array());
$this->CakeEmail->attachments(array(CAKE . 'basics.php')); $this->CakeEmail->attachments(array(CAKE . 'basics.php'));
$result = $this->CakeEmail->send('body'); $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')); $this->CakeEmail->attachments(array('my.file.txt' => CAKE . 'basics.php'));
$result = $this->CakeEmail->send('body'); $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'))); $this->CakeEmail->attachments(array('file.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain')));
$result = $this->CakeEmail->send('body'); $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'))); $this->CakeEmail->attachments(array('file2.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain', 'contentId' => 'a1b1c1')));
$result = $this->CakeEmail->send('body'); $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(); $result = $this->CakeEmail->send();
$expected = '<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>'; $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.'; $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(); $message = $this->CakeEmail->message();
$this->assertTrue(in_array('Content-Type: text/plain; charset=UTF-8', $message)); $this->assertContains('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/html; charset=UTF-8', $message);
// 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();
$this->assertTrue(in_array('Content-Type: text/plain; charset=ISO-2022-JP', $message)); $this->assertContains('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/html; charset=ISO-2022-JP', $message);
// ISO-2022-JP is 7bit // ISO-2022-JP is 7bit
$this->assertTrue($this->checkContentTransferEncoding($message, '7bit')); $this->assertTrue($this->checkContentTransferEncoding($message, '7bit'));

View file

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

View file

@ -2454,6 +2454,37 @@ class RouterTest extends CakeTestCase {
$this->assertFalse($result); $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 * test setting redirect routes
* *

View file

@ -1562,6 +1562,10 @@ class HtmlHelperTest extends CakeTestCase {
} }
$this->assertEquals($helper->parseAttributes(array('compact')), ' compact="compact"'); $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); $helper = new Html5TestHelper($this->View);
$expected = ' require'; $expected = ' require';
$this->assertEquals($helper->parseAttributes(array('require')), $expected); $this->assertEquals($helper->parseAttributes(array('require')), $expected);

View file

@ -86,6 +86,51 @@ class TextHelperTest extends CakeTestCase {
$this->assertSame($this->Text->truncate($text7, 15), 'El moño está...'); $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($text8, 15), 'Vive la R'.chr(195).chr(169).'pu...');
$this->assertSame($this->Text->truncate($text9, 10), 'НОПРСТУ...'); $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; $data += $defaults;
$files = $this->trace(array('start' => $data['start'], 'format' => 'points')); $files = $this->trace(array('start' => $data['start'], 'format' => 'points'));
$code = '';
if (isset($files[0]['file'])) {
$code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1); $code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
}
$trace = $this->trace(array('start' => $data['start'], 'depth' => '20')); $trace = $this->trace(array('start' => $data['start'], 'depth' => '20'));
$insertOpts = array('before' => '{:', 'after' => '}'); $insertOpts = array('before' => '{:', 'after' => '}');
$context = array(); $context = array();

View file

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

View file

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

View file

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

View file

@ -737,8 +737,48 @@ class FormHelper extends AppHelper {
* Returns a formatted LABEL element for HTML FORMs. Will automatically generate * Returns a formatted LABEL element for HTML FORMs. Will automatically generate
* a for attribute if one is not provided. * 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 $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. * @param mixed $options An array of HTML attributes, or a string, to be used as a class name.
* @return string The formatted LABEL element * @return string The formatted LABEL element
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::label * @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); $result = parent::_initInputField($field, $options);
if ($this->tagIsInvalid() !== false) {
$result = $this->addClass($result, 'form-error');
}
if (!empty($result['disabled']) || $secure === self::SECURE_SKIP) { if (!empty($result['disabled']) || $secure === self::SECURE_SKIP) {
return $result; return $result;
} }

View file

@ -1071,93 +1071,4 @@ class HtmlHelper extends AppHelper {
return $configs; 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,21 +323,33 @@ class TextHelper extends AppHelper {
} }
if (!$exact) { if (!$exact) {
$spacepos = mb_strrpos($truncate, ' '); $spacepos = mb_strrpos($truncate, ' ');
if (isset($spacepos)) {
if ($html) { 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); $bits = mb_substr($truncate, $spacepos);
preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER); preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
if (!empty($droppedTags)) { if (!empty($droppedTags)) {
if (!empty($openTags)) {
foreach ($droppedTags as $closingTag) { foreach ($droppedTags as $closingTag) {
if (!in_array($closingTag[1], $openTags)) { if (!in_array($closingTag[1], $openTags)) {
array_unshift($openTags, $closingTag[1]); 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; $truncate .= $ending;
if ($html) { if ($html) {

View file

@ -493,7 +493,7 @@ class View extends Object {
$this->getEventManager()->dispatch(new CakeEvent('View.beforeLayout', $this, array($layoutFileName))); $this->getEventManager()->dispatch(new CakeEvent('View.beforeLayout', $this, array($layoutFileName)));
$scripts = implode("\n\t", $this->_scripts); $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( $this->viewVars = array_merge($this->viewVars, array(
'content_for_layout' => $content, 'content_for_layout' => $content,
@ -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. * Interact with the HelperCollection to load all the helpers.
* *
@ -817,12 +827,10 @@ class View extends Object {
$data = $this->viewVars; $data = $this->viewVars;
} }
$this->_current = $viewFile; $this->_current = $viewFile;
$initialBlocks = count($this->Blocks->unclosed());
$this->getEventManager()->dispatch(new CakeEvent('View.beforeRenderFile', $this, array($viewFile))); $this->getEventManager()->dispatch(new CakeEvent('View.beforeRenderFile', $this, array($viewFile)));
$content = $this->_evaluate($viewFile, $data); $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)); $afterEvent = new CakeEvent('View.afterRenderFile', $this, array($viewFile, $content));
//TODO: For BC puporses, set extra info in the event object. Remove when appropriate //TODO: For BC puporses, set extra info in the event object. Remove when appropriate
$afterEvent->modParams = 1; $afterEvent->modParams = 1;
@ -833,11 +841,16 @@ class View extends Object {
$this->_stack[] = $this->fetch('content'); $this->_stack[] = $this->fetch('content');
$this->assign('content', $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)); $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; return $content;
} }

View file

@ -145,4 +145,13 @@ class ViewBlock {
public function active() { public function active() {
return end($this->_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;
}
} }