Merge remote-tracking branch 'origin/master' into 2.3

Conflicts:
	lib/Cake/Model/Behavior/TranslateBehavior.php
	lib/Cake/Model/CakeSchema.php
	lib/Cake/Utility/CakeTime.php
	lib/Cake/Utility/ClassRegistry.php
	lib/Cake/View/MediaView.php
This commit is contained in:
Jose Lorenzo Rodriguez 2012-09-25 16:36:03 +02:00
commit d5c9d97dc1
43 changed files with 343 additions and 172 deletions

View file

@ -72,11 +72,14 @@ class IniReader implements ConfigReaderInterface {
* Build and construct a new ini file parser. The parser can be used to read * Build and construct a new ini file parser. The parser can be used to read
* ini files that are on the filesystem. * ini files that are on the filesystem.
* *
* @param string $path Path to load ini config files from. * @param string $path Path to load ini config files from. Defaults to APP . 'Config' . DS
* @param string $section Only get one section, leave null to parse and fetch * @param string $section Only get one section, leave null to parse and fetch
* all sections in the ini file. * all sections in the ini file.
*/ */
public function __construct($path, $section = null) { public function __construct($path = null, $section = null) {
if (!$path) {
$path = APP . 'Config' . DS;
}
$this->_path = $path; $this->_path = $path;
$this->_section = $section; $this->_section = $section;
} }

View file

@ -315,8 +315,11 @@ class DbConfigTask extends AppShell {
$config = array_merge($this->_defaultConfig, $config); $config = array_merge($this->_defaultConfig, $config);
extract($config); extract($config);
if (strpos($datasource, 'Database/') === false) {
$datasource = "Database/{$datasource}";
}
$out .= "\tpublic \${$name} = array(\n"; $out .= "\tpublic \${$name} = array(\n";
$out .= "\t\t'datasource' => 'Database/{$datasource}',\n"; $out .= "\t\t'datasource' => '{$datasource}',\n";
$out .= "\t\t'persistent' => {$persistent},\n"; $out .= "\t\t'persistent' => {$persistent},\n";
$out .= "\t\t'host' => '{$host}',\n"; $out .= "\t\t'host' => '{$host}',\n";

View file

@ -84,6 +84,10 @@ class ConsoleErrorHandler {
if (Configure::read('debug') == 0) { if (Configure::read('debug') == 0) {
CakeLog::write($log, $message); CakeLog::write($log, $message);
} }
if ($log === LOG_ERR) {
$this->_stop(1);
}
} }
/** /**

View file

@ -160,11 +160,11 @@ class ShellDispatcher {
$errorHandler = new ConsoleErrorHandler(); $errorHandler = new ConsoleErrorHandler();
if (empty($error['consoleHandler'])) { if (empty($error['consoleHandler'])) {
$error['consoleHandler'] = array($errorHandler, 'handleError'); $error['consoleHandler'] = array($errorHandler, 'handleError');
Configure::write('error', $error); Configure::write('Error', $error);
} }
if (empty($exception['consoleHandler'])) { if (empty($exception['consoleHandler'])) {
$exception['consoleHandler'] = array($errorHandler, 'handleException'); $exception['consoleHandler'] = array($errorHandler, 'handleException');
Configure::write('exception', $exception); Configure::write('Exception', $exception);
} }
set_exception_handler($exception['consoleHandler']); set_exception_handler($exception['consoleHandler']);
set_error_handler($error['consoleHandler'], Configure::read('Error.level')); set_error_handler($error['consoleHandler'], Configure::read('Error.level'));

View file

@ -318,10 +318,8 @@ class PhpAco {
* @return void * @return void
*/ */
public function build(array $allow, array $deny = array()) { public function build(array $allow, array $deny = array()) {
$stack = array();
$this->_tree = array(); $this->_tree = array();
$tree = array(); $tree = array();
$root = &$tree;
foreach ($allow as $dotPath => $aros) { foreach ($allow as $dotPath => $aros) {
if (is_string($aros)) { if (is_string($aros)) {

View file

@ -327,7 +327,7 @@ class AuthComponent extends Component {
if (!empty($this->loginRedirect)) { if (!empty($this->loginRedirect)) {
$default = $this->loginRedirect; $default = $this->loginRedirect;
} }
$controller->redirect($controller->referer($default), null, true); $controller->redirect($controller->referer($default, true), null, true);
return false; return false;
} }

View file

@ -191,9 +191,6 @@ class CookieComponent extends Component {
$this->_expire($this->time); $this->_expire($this->time);
$this->_values[$this->name] = array(); $this->_values[$this->name] = array();
if (isset($_COOKIE[$this->name])) {
$this->_values[$this->name] = $this->_decrypt($_COOKIE[$this->name]);
}
} }
/** /**

View file

@ -557,7 +557,6 @@ class Controller extends Object implements CakeEventListener {
if ($mergeParent || !empty($pluginController)) { if ($mergeParent || !empty($pluginController)) {
$appVars = get_class_vars($this->_mergeParent); $appVars = get_class_vars($this->_mergeParent);
$uses = $appVars['uses'];
$merge = array('components', 'helpers'); $merge = array('components', 'helpers');
$this->_mergeVars($merge, $this->_mergeParent, true); $this->_mergeVars($merge, $this->_mergeParent, true);
} }

View file

@ -198,7 +198,6 @@ class Configure {
*/ */
public static function delete($var = null) { public static function delete($var = null) {
$keys = explode('.', $var); $keys = explode('.', $var);
$last = array_pop($keys);
self::$_values = Hash::remove(self::$_values, $var); self::$_values = Hash::remove(self::$_values, $var);
} }

View file

@ -140,7 +140,10 @@ class TranslateBehavior extends ModelBehavior {
return $query; return $query;
} }
$fields = array_merge($this->settings[$Model->alias], $this->runtime[$Model->alias]['fields']); $fields = array_merge(
$this->settings[$Model->alias],
$this->runtime[$Model->alias]['fields']
);
$addFields = array(); $addFields = array();
if (empty($query['fields'])) { if (empty($query['fields'])) {
$addFields = $fields; $addFields = $fields;
@ -148,7 +151,11 @@ class TranslateBehavior extends ModelBehavior {
foreach ($fields as $key => $value) { foreach ($fields as $key => $value) {
$field = (is_numeric($key)) ? $value : $key; $field = (is_numeric($key)) ? $value : $key;
if (in_array($Model->escapeField('*'), $query['fields']) || in_array($Model->alias . '.' . $field, $query['fields']) || in_array($field, $query['fields'])) { if (
in_array($Model->escapeField('*'), $query['fields']) ||
in_array($Model->alias . '.' . $field, $query['fields']) ||
in_array($field, $query['fields'])
) {
$addFields[] = $field; $addFields[] = $field;
} }
} }
@ -425,7 +432,11 @@ class TranslateBehavior extends ModelBehavior {
$conditions['locale'] = $_locale; $conditions['locale'] = $_locale;
$conditions['content'] = $_value; $conditions['content'] = $_value;
if (array_key_exists($_locale, $translations)) { if (array_key_exists($_locale, $translations)) {
$RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale])))); $RuntimeModel->save(array(
$RuntimeModel->alias => array_merge(
$conditions, array('id' => $translations[$_locale])
)
));
} else { } else {
$RuntimeModel->save(array($RuntimeModel->alias => $conditions)); $RuntimeModel->save(array($RuntimeModel->alias => $conditions));
} }
@ -632,7 +643,6 @@ class TranslateBehavior extends ModelBehavior {
if (is_string($fields)) { if (is_string($fields)) {
$fields = array($fields); $fields = array($fields);
} }
$RuntimeModel = $this->translateModel($Model);
$associations = array(); $associations = array();
foreach ($fields as $key => $value) { foreach ($fields as $key => $value) {

View file

@ -447,7 +447,6 @@ class CakeSession {
*/ */
protected static function _configureSession() { protected static function _configureSession() {
$sessionConfig = Configure::read('Session'); $sessionConfig = Configure::read('Session');
$iniSet = function_exists('ini_set');
if (isset($sessionConfig['defaults'])) { if (isset($sessionConfig['defaults'])) {
$defaults = self::_defaultConfig($sessionConfig['defaults']); $defaults = self::_defaultConfig($sessionConfig['defaults']);

View file

@ -150,7 +150,10 @@ class Mysql extends DboSource {
); );
$this->connected = true; $this->connected = true;
} catch (PDOException $e) { } catch (PDOException $e) {
throw new MissingConnectionException(array('class' => $e->getMessage())); throw new MissingConnectionException(array(
'class' => get_class($this),
'message' => $e->getMessage()
));
} }
$this->_useAlias = (bool)version_compare($this->getVersion(), "4.1", ">="); $this->_useAlias = (bool)version_compare($this->getVersion(), "4.1", ">=");

View file

@ -122,7 +122,10 @@ class Postgres extends DboSource {
$this->_execute('SET search_path TO ' . $config['schema']); $this->_execute('SET search_path TO ' . $config['schema']);
} }
} catch (PDOException $e) { } catch (PDOException $e) {
throw new MissingConnectionException(array('class' => $e->getMessage())); throw new MissingConnectionException(array(
'class' => get_class($this),
'message' => $e->getMessage()
));
} }
return $this->connected; return $this->connected;
@ -457,7 +460,7 @@ class Postgres extends DboSource {
) )
AND c.oid = i.indrelid AND i.indexrelid = c2.oid AND c.oid = i.indrelid AND i.indexrelid = c2.oid
ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false); ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false);
foreach ($indexes as $i => $info) { foreach ($indexes as $info) {
$key = array_pop($info); $key = array_pop($info);
if ($key['indisprimary']) { if ($key['indisprimary']) {
$key['relname'] = 'PRIMARY'; $key['relname'] = 'PRIMARY';

View file

@ -114,7 +114,10 @@ class Sqlite extends DboSource {
$this->_connection = new PDO('sqlite:' . $config['database'], null, null, $flags); $this->_connection = new PDO('sqlite:' . $config['database'], null, null, $flags);
$this->connected = true; $this->connected = true;
} catch(PDOException $e) { } catch(PDOException $e) {
throw new MissingConnectionException(array('class' => $e->getMessage())); throw new MissingConnectionException(array(
'class' => get_class($this),
'message' => $e->getMessage()
));
} }
return $this->connected; return $this->connected;
} }
@ -488,7 +491,7 @@ class Sqlite extends DboSource {
if (is_bool($indexes)) { if (is_bool($indexes)) {
return array(); return array();
} }
foreach ($indexes as $i => $info) { foreach ($indexes as $info) {
$key = array_pop($info); $key = array_pop($info);
$keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")'); $keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")');
foreach ($keyInfo as $keyCol) { foreach ($keyInfo as $keyCol) {

View file

@ -130,7 +130,10 @@ class Sqlserver extends DboSource {
); );
$this->connected = true; $this->connected = true;
} catch (PDOException $e) { } catch (PDOException $e) {
throw new MissingConnectionException(array('class' => $e->getMessage())); throw new MissingConnectionException(array(
'class' => get_class($this),
'message' => $e->getMessage()
));
} }
return $this->connected; return $this->connected;

View file

@ -253,6 +253,7 @@ class DboSource extends DataSource {
if (!$this->enabled()) { if (!$this->enabled()) {
throw new MissingConnectionException(array( throw new MissingConnectionException(array(
'class' => get_class($this), 'class' => get_class($this),
'message' => __d('cake_dev', 'Selected driver is not enabled'),
'enabled' => false 'enabled' => false
)); ));
} }
@ -1294,9 +1295,9 @@ class DboSource extends DataSource {
} }
} }
if ($type === 'hasAndBelongsToMany') { if ($type === 'hasAndBelongsToMany') {
$uniqueIds = $merge = array(); $merge = array();
foreach ($fetch as $j => $data) { foreach ($fetch as $data) {
if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$modelAlias][$modelPK]) { if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$modelAlias][$modelPK]) {
if ($habtmFieldsCount <= 2) { if ($habtmFieldsCount <= 2) {
unset($data[$with]); unset($data[$with]);
@ -1445,7 +1446,7 @@ class DboSource extends DataSource {
$data[$association] = array(); $data[$association] = array();
} }
} else { } else {
foreach ($merge as $i => $row) { foreach ($merge as $row) {
$insert = array(); $insert = array();
if (count($row) === 1) { if (count($row) === 1) {
$insert = $row[$association]; $insert = $row[$association];
@ -2413,7 +2414,7 @@ class DboSource extends DataSource {
} }
$clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i'; $clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i';
if (preg_match($clauses, $conditions, $match)) { if (preg_match($clauses, $conditions)) {
$clause = ''; $clause = '';
} }
$conditions = $this->_quoteFields($conditions); $conditions = $this->_quoteFields($conditions);
@ -2908,7 +2909,7 @@ class DboSource extends DataSource {
$columnMap[$key] = $pdoMap[$type]; $columnMap[$key] = $pdoMap[$type];
} }
foreach ($values as $row => $value) { foreach ($values as $value) {
$i = 1; $i = 1;
foreach ($value as $col => $val) { foreach ($value as $col => $val) {
$statement->bindValue($i, $val, $columnMap[$col]); $statement->bindValue($i, $val, $columnMap[$col]);
@ -3220,7 +3221,7 @@ class DboSource extends DataSource {
$isAllFloat = $isAllInt = true; $isAllFloat = $isAllInt = true;
$containsFloat = $containsInt = $containsString = false; $containsFloat = $containsInt = $containsString = false;
foreach ($value as $key => $valElement) { foreach ($value as $valElement) {
$valElement = trim($valElement); $valElement = trim($valElement);
if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) { if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) {
$isAllFloat = false; $isAllFloat = false;

View file

@ -2374,7 +2374,7 @@ class Model extends Object implements CakeEventListener {
$updateCounterCache = false; $updateCounterCache = false;
if (!empty($this->belongsTo)) { if (!empty($this->belongsTo)) {
foreach ($this->belongsTo as $parent => $assoc) { foreach ($this->belongsTo as $assoc) {
if (!empty($assoc['counterCache'])) { if (!empty($assoc['counterCache'])) {
$updateCounterCache = true; $updateCounterCache = true;
break; break;
@ -2460,7 +2460,7 @@ class Model extends Object implements CakeEventListener {
* @return void * @return void
*/ */
protected function _deleteLinks($id) { protected function _deleteLinks($id) {
foreach ($this->hasAndBelongsToMany as $assoc => $data) { foreach ($this->hasAndBelongsToMany as $data) {
list($plugin, $joinModel) = pluginSplit($data['with']); list($plugin, $joinModel) = pluginSplit($data['with']);
$records = $this->{$joinModel}->find('all', array( $records = $this->{$joinModel}->find('all', array(
'conditions' => array($this->{$joinModel}->escapeField($data['foreignKey']) => $id), 'conditions' => array($this->{$joinModel}->escapeField($data['foreignKey']) => $id),
@ -3053,7 +3053,7 @@ class Model extends Object implements CakeEventListener {
public function isForeignKey($field) { public function isForeignKey($field) {
$foreignKeys = array(); $foreignKeys = array();
if (!empty($this->belongsTo)) { if (!empty($this->belongsTo)) {
foreach ($this->belongsTo as $assoc => $data) { foreach ($this->belongsTo as $data) {
$foreignKeys[] = $data['foreignKey']; $foreignKeys[] = $data['foreignKey'];
} }
} }

View file

@ -18,8 +18,7 @@
* @since CakePHP(tm) v 2.2.0 * @since CakePHP(tm) v 2.2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
App::uses('ModelValidator', 'Model');
App::uses('CakeValidationSet', 'Model/Validator');
App::uses('Validation', 'Utility'); App::uses('Validation', 'Utility');
/** /**

View file

@ -18,7 +18,7 @@
* @since CakePHP(tm) v 2.2.0 * @since CakePHP(tm) v 2.2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
App::uses('ModelValidator', 'Model');
App::uses('CakeValidationRule', 'Model/Validator'); App::uses('CakeValidationRule', 'Model/Validator');
/** /**

View file

@ -675,7 +675,7 @@ class CakeRequest implements ArrayAccess {
public function accepts($type = null) { public function accepts($type = null) {
$raw = $this->parseAccept(); $raw = $this->parseAccept();
$accept = array(); $accept = array();
foreach ($raw as $value => $types) { foreach ($raw as $types) {
$accept = array_merge($accept, $types); $accept = array_merge($accept, $types);
} }
if ($type === null) { if ($type === null) {

View file

@ -41,14 +41,31 @@ class MailTransport extends AbstractTransport {
unset($headers['To']); unset($headers['To']);
$headers = $this->_headersToString($headers, $eol); $headers = $this->_headersToString($headers, $eol);
$message = implode($eol, $email->message()); $message = implode($eol, $email->message());
if (ini_get('safe_mode') || !isset($this->_config['additionalParameters'])) {
if (!@mail($to, $email->subject(), $message, $headers)) { $params = null;
throw new SocketException(__d('cake_dev', 'Could not send email.')); if (!ini_get('safe_mode')) {
} $params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null;
} elseif (!@mail($to, $email->subject(), $message, $headers, $this->_config['additionalParameters'])) {
throw new SocketException(__d('cake_dev', 'Could not send email.'));
} }
$this->_mail($to, $email->subject(), $message, $headers, $params);
return array('headers' => $headers, 'message' => $message); return array('headers' => $headers, 'message' => $message);
} }
/**
* Wraps internal function mail() and throws exception instead of errors if anything goes wrong
*
* @param string $to email's recipient
* @param string $subject email's subject
* @param string $message email's body
* @param string $headers email's custom headers
* @param string $params additional params for sending email
* @throws SocketException if mail could not be sent
* @return void
*/
protected function _mail($to, $subject, $message, $headers, $params = null) {
if (!@mail($to, $subject, $message, $headers, $params)) {
throw new SocketException(__d('cake_dev', 'Could not send email.'));
}
}
} }

View file

@ -523,7 +523,7 @@ class Router {
$ext = null; $ext = null;
$out = array(); $out = array();
if ($url && strpos($url, '/') !== 0) { if (strlen($url) && strpos($url, '/') !== 0) {
$url = '/' . $url; $url = '/' . $url;
} }
if (strpos($url, '?') !== false) { if (strpos($url, '?') !== false) {

View file

@ -60,6 +60,23 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
$this->Error->handleError(E_NOTICE, 'This is a notice error', '/some/file', 275); $this->Error->handleError(E_NOTICE, 'This is a notice error', '/some/file', 275);
} }
/**
* test that the console error handler can deal with fatal errors.
*
* @return void
*/
public function testHandleFatalError() {
$content = "<error>Fatal Error Error:</error> This is a fatal error in [/some/file, line 275]\n";
ConsoleErrorHandler::$stderr->expects($this->once())->method('write')
->with($content);
$this->Error->expects($this->once())
->method('_stop')
->with(1);
$this->Error->handleError(E_USER_ERROR, 'This is a fatal error', '/some/file', 275);
}
/** /**
* test that the console error handler can deal with CakeExceptions. * test that the console error handler can deal with CakeExceptions.
* *

View file

@ -271,12 +271,14 @@ class CookieComponentTest extends CakeTestCase {
$expected = array( $expected = array(
'name' => $this->Cookie->name . '[Testing]', 'name' => $this->Cookie->name . '[Testing]',
'value' => '[1,2,3]', 'value' => '[1,2,3]',
'expire' => time() + 10,
'path' => '/', 'path' => '/',
'domain' => '', 'domain' => '',
'secure' => false, 'secure' => false,
'httpOnly' => false); 'httpOnly' => false);
$result = $this->Controller->response->cookie($this->Cookie->name . '[Testing]'); $result = $this->Controller->response->cookie($this->Cookie->name . '[Testing]');
$this->assertWithinMargin($result['expire'], time() + 10, 1);
unset($result['expire']);
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }

View file

@ -573,10 +573,10 @@ class ExceptionRendererTest extends CakeTestCase {
500 500
), ),
array( array(
new MissingConnectionException(array('class' => 'Article')), new MissingConnectionException(array('class' => 'Mysql')),
array( array(
'/<h2>Missing Database Connection<\/h2>/', '/<h2>Missing Database Connection<\/h2>/',
'/Article requires a database connection/' '/A Database connection using "Mysql" was missing or unable to connect./',
), ),
500 500
), ),
@ -584,7 +584,7 @@ class ExceptionRendererTest extends CakeTestCase {
new MissingConnectionException(array('class' => 'Mysql', 'enabled' => false)), new MissingConnectionException(array('class' => 'Mysql', 'enabled' => false)),
array( array(
'/<h2>Missing Database Connection<\/h2>/', '/<h2>Missing Database Connection<\/h2>/',
'/Mysql requires a database connection/', '/A Database connection using "Mysql" was missing or unable to connect./',
'/Mysql driver is NOT enabled/' '/Mysql driver is NOT enabled/'
), ),
500 500

View file

@ -0,0 +1,84 @@
<?php
/**
* MailTransportTest file
*
* PHP 5
*
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
* @package Cake.Test.Case.Network.Email
* @since CakePHP(tm) v 2.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::uses('CakeEmail', 'Network/Email');
App::uses('AbstractTransport', 'Network/Email');
App::uses('MailTransport', 'Network/Email');
/**
* Test case
*
*/
class MailTransportTest extends CakeTestCase {
/**
* Setup
*
* @return void
*/
public function setUp() {
$this->MailTransport = $this->getMock('MailTransport', array('_mail'));
$this->MailTransport->config(array('additionalParameters' => '-f'));
}
/**
* testSend method
*
* @return void
*/
public function testSendData() {
$email = $this->getMock('CakeEmail', array('message'), array());
$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');
$email->messageID('<4d9946cf-0a44-4907-88fe-1d0ccbdd56cb@localhost>');
$email->subject('Foø Bår Béz Foø Bår Béz Foø Bår Béz Foø Bår Béz');
$date = date(DATE_RFC2822);
$email->setHeaders(array('X-Mailer' => 'CakePHP Email', 'Date' => $date));
$email->expects($this->any())->method('message')->will($this->returnValue(array('First Line', 'Second Line', '.Third Line', '')));
$data = "From: CakePHP Test <noreply@cakephp.org>" . PHP_EOL;
$data .= "Return-Path: CakePHP Return <pleasereply@cakephp.org>" . PHP_EOL;
$data .= "Cc: Mark Story <mark@cakephp.org>, Juan Basso <juan@cakephp.org>" . PHP_EOL;
$data .= "Bcc: phpnut@cakephp.org" . PHP_EOL;
$data .= "X-Mailer: CakePHP Email" . PHP_EOL;
$data .= "Date: " . $date . PHP_EOL;
$data .= "Message-ID: <4d9946cf-0a44-4907-88fe-1d0ccbdd56cb@localhost>" . PHP_EOL;
$data .= "MIME-Version: 1.0" . PHP_EOL;
$data .= "Content-Type: text/plain; charset=UTF-8" . PHP_EOL;
$data .= "Content-Transfer-Encoding: 8bit";
$subject = '=?UTF-8?B?Rm/DuCBCw6VyIELDqXogRm/DuCBCw6VyIELDqXogRm/DuCBCw6VyIELDqXog?=';
$subject .= "\r\n" . ' =?UTF-8?B?Rm/DuCBCw6VyIELDqXo=?=';
$this->MailTransport->expects($this->once())->method('_mail')
->with(
'CakePHP <cake@cakephp.org>',
$subject,
implode(PHP_EOL, array('First Line', 'Second Line', '.Third Line', '')),
$data,
'-f'
);
$this->MailTransport->send($email);
}
}

View file

@ -451,6 +451,30 @@ class RouterTest extends CakeTestCase {
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* Test that catch all routes work with a variety of falsey inputs.
*
* @return void
*/
public function testUrlCatchAllRoute() {
Router::connect('/*', array('controller' => 'categories', 'action' => 'index'));
$result = Router::url(array('controller' => 'categories', 'action' => 'index', '0'));
$this->assertEquals('/0', $result);
$expected = array(
'plugin' => null,
'controller' => 'categories',
'action' => 'index',
'pass' => array('0'),
'named' => array()
);
$result = Router::parse('/0');
$this->assertEquals($expected, $result);
$result = Router::parse('0');
$this->assertEquals($expected, $result);
}
/** /**
* Tests using arrays in named parameters * Tests using arrays in named parameters
* *

View file

@ -1049,4 +1049,19 @@ class CakeTimeTest extends CakeTestCase {
} }
} }
/**
* Tests that using CakeTime::format() with the correct sytax actually converts
* from one timezone to the other correctly
*
* @return void
**/
public function testCorrectTimezoneConversion() {
date_default_timezone_set('UTC');
$date = '2012-01-01 10:00:00';
$converted = CakeTime::format($date, '%Y-%m-%d %H:%M:%S', '', 'Europe/Copenhagen');
$expected = new DateTime($date);
$expected->setTimezone(new DateTimeZone('Europe/Copenhagen'));
$this->assertEquals($expected->format('Y-m-d H:i:s'), $converted);
}
} }

View file

@ -268,7 +268,7 @@ class DebuggerTest extends CakeTestCase {
* Test method for testing addFormat with callbacks. * Test method for testing addFormat with callbacks.
*/ */
public function customFormat($error, $strings) { public function customFormat($error, $strings) {
return $error['error'] . ': I eated an error ' . $error['path']; return $error['error'] . ': I eated an error ' . $error['file'];
} }
/** /**

View file

@ -362,11 +362,10 @@ class FileTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testLastAccess() { public function testLastAccess() {
$ts = time();
$someFile = new File(TMP . 'some_file.txt', false); $someFile = new File(TMP . 'some_file.txt', false);
$this->assertFalse($someFile->lastAccess()); $this->assertFalse($someFile->lastAccess());
$this->assertTrue($someFile->open()); $this->assertTrue($someFile->open());
$this->assertTrue($someFile->lastAccess() >= $ts); $this->assertWithinMargin($someFile->lastAccess(), time(), 2);
$someFile->close(); $someFile->close();
$someFile->delete(); $someFile->delete();
} }
@ -377,13 +376,14 @@ class FileTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testLastChange() { public function testLastChange() {
$ts = time();
$someFile = new File(TMP . 'some_file.txt', false); $someFile = new File(TMP . 'some_file.txt', false);
$this->assertFalse($someFile->lastChange()); $this->assertFalse($someFile->lastChange());
$this->assertTrue($someFile->open('r+')); $this->assertTrue($someFile->open('r+'));
$this->assertTrue($someFile->lastChange() >= $ts); $this->assertWithinMargin($someFile->lastChange(), time(), 2);
$someFile->write('something'); $someFile->write('something');
$this->assertTrue($someFile->lastChange() >= $ts); $this->assertWithinMargin($someFile->lastChange(), time(), 2);
$someFile->close(); $someFile->close();
$someFile->delete(); $someFile->delete();
} }

View file

@ -107,8 +107,7 @@ class Contact extends CakeTestModel {
'imrequiredonupdate' => array('notEmpty' => array('rule' => 'alphaNumeric', 'on' => 'update')), 'imrequiredonupdate' => array('notEmpty' => array('rule' => 'alphaNumeric', 'on' => 'update')),
'imrequiredoncreate' => array('required' => array('rule' => 'alphaNumeric', 'on' => 'create')), 'imrequiredoncreate' => array('required' => array('rule' => 'alphaNumeric', 'on' => 'create')),
'imrequiredonboth' => array( 'imrequiredonboth' => array(
'required' => array('rule' => 'alphaNumeric', 'allowEmpty' => true), 'required' => array('rule' => 'alphaNumeric'),
'check' => array('rule' => 'alphaNumeric')
), ),
'string_required' => 'notEmpty', 'string_required' => 'notEmpty',
'imalsorequired' => array('rule' => 'alphaNumeric', 'allowEmpty' => false), 'imalsorequired' => array('rule' => 'alphaNumeric', 'allowEmpty' => false),
@ -3274,80 +3273,6 @@ class FormHelperTest extends CakeTestCase {
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '1'));
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1', 'checked' => 'checked')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '0'));
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0', 'checked' => 'checked')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => null));
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'));
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$this->assertTags($result, $expected);
$result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); $result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
$expected = array( $expected = array(
'div' => array('class' => 'input radio'), 'div' => array('class' => 'input radio'),
@ -3536,6 +3461,59 @@ class FormHelperTest extends CakeTestCase {
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
} }
/**
* Test that radios with a 0 value are selected under the correct conditions.
*
* @return void
*/
public function testRadioOptionWithZeroValue() {
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0', 'checked' => 'checked')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '0'));
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => 0));
$this->assertTags($result, $expected);
$expected = array(
'fieldset' => array(),
'legend' => array(),
'Field',
'/legend',
'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
array('label' => array('for' => 'ModelField1')),
'Yes',
'/label',
array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
array('label' => array('for' => 'ModelField0')),
'No',
'/label',
'/fieldset'
);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => null));
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => ''));
$this->assertTags($result, $expected);
$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'));
$this->assertTags($result, $expected);
}
/** /**
* test disabled radio options * test disabled radio options
* *
@ -3545,7 +3523,7 @@ class FormHelperTest extends CakeTestCase {
$result = $this->Form->radio( $result = $this->Form->radio(
'Model.field', 'Model.field',
array('option A', 'option B'), array('option A', 'option B'),
array('disabled' => array('option A'), 'value' => 'option A') array('disabled' => array('option A'), 'value' => '0')
); );
$expected = array( $expected = array(
'fieldset' => array(), 'fieldset' => array(),
@ -3567,7 +3545,7 @@ class FormHelperTest extends CakeTestCase {
$result = $this->Form->radio( $result = $this->Form->radio(
'Model.field', 'Model.field',
array('option A', 'option B'), array('option A', 'option B'),
array('disabled' => true, 'value' => 'option A') array('disabled' => true, 'value' => '0')
); );
$expected = array( $expected = array(
'fieldset' => array(), 'fieldset' => array(),
@ -3589,7 +3567,7 @@ class FormHelperTest extends CakeTestCase {
$result = $this->Form->radio( $result = $this->Form->radio(
'Model.field', 'Model.field',
array('option A', 'option B'), array('option A', 'option B'),
array('disabled' => 'disabled', 'value' => 'option A') array('disabled' => 'disabled', 'value' => '0')
); );
$expected = array( $expected = array(
'fieldset' => array(), 'fieldset' => array(),
@ -4346,6 +4324,23 @@ class FormHelperTest extends CakeTestCase {
$this->assertRegExp('/"' . $key . '"/', $result); $this->assertRegExp('/"' . $key . '"/', $result);
} }
/**
* Multiple select elements should always be secured as they always participate
* in the POST data.
*
* @return void
*/
public function testSelectMultipleSecureWithNoOptions() {
$this->Form->request['_Token'] = array('key' => 'testkey');
$this->assertEquals(array(), $this->Form->fields);
$result = $this->Form->select(
'Model.select',
array(),
array('multiple' => true)
);
$this->assertEquals(array('Model.select'), $this->Form->fields);
}
/** /**
* When a select box has no options it should not be added to the fields list * When a select box has no options it should not be added to the fields list
* as it always fail post validation. * as it always fail post validation.

View file

@ -941,18 +941,11 @@ class CakeTime {
* @see CakeTime::i18nFormat() * @see CakeTime::i18nFormat()
*/ */
public static function format($date, $format = null, $default = false, $timezone = null) { public static function format($date, $format = null, $default = false, $timezone = null) {
//Backwards compatible params order //Backwards compatible params re-order test
$time = self::fromString($format, $timezone); $time = self::fromString($format, $timezone);
$_time = false;
if (!is_numeric($time)) {
$_time = self::fromString($date, $timezone);
}
if (is_numeric($_time) && $time === false) { if ($time === false) {
return self::i18nFormat($_time, $format, $default, $timezone); return self::i18nFormat($date, $format, $default, $timezone);
}
if ($time === false && $default !== false) {
return $default;
} }
return date($date, $time); return date($date, $time);
} }

View file

@ -213,7 +213,6 @@ class Debugger {
if (empty($line)) { if (empty($line)) {
$line = '??'; $line = '??';
} }
$path = self::trimPath($file);
$info = compact('code', 'description', 'file', 'line'); $info = compact('code', 'description', 'file', 'line');
if (!in_array($info, $self->errors)) { if (!in_array($info, $self->errors)) {

View file

@ -391,7 +391,7 @@ class Folder {
$paths = $this->tree($path); $paths = $this->tree($path);
foreach ($paths as $type) { foreach ($paths as $type) {
foreach ($type as $key => $fullpath) { foreach ($type as $fullpath) {
$check = explode(DS, $fullpath); $check = explode(DS, $fullpath);
$count = count($check); $count = count($check);

View file

@ -432,7 +432,6 @@ class Hash {
} }
$stack = array(); $stack = array();
$i = 1;
while (!empty($needle)) { while (!empty($needle)) {
$key = key($needle); $key = key($needle);
$val = $needle[$key]; $val = $needle[$key];

View file

@ -19,7 +19,13 @@
<h2><?php echo __d('cake_dev', 'Missing Database Connection'); ?></h2> <h2><?php echo __d('cake_dev', 'Missing Database Connection'); ?></h2>
<p class="error"> <p class="error">
<strong><?php echo __d('cake_dev', 'Error'); ?>: </strong> <strong><?php echo __d('cake_dev', 'Error'); ?>: </strong>
<?php echo __d('cake_dev', '%s requires a database connection', $class); ?> <?php echo __d('cake_dev', 'A Database connection using "%s" was missing or unable to connect. ', $class); ?>
<br />
<?php
if (isset($message)):
echo __d('cake_dev', 'The database server returned this error: %s', $message);
endif;
?>
</p> </p>
<?php if (!$enabled) : ?> <?php if (!$enabled) : ?>
<p class="error"> <p class="error">

View file

@ -168,14 +168,14 @@ class FormHelper extends AppHelper {
* *
* The $key parameter accepts the following list of values: * The $key parameter accepts the following list of values:
* *
* - key: Returns the name of the primary key for the model * - key: Returns the name of the primary key for the model
* - fields: Returns the model schema * - fields: Returns the model schema
* - validates: returns the list of fields that are required * - validates: returns the list of fields that are required
* - errors: returns the list of validation errors * - errors: returns the list of validation errors
* *
* If the $field parameter is passed if will return the information for that sole field. * If the $field parameter is passed if will return the information for that sole field.
* *
* `$this->_introspectModel('Post', 'fields', 'title');` will return the schema information for title column * `$this->_introspectModel('Post', 'fields', 'title');` will return the schema information for title column
* *
* @param string $model name of the model to extract information from * @param string $model name of the model to extract information from
* @param string $key name of the special information key to obtain (key, fields, validates, errors) * @param string $key name of the special information key to obtain (key, fields, validates, errors)
@ -194,7 +194,7 @@ class FormHelper extends AppHelper {
if ($key === 'fields') { if ($key === 'fields') {
if (!isset($this->fieldset[$model]['fields'])) { if (!isset($this->fieldset[$model]['fields'])) {
$fields = $this->fieldset[$model]['fields'] = $object->schema(); $this->fieldset[$model]['fields'] = $object->schema();
foreach ($object->hasAndBelongsToMany as $alias => $assocData) { foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
$this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple'); $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple');
} }
@ -244,20 +244,23 @@ class FormHelper extends AppHelper {
* @return boolean true if field is required to be filled, false otherwise * @return boolean true if field is required to be filled, false otherwise
*/ */
protected function _isRequiredField($validationRules) { protected function _isRequiredField($validationRules) {
if (empty($validationRules) || count($validationRules) === 0) {
return false;
}
foreach ($validationRules as $rule) { foreach ($validationRules as $rule) {
$rule->isUpdate($this->requestType === 'put'); $rule->isUpdate($this->requestType === 'put');
if (!$rule->isEmptyAllowed()) { if ($rule->isEmptyAllowed()) {
return true; return false;
} }
} }
return false; return true;
} }
/** /**
* Returns false if given form field described by the current entity has no errors. * Returns false if given form field described by the current entity has no errors.
* Otherwise it returns the validation message * Otherwise it returns the validation message
* *
* @return mixed Either false when there or no errors, or an array of error * @return mixed Either false when there are no errors, or an array of error
* strings. An error string could be ''. * strings. An error string could be ''.
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::tagIsInvalid * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::tagIsInvalid
*/ */
@ -320,7 +323,6 @@ class FormHelper extends AppHelper {
$key = null; $key = null;
if ($model !== false) { if ($model !== false) {
$object = $this->_getModel($model);
$key = $this->_introspectModel($model, 'key'); $key = $this->_introspectModel($model, 'key');
$this->setEntity($model, true); $this->setEntity($model, true);
} }
@ -717,7 +719,7 @@ 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 * ### Options
* *
@ -1375,7 +1377,7 @@ class FormHelper extends AppHelper {
foreach ($options as $optValue => $optTitle) { foreach ($options as $optValue => $optTitle) {
$optionsHere = array('value' => $optValue); $optionsHere = array('value' => $optValue);
if (isset($value) && $optValue == $value) { if (isset($value) && strval($optValue) === strval($value)) {
$optionsHere['checked'] = 'checked'; $optionsHere['checked'] = 'checked';
} }
if ($disabled && (!is_array($disabled) || in_array($optValue, $disabled))) { if ($disabled && (!is_array($disabled) || in_array($optValue, $disabled))) {
@ -1864,10 +1866,12 @@ class FormHelper extends AppHelper {
if (!empty($tag) || isset($template)) { if (!empty($tag) || isset($template)) {
$hasOptions = (count($options) > 0 || $showEmpty); $hasOptions = (count($options) > 0 || $showEmpty);
// Secure the field if there are options, or its a multi select.
// Single selects with no options don't submit, but multiselects do.
if ( if (
(!isset($secure) || $secure == true) && (!isset($secure) || $secure == true) &&
empty($attributes['disabled']) && empty($attributes['disabled']) &&
$hasOptions (!empty($attributes['multiple']) || $hasOptions)
) { ) {
$this->_secure(true); $this->_secure(true);
} }

View file

@ -254,7 +254,6 @@ class MootoolsEngineHelper extends JsBaseEngineHelper {
$options['url'] = $url; $options['url'] = $url;
$options = $this->_prepareCallbacks('request', $options); $options = $this->_prepareCallbacks('request', $options);
if (!empty($options['dataExpression'])) { if (!empty($options['dataExpression'])) {
$callbacks[] = 'data';
unset($options['dataExpression']); unset($options['dataExpression']);
} elseif (!empty($data)) { } elseif (!empty($data)) {
$data = $this->object($data); $data = $this->object($data);

View file

@ -471,7 +471,7 @@ class PaginatorHelper extends AppHelper {
$url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url); $url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url);
if ($this->{$check}($model)) { if ($this->{$check}($model)) {
return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape'))), compact('class')); return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'model'))), compact('class'));
} else { } else {
unset($options['rel']); unset($options['rel']);
return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class'))); return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));

View file

@ -237,7 +237,6 @@ class PrototypeEngineHelper extends JsBaseEngineHelper {
$url = '"' . $this->url($url) . '"'; $url = '"' . $this->url($url) . '"';
$options = $this->_mapOptions('request', $options); $options = $this->_mapOptions('request', $options);
$type = '.Request'; $type = '.Request';
$data = null;
if (isset($options['type']) && strtolower($options['type']) == 'json') { if (isset($options['type']) && strtolower($options['type']) == 'json') {
unset($options['type']); unset($options['type']);
} }

View file

@ -341,7 +341,7 @@ class RssHelper extends AppHelper {
$nodes->item(0)->setAttribute($key, $value); $nodes->item(0)->setAttribute($key, $value);
} }
} }
foreach ($children as $k => $child) { foreach ($children as $child) {
$child = $elem->createElement($name, $child); $child = $elem->createElement($name, $child);
$nodes->item(0)->appendChild($child); $nodes->item(0)->appendChild($child);
} }

View file

@ -358,7 +358,6 @@ class TimeHelper extends AppHelper {
*/ */
public function timeAgoInWords($dateTime, $options = array()) { public function timeAgoInWords($dateTime, $options = array()) {
$element = null; $element = null;
$stringDate = '';
if (is_array($options) && !empty($options['element'])) { if (is_array($options) && !empty($options['element'])) {
$element = array( $element = array(

View file

@ -297,11 +297,6 @@ class View extends Object {
*/ */
protected $_eventManager = null; protected $_eventManager = null;
/**
* The view file to be rendered, only used inside _execute()
*/
private $__viewFileName = null;
/** /**
* Whether the event manager was already configured for this object * Whether the event manager was already configured for this object
* *
@ -830,7 +825,7 @@ class View extends Object {
*/ */
public function loadHelpers() { public function loadHelpers() {
$helpers = HelperCollection::normalizeObjectArray($this->helpers); $helpers = HelperCollection::normalizeObjectArray($this->helpers);
foreach ($helpers as $name => $properties) { foreach ($helpers as $properties) {
list($plugin, $class) = pluginSplit($properties['class']); list($plugin, $class) = pluginSplit($properties['class']);
$this->{$class} = $this->Helpers->load($properties['class'], $properties['settings']); $this->{$class} = $this->Helpers->load($properties['class'], $properties['settings']);
} }