diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..217a0e30cb6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +indent_style = tab +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.bat] +end_of_line = crlf diff --git a/.travis.yml b/.travis.yml index 5af60d8d8f0..f7705b81788 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,16 @@ env: - DB=pgsql - DB=sqlite +matrix: + allow_failures: + - php: 5.4 + env: + - PHPCS=1 + include: + - php: 5.4 + env: + - PHPCS=1 + before_script: - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test2;'; fi" @@ -23,6 +33,9 @@ before_script: server.listen(80, 'localhost'); console.log('TCP server listening on port 80 at localhost.');" > app/tmp/socket.js - sudo node ./app/tmp/socket.js & + - pear channel-discover pear.cakephp.org + - pear install --alldeps cakephp/CakePHP_CodeSniffer + - phpenv rehash - set +H - echo " app/Config/database.php script: - - ./lib/Cake/Console/cake test core AllTests --stderr + - sh -c "if [ '$PHPCS' != '1' ]; then ./lib/Cake/Console/cake test core AllTests --stderr; else phpcs --extensions=php --standard=CakePHP ./lib/Cake; fi" notifications: email: false \ No newline at end of file diff --git a/app/Config/Schema/i18n.php b/app/Config/Schema/i18n.php index 8de0052ddcd..08aab843b08 100644 --- a/app/Config/Schema/i18n.php +++ b/app/Config/Schema/i18n.php @@ -19,14 +19,17 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +// @codingStandardsIgnoreStart + /* * * Using the Schema command line utility * cake schema run create i18n - * */ class i18nSchema extends CakeSchema { +// @codingStandardsIgnoreEnd + public $name = 'i18n'; public function before($event = array()) { diff --git a/app/Config/core.php b/app/Config/core.php index 99198539f5d..e0527538d41 100644 --- a/app/Config/core.php +++ b/app/Config/core.php @@ -42,7 +42,8 @@ * Options: * * - `handler` - callback - The callback to handle errors. You can set this to any callable type, - * including anonymous functions. + * including anonymous functions. + * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class * - `level` - int - The level of errors you are interested in capturing. * - `trace` - boolean - Include stack traces for errors in log files. * @@ -64,6 +65,7 @@ * * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type, * including anonymous functions. + * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you * should place the file for that class in app/Lib/Error. This class needs to implement a render method. * - `log` - boolean - Should Exceptions be logged? diff --git a/build.xml b/build.xml index fca6ec4ae19..daf292fab59 100644 --- a/build.xml +++ b/build.xml @@ -40,6 +40,9 @@ + + + @@ -197,7 +200,7 @@ - + @@ -205,10 +208,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + diff --git a/lib/Cake/Console/Command/ConsoleShell.php b/lib/Cake/Console/Command/ConsoleShell.php index d8c79c9338f..31736bbdd91 100644 --- a/lib/Cake/Console/Command/ConsoleShell.php +++ b/lib/Cake/Console/Command/ConsoleShell.php @@ -238,7 +238,9 @@ public function main($command = null) { if ($this->_isValidModel($modelToCheck)) { $findCommand = "\$data = \$this->$command;"; + //@codingStandardsIgnoreStart @eval($findCommand); + //@codingStandardsIgnoreEnd if (is_array($data)) { foreach ($data as $idx => $results) { @@ -294,7 +296,9 @@ public function main($command = null) { list($foo, $data) = explode("->save", $command); $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; + //@codingStandardsIgnoreStart @eval($saveCommand); + //@codingStandardsIgnoreEnd $this->out(__d('cake_console', 'Saved record for %s', $modelToSave)); } break; @@ -304,7 +308,9 @@ public function main($command = null) { if ($this->_isValidModel($modelToCheck)) { // Get the column info for this model $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; + //@codingStandardsIgnoreStart @eval($fieldsCommand); + //@codingStandardsIgnoreEnd if (is_array($data)) { foreach ($data as $field => $type) { @@ -326,7 +332,9 @@ public function main($command = null) { $this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true)); break; case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true): + //@codingStandardsIgnoreStart if ($url = eval('return array' . $tmp[1] . ';')) { + //@codingStandardsIgnoreEnd $this->out(Router::url($url)); } break; @@ -362,7 +370,9 @@ protected function _loadRoutes() { Router::reload(); extract(Router::getNamedExpressions()); + //@codingStandardsIgnoreStart if (!@include APP . 'Config' . DS . 'routes.php') { + //@codingStandardsIgnoreEnd return false; } CakePlugin::routes(); diff --git a/lib/Cake/Console/Command/Task/TemplateTask.php b/lib/Cake/Console/Command/Task/TemplateTask.php index 69b4cdd39d9..6efa8bbc677 100644 --- a/lib/Cake/Console/Command/Task/TemplateTask.php +++ b/lib/Cake/Console/Command/Task/TemplateTask.php @@ -78,7 +78,6 @@ protected function _findThemes() { $paths[] = $core; - // TEMPORARY TODO remove when all paths are DS terminated foreach ($paths as $i => $path) { $paths[$i] = rtrim($path, DS) . DS; } diff --git a/lib/Cake/Console/Command/TestShell.php b/lib/Cake/Console/Command/TestShell.php index 2b9892fc0c7..c9627930555 100644 --- a/lib/Cake/Console/Command/TestShell.php +++ b/lib/Cake/Console/Command/TestShell.php @@ -114,7 +114,7 @@ public function getOptionParser() { ))->addOption('stop-on-failure', array( 'help' => __d('cake_console', 'Stop execution upon first failure.'), 'boolean' => true - ))->addOption('stop-on-skipped ', array( + ))->addOption('stop-on-skipped', array( 'help' => __d('cake_console', 'Stop execution upon first skipped test.'), 'boolean' => true ))->addOption('stop-on-incomplete', array( @@ -132,7 +132,7 @@ public function getOptionParser() { ))->addOption('no-globals-backup', array( 'help' => __d('cake_console', 'Do not backup and restore $GLOBALS for each test.'), 'boolean' => true - ))->addOption('static-backup ', array( + ))->addOption('static-backup', array( 'help' => __d('cake_console', 'Backup and restore static attributes for each test.'), 'boolean' => true ))->addOption('syntax-check', array( diff --git a/lib/Cake/Console/ConsoleOptionParser.php b/lib/Cake/Console/ConsoleOptionParser.php index 1983e22ce9f..8d3cdeeed76 100644 --- a/lib/Cake/Console/ConsoleOptionParser.php +++ b/lib/Cake/Console/ConsoleOptionParser.php @@ -345,6 +345,7 @@ public function addArgument($name, $params = array()) { $arg = new ConsoleInputArgument($options); } $this->_args[$index] = $arg; + ksort($this->_args); return $this; } @@ -584,7 +585,8 @@ protected function _parseOption($name, $params) { $option = $this->_options[$name]; $isBoolean = $option->isBoolean(); $nextValue = $this->_nextToken(); - if (!$isBoolean && !empty($nextValue) && !$this->_optionExists($nextValue)) { + $emptyNextValue = (empty($nextValue) && $nextValue !== '0'); + if (!$isBoolean && !$emptyNextValue && !$this->_optionExists($nextValue)) { array_shift($this->_tokens); $value = $nextValue; } elseif ($isBoolean) { @@ -625,7 +627,7 @@ protected function _optionExists($name) { */ protected function _parseArg($argument, $args) { if (empty($this->_args)) { - array_push($args, $argument); + $args[] = $argument; return $args; } $next = count($args); @@ -634,7 +636,7 @@ protected function _parseArg($argument, $args) { } if ($this->_args[$next]->validChoice($argument)) { - array_push($args, $argument); + $args[] = $argument; return $args; } } diff --git a/lib/Cake/Console/Shell.php b/lib/Cake/Console/Shell.php index 74e5e033be4..c9dd434c4bd 100644 --- a/lib/Cake/Console/Shell.php +++ b/lib/Cake/Console/Shell.php @@ -372,7 +372,9 @@ public function runCommand($command, $argv) { if (!empty($this->params['quiet'])) { $this->_useLogger(false); } - + if (!empty($this->params['plugin'])) { + CakePlugin::load($this->params['plugin']); + } $this->command = $command; if (!empty($this->params['help'])) { return $this->_displayHelp($command); @@ -686,7 +688,9 @@ public function createFile($path, $contents) { protected function _checkUnitTest() { if (class_exists('PHPUnit_Framework_TestCase')) { return true; + //@codingStandardsIgnoreStart } elseif (@include 'PHPUnit' . DS . 'Autoload.php') { + //@codingStandardsIgnoreEnd return true; } elseif (App::import('Vendor', 'phpunit', array('file' => 'PHPUnit' . DS . 'Autoload.php'))) { return true; diff --git a/lib/Cake/Console/ShellDispatcher.php b/lib/Cake/Console/ShellDispatcher.php index 162e8f50833..426e73485ad 100644 --- a/lib/Cake/Console/ShellDispatcher.php +++ b/lib/Cake/Console/ShellDispatcher.php @@ -215,7 +215,7 @@ public function dispatch() { return $Shell->main(); } } - + throw new MissingShellMethodException(array('shell' => $shell, 'method' => $command)); } diff --git a/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php b/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php index 1a29ffd986a..1b8b08735bf 100644 --- a/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php +++ b/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php @@ -21,14 +21,17 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +// @codingStandardsIgnoreStart + /* * * Using the Schema command line utility * cake schema run create i18n - * */ class i18nSchema extends CakeSchema { +// @codingStandardsIgnoreEnd + public $name = 'i18n'; public function before($event = array()) { diff --git a/lib/Cake/Console/Templates/skel/Config/core.php b/lib/Cake/Console/Templates/skel/Config/core.php index 7720e9ed686..14ad6976ba5 100644 --- a/lib/Cake/Console/Templates/skel/Config/core.php +++ b/lib/Cake/Console/Templates/skel/Config/core.php @@ -42,7 +42,8 @@ * Options: * * - `handler` - callback - The callback to handle errors. You can set this to any callable type, - * including anonymous functions. + * including anonymous functions. + * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class * - `level` - int - The level of errors you are interested in capturing. * - `trace` - boolean - Include stack traces for errors in log files. * @@ -64,6 +65,7 @@ * * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type, * including anonymous functions. + * Make sure you add App::uses('MyHandler', 'Error'); when using a custom handler class * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you * should place the file for that class in app/Lib/Error. This class needs to implement a render method. * - `log` - boolean - Should Exceptions be logged? diff --git a/lib/Cake/Controller/CakeErrorController.php b/lib/Cake/Controller/CakeErrorController.php index 5778648818a..6b0423a322e 100644 --- a/lib/Cake/Controller/CakeErrorController.php +++ b/lib/Cake/Controller/CakeErrorController.php @@ -19,6 +19,8 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +App::uses('AppController', 'Controller'); + /** * Error Handling Controller * @@ -50,18 +52,18 @@ class CakeErrorController extends AppController { */ public function __construct($request = null, $response = null) { parent::__construct($request, $response); - if (count(Router::extensions())) { - $this->components[] = 'RequestHandler'; - } $this->constructClasses(); + if (count(Router::extensions()) && + !$this->Components->attached('RequestHandler') + ) { + $this->RequestHandler = $this->Components->load('RequestHandler'); + } if ($this->Components->enabled('Auth')) { $this->Components->disable('Auth'); } if ($this->Components->enabled('Security')) { $this->Components->disable('Security'); } - $this->startupProcess(); - $this->_set(array('cacheAction' => false, 'viewPath' => 'Errors')); } diff --git a/lib/Cake/Controller/Component/Acl/AclInterface.php b/lib/Cake/Controller/Component/Acl/AclInterface.php index 7cd98135b81..24c85e9e449 100644 --- a/lib/Cake/Controller/Component/Acl/AclInterface.php +++ b/lib/Cake/Controller/Component/Acl/AclInterface.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl * @since CakePHP(tm) v 0.10.0.1076 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ @@ -17,7 +17,7 @@ * Access Control List interface. * Implementing classes are used by AclComponent to perform ACL checks in Cake. * - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl */ interface AclInterface { diff --git a/lib/Cake/Controller/Component/Acl/DbAcl.php b/lib/Cake/Controller/Component/Acl/DbAcl.php index 37c756bcb6f..679bbd0df0f 100644 --- a/lib/Cake/Controller/Component/Acl/DbAcl.php +++ b/lib/Cake/Controller/Component/Acl/DbAcl.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl * @since CakePHP(tm) v 0.10.0.1076 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ @@ -32,7 +32,7 @@ * edit * }}} * - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl */ class DbAcl extends Object implements AclInterface { diff --git a/lib/Cake/Controller/Component/Acl/IniAcl.php b/lib/Cake/Controller/Component/Acl/IniAcl.php index d915443fb42..49ae575b53b 100644 --- a/lib/Cake/Controller/Component/Acl/IniAcl.php +++ b/lib/Cake/Controller/Component/Acl/IniAcl.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl * @since CakePHP(tm) v 0.10.0.1076 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ @@ -18,7 +18,7 @@ * IniAcl implements an access control system using an INI file. An example * of the ini file used can be found in /config/acl.ini.php. * - * @package Cake.Controller.Component + * @package Cake.Controller.Component.Acl */ class IniAcl extends Object implements AclInterface { diff --git a/lib/Cake/Controller/Component/Auth/BaseAuthenticate.php b/lib/Cake/Controller/Component/Auth/BaseAuthenticate.php index d9dc7248f50..e5bd08cfea0 100644 --- a/lib/Cake/Controller/Component/Auth/BaseAuthenticate.php +++ b/lib/Cake/Controller/Component/Auth/BaseAuthenticate.php @@ -84,7 +84,7 @@ protected function _findUser($username, $password) { } $result = ClassRegistry::init($userModel)->find('first', array( 'conditions' => $conditions, - 'recursive' => (int)$this->settings['recursive'], + 'recursive' => $this->settings['recursive'], 'contain' => $this->settings['contain'], )); if (empty($result) || empty($result[$model])) { diff --git a/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php b/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php index 5e0fb0ee5a2..21c353339ae 100644 --- a/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php +++ b/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php @@ -170,7 +170,7 @@ protected function _findUser($username, $password) { } $result = ClassRegistry::init($userModel)->find('first', array( 'conditions' => $conditions, - 'recursive' => (int)$this->settings['recursive'] + 'recursive' => $this->settings['recursive'] )); if (empty($result) || empty($result[$model])) { return false; diff --git a/lib/Cake/Controller/Component/EmailComponent.php b/lib/Cake/Controller/Component/EmailComponent.php index 702ec911d1c..e6cd78876bb 100644 --- a/lib/Cake/Controller/Component/EmailComponent.php +++ b/lib/Cake/Controller/Component/EmailComponent.php @@ -288,6 +288,7 @@ public function initialize(Controller $controller) { public function send($content = null, $template = null, $layout = null) { $lib = new CakeEmail(); $lib->charset = $this->charset; + $lib->headerCharset = $this->charset; $lib->from($this->_formatAddresses((array)$this->from)); if (!empty($this->to)) { diff --git a/lib/Cake/Controller/Component/SecurityComponent.php b/lib/Cake/Controller/Component/SecurityComponent.php index 907b43a8f1e..f8fe0dfbb97 100644 --- a/lib/Cake/Controller/Component/SecurityComponent.php +++ b/lib/Cake/Controller/Component/SecurityComponent.php @@ -218,6 +218,10 @@ public function startup(Controller $controller) { $controller->request->params['requested'] != 1 ); + if ($this->_action == $this->blackHoleCallback) { + return $this->blackhole($controller, 'auth'); + } + if ($isPost && $isNotRequestAction && $this->validatePost) { if ($this->_validatePost($controller) === false) { return $this->blackHole($controller, 'auth'); @@ -309,11 +313,10 @@ public function requireAuth() { * @throws BadRequestException */ public function blackHole(Controller $controller, $error = '') { - if ($this->blackHoleCallback == null) { + if (!$this->blackHoleCallback) { throw new BadRequestException(__d('cake_dev', 'The request has been black-holed')); - } else { - return $this->_callback($controller, $this->blackHoleCallback, array($error)); } + return $this->_callback($controller, $this->blackHoleCallback, array($error)); } /** diff --git a/lib/Cake/Controller/Controller.php b/lib/Cake/Controller/Controller.php index 978dade8420..d489927f5ef 100644 --- a/lib/Cake/Controller/Controller.php +++ b/lib/Cake/Controller/Controller.php @@ -755,7 +755,7 @@ public function redirect($url, $status = null, $exit = true) { extract($status, EXTR_OVERWRITE); } $event = new CakeEvent('Controller.beforeRedirect', $this, array($url, $status, $exit)); - //TODO: Remove the following line when the events are fully migrated to the CakeEventManager + list($event->break, $event->breakOn, $event->collectReturn) = array(true, false, true); $this->getEventManager()->dispatch($event); @@ -796,7 +796,7 @@ public function redirect($url, $status = null, $exit = true) { * @return array Array with keys url, status and exit */ protected function _parseBeforeRedirect($response, $url, $status, $exit) { - if (is_array($response) && isset($response[0])) { + if (is_array($response) && array_key_exists(0, $response)) { foreach ($response as $resp) { if (is_array($resp) && isset($resp['url'])) { extract($resp, EXTR_OVERWRITE); diff --git a/lib/Cake/Controller/Scaffold.php b/lib/Cake/Controller/Scaffold.php index 85431268611..fbd3d6fa24a 100644 --- a/lib/Cake/Controller/Scaffold.php +++ b/lib/Cake/Controller/Scaffold.php @@ -433,8 +433,14 @@ protected function _associations() { $associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey']; + list($plugin, $model) = pluginSplit($assocData['className']); + if ($plugin) { + $plugin = Inflector::underscore($plugin); + } + $associations[$type][$assocKey]['plugin'] = $plugin; + $associations[$type][$assocKey]['controller'] = - Inflector::pluralize(Inflector::underscore($assocData['className'])); + Inflector::pluralize(Inflector::underscore($model)); if ($type == 'hasAndBelongsToMany') { $associations[$type][$assocKey]['with'] = $assocData['with']; diff --git a/lib/Cake/Core/Object.php b/lib/Cake/Core/Object.php index d95855788f4..15a58309def 100644 --- a/lib/Cake/Core/Object.php +++ b/lib/Cake/Core/Object.php @@ -73,9 +73,13 @@ public function requestAction($url, $extra = array()) { $extra['autoRender'] = 1; unset($extra[$index]); } - if (is_array($url) && !isset($extra['url'])) { + $arrayUrl = is_array($url); + if ($arrayUrl && !isset($extra['url'])) { $extra['url'] = array(); } + if ($arrayUrl && !isset($extra['data'])) { + $extra['data'] = array(); + } $extra = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra); $data = isset($extra['data']) ? $extra['data'] : null; unset($extra['data']); @@ -88,11 +92,12 @@ public function requestAction($url, $extra = array()) { } elseif (is_array($url)) { $params = $url + array('pass' => array(), 'named' => array(), 'base' => false); $params = array_merge($params, $extra); - $request = new CakeRequest(Router::reverse($params), false); + $request = new CakeRequest(Router::reverse($params)); } if (isset($data)) { $request->data = $data; } + $dispatcher = new Dispatcher(); $result = $dispatcher->dispatch($request, new CakeResponse(), $extra); Router::popRequest(); diff --git a/lib/Cake/Error/ExceptionRenderer.php b/lib/Cake/Error/ExceptionRenderer.php index a404d3be845..2ee47322127 100644 --- a/lib/Cake/Error/ExceptionRenderer.php +++ b/lib/Cake/Error/ExceptionRenderer.php @@ -142,16 +142,19 @@ public function __construct(Exception $exception) { * @return Controller */ protected function _getController($exception) { + App::uses('AppController', 'Controller'); App::uses('CakeErrorController', 'Controller'); if (!$request = Router::getRequest(true)) { $request = new CakeRequest(); } $response = new CakeResponse(array('charset' => Configure::read('App.encoding'))); try { - if (class_exists('AppController')) { - $controller = new CakeErrorController($request, $response); - } + $controller = new CakeErrorController($request, $response); + $controller->startupProcess(); } catch (Exception $e) { + if (!empty($controller) && $controller->Components->enabled('RequestHandler')) { + $controller->RequestHandler->startup($controller); + } } if (empty($controller)) { $controller = new Controller($request, $response); @@ -269,10 +272,11 @@ protected function _outputMessage($template) { $this->controller->afterFilter(); $this->controller->response->send(); } catch (MissingViewException $e) { - try { - $this->_outputMessage('error500'); - } catch (Exception $e) { + $attributes = $e->getAttributes(); + if (isset($attributes['file']) && strpos($attributes['file'], 'error500') !== false) { $this->_outputMessageSafe('error500'); + } else { + $this->_outputMessage('error500'); } } catch (Exception $e) { $this->_outputMessageSafe('error500'); diff --git a/lib/Cake/Error/exceptions.php b/lib/Cake/Error/exceptions.php index 94952f12e4f..4f0fe984329 100644 --- a/lib/Cake/Error/exceptions.php +++ b/lib/Cake/Error/exceptions.php @@ -224,9 +224,11 @@ class MissingControllerException extends CakeException { protected $_messageTemplate = 'Controller class %s could not be found.'; +//@codingStandardsIgnoreStart public function __construct($message, $code = 404) { parent::__construct($message, $code); } +//@codingStandardsIgnoreEnd } @@ -240,9 +242,11 @@ class MissingActionException extends CakeException { protected $_messageTemplate = 'Action %s::%s() could not be found.'; +//@codingStandardsIgnoreStart public function __construct($message, $code = 404) { parent::__construct($message, $code); } +//@codingStandardsIgnoreEnd } @@ -256,9 +260,11 @@ class PrivateActionException extends CakeException { protected $_messageTemplate = 'Private Action %s::%s() is not directly accessible.'; +//@codingStandardsIgnoreStart public function __construct($message, $code = 404, Exception $previous = null) { parent::__construct($message, $code, $previous); } +//@codingStandardsIgnoreEnd } @@ -572,8 +578,10 @@ class NotImplementedException extends CakeException { protected $_messageTemplate = '%s is not implemented.'; +//@codingStandardsIgnoreStart public function __construct($message, $code = 501) { parent::__construct($message, $code); } +//@codingStandardsIgnoreEnd } diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index 7678ba6aed5..862414b7cc0 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -24,13 +24,6 @@ App::uses('L10n', 'I18n'); App::uses('Multibyte', 'I18n'); -if (function_exists('mb_internal_encoding')) { - $encoding = Configure::read('App.encoding'); - if (!empty($encoding)) { - mb_internal_encoding($encoding); - } -} - /** * I18n handles translation of Text and time format strings. * diff --git a/lib/Cake/I18n/Multibyte.php b/lib/Cake/I18n/Multibyte.php index b741ef4d604..5dc47f22308 100644 --- a/lib/Cake/I18n/Multibyte.php +++ b/lib/Cake/I18n/Multibyte.php @@ -987,7 +987,6 @@ public static function substr($string, $start, $length = null) { * @param string $charset charset to use for encoding. defaults to UTF-8 * @param string $newline * @return string - * @TODO: add support for 'Q'('Quoted Printable') encoding */ public static function mimeEncode($string, $charset = null, $newline = "\r\n") { if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) { @@ -1008,7 +1007,8 @@ public static function mimeEncode($string, $charset = null, $newline = "\r\n") { if ($charset == 'UTF-8') { $parts = array(); $maxchars = floor(($length * 3) / 4); - while (strlen($string) > $maxchars) { + $stringLength = strlen($string); + while ($stringLength > $maxchars) { $i = (int)$maxchars; $test = ord($string[$i]); while ($test >= 128 && $test <= 191) { @@ -1017,6 +1017,7 @@ public static function mimeEncode($string, $charset = null, $newline = "\r\n") { } $parts[] = base64_encode(substr($string, 0, $i)); $string = substr($string, $i); + $stringLength = strlen($string); } $parts[] = base64_encode($string); $string = implode($spacer, $parts); diff --git a/lib/Cake/Model/Behavior/ContainableBehavior.php b/lib/Cake/Model/Behavior/ContainableBehavior.php index 98c06746899..799f6e238e9 100644 --- a/lib/Cake/Model/Behavior/ContainableBehavior.php +++ b/lib/Cake/Model/Behavior/ContainableBehavior.php @@ -91,21 +91,25 @@ public function setup(Model $Model, $settings = array()) { */ public function beforeFind(Model $Model, $query) { $reset = (isset($query['reset']) ? $query['reset'] : true); - $noContain = ( - (isset($this->runtime[$Model->alias]['contain']) && empty($this->runtime[$Model->alias]['contain'])) || - (isset($query['contain']) && empty($query['contain'])) - ); + $noContain = false; $contain = array(); + if (isset($this->runtime[$Model->alias]['contain'])) { + $noContain = empty($this->runtime[$Model->alias]['contain']); $contain = $this->runtime[$Model->alias]['contain']; unset($this->runtime[$Model->alias]['contain']); } + if (isset($query['contain'])) { - $contain = array_merge($contain, (array)$query['contain']); + $noContain = $noContain || empty($query['contain']); + if ($query['contain'] !== false) { + $contain = array_merge($contain, (array)$query['contain']); + } } + $noContain = $noContain && empty($contain); + if ( - $noContain || !$contain || in_array($contain, array(null, false), true) || - (isset($contain[0]) && $contain[0] === null) + $noContain || empty($contain) || (isset($contain[0]) && $contain[0] === null) ) { if ($noContain) { $query['recursive'] = -1; diff --git a/lib/Cake/Model/Behavior/TranslateBehavior.php b/lib/Cake/Model/Behavior/TranslateBehavior.php index dd1243b913b..01a2c0d53e9 100644 --- a/lib/Cake/Model/Behavior/TranslateBehavior.php +++ b/lib/Cake/Model/Behavior/TranslateBehavior.php @@ -383,6 +383,21 @@ protected function _setRuntimeData(Model $Model) { $this->runtime[$Model->alias]['beforeSave'] = $tempData; } +/** + * Restores model data to the original data. + * This solves issues with saveAssociated and validate = first. + * + * @param Model $model + * @return void + */ + public function afterValidate(Model $Model) { + $Model->data[$Model->alias] = array_merge( + $Model->data[$Model->alias], + $this->runtime[$Model->alias]['beforeSave'] + ); + return true; + } + /** * afterSave Callback * diff --git a/lib/Cake/Model/Behavior/TreeBehavior.php b/lib/Cake/Model/Behavior/TreeBehavior.php index 6895fe9d037..fd85826084b 100644 --- a/lib/Cake/Model/Behavior/TreeBehavior.php +++ b/lib/Cake/Model/Behavior/TreeBehavior.php @@ -52,7 +52,7 @@ class TreeBehavior extends ModelBehavior { * * @var array */ - protected $_deletedRow = null; + protected $_deletedRow = array(); /** * Initiate Tree behavior @@ -107,7 +107,7 @@ public function afterSave(Model $Model, $created) { * @return array */ public function beforeFind(Model $Model, $query) { - if ($Model->findQueryType == 'threaded' && !isset($query['parent'])) { + if ($Model->findQueryType === 'threaded' && !isset($query['parent'])) { $query['parent'] = $this->settings[$Model->alias]['parent']; } return $query; @@ -129,7 +129,7 @@ public function beforeDelete(Model $Model, $cascade = true) { 'fields' => array($Model->escapeField($left), $Model->escapeField($right)), 'recursive' => -1)); if ($data) { - $this->_deletedRow = current($data); + $this->_deletedRow[$Model->alias] = current($data); } return true; } @@ -144,8 +144,8 @@ public function beforeDelete(Model $Model, $cascade = true) { */ public function afterDelete(Model $Model) { extract($this->settings[$Model->alias]); - $data = $this->_deletedRow; - $this->_deletedRow = null; + $data = $this->_deletedRow[$Model->alias]; + $this->_deletedRow[$Model->alias] = null; if (!$data[$right] || !$data[$left]) { return true; @@ -588,7 +588,6 @@ public function moveUp(Model $Model, $id = null, $number = 1) { * 'parent' the values of the parent_id field will be used to populate the left and right fields. The missingParentAction * parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present. * - * @todo Could be written to be faster, *maybe*. Ideally using a subquery and putting all the logic burden on the DB. * @param Model $Model Model instance * @param string $mode parent or tree * @param string|integer $missingParentAction 'return' to do nothing and return, 'delete' to @@ -602,7 +601,7 @@ public function recover(Model $Model, $mode = 'parent', $missingParentAction = n } extract($this->settings[$Model->alias]); $Model->recursive = $recursive; - if ($mode == 'parent') { + if ($mode === 'parent') { $Model->bindModel(array('belongsTo' => array('VerifyParent' => array( 'className' => $Model->name, 'foreignKey' => $parent, @@ -616,15 +615,15 @@ public function recover(Model $Model, $mode = 'parent', $missingParentAction = n )); $Model->unbindModel(array('belongsTo' => array('VerifyParent'))); if ($missingParents) { - if ($missingParentAction == 'return') { + if ($missingParentAction === 'return') { foreach ($missingParents as $id => $display) { $this->errors[] = 'cannot find the parent for ' . $Model->alias . ' with id ' . $id . '(' . $display . ')'; } return false; - } elseif ($missingParentAction == 'delete') { - $Model->deleteAll(array($Model->primaryKey => array_flip($missingParents))); + } elseif ($missingParentAction === 'delete') { + $Model->deleteAll(array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)), false); } else { - $Model->updateAll(array($parent => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents))); + $Model->updateAll(array($Model->escapeField($parent) => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents))); } } $count = 1; @@ -986,14 +985,14 @@ protected function _sync(Model $Model, $shift, $dir = '+', $conditions = array() extract($this->settings[$Model->alias]); $Model->recursive = $recursive; - if ($field == 'both') { + if ($field === 'both') { $this->_sync($Model, $shift, $dir, $conditions, $created, $left); $field = $right; } if (is_string($conditions)) { $conditions = array($Model->escapeField($field) . " {$conditions}"); } - if (($scope != '1 = 1' && $scope !== true) && $scope) { + if (($scope !== '1 = 1' && $scope !== true) && $scope) { $conditions[] = $scope; } if ($created) { diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index fff3e7d5b4a..56be23eac33 100644 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -55,7 +55,6 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener { /** * Attaches a model object and loads a list of behaviors * - * @todo Make this method a constructor instead.. * @param string $modelName * @param array $behaviors * @return void diff --git a/lib/Cake/Model/CakeSchema.php b/lib/Cake/Model/CakeSchema.php index 2fda5f75e87..58302e78138 100644 --- a/lib/Cake/Model/CakeSchema.php +++ b/lib/Cake/Model/CakeSchema.php @@ -574,13 +574,17 @@ protected function _values($values) { if (is_array($values)) { foreach ($values as $key => $val) { if (is_array($val)) { - $vals[] = "'{$key}' => array('" . implode("', '", $val) . "')"; - } elseif (!is_numeric($key)) { + $vals[] = "'{$key}' => array(" . implode(", ", $this->_values($val)) . ")"; + } else { $val = var_export($val, true); if ($val === 'NULL') { $val = 'null'; } - $vals[] = "'{$key}' => {$val}"; + if (!is_numeric($key)) { + $vals[] = "'{$key}' => {$val}"; + } else { + $vals[] = "{$val}"; + } } } } diff --git a/lib/Cake/Model/Datasource/DataSource.php b/lib/Cake/Model/Datasource/DataSource.php index af65276ceb9..c04e6771a3b 100644 --- a/lib/Cake/Model/Datasource/DataSource.php +++ b/lib/Cake/Model/Datasource/DataSource.php @@ -322,7 +322,6 @@ protected function _cacheDescription($object, $data = null) { * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$ * @param array $stack * @return string String of query data with placeholders replaced. - * @todo Remove and refactor $assocData, ensure uses of the method have the param removed too. */ public function insertQueryData($query, $data, $association, $assocData, Model $model, Model $linkModel, $stack) { $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}'); diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php index f96dfa42026..d002c50df05 100644 --- a/lib/Cake/Model/Datasource/Database/Mysql.php +++ b/lib/Cake/Model/Datasource/Database/Mysql.php @@ -443,6 +443,12 @@ public function index($model) { $col[] = $idx->Column_name; $index[$idx->Key_name]['column'] = $col; } + if (!empty($idx->Sub_part)) { + if (!isset($index[$idx->Key_name]['length'])) { + $index[$idx->Key_name]['length'] = array(); + } + $index[$idx->Key_name]['length'][$idx->Column_name] = $idx->Sub_part; + } } // @codingStandardsIgnoreEnd $indices->closeCursor(); @@ -543,6 +549,55 @@ protected function _alterTableParameters($table, $parameters) { return array(); } +/** + * Format indexes for create table + * + * @param array $indexes An array of indexes to generate SQL from + * @param string $table Optional table name, not used + * @return array An array of SQL statements for indexes + * @see DboSource::buildIndex() + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + foreach ($indexes as $name => $value) { + $out = ''; + if ($name === 'PRIMARY') { + $out .= 'PRIMARY '; + $name = null; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + $name = $this->startQuote . $name . $this->endQuote; + } + // length attribute only used for MySQL datasource, for TEXT/BLOB index columns + $out .= 'KEY ' . $name . ' ('; + if (is_array($value['column'])) { + if (isset($value['length'])) { + $vals = array(); + foreach ($value['column'] as $column) { + $name = $this->name($column); + if (isset($value['length'])) { + $name .= $this->_buildIndexSubPart($value['length'], $column); + } + $vals[] = $name; + } + $out .= implode(', ', $vals); + } else { + $out .= implode(', ', array_map(array(&$this, 'name'), $value['column'])); + } + } else { + $out .= $this->name($value['column']); + if (isset($value['length'])) { + $out .= $this->_buildIndexSubPart($value['length'], $value['column']); + } + } + $out .= ')'; + $join[] = $out; + } + return $join; + } + /** * Generate MySQL index alteration statements for a table. * @@ -558,33 +613,37 @@ protected function _alterIndexes($table, $indexes) { if ($name == 'PRIMARY') { $out .= 'PRIMARY KEY'; } else { - $out .= 'KEY ' . $name; + $out .= 'KEY ' . $this->startQuote . $name . $this->endQuote; } $alter[] = $out; } } if (isset($indexes['add'])) { - foreach ($indexes['add'] as $name => $value) { - $out = 'ADD '; - if ($name == 'PRIMARY') { - $out .= 'PRIMARY '; - $name = null; - } else { - if (!empty($value['unique'])) { - $out .= 'UNIQUE '; - } - } - if (is_array($value['column'])) { - $out .= 'KEY ' . $name . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; - } else { - $out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')'; - } - $alter[] = $out; + $add = $this->buildIndex($indexes['add']); + foreach ($add as $index) { + $alter[] = 'ADD ' . $index; } } return $alter; } +/** + * Format length for text indexes + * + * @param array $lengths An array of lengths for a single index + * @param string $column The column for which to generate the index length + * @return string Formatted length part of an index field + */ + protected function _buildIndexSubPart($lengths, $column) { + if (is_null($lengths)) { + return ''; + } + if (!isset($lengths[$column])) { + return ''; + } + return '(' . $lengths[$column] . ')'; + } + /** * Returns an detailed array of sources (tables) in the database. * diff --git a/lib/Cake/Model/Datasource/Database/Sqlite.php b/lib/Cake/Model/Datasource/Database/Sqlite.php index d30cc67dfdf..0990e4a3cae 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlite.php +++ b/lib/Cake/Model/Datasource/Database/Sqlite.php @@ -249,7 +249,9 @@ public function column($real) { $col = strtolower(str_replace(')', '', $real)); $limit = null; - @list($col, $limit) = explode('(', $col); + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) { return $col; diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index 035f120ea51..174bb0c9018 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -286,9 +286,14 @@ public function fields(Model $model, $alias = null, $fields = array(), $quote = $fieldAlias = $this->name($alias . '__' . $fields[$i]); } else { $build = explode('.', $fields[$i]); - $this->_fieldMappings[$build[0] . '__' . $build[1]] = $fields[$i]; - $fieldName = $this->name($build[0] . '.' . $build[1]); - $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $build[1]); + $build[0] = trim($build[0], '[]'); + $build[1] = trim($build[1], '[]'); + $name = $build[0] . '.' . $build[1]; + $alias = $build[0] . '__' . $build[1]; + + $this->_fieldMappings[$alias] = $name; + $fieldName = $this->name($name); + $fieldAlias = $this->name($alias); } if ($model->getColumnType($fields[$i]) == 'datetime') { $fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)"; diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index ea1c7dbca02..c841718108d 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -830,7 +830,7 @@ public function name($data) { } if (preg_match('/^([\w-]+)\((.*)\)$/', $data, $matches)) { // Functions return $this->cacheMethod(__FUNCTION__, $cacheKey, - $matches[1] . '(' . $this->name($matches[2]) . ')' + $matches[1] . '(' . $this->name($matches[2]) . ')' ); } if ( @@ -2282,7 +2282,8 @@ public function fields(Model $model, $alias = null, $fields = array(), $quote = $virtualFields, $fields, $quote, - ConnectionManager::getSourceName($this) + ConnectionManager::getSourceName($this), + $model->table ); $cacheKey = md5(serialize($cacheKey)); if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) { @@ -2464,6 +2465,10 @@ public function conditionKeysToString($conditions, $quoteValues = true, $model = $not = 'NOT '; } + if (empty($value)) { + continue; + } + if (empty($value[1])) { if ($not) { $out[] = $not . '(' . $value[0] . ')'; @@ -2490,16 +2495,16 @@ public function conditionKeysToString($conditions, $quoteValues = true, $model = $count = count($value); if ($count === 1 && !preg_match("/\s+NOT$/", $key)) { $data = $this->_quoteFields($key) . ' = ('; - } else { - $data = $this->_quoteFields($key) . ' IN ('; - } - if ($quoteValues) { - if (is_object($model)) { - $columnType = $model->getColumnType($key); + if ($quoteValues) { + if (is_object($model)) { + $columnType = $model->getColumnType($key); + } + $data .= implode(', ', $this->value($value, $columnType)); } - $data .= implode(', ', $this->value($value, $columnType)); + $data .= ')'; + } else { + $data = $this->_parseKey($model, $key, $value); } - $data .= ')'; } else { $ret = $this->conditionKeysToString($value, $quoteValues, $model); if (count($ret) > 1) { @@ -2569,7 +2574,11 @@ protected function _parseKey($model, $key, $value) { $value = $this->value($value, $type); if (!$virtual && $key !== '?') { - $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false); + $isKey = ( + strpos($key, '(') !== false || + strpos($key, ')') !== false || + strpos($key, '|') !== false + ); $key = $isKey ? $this->_quoteFields($key) : $this->name($key); } @@ -2917,6 +2926,10 @@ public function insertMulti($table, $fields, $values) { } $statement->execute(); $statement->closeCursor(); + + if ($this->fullDebug) { + $this->logQuery($sql, $value); + } } return $this->commit(); } @@ -2984,7 +2997,7 @@ public function createSchema($schema, $tableName = null) { $tableParameters = array_merge($tableParameters, $this->buildTableParameters($col, $table)); } } - if (empty($indexes) && !empty($primary)) { + if (!isset($columns['indexes']['PRIMARY']) && !empty($primary)) { $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1)); $indexes = array_merge($indexes, $this->buildIndex($col, $table)); } diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 51c3ed4941c..51780e28144 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -558,6 +558,8 @@ class Model extends Object implements CakeEventListener { */ protected $_associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); +// @codingStandardsIgnoreStart + /** * Holds model associations temporarily to allow for dynamic (un)binding. * @@ -586,6 +588,8 @@ class Model extends Object implements CakeEventListener { */ public $__backContainableAssociation = array(); +// @codingStandardsIgnoreEnd + /** * The ID of the model record that was last inserted. * @@ -1300,10 +1304,8 @@ public function schema($field = false) { if ($this->useTable !== false && (!is_array($this->_schema) || $field === true)) { $db = $this->getDataSource(); $db->cacheSources = ($this->cacheSources && $db->cacheSources); - if (method_exists($db, 'describe') && $this->useTable !== false) { + if (method_exists($db, 'describe')) { $this->_schema = $db->describe($this); - } elseif ($this->useTable === false) { - $this->_schema = array(); } } if (is_string($field)) { @@ -1473,7 +1475,8 @@ public function create($data = array(), $filterKey = false) { $this->validationErrors = array(); if ($data !== null && $data !== false) { - foreach ($this->schema() as $field => $properties) { + $schema = (array)$this->schema(); + foreach ($schema as $field => $properties) { if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') { $defaults[$field] = $properties['default']; } @@ -1565,7 +1568,9 @@ public function field($name, $conditions = null, $order = null) { * * @param string $name Name of the table field * @param mixed $value Value of the field - * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed + * @param boolean|array $validate Either a boolean, or an array. + * If a boolean, indicates whether or not to validate before saving. + * If an array, allows control of 'validate' and 'callbacks' options. * @return boolean See Model::save() * @see Model::save() * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false @@ -1743,6 +1748,10 @@ public function save($data = null, $validate = true, $fieldList = array()) { $this->_saveMulti($joined, $this->id, $db); } + if ($success && $count === 0) { + $success = false; + } + if ($success && $count > 0) { if (!empty($this->data)) { if ($created) { @@ -2017,7 +2026,7 @@ protected function _prepareUpdateFields($data) { * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array */ - public function saveAll($data, $options = array()) { + public function saveAll($data = array(), $options = array()) { $options = array_merge(array('validate' => 'first'), $options); if (Hash::numeric(array_keys($data))) { if ($options['validate'] === 'only') { @@ -2183,7 +2192,7 @@ public function saveAssociated($data = null, $options = array()) { if ($options['validate'] === 'first') { $validates = $this->validateAssociated($data, $options); - if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { + if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, Hash::flatten($validates), true))) { return $validates; } $options['validate'] = false; @@ -2613,10 +2622,13 @@ public function hasAny($conditions = null) { * * Note: find(list) + database views have issues with MySQL 5.0. Try upgrading to MySQL 5.1 if you * have issues with database views. + * + * Note: find(count) has its own return values. + * * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) - * @return array Array of records - * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall + * @return array Array of records, or Null on failure. + * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html */ public function find($type = 'first', $query = array()) { $this->findQueryType = $type; @@ -2638,10 +2650,10 @@ public function find($type = 'first', $query = array()) { if ($type === 'all') { return $results; - } else { - if ($this->findMethods[$type] === true) { - return $this->{'_find' . ucfirst($type)}('after', $query, $results); - } + } + + if ($this->findMethods[$type] === true) { + return $this->{'_find' . ucfirst($type)}('after', $query, $results); } } @@ -2707,7 +2719,7 @@ protected function _findFirst($state, $query, $results = array()) { return $query; } elseif ($state === 'after') { if (empty($results[0])) { - return false; + return array(); } return $results[0]; } diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 1a193793935..5b401aff628 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -156,7 +156,7 @@ public function validateAssociated(&$data, $options = array()) { $data[$association] = $model->{$association}->data[$model->{$association}->alias]; } if (is_array($validates)) { - if (in_array(false, $validates, true)) { + if (in_array(false, Hash::flatten($validates), true)) { $validates = false; } else { $validates = true; @@ -220,7 +220,7 @@ public function validateMany(&$data, $options = array()) { $validates = $model->set($record) && $model->validates($options); $data[$key] = $model->data; } - if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) { + if ($validates === false || (is_array($validates) && in_array(false, Hash::flatten($validates), true))) { $validationErrors[$key] = $model->validationErrors; $validates = false; } else { diff --git a/lib/Cake/Model/Permission.php b/lib/Cake/Model/Permission.php index b7c92d1a77a..8d00f23ac4a 100644 --- a/lib/Cake/Model/Permission.php +++ b/lib/Cake/Model/Permission.php @@ -146,7 +146,6 @@ public function check($aro, $aco, $action = "*") { return false; case 0: continue; - break; case 1: return true; } diff --git a/lib/Cake/Model/Validator/CakeValidationRule.php b/lib/Cake/Model/Validator/CakeValidationRule.php index 72c97fe6662..7f14d27640a 100644 --- a/lib/Cake/Model/Validator/CakeValidationRule.php +++ b/lib/Cake/Model/Validator/CakeValidationRule.php @@ -163,9 +163,9 @@ public function isRequired() { */ public function checkRequired($field, &$data) { return ( - (!isset($data[$field]) && $this->isRequired() === true) || + (!array_key_exists($field, $data) && $this->isRequired() === true) || ( - isset($data[$field]) && (empty($data[$field]) && + array_key_exists($field, $data) && (empty($data[$field]) && !is_numeric($data[$field])) && $this->allowEmpty === false ) ); diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 60d41c6fe85..4ada8bd2714 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -192,7 +192,7 @@ public function getRules() { * @return CakeValidationSet this instance */ public function setRule($name, $rule) { - if (!$rule instanceof CakeValidationRule) { + if (!($rule instanceof CakeValidationRule)) { $rule = new CakeValidationRule($rule); } $this->_rules[$name] = $rule; @@ -236,9 +236,10 @@ public function removeRule($name) { */ public function setRules($rules = array(), $mergeVars = true) { if ($mergeVars === false) { - $this->_rules = $rules; - } else { - $this->_rules = array_merge($this->_rules, $rules); + $this->_rules = array(); + } + foreach ($rules as $name => $rule) { + $this->setRule($name, $rule); } return $this; } diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index bee09756544..903fa72ca58 100644 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -15,8 +15,7 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ - -App::uses('Set', 'Utility'); +App::uses('Hash', 'Utility'); /** * A class that helps wrap Request information and particulars about a single request. @@ -163,11 +162,12 @@ public function __construct($url = null, $parseEnvironment = true) { protected function _processPost() { if ($_POST) { $this->data = $_POST; - } elseif ($this->is('put') || $this->is('delete')) { - $this->data = $this->_readInput(); - if (strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0) { - parse_str($this->data, $this->data); - } + } elseif ( + ($this->is('put') || $this->is('delete')) && + strpos(env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0 + ) { + $data = $this->_readInput(); + parse_str($data, $this->data); } if (ini_get('magic_quotes_gpc') === '1') { $this->data = stripslashes_deep($this->data); @@ -353,7 +353,7 @@ protected function _processFileData($path, $data, $field) { $this->_processFileData($newPath, $fields, $field); } else { $newPath .= '.' . $field; - $this->data = Set::insert($this->data, $newPath, $fields); + $this->data = Hash::insert($this->data, $newPath, $fields); } } } diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index 79be981acb0..33be2d0f56f 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -411,6 +411,8 @@ protected function _setContentType() { } if (strpos($this->_contentType, 'text/') === 0) { $this->header('Content-Type', "{$this->_contentType}; charset={$this->_charset}"); + } elseif ($this->_contentType === 'application/json') { + $this->header('Content-Type', "{$this->_contentType}; charset=UTF-8"); } else { $this->header('Content-Type', "{$this->_contentType}"); } diff --git a/lib/Cake/Network/CakeSocket.php b/lib/Cake/Network/CakeSocket.php index aa0eb4fbd43..37fc02e5059 100644 --- a/lib/Cake/Network/CakeSocket.php +++ b/lib/Cake/Network/CakeSocket.php @@ -105,11 +105,13 @@ public function connect() { $scheme = 'ssl://'; } + //@codingStandardsIgnoreStart if ($this->config['persistent'] == true) { $this->connection = @pfsockopen($scheme . $this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); } else { $this->connection = @fsockopen($scheme . $this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); } + //@codingStandardsIgnoreEnd if (!empty($errNum) || !empty($errStr)) { $this->setLastError($errNum, $errStr); diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index 9bf4d434e1c..8237284bbe6 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -1237,7 +1237,7 @@ protected function _encodeString($text, $charset) { * @param string $message Message to wrap * @return array Wrapped message */ - protected function _wrap($message) { + protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST) { $message = str_replace(array("\r\n", "\r"), "\n", $message); $lines = explode("\n", $message); $formatted = array(); @@ -1248,7 +1248,10 @@ protected function _wrap($message) { continue; } if (!preg_match('/\<[a-z]/i', $line)) { - $formatted = array_merge($formatted, explode("\n", wordwrap($line, self::LINE_LENGTH_SHOULD, "\n"))); + $formatted = array_merge( + $formatted, + explode("\n", wordwrap($line, $wrapLength, "\n")) + ); continue; } @@ -1261,7 +1264,7 @@ protected function _wrap($message) { $tag .= $char; if ($char === '>') { $tagLength = strlen($tag); - if ($tagLength + $tmpLineLength < self::LINE_LENGTH_SHOULD) { + if ($tagLength + $tmpLineLength < $wrapLength) { $tmpLine .= $tag; $tmpLineLength += $tagLength; } else { @@ -1270,7 +1273,7 @@ protected function _wrap($message) { $tmpLine = ''; $tmpLineLength = 0; } - if ($tagLength > self::LINE_LENGTH_SHOULD) { + if ($tagLength > $wrapLength) { $formatted[] = $tag; } else { $tmpLine = $tag; @@ -1287,14 +1290,14 @@ protected function _wrap($message) { $tag = '<'; continue; } - if ($char === ' ' && $tmpLineLength >= self::LINE_LENGTH_SHOULD) { + if ($char === ' ' && $tmpLineLength >= $wrapLength) { $formatted[] = $tmpLine; $tmpLineLength = 0; continue; } $tmpLine .= $char; $tmpLineLength++; - if ($tmpLineLength === self::LINE_LENGTH_SHOULD) { + if ($tmpLineLength === $wrapLength) { $nextChar = $line[$i + 1]; if ($nextChar === ' ' || $nextChar === '<') { $formatted[] = trim($tmpLine); diff --git a/lib/Cake/Network/Email/MailTransport.php b/lib/Cake/Network/Email/MailTransport.php index 2b849772ac0..950459a7504 100644 --- a/lib/Cake/Network/Email/MailTransport.php +++ b/lib/Cake/Network/Email/MailTransport.php @@ -42,11 +42,7 @@ public function send(CakeEmail $email) { $headers = $this->_headersToString($headers, $eol); $message = implode($eol, $email->message()); - $params = null; - if (!ini_get('safe_mode')) { - $params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null; - } - + $params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null; $this->_mail($to, $email->subject(), $message, $headers, $params); return array('headers' => $headers, 'message' => $message); } @@ -58,12 +54,18 @@ public function send(CakeEmail $email) { * @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 + * @param string $params additional params for sending email, will be ignored when in safe_mode * @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)) { + if (ini_get('safe_mode')) { + //@codingStandardsIgnoreStart + if (!@mail($to, $subject, $message, $headers)) { + throw new SocketException(__d('cake_dev', 'Could not send email.')); + } + } elseif (!@mail($to, $subject, $message, $headers, $params)) { + //@codingStandardsIgnoreEnd throw new SocketException(__d('cake_dev', 'Could not send email.')); } } diff --git a/lib/Cake/Network/Http/HttpResponse.php b/lib/Cake/Network/Http/HttpResponse.php index dec5070417c..90c622588f4 100644 --- a/lib/Cake/Network/Http/HttpResponse.php +++ b/lib/Cake/Network/Http/HttpResponse.php @@ -219,7 +219,6 @@ protected function _decodeChunkedBody($body) { $chunkSize = 0; $hexLength = 0; - $chunkExtensionName = ''; $chunkExtensionValue = ''; if (isset($match[0])) { $chunkSize = $match[0]; @@ -227,9 +226,6 @@ protected function _decodeChunkedBody($body) { if (isset($match[1])) { $hexLength = $match[1]; } - if (isset($match[2])) { - $chunkExtensionName = $match[2]; - } if (isset($match[3])) { $chunkExtensionValue = $match[3]; } @@ -237,9 +233,6 @@ protected function _decodeChunkedBody($body) { $body = substr($body, strlen($chunkSize)); $chunkLength = hexdec($hexLength); $chunk = substr($body, 0, $chunkLength); - if (!empty($chunkExtensionName)) { - // @todo See if there are popular chunk extensions we should implement - } $decodedBody .= $chunk; if ($chunkLength !== 0) { $body = substr($body, $chunkLength + strlen("\r\n")); @@ -291,7 +284,6 @@ protected function _parseHeader($header) { * * @param array $header Header array containing one ore more 'Set-Cookie' headers. * @return mixed Either false on no cookies, or an array of cookies received. - * @todo Make this 100% RFC 2965 confirm */ public function parseCookies($header) { $cookieHeader = $this->getHeader('Set-Cookie', $header); @@ -334,7 +326,6 @@ public function parseCookies($header) { * @param string $token Token to unescape * @param array $chars * @return string Unescaped token - * @todo Test $chars parameter */ protected function _unescapeToken($token, $chars = null) { $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/'; @@ -348,7 +339,6 @@ protected function _unescapeToken($token, $chars = null) { * @param boolean $hex true to get them as HEX values, false otherwise * @param array $chars * @return array Escape chars - * @todo Test $chars parameter */ protected function _tokenEscapeChars($hex = true, $chars = null) { if (!empty($chars)) { diff --git a/lib/Cake/Network/Http/HttpSocket.php b/lib/Cake/Network/Http/HttpSocket.php index 502505676a9..44960cd5089 100644 --- a/lib/Cake/Network/Http/HttpSocket.php +++ b/lib/Cake/Network/Http/HttpSocket.php @@ -403,7 +403,7 @@ public function request($request = array()) { } if ($this->request['redirect'] && $this->response->isRedirect()) { - $request['uri'] = $this->response->getHeader('Location'); + $request['uri'] = trim(urldecode($this->response->getHeader('Location')), '='); $request['redirect'] = is_int($this->request['redirect']) ? $this->request['redirect'] - 1 : $this->request['redirect']; $this->response = $this->request($request); } @@ -901,7 +901,6 @@ protected function _buildHeader($header, $mode = 'standard') { * * @param array $cookies Array of cookies to send with the request. * @return string Cookie header string to be sent with the request. - * @todo Refactor token escape mechanism to be configurable */ public function buildCookies($cookies) { $header = array(); @@ -917,7 +916,6 @@ public function buildCookies($cookies) { * @param string $token Token to escape * @param array $chars * @return string Escaped token - * @todo Test $chars parameter */ protected function _escapeToken($token, $chars = null) { $regex = '/([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])/'; @@ -931,7 +929,6 @@ protected function _escapeToken($token, $chars = null) { * @param boolean $hex true to get them as HEX values, false otherwise * @param array $chars * @return array Escape chars - * @todo Test $chars parameter */ protected function _tokenEscapeChars($hex = true, $chars = null) { if (!empty($chars)) { diff --git a/lib/Cake/Routing/Route/CakeRoute.php b/lib/Cake/Routing/Route/CakeRoute.php index 8fab212cf57..25ec14a5a85 100644 --- a/lib/Cake/Routing/Route/CakeRoute.php +++ b/lib/Cake/Routing/Route/CakeRoute.php @@ -11,8 +11,7 @@ * @since CakePHP(tm) v 1.3 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ - -App::uses('Set', 'Utility'); +App::uses('Hash', 'Utility'); /** * A single Route used by the Router to connect requests to diff --git a/lib/Cake/Routing/Router.php b/lib/Cake/Routing/Router.php index 2a02570cd6a..4e9478046e4 100644 --- a/lib/Cake/Routing/Router.php +++ b/lib/Cake/Routing/Router.php @@ -259,18 +259,28 @@ public static function resourceMap($resourceMap = null) { * $options offers four 'special' keys. `pass`, `named`, `persist` and `routeClass` * have special meaning in the $options array. * - * `pass` is used to define which of the routed parameters should be shifted into the pass array. Adding a - * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')` + * - `pass` is used to define which of the routed parameters should be shifted into the pass array. Adding a + * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')` + * - `persist` is used to define which route parameters should be automatically included when generating + * new urls. You can override persistent parameters by redefining them in a url or remove them by + * setting the parameter to `false`. Ex. `'persist' => array('lang')` + * - `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing, + * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'` + * - `named` is used to configure named parameters at the route level. This key uses the same options + * as Router::connectNamed() * - * `persist` is used to define which route parameters should be automatically included when generating - * new urls. You can override persistent parameters by redefining them in a url or remove them by - * setting the parameter to `false`. Ex. `'persist' => array('lang')` + * You can also add additional conditions for matching routes to the $defaults array. + * The following conditions can be used: * - * `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing, - * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'` + * - `[type]` Only match requests for specific content types. + * - `[method]` Only match requests with specific HTTP verbs. + * - `[server]` Only match when $_SERVER['SERVER_NAME'] matches the given value. * - * `named` is used to configure named parameters at the route level. This key uses the same options - * as Router::connectNamed() + * Example of using the `[method]` condition: + * + * `Router::connect('/tasks', array('controller' => 'tasks', 'action' => 'index', '[method]' => 'GET'));` + * + * The above route will only be matched for GET requests. POST requests will fail to match this route. * * @param string $route A string describing the template of the route * @param array $defaults An array describing the default route parameters. These parameters will be used by default diff --git a/lib/Cake/Test/Case/BasicsTest.php b/lib/Cake/Test/Case/BasicsTest.php index 0c12fed6fab..10dd24cf472 100644 --- a/lib/Cake/Test/Case/BasicsTest.php +++ b/lib/Cake/Test/Case/BasicsTest.php @@ -67,7 +67,7 @@ public function testArrayDiffKey() { $one = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true); $two = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true); $result = array_diff_key($one, $two); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** @@ -282,7 +282,9 @@ public function testCache() { $result = cache('basics_test'); $this->assertEquals('simple cache write', $result); - @unlink(CACHE . 'basics_test'); + if (file_exists(CACHE . 'basics_test')) { + unlink(CACHE . 'basics_test'); + } cache('basics_test', 'expired', '+1 second'); sleep(2); @@ -603,7 +605,9 @@ public function testTranslateDomainCategoryPlural() { * @return void */ public function testLogError() { - @unlink(LOGS . 'error.log'); + if (file_exists(LOGS . 'error.log')) { + unlink(LOGS . 'error.log'); + } // disable stderr output for this test if (CakeLog::stream('stderr')) { diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index a1c2abd5bf4..27453d84e8c 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -111,9 +111,11 @@ public function testMultipleServers() { foreach ($servers as $server) { list($host, $port) = explode(':', $server); + //@codingStandardsIgnoreStart if (!@$Memcache->connect($host, $port)) { $available = false; } + //@codingStandardsIgnoreEnd } $this->skipIf(!$available, 'Need memcache servers at ' . implode(', ', $servers) . ' to run this test.'); @@ -121,7 +123,6 @@ public function testMultipleServers() { $Memcache = new MemcacheEngine(); $Memcache->init(array('engine' => 'Memcache', 'servers' => $servers)); - $servers = array_keys($Memcache->__Memcache->getExtendedStats()); $settings = $Memcache->settings(); $this->assertEquals($settings['servers'], $servers); Cache::drop('dual_server'); @@ -230,12 +231,11 @@ public function testExpiry() { $result = Cache::write('other_test', $data, 'memcache'); $this->assertTrue($result); - sleep(2); + sleep(3); $result = Cache::read('other_test', 'memcache'); $this->assertFalse($result); Cache::config('memcache', array('duration' => '+1 second')); - sleep(2); $result = Cache::read('other_test', 'memcache'); $this->assertFalse($result); diff --git a/lib/Cake/Test/Case/Console/Command/AclShellTest.php b/lib/Cake/Test/Case/Console/Command/AclShellTest.php index 7dfbbc0616a..cf98cabf5d1 100644 --- a/lib/Cake/Test/Case/Console/Command/AclShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/AclShellTest.php @@ -177,7 +177,7 @@ public function testDelete() { $Aro = ClassRegistry::init('Aro'); $result = $Aro->findById(3); - $this->assertFalse($result); + $this->assertSame(array(), $result); } /** diff --git a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php index a17d2bb9de0..4d11cbe6000 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php @@ -181,7 +181,7 @@ public function testGetNameInvalidIndex() { public function testDoHelpersNo() { $this->Task->expects($this->any())->method('in')->will($this->returnValue('n')); $result = $this->Task->doHelpers(); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** @@ -218,7 +218,7 @@ public function testDoHelpersTrailingCommas() { public function testDoComponentsNo() { $this->Task->expects($this->any())->method('in')->will($this->returnValue('n')); $result = $this->Task->doComponents(); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** diff --git a/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php b/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php index 0677b09c4a0..7d3a23ec12c 100644 --- a/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php +++ b/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php @@ -78,6 +78,18 @@ public function testAddOptionLong() { $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out'); } +/** + * test adding an option with a zero value + * + * @return void + */ + public function testAddOptionZero() { + $parser = new ConsoleOptionParser('test', false); + $parser->addOption('count', array()); + $result = $parser->parse(array('--count', '0')); + $this->assertEquals(array('count' => '0', 'help' => false), $result[0], 'Zero parameter did not parse out'); + } + /** * test addOption with an object. * @@ -314,10 +326,29 @@ public function testAddArgumentObject() { $parser = new ConsoleOptionParser('test', false); $parser->addArgument(new ConsoleInputArgument('test')); $result = $parser->arguments(); - $this->assertEquals(1, count($result)); + $this->assertCount(1, $result); $this->assertEquals('test', $result[0]->name()); } +/** + * Test adding arguments out of order. + * + * @return void + */ + public function testAddArgumentOutOfOrder() { + $parser = new ConsoleOptionParser('test', false); + $parser->addArgument('name', array('index' => 1, 'help' => 'first argument')) + ->addArgument('bag', array('index' => 2, 'help' => 'second argument')) + ->addArgument('other', array('index' => 0, 'help' => 'Zeroth argument')); + + $result = $parser->arguments(); + $this->assertCount(3, $result); + $this->assertEquals('other', $result[0]->name()); + $this->assertEquals('name', $result[1]->name()); + $this->assertEquals('bag', $result[2]->name()); + $this->assertSame(array(0, 1, 2), array_keys($result)); + } + /** * test overwriting positional arguments. * diff --git a/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php b/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php index bfc33f68a71..c0964ccb004 100644 --- a/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php @@ -483,14 +483,14 @@ public function testRevoke() { /** * debug function - to help editing/creating test cases for the ACL component * - * To check the overall ACL status at any time call $this->__debug(); + * To check the overall ACL status at any time call $this->_debug(); * Generates a list of the current aro and aco structures and a grid dump of the permissions that are defined * Only designed to work with the db based ACL * * @param bool $treesToo * @return void */ - protected function __debug($printTreesToo = false) { + protected function _debug($printTreesToo = false) { $this->Acl->Aro->displayField = 'alias'; $this->Acl->Aco->displayField = 'alias'; $aros = $this->Acl->Aro->find('list', array('order' => 'lft')); @@ -518,10 +518,10 @@ protected function __debug($printTreesToo = false) { } foreach ($permissions as $key => $values) { array_unshift($values, $key); - $values = array_map(array(&$this, '__pad'), $values); + $values = array_map(array(&$this, '_pad'), $values); $permissions[$key] = implode (' ', $values); } - $permisssions = array_map(array(&$this, '__pad'), $permissions); + $permisssions = array_map(array(&$this, '_pad'), $permissions); array_unshift($permissions, 'Current Permissions :'); if ($printTreesToo) { debug(array('aros' => $this->Acl->Aro->generateTreeList(), 'acos' => $this->Acl->Aco->generateTreeList())); @@ -537,7 +537,7 @@ protected function __debug($printTreesToo = false) { * @param integer $len * @return void */ - protected function __pad($string = '', $len = 14) { + protected function _pad($string = '', $len = 14) { return str_pad($string, $len); } } diff --git a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php index 380c2909908..7c4be8972a7 100644 --- a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php @@ -412,11 +412,11 @@ public function testReadingCookieDataOnStartup() { $this->assertNull($data); $_COOKIE['CakeTestCookie'] = array( - 'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')), + 'Encrytped_array' => $this->_encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')), 'Encrytped_multi_cookies' => array( - 'name' => $this->__encrypt('CakePHP'), - 'version' => $this->__encrypt('1.2.0.x'), - 'tag' => $this->__encrypt('CakePHP Rocks!')), + 'name' => $this->_encrypt('CakePHP'), + 'version' => $this->_encrypt('1.2.0.x'), + 'tag' => $this->_encrypt('CakePHP Rocks!')), 'Plain_array' => '{"name":"CakePHP","version":"1.2.0.x","tag":"CakePHP Rocks!"}', 'Plain_multi_cookies' => array( 'name' => 'CakePHP', @@ -467,11 +467,11 @@ public function testReadingCookieDataWithoutStartup() { $this->assertEquals($expected, $data); $_COOKIE['CakeTestCookie'] = array( - 'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')), + 'Encrytped_array' => $this->_encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!')), 'Encrytped_multi_cookies' => array( - 'name' => $this->__encrypt('CakePHP'), - 'version' => $this->__encrypt('1.2.0.x'), - 'tag' => $this->__encrypt('CakePHP Rocks!')), + 'name' => $this->_encrypt('CakePHP'), + 'version' => $this->_encrypt('1.2.0.x'), + 'tag' => $this->_encrypt('CakePHP Rocks!')), 'Plain_array' => '{"name":"CakePHP","version":"1.2.0.x","tag":"CakePHP Rocks!"}', 'Plain_multi_cookies' => array( 'name' => 'CakePHP', @@ -594,7 +594,7 @@ protected function _implode(array $array) { * @param array|string $value * @return string */ - protected function __encrypt($value) { + protected function _encrypt($value) { if (is_array($value)) { $value = $this->_implode($value); } diff --git a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php index 84f78036b9a..1d1e7f85235 100644 --- a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php @@ -194,6 +194,22 @@ public function testBlackholeWithBrokenCallback() { $this->Controller->Security->blackHole($this->Controller, 'csrf'); } +/** + * Ensure that directly requesting the blackholeCallback as the controller + * action results in an exception. + * + * @return void + */ + public function testExceptionWhenActionIsBlackholeCallback() { + $this->Controller->request->addParams(array( + 'controller' => 'posts', + 'action' => 'fail' + )); + $this->assertFalse($this->Controller->failed); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed, 'Request was blackholed.'); + } + /** * test that initialize can set properties. * diff --git a/lib/Cake/Test/Case/Controller/ScaffoldTest.php b/lib/Cake/Test/Case/Controller/ScaffoldTest.php index 14fe1826454..63bc445c94b 100644 --- a/lib/Cake/Test/Case/Controller/ScaffoldTest.php +++ b/lib/Cake/Test/Case/Controller/ScaffoldTest.php @@ -217,6 +217,7 @@ public function testScaffoldVariableSetting() { $this->assertEquals('scaffoldMock', $result['singularVar']); $this->assertEquals('scaffoldMock', $result['pluralVar']); $this->assertEquals(array('id', 'user_id', 'title', 'body', 'published', 'created', 'updated'), $result['scaffoldFields']); + $this->assertArrayHasKey('plugin', $result['associations']['belongsTo']['User']); } /** diff --git a/lib/Cake/Test/Case/Core/AppTest.php b/lib/Cake/Test/Case/Core/AppTest.php index 3b6cef55e07..0c66766cd55 100644 --- a/lib/Cake/Test/Case/Core/AppTest.php +++ b/lib/Cake/Test/Case/Core/AppTest.php @@ -347,7 +347,7 @@ public function testListObjects() { $this->assertEquals($expected, $result); $result = App::objects('NonExistingType'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); App::build(array( 'plugins' => array( @@ -414,9 +414,9 @@ public function testListObjectsInPlugin() { $this->assertTrue(in_array('OtherComponent', $result)); $result = App::objects('TestPluginTwo.behavior'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = App::objects('TestPluginTwo.Model/Behavior'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = App::objects('model', null, false); $this->assertTrue(in_array('Comment', $result)); diff --git a/lib/Cake/Test/Case/Core/ConfigureTest.php b/lib/Cake/Test/Case/Core/ConfigureTest.php index 7351f83f199..224e7b5e954 100644 --- a/lib/Cake/Test/Case/Core/ConfigureTest.php +++ b/lib/Cake/Test/Case/Core/ConfigureTest.php @@ -383,7 +383,9 @@ public function testDump() { $result = file_get_contents(TMP . 'config_test.php'); $this->assertContains('assertContains('$config = ', $result); - @unlink(TMP . 'config_test.php'); + if (file_exists(TMP . 'config_test.php')) { + unlink(TMP . 'config_test.php'); + } } /** @@ -402,7 +404,9 @@ public function testDumpPartial() { $this->assertContains('Error', $result); $this->assertNotContains('debug', $result); - @unlink(TMP . 'config_test.php'); + if (file_exists(TMP . 'config_test.php')) { + unlink(TMP . 'config_test.php'); + } } } diff --git a/lib/Cake/Test/Case/Core/ObjectTest.php b/lib/Cake/Test/Case/Core/ObjectTest.php index 222a231a7f2..5738e201c11 100644 --- a/lib/Cake/Test/Case/Core/ObjectTest.php +++ b/lib/Cake/Test/Case/Core/ObjectTest.php @@ -619,6 +619,24 @@ public function testRequestActionParamParseAndPass() { $this->assertEquals($expected, $result['named']); } +/** + * Test that requestAction handles get parameters correctly. + * + * @return void + */ + public function testRequestActionGetParameters() { + $result = $this->object->requestAction( + '/request_action/params_pass?get=value&limit=5' + ); + $this->assertEquals('value', $result->query['get']); + + $result = $this->object->requestAction( + array('controller' => 'request_action', 'action' => 'params_pass'), + array('url' => array('get' => 'value', 'limit' => 5)) + ); + $this->assertEquals('value', $result->query['get']); + } + /** * test that requestAction does not fish data out of the POST * superglobal. @@ -632,7 +650,6 @@ public function testRequestActionNoPostPassing() { 'item' => 'value' )); $result = $this->object->requestAction(array('controller' => 'request_action', 'action' => 'post_pass')); - $expected = null; $this->assertEmpty($result); $result = $this->object->requestAction( diff --git a/lib/Cake/Test/Case/Error/ErrorHandlerTest.php b/lib/Cake/Test/Case/Error/ErrorHandlerTest.php index e4bd5790201..4945533ff5c 100644 --- a/lib/Cake/Test/Case/Error/ErrorHandlerTest.php +++ b/lib/Cake/Test/Case/Error/ErrorHandlerTest.php @@ -124,7 +124,9 @@ public function testErrorSuppressed() { $this->_restoreError = true; ob_start(); + //@codingStandardsIgnoreStart @include 'invalid.file'; + //@codingStandardsIgnoreEnd $result = ob_get_clean(); $this->assertTrue(empty($result)); } @@ -138,7 +140,7 @@ public function testHandleErrorDebugOff() { Configure::write('debug', 0); Configure::write('Error.trace', false); if (file_exists(LOGS . 'debug.log')) { - @unlink(LOGS . 'debug.log'); + unlink(LOGS . 'debug.log'); } set_error_handler('ErrorHandler::handleError'); @@ -152,7 +154,9 @@ public function testHandleErrorDebugOff() { '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (Notice|Debug): Notice \(8\): Undefined variable:\s+out in \[.+ line \d+\]$/', $result[0] ); - @unlink(LOGS . 'debug.log'); + if (file_exists(LOGS . 'debug.log')) { + unlink(LOGS . 'debug.log'); + } } /** @@ -164,7 +168,7 @@ public function testHandleErrorLoggingTrace() { Configure::write('debug', 0); Configure::write('Error.trace', true); if (file_exists(LOGS . 'debug.log')) { - @unlink(LOGS . 'debug.log'); + unlink(LOGS . 'debug.log'); } set_error_handler('ErrorHandler::handleError'); @@ -179,7 +183,9 @@ public function testHandleErrorLoggingTrace() { ); $this->assertRegExp('/^Trace:/', $result[1]); $this->assertRegExp('/^ErrorHandlerTest\:\:testHandleErrorLoggingTrace\(\)/', $result[2]); - @unlink(LOGS . 'debug.log'); + if (file_exists(LOGS . 'debug.log')) { + unlink(LOGS . 'debug.log'); + } } /** diff --git a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php index a5704433be4..d11c664135e 100644 --- a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php +++ b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php @@ -19,7 +19,6 @@ App::uses('ExceptionRenderer', 'Error'); App::uses('Controller', 'Controller'); -App::uses('AppController', 'Controller'); App::uses('Component', 'Controller'); App::uses('Router', 'Routing'); diff --git a/lib/Cake/Test/Case/Log/CakeLogTest.php b/lib/Cake/Test/Case/Log/CakeLogTest.php index e54f2c71a67..13d90989f7c 100644 --- a/lib/Cake/Test/Case/Log/CakeLogTest.php +++ b/lib/Cake/Test/Case/Log/CakeLogTest.php @@ -150,7 +150,7 @@ public function testConfig() { $this->assertEquals(array('file'), $result); if (file_exists(LOGS . 'error.log')) { - @unlink(LOGS . 'error.log'); + unlink(LOGS . 'error.log'); } CakeLog::write(LOG_WARNING, 'Test warning'); $this->assertTrue(file_exists(LOGS . 'error.log')); @@ -175,7 +175,7 @@ public function testDrop() { CakeLog::drop('file'); $result = CakeLog::configured(); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** @@ -383,7 +383,6 @@ public function testScopedLoggingBC() { CakeLog::drop('shops'); } - public function testScopedLoggingExclusive() { $this->_deleteLogs(); diff --git a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php index 8345c87d616..d84f07e67e8 100644 --- a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php @@ -261,6 +261,19 @@ public function testContain() { $this->assertFalse(Set::matches('/Comment/User', $r)); } +/** + * Test that mixing contain() and the contain find option. + * + * @return void + */ + public function testContainAndContainOption() { + $this->Article->contain(); + $r = $this->Article->find('all', array( + 'contain' => array('Comment') + )); + $this->assertTrue(isset($r[0]['Comment']), 'No comment returned'); + } + /** * testFindEmbeddedNoBindings method * @@ -2980,7 +2993,8 @@ public function testHasOneFieldsInContain() { 'User' => array( 'fields' => array('user') ) - ) + ), + 'order' => 'Article.id ASC', )); $this->assertTrue(isset($result[0]['Article']['title']), 'title missing %s'); $this->assertTrue(isset($result[0]['Article']['body']), 'body missing %s'); @@ -3003,7 +3017,10 @@ public function testFindConditionalBinding() { 'conditions' => array('created >=' => '2007-03-18 12:24') ) )); - $result = $this->Article->find('all', array('fields' => array('title'), 'order' => array('Article.id' => 'ASC'))); + $result = $this->Article->find('all', array( + 'fields' => array('title'), + 'order' => array('Article.id' => 'ASC') + )); $expected = array( array( 'Article' => array('id' => 1, 'title' => 'First Article'), diff --git a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php index 9a7ab43426c..0a4bf9e15cc 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php @@ -422,7 +422,7 @@ public function testMissingTranslation() { $TestModel = new TranslatedItem(); $TestModel->locale = 'rus'; $result = $TestModel->read(null, 1); - $this->assertFalse($result); + $this->assertSame(array(), $result); $TestModel->locale = array('rus'); $result = $TestModel->read(null, 1); @@ -460,10 +460,10 @@ public function testTranslatedFindList() { Configure::write('debug', 0); $result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => false)); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => 'after')); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); Configure::write('debug', $debug); } @@ -530,6 +530,76 @@ public function testSaveCreate() { $this->assertEquals($expected, $result); } +/** + * test save multiple locales method + * + * @return void + */ + public function testSaveMultipleLocales() { + $this->loadFixtures('Translate', 'TranslatedItem'); + + $TestModel = new TranslatedItem(); + $data = array( + 'slug' => 'fourth_translated', + 'title' => array( + 'eng' => 'Title #4', + 'spa' => 'Leyenda #4', + ), + 'content' => array( + 'eng' => 'Content #4', + 'spa' => 'Contenido #4', + ), + 'translated_article_id' => 1, + ); + $TestModel->create(); + $TestModel->save($data); + + $translations = array('title' => 'Title', 'content' => 'Content'); + $TestModel->bindTranslation($translations, false); + $TestModel->locale = array('eng', 'spa'); + $result = $TestModel->read(); + + $this->assertCount(2, $result['Title']); + $this->assertEquals($result['Title'][0]['locale'], 'eng'); + $this->assertEquals($result['Title'][0]['content'], 'Title #4'); + $this->assertEquals($result['Title'][1]['locale'], 'spa'); + $this->assertEquals($result['Title'][1]['content'], 'Leyenda #4'); + + $this->assertCount(2, $result['Content']); + } + +/** + * testSaveAssociatedCreate method + * + * @return void + */ + public function testSaveAssociatedMultipleLocale() { + $this->loadFixtures('Translate', 'TranslatedItem'); + + $TestModel = new TranslatedItem(); + $data = array( + 'slug' => 'fourth_translated', + 'title' => array( + 'eng' => 'Title #4', + 'spa' => 'Leyenda #4', + ), + 'content' => array( + 'eng' => 'Content #4', + 'spa' => 'Contenido #4', + ), + 'translated_article_id' => 1, + ); + $TestModel->create(); + $TestModel->saveAssociated($data); + + $translations = array('title' => 'Title', 'content' => 'Content'); + $TestModel->bindTranslation($translations, false); + $TestModel->locale = array('eng', 'spa'); + $result = $TestModel->read(); + $this->assertCount(2, $result['Title']); + $this->assertCount(2, $result['Content']); + } + /** * Test that saving only some of the translated fields allows the record to be found again. * diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php index adcbf4c581d..4b316e967f1 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php @@ -183,19 +183,19 @@ public function testRecoverUsingParentMode() { $this->Tree = new $modelClass(); $this->Tree->Behaviors->disable('Tree'); - $this->Tree->save(array('parent_id' => null, 'name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0)); - $node1 = $this->Tree->id; + $this->Tree->save(array('name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0)); + $node1 = $this->Tree->id; $this->Tree->create(); - $this->Tree->save(array('parent_id' => null, 'name' => 'About Us', $parentField => $node1, $leftField => 0, $rightField => 0)); - $node11 = $this->Tree->id; + $this->Tree->save(array('name' => 'About Us', $parentField => $node1, $leftField => 0, $rightField => 0)); + $node11 = $this->Tree->id; $this->Tree->create(); - $this->Tree->save(array('parent_id' => null, 'name' => 'Programs', $parentField => $node1, $leftField => 0, $rightField => 0)); - $node12 = $this->Tree->id; + $this->Tree->save(array('name' => 'Programs', $parentField => $node1, $leftField => 0, $rightField => 0)); + $node12 = $this->Tree->id; $this->Tree->create(); - $this->Tree->save(array('parent_id' => null, 'name' => 'Mission and History', $parentField => $node11, $leftField => 0, $rightField => 0)); + $this->Tree->save(array('name' => 'Mission and History', $parentField => $node11, $leftField => 0, $rightField => 0)); $this->Tree->create(); - $this->Tree->save(array('parent_id' => null, 'name' => 'Overview', $parentField => $node12, $leftField => 0, $rightField => 0)); + $this->Tree->save(array('name' => 'Overview', $parentField => $node12, $leftField => 0, $rightField => 0)); $this->Tree->Behaviors->enable('Tree'); @@ -224,6 +224,74 @@ public function testRecoverUsingParentMode() { $this->assertEquals($expected, $result); } +/** + * testRecoverUsingParentModeAndDelete method + * + * @return void + */ + public function testRecoverUsingParentModeAndDelete() { + extract($this->settings); + $this->Tree = new $modelClass(); + $this->Tree->Behaviors->disable('Tree'); + + $this->Tree->save(array('name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0)); + $node1 = $this->Tree->id; + + $this->Tree->create(); + $this->Tree->save(array('name' => 'About Us', $parentField => $node1, $leftField => 0, $rightField => 0)); + $node11 = $this->Tree->id; + $this->Tree->create(); + $this->Tree->save(array('name' => 'Programs', $parentField => $node1, $leftField => 0, $rightField => 0)); + $node12 = $this->Tree->id; + $this->Tree->create(); + $this->Tree->save(array('name' => 'Mission and History', $parentField => $node11, $leftField => 0, $rightField => 0)); + $this->Tree->create(); + $this->Tree->save(array('name' => 'Overview', $parentField => $node12, $leftField => 0, $rightField => 0)); + $this->Tree->create(); + $this->Tree->save(array('name' => 'Lost', $parentField => 9, $leftField => 0, $rightField => 0)); + + $this->Tree->Behaviors->enable('Tree'); + + $this->Tree->bindModel(array('belongsTo' => array('Parent' => array( + 'className' => $this->Tree->name, + 'foreignKey' => $parentField + )))); + $this->Tree->bindModel(array('hasMany' => array('Child' => array( + 'className' => $this->Tree->name, + 'foreignKey' => $parentField + )))); + + $result = $this->Tree->verify(); + $this->assertNotSame($result, true); + + $count = $this->Tree->find('count'); + $this->assertEquals(6, $count); + + $result = $this->Tree->recover('parent', 'delete'); + $this->assertTrue($result); + + $result = $this->Tree->verify(); + $this->assertTrue($result); + + $count = $this->Tree->find('count'); + $this->assertEquals(5, $count); + + $result = $this->Tree->find('first', array( + 'fields' => array('name', $parentField, $leftField, $rightField), + 'conditions' => array('name' => 'Main'), + 'recursive' => -1 + )); + $expected = array( + $modelClass => array( + 'name' => 'Main', + $parentField => null, + $leftField => 1, + $rightField => 10 + ) + ); + $this->assertEquals($expected, $result); + } + /** * testRecoverFromMissingParent method * diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php index b9e829758b9..66adac3e5c3 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php @@ -169,12 +169,13 @@ public function testMoveDownWithScope() { public function testTranslatingTree() { $this->Tree = new FlagTree(); $this->Tree->cacheQueries = false; - $this->Tree->Behaviors->attach('Translate', array('name')); + $this->Tree->Behaviors->attach('Translate', array('title')); //Save $this->Tree->locale = 'eng'; $data = array('FlagTree' => array( - 'name' => 'name #1', + 'title' => 'name #1', + 'name' => 'test', 'locale' => 'eng', 'parent_id' => null, )); @@ -182,7 +183,8 @@ public function testTranslatingTree() { $result = $this->Tree->find('all'); $expected = array(array('FlagTree' => array( 'id' => 1, - 'name' => 'name #1', + 'title' => 'name #1', + 'name' => 'test', 'parent_id' => null, 'lft' => 1, 'rght' => 2, @@ -191,15 +193,16 @@ public function testTranslatingTree() { ))); $this->assertEquals($expected, $result); - //update existing record, same locale + // update existing record, same locale $this->Tree->create(); - $data['FlagTree']['name'] = 'Named 2'; + $data['FlagTree']['title'] = 'Named 2'; $this->Tree->id = 1; $this->Tree->save($data); $result = $this->Tree->find('all'); $expected = array(array('FlagTree' => array( 'id' => 1, - 'name' => 'Named 2', + 'title' => 'Named 2', + 'name' => 'test', 'parent_id' => null, 'lft' => 1, 'rght' => 2, @@ -208,51 +211,67 @@ public function testTranslatingTree() { ))); $this->assertEquals($expected, $result); - //update different locale, same record + // update different locale, same record $this->Tree->create(); $this->Tree->locale = 'deu'; $this->Tree->id = 1; $data = array('FlagTree' => array( 'id' => 1, 'parent_id' => null, - 'name' => 'namen #1', + 'title' => 'namen #1', + 'name' => 'test', 'locale' => 'deu', )); $this->Tree->save($data); $this->Tree->locale = 'deu'; $result = $this->Tree->find('all'); - $expected = array(array('FlagTree' => array( - 'id' => 1, - 'name' => 'namen #1', - 'parent_id' => null, - 'lft' => 1, - 'rght' => 2, - 'flag' => 0, - 'locale' => 'deu', - ))); + $expected = array( + array( + 'FlagTree' => array( + 'id' => 1, + 'title' => 'namen #1', + 'name' => 'test', + 'parent_id' => null, + 'lft' => 1, + 'rght' => 2, + 'flag' => 0, + 'locale' => 'deu', + ) + ) + ); $this->assertEquals($expected, $result); - //Save with bindTranslation + // Save with bindTranslation $this->Tree->locale = 'eng'; $data = array( - 'name' => array('eng' => 'New title', 'spa' => 'Nuevo leyenda'), + 'title' => array('eng' => 'New title', 'spa' => 'Nuevo leyenda'), + 'name' => 'test', 'parent_id' => null ); $this->Tree->create($data); $this->Tree->save(); $this->Tree->unbindTranslation(); - $translations = array('name' => 'Name'); + $translations = array('title' => 'Title'); $this->Tree->bindTranslation($translations, false); $this->Tree->locale = array('eng', 'spa'); $result = $this->Tree->read(); $expected = array( - 'FlagTree' => array('id' => 2, 'parent_id' => null, 'locale' => 'eng', 'name' => 'New title', 'flag' => 0, 'lft' => 3, 'rght' => 4), - 'Name' => array( - array('id' => 21, 'locale' => 'eng', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'name', 'content' => 'New title'), - array('id' => 22, 'locale' => 'spa', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'name', 'content' => 'Nuevo leyenda') + 'FlagTree' => array( + 'id' => 2, + 'parent_id' => null, + 'locale' => 'eng', + 'name' => 'test', + 'title' => 'New title', + 'flag' => 0, + 'lft' => 3, + 'rght' => 4 + ), + 'Title' => array( + array('id' => 21, 'locale' => 'eng', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'title', 'content' => 'New title'), + array('id' => 22, 'locale' => 'spa', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Nuevo leyenda') ), ); $this->assertEquals($expected, $result); diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php index bd3590b3ce3..ce0b32ac6c3 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php @@ -248,7 +248,10 @@ public function testNoAmbiguousColumn() { $this->Tree->bindModel(array('belongsTo' => array('Dummy' => array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false); - $data = $this->Tree->find('first', array('conditions' => array($modelClass . '.name' => '1. Root'))); + $data = $this->Tree->find('first', array( + 'conditions' => array($modelClass . '.name' => '1. Root'), + 'recursive' => -1 + )); $this->Tree->id = $data[$modelClass]['id']; $direct = $this->Tree->children(null, true, array('name', $leftField, $rightField)); diff --git a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php index 19497f7a622..d2df347aa68 100644 --- a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php +++ b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php @@ -66,15 +66,12 @@ public function beforeFind(Model $model, $query) { switch ($settings['beforeFind']) { case 'on': return false; - break; case 'test': return null; - break; case 'modify': $query['fields'] = array($model->alias . '.id', $model->alias . '.name', $model->alias . '.mytime'); $query['recursive'] = -1; return $query; - break; } } @@ -94,16 +91,12 @@ public function afterFind(Model $model, $results, $primary) { switch ($settings['afterFind']) { case 'on': return array(); - break; case 'test': return true; - break; case 'test2': return null; - break; case 'modify': return Hash::extract($results, "{n}.{$model->alias}"); - break; } } @@ -121,14 +114,11 @@ public function beforeSave(Model $model) { switch ($settings['beforeSave']) { case 'on': return false; - break; case 'test': return true; - break; case 'modify': $model->data[$model->alias]['name'] .= ' modified before'; return true; - break; } } @@ -157,7 +147,6 @@ public function afterSave(Model $model, $created) { break; case 'test2': return false; - break; case 'modify': $model->data[$model->alias]['name'] .= ' ' . $string; break; @@ -179,18 +168,14 @@ public function beforeValidate(Model $model) { case 'on': $model->invalidate('name'); return true; - break; case 'test': return null; - break; case 'whitelist': $this->_addToWhitelist($model, array('name')); return true; - break; case 'stop': $model->invalidate('name'); return false; - break; } } @@ -209,11 +194,9 @@ public function afterValidate(Model $model) { switch ($settings['afterValidate']) { case 'on': return false; - break; case 'test': $model->data = array('foo'); return true; - break; } } @@ -232,17 +215,14 @@ public function beforeDelete(Model $model, $cascade = true) { switch ($settings['beforeDelete']) { case 'on': return false; - break; case 'test': return null; - break; case 'test2': echo 'beforeDelete success'; if ($cascade) { echo ' (cascading) '; } return true; - break; } } @@ -786,21 +766,22 @@ public function testBehaviorHasOneFindCallbacks() { public function testBehaviorBelongsToFindCallbacks() { $this->skipIf($this->db instanceof Sqlserver, 'This test is not compatible with SQL Server.'); + $conditions = array('order' => 'Apple.id ASC'); $Apple = new Apple(); $Apple->unbindModel(array('hasMany' => array('Child'), 'hasOne' => array('Sample')), false); - $expected = $Apple->find('all'); + $expected = $Apple->find('all', $conditions); $Apple->unbindModel(array('belongsTo' => array('Parent'))); - $wellBehaved = $Apple->find('all'); + $wellBehaved = $Apple->find('all', $conditions); $Apple->Parent->Behaviors->attach('Test'); $Apple->unbindModel(array('belongsTo' => array('Parent'))); - $this->assertSame($Apple->find('all'), $wellBehaved); + $this->assertSame($Apple->find('all', $conditions), $wellBehaved); $Apple->Parent->Behaviors->attach('Test', array('before' => 'off')); - $this->assertSame($expected, $Apple->find('all')); + $this->assertSame($expected, $Apple->find('all', $conditions)); $Apple->Parent->Behaviors->attach('Test', array('before' => 'test')); - $this->assertSame($expected, $Apple->find('all')); + $this->assertSame($expected, $Apple->find('all', $conditions)); $Apple->Parent->Behaviors->attach('Test', array('before' => 'modify')); $expected2 = array( @@ -816,22 +797,23 @@ public function testBehaviorBelongsToFindCallbacks() { ); $result2 = $Apple->find('all', array( 'fields' => array('Apple.id', 'Parent.id', 'Parent.name', 'Parent.mytime'), - 'conditions' => array('Apple.id <' => '4') + 'conditions' => array('Apple.id <' => '4'), + 'order' => 'Apple.id ASC', )); $this->assertEquals($expected2, $result2); $Apple->Parent->Behaviors->disable('Test'); - $result = $Apple->find('all'); + $result = $Apple->find('all', $conditions); $this->assertEquals($expected, $result); $Apple->Parent->Behaviors->attach('Test', array('after' => 'off')); - $this->assertEquals($expected, $Apple->find('all')); + $this->assertEquals($expected, $Apple->find('all', $conditions)); $Apple->Parent->Behaviors->attach('Test', array('after' => 'test')); - $this->assertEquals($expected, $Apple->find('all')); + $this->assertEquals($expected, $Apple->find('all', $conditions)); $Apple->Parent->Behaviors->attach('Test', array('after' => 'test2')); - $this->assertEquals($expected, $Apple->find('all')); + $this->assertEquals($expected, $Apple->find('all', $conditions)); } /** diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index f14bf611682..e4b0423cd0a 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -313,6 +313,26 @@ public function testIndexDetection() { $result = $this->Dbo->index('with_multiple_compound_keys', false); $this->Dbo->rawQuery('DROP TABLE ' . $name); $this->assertEquals($expected, $result); + + $name = $this->Dbo->fullTableName('with_text_index'); + $this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field text, primary key(id), KEY `text_index` ( `text_field`(20) ));'); + $expected = array( + 'PRIMARY' => array('column' => 'id', 'unique' => 1), + 'text_index' => array('column' => 'text_field', 'unique' => 0, 'length' => array('text_field' => 20)), + ); + $result = $this->Dbo->index('with_text_index', false); + $this->Dbo->rawQuery('DROP TABLE ' . $name); + $this->assertEquals($expected, $result); + + $name = $this->Dbo->fullTableName('with_compound_text_index'); + $this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field1 text, text_field2 text, primary key(id), KEY `text_index` ( `text_field1`(20), `text_field2`(20) ));'); + $expected = array( + 'PRIMARY' => array('column' => 'id', 'unique' => 1), + 'text_index' => array('column' => array('text_field1', 'text_field2'), 'unique' => 0, 'length' => array('text_field1' => 20, 'text_field2' => 20)), + ); + $result = $this->Dbo->index('with_compound_text_index', false); + $this->Dbo->rawQuery('DROP TABLE ' . $name); + $this->assertEquals($expected, $result); } /** @@ -548,9 +568,9 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaB->compare($schemaA)); $this->assertContains("ALTER TABLE $table", $result); - $this->assertContains('ADD KEY name_idx (`name`),', $result); - $this->assertContains('ADD KEY group_idx (`group1`),', $result); - $this->assertContains('ADD KEY compound_idx (`group1`, `group2`),', $result); + $this->assertContains('ADD KEY `name_idx` (`name`),', $result); + $this->assertContains('ADD KEY `group_idx` (`group1`),', $result); + $this->assertContains('ADD KEY `compound_idx` (`group1`, `group2`),', $result); $this->assertContains('ADD PRIMARY KEY (`id`);', $result); //Test that the string is syntactically correct @@ -576,13 +596,13 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaC->compare($schemaB)); $this->assertContains("ALTER TABLE $table", $result); $this->assertContains('DROP PRIMARY KEY,', $result); - $this->assertContains('DROP KEY name_idx,', $result); - $this->assertContains('DROP KEY group_idx,', $result); - $this->assertContains('DROP KEY compound_idx,', $result); - $this->assertContains('ADD KEY id_name_idx (`id`, `name`),', $result); - $this->assertContains('ADD UNIQUE KEY name_idx (`name`),', $result); - $this->assertContains('ADD KEY group_idx (`group2`),', $result); - $this->assertContains('ADD KEY compound_idx (`group2`, `group1`);', $result); + $this->assertContains('DROP KEY `name_idx`,', $result); + $this->assertContains('DROP KEY `group_idx`,', $result); + $this->assertContains('DROP KEY `compound_idx`,', $result); + $this->assertContains('ADD KEY `id_name_idx` (`id`, `name`),', $result); + $this->assertContains('ADD UNIQUE KEY `name_idx` (`name`),', $result); + $this->assertContains('ADD KEY `group_idx` (`group2`),', $result); + $this->assertContains('ADD KEY `compound_idx` (`group2`, `group1`);', $result); $query = $this->Dbo->getConnection()->prepare($result); $this->assertEquals($query->queryString, $result); @@ -594,10 +614,10 @@ public function testAlterSchemaIndexes() { $result = $this->Dbo->alterSchema($schemaA->compare($schemaC)); $this->assertContains("ALTER TABLE $table", $result); - $this->assertContains('DROP KEY name_idx,', $result); - $this->assertContains('DROP KEY group_idx,', $result); - $this->assertContains('DROP KEY compound_idx,', $result); - $this->assertContains('DROP KEY id_name_idx;', $result); + $this->assertContains('DROP KEY `name_idx`,', $result); + $this->assertContains('DROP KEY `group_idx`,', $result); + $this->assertContains('DROP KEY `compound_idx`,', $result); + $this->assertContains('DROP KEY `id_name_idx`;', $result); $query = $this->Dbo->getConnection()->prepare($result); $this->assertEquals($query->queryString, $result); @@ -877,6 +897,52 @@ public function testTwoColumnsWithPrimaryKey() { $this->assertContains('`user_id` int(11) NOT NULL,', $result); } +/** + * Test that the primary flag is handled correctly. + * + * @return void + */ + public function testCreateSchemaAutoPrimaryKey() { + $schema = new CakeSchema(); + $schema->tables = array( + 'no_indexes' => array( + 'id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'), + 'data' => array('type' => 'integer', 'null' => false), + 'indexes' => array(), + ) + ); + $result = $this->Dbo->createSchema($schema, 'no_indexes'); + $this->assertContains('PRIMARY KEY (`id`)', $result); + $this->assertNotContains('UNIQUE KEY', $result); + + $schema->tables = array( + 'primary_index' => array( + 'id' => array('type' => 'integer', 'null' => false), + 'data' => array('type' => 'integer', 'null' => false), + 'indexes' => array( + 'PRIMARY' => array('column' => 'id', 'unique' => 1), + 'some_index' => array('column' => 'data', 'unique' => 1) + ), + ) + ); + $result = $this->Dbo->createSchema($schema, 'primary_index'); + $this->assertContains('PRIMARY KEY (`id`)', $result); + $this->assertContains('UNIQUE KEY `some_index` (`data`)', $result); + + $schema->tables = array( + 'primary_flag_has_index' => array( + 'id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'), + 'data' => array('type' => 'integer', 'null' => false), + 'indexes' => array ( + 'some_index' => array('column' => 'data', 'unique' => 1) + ), + ) + ); + $result = $this->Dbo->createSchema($schema, 'primary_flag_has_index'); + $this->assertContains('PRIMARY KEY (`id`)', $result); + $this->assertContains('UNIQUE KEY `some_index` (`data`)', $result); + } + /** * Tests that listSources method sends the correct query and parses the result accordingly * @return void @@ -1914,7 +1980,7 @@ public function testStringConditionsParsing() { $this->assertEquals($expected, $result); $result = $this->Dbo->conditions(array('score' => array(2 => 1, 2, 10))); - $expected = " WHERE score IN (1, 2, 10)"; + $expected = " WHERE `score` IN (1, 2, 10)"; $this->assertEquals($expected, $result); $result = $this->Dbo->conditions("Aro.rght = Aro.lft + 1.1"); @@ -2161,6 +2227,12 @@ public function testArrayConditionsParsing() { $expected = " WHERE `HardCandy`.`name` LIKE 'to be or%' AND `Candy`.`name` LIKE '%not to be%'"; $this->assertEquals($expected, $result); + $result = $this->Dbo->conditions(array( + "Person.name || ' ' || Person.surname ILIKE" => '%mark%' + )); + $expected = " WHERE `Person`.`name` || ' ' || `Person`.`surname` ILIKE '%mark%'"; + $this->assertEquals($expected, $result); + $result = $this->Dbo->conditions(array('score BETWEEN ? AND ?' => array(90.1, 95.7))); $expected = " WHERE `score` BETWEEN 90.1 AND 95.7"; $this->assertEquals($expected, $result); @@ -2194,7 +2266,7 @@ public function testArrayConditionsParsing() { $this->assertEquals($expected, $result); $result = $this->Dbo->conditions(array('score' => array(1, 2, 10))); - $expected = " WHERE score IN (1, 2, 10)"; + $expected = " WHERE `score` IN (1, 2, 10)"; $this->assertEquals($expected, $result); $result = $this->Dbo->conditions(array('score' => array())); @@ -2285,7 +2357,7 @@ public function testArrayConditionsParsing() { 'NOT' => array('Course.id' => null, 'Course.vet' => 'N', 'level_of_education_id' => array(912,999)), 'Enrollment.yearcompleted >' => '0') ); - $this->assertRegExp('/^\s*WHERE\s+\(NOT\s+\(`Course`\.`id` IS NULL\)\s+AND NOT\s+\(`Course`\.`vet`\s+=\s+\'N\'\)\s+AND NOT\s+\(level_of_education_id IN \(912, 999\)\)\)\s+AND\s+`Enrollment`\.`yearcompleted`\s+>\s+\'0\'\s*$/', $result); + $this->assertRegExp('/^\s*WHERE\s+\(NOT\s+\(`Course`\.`id` IS NULL\)\s+AND NOT\s+\(`Course`\.`vet`\s+=\s+\'N\'\)\s+AND NOT\s+\(`level_of_education_id` IN \(912, 999\)\)\)\s+AND\s+`Enrollment`\.`yearcompleted`\s+>\s+\'0\'\s*$/', $result); $result = $this->Dbo->conditions(array('id <>' => '8')); $this->assertRegExp('/^\s*WHERE\s+`id`\s+<>\s+\'8\'\s*$/', $result); @@ -2321,7 +2393,7 @@ public function testArrayConditionsParsing() { $conditions = array('id' => array(2, 5, 6, 9, 12, 45, 78, 43, 76)); $result = $this->Dbo->conditions($conditions); - $expected = " WHERE id IN (2, 5, 6, 9, 12, 45, 78, 43, 76)"; + $expected = " WHERE `id` IN (2, 5, 6, 9, 12, 45, 78, 43, 76)"; $this->assertEquals($expected, $result); $conditions = array('title' => 'user(s)'); @@ -2856,6 +2928,20 @@ public function testBuildIndex() { $result = $this->Dbo->buildIndex($data); $expected = array('UNIQUE KEY `MyIndex` (`id`, `name`)'); $this->assertEquals($expected, $result); + + $data = array( + 'MyTextIndex' => array('column' => 'text_field', 'length' => array('text_field' => 20)) + ); + $result = $this->Dbo->buildIndex($data); + $expected = array('KEY `MyTextIndex` (`text_field`(20))'); + $this->assertEquals($expected, $result); + + $data = array( + 'MyMultiTextIndex' => array('column' => array('text_field1', 'text_field2'), 'length' => array('text_field1' => 20, 'text_field2' => 20)) + ); + $result = $this->Dbo->buildIndex($data); + $expected = array('KEY `MyMultiTextIndex` (`text_field1`(20), `text_field2`(20))'); + $this->assertEquals($expected, $result); } /** @@ -2926,7 +3012,7 @@ public function testBuildColumn2() { 'type' => 'timestamp', 'default' => 'current_timestamp', 'null' => false, - ); + ); $result = $this->Dbo->buildColumn($data); $expected = '`created` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL'; $this->assertEquals($expected, $result); diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index e31595bf32d..ee7695a5f65 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -574,7 +574,7 @@ public function testBuildIndex() { $indexes = array('client_id' => array('column' => 'client_id')); $result = $this->db->buildIndex($indexes, 'items'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $indexes = array('client_id' => array('column' => array('client_id', 'period_id'), 'unique' => 1)); $result = $this->db->buildIndex($indexes, 'items'); diff --git a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php index de8cc903f1b..e049be48075 100644 --- a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php @@ -159,6 +159,19 @@ public function testBooleanNullConditionsParsing() { $this->assertEquals(' WHERE 1 = 1', $result); } +/** + * test that booleans work on empty set. + * + * @return void + */ + public function testBooleanEmptyConditionsParsing() { + $result = $this->testDb->conditions(array('OR' => array())); + $this->assertEquals(' WHERE 1 = 1', $result, 'empty conditions failed'); + + $result = $this->testDb->conditions(array('OR' => array('OR' => array()))); + $this->assertEquals(' WHERE 1 = 1', $result, 'nested empty conditions failed'); + } + /** * test that order() will accept objects made from DboSource::expression * @@ -1103,4 +1116,73 @@ public function testBuildJoinStatement($join, $expected) { $this->assertEquals($expected, $result); } +/** + * Test conditionKeysToString() + * + * @return void + */ + public function testConditionKeysToString() { + $Article = ClassRegistry::init('Article'); + $conn = $this->getMock('MockPDO', array('quote')); + $db = new DboTestSource; + $db->setConnection($conn); + + $conn->expects($this->at(0)) + ->method('quote') + ->will($this->returnValue('just text')); + + $conditions = array('Article.name' => 'just text'); + $result = $db->conditionKeysToString($conditions, true, $Article); + $expected = "Article.name = just text"; + $this->assertEquals($expected, $result[0]); + + $conn->expects($this->at(0)) + ->method('quote') + ->will($this->returnValue('just text')); + $conn->expects($this->at(1)) + ->method('quote') + ->will($this->returnValue('other text')); + + $conditions = array('Article.name' => array('just text', 'other text')); + $result = $db->conditionKeysToString($conditions, true, $Article); + $expected = "Article.name IN (just text, other text)"; + $this->assertEquals($expected, $result[0]); + } + +/** + * Test conditionKeysToString() with virtual field + * + * @return void + */ + public function testConditionKeysToStringVirtualField() { + $Article = ClassRegistry::init('Article'); + $Article->virtualFields = array( + 'extra' => 'something virtual' + ); + $conn = $this->getMock('MockPDO', array('quote')); + $db = new DboTestSource; + $db->setConnection($conn); + + $conn->expects($this->at(0)) + ->method('quote') + ->will($this->returnValue('just text')); + + $conditions = array('Article.extra' => 'just text'); + $result = $db->conditionKeysToString($conditions, true, $Article); + $expected = "(" . $Article->virtualFields['extra'] . ") = just text"; + $this->assertEquals($expected, $result[0]); + + $conn->expects($this->at(0)) + ->method('quote') + ->will($this->returnValue('just text')); + $conn->expects($this->at(1)) + ->method('quote') + ->will($this->returnValue('other text')); + + $conditions = array('Article.extra' => array('just text', 'other text')); + $result = $db->conditionKeysToString($conditions, true, $Article); + $expected = "(" . $Article->virtualFields['extra'] . ") IN (just text, other text)"; + $this->assertEquals($expected, $result[0]); + } + } diff --git a/lib/Cake/Test/Case/Model/ModelDeleteTest.php b/lib/Cake/Test/Case/Model/ModelDeleteTest.php index 329eef3878b..7a69585c6f6 100644 --- a/lib/Cake/Test/Case/Model/ModelDeleteTest.php +++ b/lib/Cake/Test/Case/Model/ModelDeleteTest.php @@ -107,12 +107,12 @@ public function testDeleteHabtmReferenceWithConditions() { $result = $Portfolio->find('first', array( 'conditions' => array('Portfolio.id' => 1) )); - $this->assertFalse($result); + $this->assertSame(array(), $result); $result = $Portfolio->ItemsPortfolio->find('all', array( 'conditions' => array('ItemsPortfolio.portfolio_id' => 1) )); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** @@ -195,7 +195,7 @@ public function testDelete() { $this->assertTrue($result); $result = $TestModel->read(null, 2); - $this->assertFalse($result); + $this->assertSame(array(), $result); $TestModel->recursive = -1; $result = $TestModel->find('all', array( @@ -216,7 +216,7 @@ public function testDelete() { $this->assertTrue($result); $result = $TestModel->read(null, 3); - $this->assertFalse($result); + $this->assertSame(array(), $result); $TestModel->recursive = -1; $result = $TestModel->find('all', array( @@ -448,16 +448,16 @@ public function testRecursiveDel() { $TestModel->recursive = 2; $result = $TestModel->read(null, 2); - $this->assertFalse($result); + $this->assertSame(array(), $result); $result = $TestModel->Comment->read(null, 5); - $this->assertFalse($result); + $this->assertSame(array(), $result); $result = $TestModel->Comment->read(null, 6); - $this->assertFalse($result); + $this->assertSame(array(), $result); $result = $TestModel->Comment->Attachment->read(null, 1); - $this->assertFalse($result); + $this->assertSame(array(), $result); $result = $TestModel->find('count'); $this->assertEquals(2, $result); diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index a7063c0fd40..7670983af99 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -906,6 +906,20 @@ public function testSchema() { $this->assertEquals($Post->getColumnTypes(), array_combine($columns, $types)); } +/** + * Check schema() on a model with useTable = false; + * + * @return void + */ + public function testSchemaUseTableFalse() { + $model = new TheVoid(); + $result = $model->schema(); + $this->assertNull($result); + + $result = $model->create(); + $this->assertEmpty($result); + } + /** * data provider for time tests. * diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 6f14e47a208..65e7f90eb72 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -4054,7 +4054,6 @@ public function testSaveEmpty() { /** * testFindAllWithConditionInChildQuery * - * @todo external conditions like this are going to need to be revisited at some point * @return void */ public function testFindAllWithConditionInChildQuery() { @@ -4203,7 +4202,8 @@ public function testBindUnbind() { $this->assertTrue($result); $result = $TestModel->find('all', array( - 'fields' => 'User.id, User.user' + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), )); $expected = array( array( @@ -4290,13 +4290,14 @@ public function testBindUnbind() { $TestModel->resetAssociations(); $result = $TestModel->hasMany; - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = $TestModel->bindModel(array('hasMany' => array('Comment')), false); $this->assertTrue($result); $result = $TestModel->find('all', array( - 'fields' => 'User.id, User.user' + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), )); $expected = array( @@ -4407,7 +4408,8 @@ public function testBindUnbind() { $this->assertEquals($expected, $result); $result = $TestModel->find('all', array( - 'fields' => 'User.id, User.user' + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), )); $expected = array( array('User' => array('id' => '1', 'user' => 'mariano')), @@ -4417,7 +4419,8 @@ public function testBindUnbind() { $this->assertEquals($expected, $result); $result = $TestModel->find('all', array( - 'fields' => 'User.id, User.user' + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), )); $expected = array( array( @@ -4505,7 +4508,10 @@ public function testBindUnbind() { $result = $TestModel->unbindModel(array('hasMany' => array('Comment')), false); $this->assertTrue($result); - $result = $TestModel->find('all', array('fields' => 'User.id, User.user')); + $result = $TestModel->find('all', array( + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), + )); $expected = array( array('User' => array('id' => '1', 'user' => 'mariano')), array('User' => array('id' => '2', 'user' => 'nate')), @@ -4522,7 +4528,10 @@ public function testBindUnbind() { ))); $this->assertTrue($result); - $result = $TestModel->find('all', array('fields' => 'User.id, User.user')); + $result = $TestModel->find('all', array( + 'fields' => 'User.id, User.user', + 'order' => array('User.id' => 'ASC'), + )); $expected = array( array( 'User' => array( @@ -4959,7 +4968,9 @@ public function testUnBindMultipleTimesWithDifferentResetSettings() { public function testAssociationAfterFind() { $this->loadFixtures('Post', 'Author', 'Comment'); $TestModel = new Post(); - $result = $TestModel->find('all'); + $result = $TestModel->find('all', array( + 'order' => array('Post.id' => 'ASC') + )); $expected = array( array( 'Post' => array( @@ -5028,6 +5039,7 @@ public function testAssociationAfterFind() { ))); $result = $Author->find('all', array( 'conditions' => array('Author.id' => 1), + 'order' => array('Author.id' => 'ASC'), 'recursive' => 2 )); $expected = array( @@ -5110,7 +5122,10 @@ public function testCallbackDisabling() { public function testAssociationAfterFindCalbacksDisabled() { $this->loadFixtures('Post', 'Author', 'Comment'); $TestModel = new Post(); - $result = $TestModel->find('all', array('callbacks' => false)); + $result = $TestModel->find('all', array( + 'callbacks' => false, + 'order' => array('Post.id' => 'ASC'), + )); $expected = array( array( 'Post' => array( @@ -5177,6 +5192,7 @@ public function testAssociationAfterFindCalbacksDisabled() { $result = $Author->find('all', array( 'conditions' => array('Author.id' => 1), 'recursive' => 2, + 'order' => array('Author.id' => 'ASC'), 'callbacks' => false )); $expected = array( @@ -5384,7 +5400,9 @@ public function testNonNumericHabtmJoinKey() { )); $Post->Tag->primaryKey = 'tag'; - $result = $Post->find('all'); + $result = $Post->find('all', array( + 'order' => 'Post.id ASC', + )); $expected = array( array( 'Post' => array( @@ -5599,7 +5617,9 @@ public function testHasManyLimitOptimization() { $Project = new Project(); $Project->recursive = 3; - $result = $Project->find('all'); + $result = $Project->find('all', array( + 'order' => 'Project.id ASC', + )); $expected = array( array( 'Project' => array( @@ -5713,7 +5733,9 @@ public function testFindAllRecursiveSelfJoin() { $TestModel = new Home(); $TestModel->recursive = 2; - $result = $TestModel->find('all'); + $result = $TestModel->find('all', array( + 'order' => 'Home.id ASC', + )); $expected = array( array( 'Home' => array( @@ -5826,7 +5848,9 @@ public function testFindAllRecursiveWithHabtm() { $MyUser = new MyUser(); $MyUser->recursive = 2; - $result = $MyUser->find('all'); + $result = $MyUser->find('all', array( + 'order' => 'MyUser.id ASC' + )); $expected = array( array( 'MyUser' => array('id' => '1', 'firstname' => 'userA'), @@ -6017,7 +6041,7 @@ public function testFindAllFakeThread() { $fullDebug = $this->db->fullDebug; $this->db->fullDebug = true; $TestModel->recursive = 6; - $result = $TestModel->find('all', null, null, 'CategoryThread.id ASC'); + $result = $TestModel->find('all'); $expected = array( array( 'CategoryThread' => array( @@ -7756,7 +7780,9 @@ public function testVirtualFieldsOrder() { $this->assertEquals($expected, $result); $Post->Author->virtualFields = array('joined' => 'Post.id * Author.id'); - $result = $Post->find('all'); + $result = $Post->find('all', array( + 'order' => array('Post.id' => 'ASC') + )); $result = Hash::extract($result, '{n}.Author.joined'); $expected = array(1, 6, 3); $this->assertEquals($expected, $result); diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 6c6fdd914af..118169719b9 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -2227,4 +2227,124 @@ public function testCustomMethodWithEmptyValue() { $this->assertFalse($model->validates()); } +/** + * Test validateAssociated with atomic=false & deep=true + * + * @return void + */ + public function testValidateAssociatedAtomicFalseDeepTrueWithErrors() { + $this->loadFixtures('Comment', 'Article', 'User', 'Attachment'); + $Attachment = ClassRegistry::init('Attachment'); + $Attachment->Comment->validator()->add('comment', array( + array('rule' => 'notEmpty') + )); + $Attachment->Comment->User->bindModel(array( + 'hasMany' => array( + 'Article', + 'Comment' + )), + false + ); + + $data = array( + 'Attachment' => array( + 'attachment' => 'text', + 'Comment' => array( + 'comment' => '', + 'published' => 'N', + 'User' => array( + 'user' => 'Foo', + 'password' => 'mypassword', + 'Comment' => array( + array( + 'comment' => '' + ) + ) + ) + ) + ) + ); + $result = $Attachment->validateAssociated($data, array('atomic' => false, 'deep' => true)); + + $result = $Attachment->validationErrors; + $expected = array( + 'Comment' => array( + 'comment' => array( + 0 => 'This field cannot be left blank', + ), + 'User' => array( + 'Comment' => array( + 0 => array( + 'comment' => array( + 0 => 'This field cannot be left blank', + ), + ), + ), + ), + ), + ); + $this->assertEquals($result, $expected); + } + +/** + * Test validateMany with atomic=false & deep=true + * + * @return void + */ + public function testValidateManyAtomicFalseDeepTrueWithErrors() { + $this->loadFixtures('Comment', 'Article', 'User'); + $Article = ClassRegistry::init('Article'); + $Article->Comment->validator()->add('comment', array( + array('rule' => 'notEmpty') + )); + + $data = array( + array( + 'Article' => array( + 'user_id' => 1, + 'title' => 'Foo', + 'body' => 'text', + 'published' => 'N' + ), + 'Comment' => array( + array( + 'user_id' => 1, + 'comment' => 'Baz', + 'published' => 'N', + ) + ), + ), + array( + 'Article' => array( + 'user_id' => 1, + 'title' => 'Bar', + 'body' => 'text', + 'published' => 'N' + ), + 'Comment' => array( + array( + 'user_id' => 1, + 'comment' => '', + 'published' => 'N', + ) + ), + ), + ); + $Article->validateMany($data, array('atomic' => false, 'deep' => true)); + + $result = $Article->validationErrors; + $expected = array( + 1 => array( + 'Comment' => array( + 0 => array( + 'comment' => array( + 0 => 'This field cannot be left blank', + ), + ), + ), + ), + ); + $this->assertEquals($result, $expected); + } + } diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 31327e7deec..d901063eb9c 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -24,6 +24,24 @@ */ class ModelWriteTest extends BaseModelTest { +/** + * Test save() failing when there is no data. + * + * @return void + */ + public function testInsertNoData() { + $this->loadFixtures('Bid'); + $Bid = ClassRegistry::init('Bid'); + + $this->assertFalse($Bid->save()); + + $result = $Bid->save(array('Bid' => array())); + $this->assertFalse($result); + + $result = $Bid->save(array('Bid' => array('not in schema' => 1))); + $this->assertFalse($result); + } + /** * testInsertAnotherHabtmRecordWithSameForeignKey method * @@ -4536,7 +4554,7 @@ public function testSaveAllValidateFirst() { $this->assertFalse($result); $result = $model->find('all'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $expected = array('Comment' => array( 1 => array('comment' => array('This field cannot be left blank')) )); @@ -4725,6 +4743,32 @@ public function testSaveAllHasManyValidationOnly() { $this->assertEquals($expected, $TestModel->Comment->validationErrors); } +/** + * test that saveAll still behaves like previous versions (does not necessarily need a first argument) + * + * @return void + */ + public function testSaveAllWithSet() { + $this->loadFixtures('Article', 'Tag', 'Comment', 'User', 'ArticlesTag'); + $data = array( + 'Article' => array( + 'user_id' => 1, + 'title' => 'Article Has and belongs to Many Tags' + ), + 'Tag' => array( + 'Tag' => array(1, 2) + ), + 'Comment' => array( + array( + 'comment' => 'Article comment', + 'user_id' => 1 + ))); + $Article = new Article(); + $Article->set($data); + $result = $Article->saveAll(); + $this->assertFalse(empty($result)); + } + /** * test that saveAll behaves like plain save() when supplied empty data * @@ -4740,7 +4784,7 @@ public function testSaveAllEmptyData() { $this->assertFalse(empty($result)); $model = new ProductUpdateAll(); - $result = $model->saveAll(array()); + $result = $model->saveAll(); $this->assertFalse($result); } @@ -4830,6 +4874,51 @@ public function testSaveAssociated() { $this->assertEquals($expected, $result[6]['Attachment']); } +/** + * Test that validate = first, atomic = false works when associated records + * fail validation. + * + * @return void + */ + public function testSaveAssociatedAtomicFalseValidateFirstWithErrors() { + $this->loadFixtures('Comment', 'Article', 'User'); + $Article = ClassRegistry::init('Article'); + $Article->Comment->validator()->add('comment', array( + array('rule' => 'notEmpty') + )); + + $data = array( + 'Article' => array( + 'user_id' => 1, + 'title' => 'Foo', + 'body' => 'text', + 'published' => 'N' + ), + 'Comment' => array( + array( + 'user_id' => 1, + 'comment' => '', + 'published' => 'N', + ) + ), + ); + + $Article->saveAssociated( + $data, + array('validate' => 'first', 'atomic' => false) + ); + + $result = $Article->validationErrors; + $expected = array( + 'Comment' => array( + array( + 'comment' => array( 'This field cannot be left blank' ) + ) + ) + ); + $this->assertEquals($expected, $result); + } + /** * testSaveMany method * @@ -5869,7 +5958,7 @@ public function testSaveAssociatedValidateFirst() { $this->assertFalse($result); $result = $model->find('all'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $expected = array('Comment' => array( 1 => array('comment' => array('This field cannot be left blank')) )); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php index e0d9c8b35f0..f4510b9af94 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php @@ -171,4 +171,29 @@ public function testIsEmptyAllowed() { $Rule->isUpdate(true); $this->assertTrue($Rule->isEmptyAllowed()); } + +/** + * Test checkRequired method + * + * @return void + */ + public function testCheckRequiredWhenRequiredAndAllowEmpty() { + $Rule = $this->getMock('CakeValidationRule', array('isRequired')); + $Rule->expects($this->any()) + ->method('isRequired') + ->will($this->returnValue(true)); + $Rule->allowEmpty = true; + + $fieldname = 'field'; + $data = array( + $fieldname => null + ); + + $this->assertFalse($Rule->checkRequired($fieldname, $data), "A null but present field should not fail requirement check if allowEmpty is true"); + + $Rule->allowEmpty = false; + + $this->assertTrue($Rule->checkRequired($fieldname, $data), "A null but present field should fail requirement check if allowEmpty is false"); + } + } diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 10fa6fdf6d4..aa604fac983 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -156,10 +156,22 @@ public function testSetRules() { $result = $Field->getRules(); $this->assertEquals(array('validEmail'), array_keys($result)); + $Field->setRules(array('validEmail' => $rule), false); + $result = $Field->getRules(); + $this->assertEquals(array('validEmail'), array_keys($result)); + $this->assertTrue(array_pop($result) instanceof CakeValidationRule); + $rules = array('notEmpty' => $RuleEmpty); $Field->setRules($rules, true); $result = $Field->getRules(); $this->assertEquals(array('validEmail', 'notEmpty'), array_keys($result)); + + $rules = array('notEmpty' => array('rule' => 'notEmpty')); + $Field->setRules($rules, true); + $result = $Field->getRules(); + $this->assertEquals(array('validEmail', 'notEmpty'), array_keys($result)); + $this->assertTrue(array_pop($result) instanceof CakeValidationRule); + $this->assertTrue(array_pop($result) instanceof CakeValidationRule); } /** diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index e5ba8f8502a..487f60e8050 100644 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -311,9 +311,10 @@ public function testPutParsingJSON() { $request = $this->getMock('TestCakeRequest', array('_readInput')); $request->expects($this->at(0))->method('_readInput') - ->will($this->returnValue('{Article":["title"]}')); + ->will($this->returnValue('{"Article":["title"]}')); $request->reConstruct(); - $this->assertEquals('{Article":["title"]}', $request->data); + $result = $request->input('json_decode', true); + $this->assertEquals(array('title'), $result['Article']); } /** @@ -1709,7 +1710,7 @@ public static function environmentGenerator() { */ public function testEnvironmentDetection($name, $env, $expected) { $_GET = array(); - $this->__loadEnvironment($env); + $this->_loadEnvironment($env); $request = new CakeRequest(); $this->assertEquals($expected['url'], $request->url, "url error"); @@ -1912,7 +1913,7 @@ public function testIsRequested() { * @param array $env * @return void */ - protected function __loadEnvironment($env) { + protected function _loadEnvironment($env) { if (isset($env['App'])) { Configure::write('App', $env['App']); } diff --git a/lib/Cake/Test/Case/Network/CakeResponseTest.php b/lib/Cake/Test/Case/Network/CakeResponseTest.php index 9f601b1b452..989ce62fa8e 100644 --- a/lib/Cake/Test/Case/Network/CakeResponseTest.php +++ b/lib/Cake/Test/Case/Network/CakeResponseTest.php @@ -199,7 +199,7 @@ public function testSend() { * Tests the send method and changing the content type * */ - public function testSendChangingContentYype() { + public function testSendChangingContentType() { $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); $response->type('mp3'); $response->body('the response body'); @@ -215,12 +215,12 @@ public function testSendChangingContentYype() { } /** - * Tests the send method and changing the content type + * Tests the send method and changing the content type to JSON * */ - public function testSendChangingContentType() { + public function testSendChangingContentTypeJSON() { $response = $this->getMock('CakeResponse', array('_sendHeader', '_sendContent', '_setCookies')); - $response->type('mp3'); + $response->type('json'); $response->body('the response body'); $response->expects($this->once())->method('_sendContent')->with('the response body'); $response->expects($this->at(0))->method('_setCookies'); @@ -229,7 +229,7 @@ public function testSendChangingContentType() { $response->expects($this->at(2)) ->method('_sendHeader')->with('Content-Length', 17); $response->expects($this->at(3)) - ->method('_sendHeader')->with('Content-Type', 'audio/mpeg'); + ->method('_sendHeader')->with('Content-Type', 'application/json; charset=UTF-8'); $response->send(); } diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index 27e593f66eb..921d3315569 100644 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -42,8 +42,8 @@ public function formatAddress($address) { * Wrap to protected method * */ - public function wrap($text) { - return parent::_wrap($text); + public function wrap($text, $length = CakeEmail::LINE_LENGTH_MUST) { + return parent::_wrap($text, $length); } /** @@ -234,7 +234,7 @@ public static function invalidEmails() { array(''), array('string'), array(''), - array('some@one.whereis'), + array('some@one-whereis'), array('wrong@key' => 'Name'), array(array('ok@cakephp.org', 1.0, '', 'string')) ); @@ -1354,7 +1354,7 @@ public function testResetWithCharset() { */ public function testWrap() { $text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac turpis orci, non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac turpis orci,', 'non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.', @@ -1363,7 +1363,7 @@ public function testWrap() { $this->assertSame($expected, $result); $text = 'Lorem ipsum dolor sit amet, consectetur < adipiscing elit. Donec ac turpis orci, non commodo odio. Morbi nibh nisi, vehicula > pellentesque accumsan amet.'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( 'Lorem ipsum dolor sit amet, consectetur < adipiscing elit. Donec ac turpis', 'orci, non commodo odio. Morbi nibh nisi, vehicula > pellentesque accumsan', @@ -1373,7 +1373,7 @@ public function testWrap() { $this->assertSame($expected, $result); $text = '

Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec ac turpis orci, non commodo odio.
Morbi nibh nisi, vehicula pellentesque accumsan amet.


'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( '

Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec ac', 'turpis orci, non commodo odio.
Morbi nibh nisi, vehicula', @@ -1383,7 +1383,7 @@ public function testWrap() { $this->assertSame($expected, $result); $text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac turpis orci, non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac', 'turpis orci, non commodo odio. Morbi nibh', @@ -1393,7 +1393,7 @@ public function testWrap() { $this->assertSame($expected, $result); $text = 'Lorem ipsum ok'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( 'Lorem ipsum', '', @@ -1403,7 +1403,7 @@ public function testWrap() { $this->assertSame($expected, $result); $text = 'Lorem ipsum withonewordverybigMorethanthelineshouldsizeofrfcspecificationbyieeeavailableonieeesite ok.'; - $result = $this->CakeEmail->wrap($text); + $result = $this->CakeEmail->wrap($text, CakeEmail::LINE_LENGTH_SHOULD); $expected = array( 'Lorem ipsum', 'withonewordverybigMorethanthelineshouldsizeofrfcspecificationbyieeeavailableonieeesite', diff --git a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php index dfd7db98b21..a742adbed14 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php @@ -762,6 +762,38 @@ public function testRequestCustomResponse() { $this->assertEquals('HTTP/1.x 2', $response->first10); } +/** + * Test that redirect urls are urldecoded + * + * @return void + */ + public function testRequestWithRedirectUrlEncoded() { + $request = array( + 'uri' => 'http://localhost/oneuri', + 'redirect' => 1 + ); + $serverResponse1 = "HTTP/1.x 302 Found\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\nLocation: http://i.cmpnet.com%2Ftechonline%2Fpdf%2Fa.pdf=\r\n\r\n"; + $serverResponse2 = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n

You have been redirected

"; + + $this->Socket->expects($this->at(1)) + ->method('read') + ->will($this->returnValue($serverResponse1)); + + $this->Socket->expects($this->at(3)) + ->method('write') + ->with($this->logicalAnd( + $this->stringContains('Host: i.cmpnet.com'), + $this->stringContains('GET /techonline/pdf/a.pdf') + )); + + $this->Socket->expects($this->at(4)) + ->method('read') + ->will($this->returnValue($serverResponse2)); + + $response = $this->Socket->request($request); + $this->assertEquals('

You have been redirected

', $response->body()); + } + /** * testRequestWithRedirect method * @@ -781,6 +813,11 @@ public function testRequestWithRedirectAsTrue() { $this->assertEquals('

You have been redirected

', $response->body()); } +/** + * Test that redirects with a count limit are decremented. + * + * @return void + */ public function testRequestWithRedirectAsInt() { $request = array( 'uri' => 'http://localhost/oneuri', @@ -795,6 +832,11 @@ public function testRequestWithRedirectAsInt() { $this->assertEquals(1, $this->Socket->request['redirect']); } +/** + * Test that redirects after the redirect count reaches 9 are not followed. + * + * @return void + */ public function testRequestWithRedirectAsIntReachingZero() { $request = array( 'uri' => 'http://localhost/oneuri', @@ -1509,7 +1551,6 @@ public function testBuildHeader() { * testBuildCookies method * * @return void - * @todo Test more scenarios */ public function testBuildCookies() { $cookies = array( diff --git a/lib/Cake/Test/Case/Routing/DispatcherTest.php b/lib/Cake/Test/Case/Routing/DispatcherTest.php index a0e3df7ba6d..cf0ce8752d5 100644 --- a/lib/Cake/Test/Case/Routing/DispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/DispatcherTest.php @@ -1574,7 +1574,7 @@ public function testFullPageCachingDispatch($url) { $this->assertTextEquals($out, $cached); - $filename = $this->__cachePath($request->here()); + $filename = $this->_cachePath($request->here()); unlink($filename); } @@ -1657,79 +1657,13 @@ public function testHttpMethodOverrides() { unset($_POST['_method']); } -/** - * backupEnvironment method - * - * @return void - */ - protected function __backupEnvironment() { - return array( - 'App' => Configure::read('App'), - 'GET' => $_GET, - 'POST' => $_POST, - 'SERVER' => $_SERVER - ); - } - -/** - * reloadEnvironment method - * - * @return void - */ - protected function __reloadEnvironment() { - foreach ($_GET as $key => $val) { - unset($_GET[$key]); - } - foreach ($_POST as $key => $val) { - unset($_POST[$key]); - } - foreach ($_SERVER as $key => $val) { - unset($_SERVER[$key]); - } - Configure::write('App', array()); - } - -/** - * loadEnvironment method - * - * @param array $env - * @return void - */ - protected function __loadEnvironment($env) { - if ($env['reload']) { - $this->__reloadEnvironment(); - } - - if (isset($env['App'])) { - Configure::write('App', $env['App']); - } - - if (isset($env['GET'])) { - foreach ($env['GET'] as $key => $val) { - $_GET[$key] = $val; - } - } - - if (isset($env['POST'])) { - foreach ($env['POST'] as $key => $val) { - $_POST[$key] = $val; - } - } - - if (isset($env['SERVER'])) { - foreach ($env['SERVER'] as $key => $val) { - $_SERVER[$key] = $val; - } - } - } - /** * cachePath method * * @param string $here * @return string */ - protected function __cachePath($here) { + protected function _cachePath($here) { $path = $here; if ($here == '/') { $path = 'home'; diff --git a/lib/Cake/Test/Case/Routing/RouterTest.php b/lib/Cake/Test/Case/Routing/RouterTest.php index b4c956de04e..dd12924ceda 100644 --- a/lib/Cake/Test/Case/Routing/RouterTest.php +++ b/lib/Cake/Test/Case/Routing/RouterTest.php @@ -111,7 +111,7 @@ public function testMapResources() { $_SERVER['REQUEST_METHOD'] = 'GET'; $result = Router::parse('/posts/add'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); Router::reload(); $resources = Router::mapResources('Posts', array('id' => '[a-z0-9_]+')); @@ -1538,7 +1538,6 @@ public function testNamedArgsUrlParsing() { * test url generation with legacy (1.2) style prefix routes. * * @return void - * @todo Remove tests related to legacy style routes. * @see testUrlGenerationWithAutoPrefixes */ public function testUrlGenerationWithLegacyPrefixes() { @@ -1907,7 +1906,7 @@ public function testParsingWithPatternOnAction() { $this->assertEquals($expected, $result); $result = Router::parse('/blog/foobar'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = Router::url(array('controller' => 'blog_posts', 'action' => 'foo')); $this->assertEquals('/blog_posts/foo', $result); @@ -2117,7 +2116,7 @@ public function testRegexRouteMatching() { $this->assertEquals($expected, $result); $result = Router::parse('/badness/test/test_action'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); Router::reload(); Router::connect('/:locale/:controller/:action/*', array(), array('locale' => 'dan|eng')); diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 7185ef00fd5..f21d066c88a 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -340,14 +340,14 @@ public function testNice() { $this->assertEquals(date('Y-d-m', $time), $this->Time->nice($time)); $this->assertEquals('%Y-%d-%m', $this->Time->niceFormat); - CakeTime::$niceFormat = '%Y-%d-%m %H:%M:%S'; - $this->assertEquals(date('Y-d-m H:i:s', $time), $this->Time->nice($time)); - $this->assertEquals('%Y-%d-%m %H:%M:%S', $this->Time->niceFormat); + CakeTime::$niceFormat = '%Y-%d-%m %H:%M'; + $this->assertEquals(date('Y-d-m H:i', $time), $this->Time->nice($time)); + $this->assertEquals('%Y-%d-%m %H:%M', $this->Time->niceFormat); date_default_timezone_set('UTC'); $result = $this->Time->nice(null, 'America/New_York'); $expected = $this->Time->nice(time(), 'America/New_York'); - $this->assertEquals($expected, $result); + $this->assertEquals(substr($expected, 0, -1), substr($result, 0, -1)); $this->_restoreSystemTimezone(); } @@ -474,7 +474,7 @@ public function testToServer() { date_default_timezone_set('UTC'); - $serverTime = new DateTime('now'); + $serverTime = new DateTime('2012-12-11 14:15:20'); $timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu'); foreach ($timezones as $timezone) { @@ -509,17 +509,18 @@ public function testToAtom() { * @return void */ public function testToRss() { - $this->assertEquals(date('r'), $this->Time->toRss(time())); - - if (!$this->skipIf(!class_exists('DateTimeZone'), '%s DateTimeZone class not available.')) { - $timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu'); - foreach ($timezones as $timezone) { - $yourTimezone = new DateTimeZone($timezone); - $yourTime = new DateTime('now', $yourTimezone); - $userOffset = $yourTimezone->getOffset($yourTime) / HOUR; - $this->assertEquals($yourTime->format('r'), $this->Time->toRss(time(), $userOffset)); - $this->assertEquals($yourTime->format('r'), $this->Time->toRss(time(), $timezone)); - } + $date = '2012-08-12 12:12:45'; + $time = strtotime($date); + $this->assertEquals(date('r', $time), $this->Time->toRss($time)); + + $timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu'); + foreach ($timezones as $timezone) { + $yourTimezone = new DateTimeZone($timezone); + $yourTime = new DateTime($date, $yourTimezone); + $userOffset = $yourTimezone->getOffset($yourTime) / HOUR; + $time = $yourTime->format('U'); + $this->assertEquals($yourTime->format('r'), $this->Time->toRss($time, $userOffset), "Failed on $timezone"); + $this->assertEquals($yourTime->format('r'), $this->Time->toRss($time, $timezone), "Failed on $timezone"); } } diff --git a/lib/Cake/Test/Case/Utility/DebuggerTest.php b/lib/Cake/Test/Case/Utility/DebuggerTest.php index 8040073c338..ea6912122c7 100644 --- a/lib/Cake/Test/Case/Utility/DebuggerTest.php +++ b/lib/Cake/Test/Case/Utility/DebuggerTest.php @@ -84,7 +84,13 @@ public function testExcerpt() { $this->assertTrue(is_array($result)); $this->assertEquals(4, count($result)); - $pattern = '/.*?<\?php/'; + $pattern = '/.*?.*?<\?php/'; + $this->assertRegExp($pattern, $result[0]); + + $result = Debugger::excerpt(__FILE__, 10, 2); + $this->assertEquals(5, count($result)); + + $pattern = '/\*<\/span>/'; $this->assertRegExp($pattern, $result[0]); $return = Debugger::excerpt('[internal]', 2, 2); diff --git a/lib/Cake/Test/Case/Utility/HashTest.php b/lib/Cake/Test/Case/Utility/HashTest.php index 40fcd7b5483..059de1839ee 100644 --- a/lib/Cake/Test/Case/Utility/HashTest.php +++ b/lib/Cake/Test/Case/Utility/HashTest.php @@ -298,7 +298,6 @@ public function testFlatten() { 'Author' => array('id' => '3', 'user' => 'larry', 'password' => null), ) ); - $result = Hash::flatten($data); $expected = array( '0.Post.id' => '1', @@ -317,6 +316,21 @@ public function testFlatten() { ); $this->assertEquals($expected, $result); + $data = array( + array( + 'Post' => array('id' => '1', 'author_id' => null, 'title' => 'First Post'), + 'Author' => array(), + ) + ); + $result = Hash::flatten($data); + $expected = array( + '0.Post.id' => '1', + '0.Post.author_id' => null, + '0.Post.title' => 'First Post', + '0.Author' => array() + ); + $this->assertEquals($expected, $result); + $data = array( array('Post' => array('id' => 1)), array('Post' => array('id' => 2)), @@ -914,6 +928,9 @@ public function testExtractUnevenKeys() { * @return void */ public function testSort() { + $result = Hash::sort(array(), '{n}.name', 'asc'); + $this->assertEquals(array(), $result); + $a = array( 0 => array( 'Person' => array('name' => 'Jeff'), @@ -2148,4 +2165,142 @@ public function testExpand() { $this->assertEquals($result, $expected); } +/** + * Test that flattening a large complex set doesn't loop forever. + * + * @return void + */ + public function testFlattenInfiniteLoop() { + $data = array( + 'Order.ASI' => '0', + 'Order.Accounting' => '0', + 'Order.Admin' => '0', + 'Order.Art' => '0', + 'Order.ArtChecker' => '0', + 'Order.Canned' => '0', + 'Order.Customer_Tags' => '', + 'Order.Embroidery' => '0', + 'Order.Item.0.Product.style_number' => 'a11222', + 'Order.Item.0.Product.slug' => 'a11222', + 'Order.Item.0.Product._id' => '4ff8b8d3d7bbe8ad30000000', + 'Order.Item.0.Product.Color.slug' => 'kelly_green', + 'Order.Item.0.Product.ColorSizes.0.Color.color' => 'Sport Grey', + 'Order.Item.0.Product.ColorSizes.0.Color.slug' => 'sport_grey', + 'Order.Item.0.Product.ColorSizes.1.Color.color' => 'Kelly Green', + 'Order.Item.0.Product.ColorSizes.1.Color.slug' => 'kelly_green', + 'Order.Item.0.Product.ColorSizes.2.Color.color' => 'Orange', + 'Order.Item.0.Product.ColorSizes.2.Color.slug' => 'orange', + 'Order.Item.0.Product.ColorSizes.3.Color.color' => 'Yellow Haze', + 'Order.Item.0.Product.ColorSizes.3.Color.slug' => 'yellow_haze', + 'Order.Item.0.Product.brand' => 'OUTER BANKS', + 'Order.Item.0.Product.style' => 'T-shirt', + 'Order.Item.0.Product.description' => 'uhiuhuih oin ooi ioo ioio', + 'Order.Item.0.Product.sizes.0.Size.qty' => '', + 'Order.Item.0.Product.sizes.0.Size.size' => '0-3mo', + 'Order.Item.0.Product.sizes.0.Size.id' => '38', + 'Order.Item.0.Product.sizes.1.Size.qty' => '', + 'Order.Item.0.Product.sizes.1.Size.size' => '3-6mo', + 'Order.Item.0.Product.sizes.1.Size.id' => '39', + 'Order.Item.0.Product.sizes.2.Size.qty' => '78', + 'Order.Item.0.Product.sizes.2.Size.size' => '6-9mo', + 'Order.Item.0.Product.sizes.2.Size.id' => '40', + 'Order.Item.0.Product.sizes.3.Size.qty' => '', + 'Order.Item.0.Product.sizes.3.Size.size' => '6-12mo', + 'Order.Item.0.Product.sizes.3.Size.id' => '41', + 'Order.Item.0.Product.sizes.4.Size.qty' => '', + 'Order.Item.0.Product.sizes.4.Size.size' => '12-18mo', + 'Order.Item.0.Product.sizes.4.Size.id' => '42', + 'Order.Item.0.Art.imprint_locations.0.id' => 2, + 'Order.Item.0.Art.imprint_locations.0.name' => 'Left Chest', + 'Order.Item.0.Art.imprint_locations.0.imprint_type.id' => 7, + 'Order.Item.0.Art.imprint_locations.0.imprint_type.type' => 'Embroidery', + 'Order.Item.0.Art.imprint_locations.0.art' => '', + 'Order.Item.0.Art.imprint_locations.0.num_colors' => 3, + 'Order.Item.0.Art.imprint_locations.0.description' => 'Wooo! This is Embroidery!!', + 'Order.Item.0.Art.imprint_locations.0.lines.0' => 'Platen', + 'Order.Item.0.Art.imprint_locations.0.lines.1' => 'Logo', + 'Order.Item.0.Art.imprint_locations.0.height' => 4, + 'Order.Item.0.Art.imprint_locations.0.width' => 5, + 'Order.Item.0.Art.imprint_locations.0.stitch_density' => 'Light', + 'Order.Item.0.Art.imprint_locations.0.metallic_thread' => true, + 'Order.Item.0.Art.imprint_locations.1.id' => 4, + 'Order.Item.0.Art.imprint_locations.1.name' => 'Full Back', + 'Order.Item.0.Art.imprint_locations.1.imprint_type.id' => 6, + 'Order.Item.0.Art.imprint_locations.1.imprint_type.type' => 'Screenprinting', + 'Order.Item.0.Art.imprint_locations.1.art' => '', + 'Order.Item.0.Art.imprint_locations.1.num_colors' => 3, + 'Order.Item.0.Art.imprint_locations.1.description' => 'Wooo! This is Screenprinting!!', + 'Order.Item.0.Art.imprint_locations.1.lines.0' => 'Platen', + 'Order.Item.0.Art.imprint_locations.1.lines.1' => 'Logo', + 'Order.Item.0.Art.imprint_locations.2.id' => 26, + 'Order.Item.0.Art.imprint_locations.2.name' => 'HS - JSY Name Below', + 'Order.Item.0.Art.imprint_locations.2.imprint_type.id' => 9, + 'Order.Item.0.Art.imprint_locations.2.imprint_type.type' => 'Names', + 'Order.Item.0.Art.imprint_locations.2.description' => 'Wooo! This is Names!!', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.active' => 1, + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.name' => 'Benjamin Talavera', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.color' => 'Red', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.height' => '3', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.layout' => 'Arched', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.style' => 'Classic', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.active' => 0, + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.name' => 'Rishi Narayan', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.color' => 'Cardinal', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.height' => '4', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.layout' => 'Straight', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.style' => 'Team US', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.active' => 1, + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.name' => 'Brandon Plasters', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.color' => 'Red', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.height' => '3', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.layout' => 'Arched', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.style' => 'Classic', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.active' => 0, + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.name' => 'Andrew Reed', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.color' => 'Cardinal', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.height' => '4', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.layout' => 'Straight', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.style' => 'Team US', + 'Order.Job.0._id' => 'job-1', + 'Order.Job.0.type' => 'screenprinting', + 'Order.Job.0.postPress' => 'job-2', + 'Order.Job.1._id' => 'job-2', + 'Order.Job.1.type' => 'embroidery', + 'Order.Postpress' => '0', + 'Order.PriceAdjustment.0._id' => 'price-adjustment-1', + 'Order.PriceAdjustment.0.adjustment' => '-20', + 'Order.PriceAdjustment.0.adjustment_type' => 'percent', + 'Order.PriceAdjustment.0.type' => 'grand_total', + 'Order.PriceAdjustment.1.adjustment' => '20', + 'Order.PriceAdjustment.1.adjustment_type' => 'flat', + 'Order.PriceAdjustment.1.min-items' => '10', + 'Order.PriceAdjustment.1.type' => 'min-items', + 'Order.PriceAdjustment.1._id' => 'another-test-adjustment', + 'Order.Purchasing' => '0', + 'Order.QualityControl' => '0', + 'Order.Receiving' => '0', + 'Order.ScreenPrinting' => '0', + 'Order.Stage.art_approval' => 0, + 'Order.Stage.draft' => 1, + 'Order.Stage.quote' => 1, + 'Order.Stage.order' => 1, + 'Order.StoreLiason' => '0', + 'Order.Tag_UI_Email' => '', + 'Order.Tags' => '', + 'Order._id' => 'test-2', + 'Order.add_print_location' => '', + 'Order.created' => '2011-Dec-29 05:40:18', + 'Order.force_admin' => '0', + 'Order.modified' => '2012-Jul-25 01:24:49', + 'Order.name' => 'towering power', + 'Order.order_id' => '135961', + 'Order.slug' => 'test-2', + 'Order.title' => 'test job 2', + 'Order.type' => 'ttt' + ); + $expanded = Hash::expand($data); + $flattened = Hash::flatten($expanded); + $this->assertEquals($data, $flattened); + } + } diff --git a/lib/Cake/Test/Case/Utility/InflectorTest.php b/lib/Cake/Test/Case/Utility/InflectorTest.php index cd19892480a..9e7d43add10 100644 --- a/lib/Cake/Test/Case/Utility/InflectorTest.php +++ b/lib/Cake/Test/Case/Utility/InflectorTest.php @@ -109,6 +109,7 @@ public function testInflectingSingulars() { $this->assertEquals(Inflector::singularize('roofs'), 'roof'); $this->assertEquals(Inflector::singularize('foes'), 'foe'); $this->assertEquals(Inflector::singularize('databases'), 'database'); + $this->assertEquals(Inflector::singularize('cookies'), 'cookie'); $this->assertEquals(Inflector::singularize(''), ''); } @@ -160,6 +161,7 @@ public function testInflectingPlurals() { $this->assertEquals(Inflector::pluralize('cafe'), 'cafes'); $this->assertEquals(Inflector::pluralize('roof'), 'roofs'); $this->assertEquals(Inflector::pluralize('foe'), 'foes'); + $this->assertEquals(Inflector::pluralize('cookie'), 'cookies'); $this->assertEquals(Inflector::pluralize(''), ''); } diff --git a/lib/Cake/Test/Case/Utility/SetTest.php b/lib/Cake/Test/Case/Utility/SetTest.php index e1c18a8a5b0..4dd79f7ab0f 100644 --- a/lib/Cake/Test/Case/Utility/SetTest.php +++ b/lib/Cake/Test/Case/Utility/SetTest.php @@ -152,6 +152,9 @@ public function testMerge() { $r = Set::merge('foo', 'bar'); $this->assertEquals(array('foo', 'bar'), $r); + $r = Set::merge(array('foo'), array(), array('bar')); + $this->assertEquals(array('foo', 'bar'), $r); + $r = Set::merge('foo', array('user' => 'bob', 'no-bar'), 'bar'); $this->assertEquals(array('foo', 'user' => 'bob', 'no-bar', 'bar'), $r); @@ -222,6 +225,9 @@ public function testMerge() { * @return void */ public function testSort() { + $result = Set::sort(array(), '{n}.name', 'asc'); + $this->assertEquals(array(), $result); + $a = array( 0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))), 1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay'))) @@ -1967,7 +1973,7 @@ public function testCombine() { $this->assertEquals($expected, $result); $result = Set::combine($a, 'fail', 'fail'); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); } /** @@ -3054,6 +3060,7 @@ public function testStrictKeyCheck() { /** * Tests Set::flatten * + * @see Hash test cases, as Set::flatten() is just a proxy. * @return void */ public function testFlatten() { @@ -3064,6 +3071,21 @@ public function testFlatten() { $data[9] = 'Shemp'; $result = Set::flatten($data); $this->assertEquals($data, $result); + + $data = array( + array( + 'Post' => array('id' => '1', 'author_id' => null, 'title' => 'First Post'), + 'Author' => array(), + ) + ); + $result = Set::flatten($data); + $expected = array( + '0.Post.id' => '1', + '0.Post.author_id' => null, + '0.Post.title' => 'First Post', + '0.Author' => array() + ); + $this->assertEquals($expected, $result); } /** diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index 22afee2c904..db9a27d941d 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -1655,6 +1655,11 @@ public function testEmail() { $this->assertTrue(Validation::email('abc@example.travel')); $this->assertTrue(Validation::email('someone@st.t-com.hr')); + // gTLD's + $this->assertTrue(Validation::email('example@host.local')); + $this->assertTrue(Validation::email('example@x.org')); + $this->assertTrue(Validation::email('example@host.xxx')); + // strange, but technically valid email addresses $this->assertTrue(Validation::email('S=postmaster/OU=rz/P=uni-frankfurt/A=d400/C=de@gateway.d400.de')); $this->assertTrue(Validation::email('customer/department=shipping@example.com')); @@ -1669,7 +1674,6 @@ public function testEmail() { $this->assertFalse(Validation::email('abc.@example.com')); $this->assertFalse(Validation::email('abc@example..com')); $this->assertFalse(Validation::email('abc@example.com.a')); - $this->assertFalse(Validation::email('abc@example.toolong')); $this->assertFalse(Validation::email('abc;@example.com')); $this->assertFalse(Validation::email('abc@example.com;')); $this->assertFalse(Validation::email('abc@efg@example.com')); @@ -1839,11 +1843,11 @@ public function testUrl() { $this->assertTrue(Validation::url('http://example.com/~userdir/')); $this->assertTrue(Validation::url('http://underscore_subdomain.example.org')); $this->assertTrue(Validation::url('http://_jabber._tcp.gmail.com')); + $this->assertTrue(Validation::url('http://www.domain.longttldnotallowed')); $this->assertFalse(Validation::url('ftps://256.168.0.1/pub/cake')); $this->assertFalse(Validation::url('ftp://256.168.0.1/pub/cake')); $this->assertFalse(Validation::url('http://w_w.domain.co_m')); $this->assertFalse(Validation::url('http://www.domain.12com')); - $this->assertFalse(Validation::url('http://www.domain.longttldnotallowed')); $this->assertFalse(Validation::url('http://www.-invaliddomain.tld')); $this->assertFalse(Validation::url('http://www.domain.-invalidtld')); $this->assertFalse(Validation::url('http://this-domain-is-too-loooooong-by-icann-rules-maximum-length-is-63.com')); diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index 7108d75a791..e90217d9330 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -177,6 +177,7 @@ public static function invalidDataProvider() { array(null), array(false), array(''), + array('http://localhost/notthere.xml'), ); } diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 9da345458d1..ae5b8fc9155 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -115,10 +115,19 @@ class Contact extends CakeTestModel { 'required_one' => array('required' => array('rule' => array('notEmpty'))), 'imnotrequired' => array('required' => false, 'rule' => 'alphaNumeric', 'allowEmpty' => true), 'imalsonotrequired' => array( + 'alpha' => array('rule' => 'alphaNumeric', 'allowEmpty' => true), + 'between' => array('rule' => array('between', 5, 30)), + ), + 'imalsonotrequired2' => array( 'alpha' => array('rule' => 'alphaNumeric', 'allowEmpty' => true), 'between' => array('rule' => array('between', 5, 30), 'allowEmpty' => true), ), 'imnotrequiredeither' => array('required' => true, 'rule' => array('between', 5, 30), 'allowEmpty' => true), + 'iamrequiredalways' => array( + 'email' => array('rule' => 'email'), + 'rule_on_create' => array('rule' => array('maxLength', 50), 'on' => 'create'), + 'rule_on_update' => array('rule' => array('between', 1, 50), 'on' => 'update'), + ), ); /** @@ -2144,6 +2153,23 @@ public function testInput() { $this->assertTags($result, $expected); } +/** + * Test that inputs with 0 can be created. + * + * @return void + */ + public function testInputZero() { + $this->Form->create('User'); + $result = $this->Form->input('0'); + $expected = array( + 'div' => array('class' => 'input text'), + 'label' => array('for' => 'User0'), '/label', + 'input' => array('type' => 'text', 'name' => 'data[User][0]', 'id' => 'User0'), + '/div' + ); + $this->assertTags($result, $expected); + } + /** * test input() with checkbox creation * @@ -2224,9 +2250,9 @@ public function testInputTime() { 'type' => 'time', 'selected' => '18:15' )); - $this->assertRegExp('##', $result); - $this->assertRegExp('##', $result); - $this->assertRegExp('##', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); } /** @@ -2235,6 +2261,24 @@ public function testInputTime() { * @return void */ public function testTimeSelectedWithInterval() { + $result = $this->Form->input('Model.start_time', array( + 'type' => 'time', + 'interval' => 15, + 'selected' => array('hour' => '3', 'min' => '57', 'meridian' => 'pm') + )); + $this->assertContains('', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); + + $result = $this->Form->input('Model.start_time', array( + 'type' => 'time', + 'interval' => 15, + 'selected' => '2012-10-23 15:57:00' + )); + $this->assertContains('', $result); + $this->assertContains('', $result); + $this->assertContains('', $result); + $result = $this->Form->input('Model.start_time', array( 'timeFormat' => 24, 'type' => 'time', @@ -2614,6 +2658,36 @@ public function testInputOverridingMagicSelectType() { $this->assertTags($result, $expected); } +/** + * Test that magic input() selects are created for type=number + * + * @return void + */ + public function testInputMagicSelectForTypeNumber() { + $this->View->viewVars['balances'] = array(0 => 'nothing', 1 => 'some', 100 => 'a lot'); + $this->Form->request->data = array('ValidateUser' => array('balance' => 1)); + $result = $this->Form->input('ValidateUser.balance'); + $expected = array( + 'div' => array('class' => 'input select'), + 'label' => array('for' => 'ValidateUserBalance'), + 'Balance', + '/label', + 'select' => array('name' => 'data[ValidateUser][balance]', 'id' => 'ValidateUserBalance'), + array('option' => array('value' => '0')), + 'nothing', + '/option', + array('option' => array('value' => '1', 'selected' => 'selected')), + 'some', + '/option', + array('option' => array('value' => '100')), + 'a lot', + '/option', + '/select', + '/div' + ); + $this->assertTags($result, $expected); + } + /** * Test that magic input() selects can easily be converted into radio types without error. * @@ -3220,83 +3294,6 @@ public function testRadio() { ); $this->assertTags($result, $expected); - $result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'fieldset' => array(), - 'legend' => array(), - 'Legend title', - '/legend', - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - array('label' => array('for' => 'NewsletterSubscribe0')), - 'Unsubscribe', - '/label', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - array('label' => array('for' => 'NewsletterSubscribe1')), - 'Subscribe', - '/label', - '/fieldset', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - array('label' => array('for' => 'NewsletterSubscribe0')), - 'Unsubscribe', - '/label', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - array('label' => array('for' => 'NewsletterSubscribe1')), - 'Subscribe', - '/label', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'fieldset' => array(), - 'legend' => array(), - 'Legend title', - '/legend', - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - 'Subscribe', - '/fieldset', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - 'Subscribe', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'value' => '1', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1', 'checked' => 'checked')), - 'Subscribe', - '/div' - ); - $this->assertTags($result, $expected); - $result = $this->Form->radio('Employee.gender', array('male' => 'Male', 'female' => 'Female')); $expected = array( 'fieldset' => array(), @@ -3410,10 +3407,11 @@ public function testRadio() { /** * Test that radios with a 0 value are selected under the correct conditions. + * Also ensure that values that are booleanish are handled correctly. * * @return void */ - public function testRadioOptionWithZeroValue() { + public function testRadioOptionWithBooleanishValues() { $expected = array( 'fieldset' => array(), 'legend' => array(), @@ -3435,6 +3433,9 @@ public function testRadioOptionWithZeroValue() { $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' => false)); + $this->assertTags($result, $expected); + $expected = array( 'fieldset' => array(), 'legend' => array(), @@ -3459,6 +3460,30 @@ public function testRadioOptionWithZeroValue() { $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No')); $this->assertTags($result, $expected); + + $expected = array( + 'fieldset' => array(), + 'legend' => array(), + 'Field', + '/legend', + array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'checked' => 'checked', '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' => 1)); + $this->assertTags($result, $expected); + + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '1')); + $this->assertTags($result, $expected); + + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => true)); + $this->assertTags($result, $expected); } /** @@ -5621,26 +5646,10 @@ public function testHour() { $this->Form->request->data['Model']['field'] = ''; $result = $this->Form->hour('Model.field', true, array('value' => '23')); - $expected = array( - array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')), - array('option' => array('value' => '')), - '/option', - array('option' => array('value' => '00')), - '0', - '/option', - array('option' => array('value' => '01')), - '1', - '/option', - array('option' => array('value' => '02')), - '2', - '/option', - $hoursRegex, - array('option' => array('value' => '23', 'selected' => 'selected')), - '23', - '/option', - '/select', - ); - $this->assertTags($result, $expected); + $this->assertContains('', $result); + + $result = $this->Form->hour('Model.field', false, array('value' => '23')); + $this->assertContains('', $result); $this->Form->request->data['Model']['field'] = '2006-10-10 00:12:32'; $result = $this->Form->hour('Model.field', true); @@ -5960,6 +5969,10 @@ public function testFileUploadField() { '/div' ); $this->assertTags($result, $expected); + + $this->Form->request->data['Model']['upload'] = 'no data should be set in value'; + $result = $this->Form->file('Model.upload'); + $this->assertTags($result, array('input' => array('type' => 'file', 'name' => 'data[Model][upload]', 'id' => 'ModelUpload'))); } /** @@ -7022,6 +7035,20 @@ public function testFormInputRequiredDetection() { ); $this->assertTags($result, $expected); + $result = $this->Form->input('Contact.imalsonotrequired2'); + $expected = array( + 'div' => array('class' => 'input text'), + 'label' => array('for' => 'ContactImalsonotrequired2'), + 'Imalsonotrequired2', + '/label', + 'input' => array( + 'type' => 'text', 'name' => 'data[Contact][imalsonotrequired2]', + 'id' => 'ContactImalsonotrequired2' + ), + '/div' + ); + $this->assertTags($result, $expected); + $result = $this->Form->input('Contact.imnotrequiredeither'); $expected = array( 'div' => array('class' => 'input text'), @@ -7035,6 +7062,20 @@ public function testFormInputRequiredDetection() { '/div' ); $this->assertTags($result, $expected); + + $result = $this->Form->input('Contact.iamrequiredalways'); + $expected = array( + 'div' => array('class' => 'input text required'), + 'label' => array('for' => 'ContactIamrequiredalways'), + 'Iamrequiredalways', + '/label', + 'input' => array( + 'type' => 'text', 'name' => 'data[Contact][iamrequiredalways]', + 'id' => 'ContactIamrequiredalways' + ), + '/div' + ); + $this->assertTags($result, $expected); } /** diff --git a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php index 4823f1ec5fc..4114b65e3f0 100644 --- a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php @@ -215,6 +215,18 @@ public function testRequest() { $this->assertEquals($expected, $result); } +/** + * Test that querystring arguments are not double escaped. + * + * @return void + */ + public function testRequestWithQueryStringArguments() { + $url = '/users/search/sort:User.name/direction:desc?nome=&cpm=&audience=public'; + $result = $this->Jquery->request($url); + $expected = '$.ajax({url:"\\/users\\/search\\/sort:User.name\\/direction:desc?nome=&cpm=&audience=public"});'; + $this->assertEquals($expected, $result); + } + /** * test that alternate jQuery object values work for request() * diff --git a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php index 950e5c22125..53b8cce9708 100644 --- a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php @@ -29,7 +29,9 @@ class JsEncodingObject { protected $_title = 'Old thing'; + //@codingStandardsIgnoreStart private $__noshow = 'Never ever'; + //@codingStandardsIgnoreEnd } @@ -366,7 +368,9 @@ public function testWriteScriptsInFile() { $this->assertTrue(file_exists(WWW_ROOT . $filename[1])); $contents = file_get_contents(WWW_ROOT . $filename[1]); $this->assertRegExp('/one\s=\s1;\ntwo\s=\s2;/', $contents); - @unlink(WWW_ROOT . $filename[1]); + if (file_exists(WWW_ROOT . $filename[1])) { + unlink(WWW_ROOT . $filename[1]); + } Configure::write('Cache.disable', true); $this->Js->buffer('one = 1;'); @@ -904,7 +908,7 @@ public function testObject() { public function testOptionMapping() { $JsEngine = new OptionEngineHelper($this->View); $result = $JsEngine->testMap(); - $this->assertEquals(array(), $result); + $this->assertSame(array(), $result); $result = $JsEngine->testMap(array('foo' => 'bar', 'baz' => 'sho')); $this->assertEquals(array('foo' => 'bar', 'baz' => 'sho'), $result); diff --git a/lib/Cake/Test/Case/View/HelperTest.php b/lib/Cake/Test/Case/View/HelperTest.php index 65d9d8eb5d9..6cecf1ea1d4 100644 --- a/lib/Cake/Test/Case/View/HelperTest.php +++ b/lib/Cake/Test/Case/View/HelperTest.php @@ -622,6 +622,9 @@ public function testAssetUrl() { $result = $this->Helper->assetUrl('style', array('ext' => '.css')); $this->assertEquals('style.css', $result); + $result = $this->Helper->assetUrl('dir/sub dir/my image', array('ext' => '.jpg')); + $this->assertEquals('dir/sub%20dir/my%20image.jpg', $result); + $result = $this->Helper->assetUrl('foo.jpg?one=two&three=four'); $this->assertEquals('foo.jpg?one=two&three=four', $result); } diff --git a/lib/Cake/Test/Case/View/MediaViewTest.php b/lib/Cake/Test/Case/View/MediaViewTest.php index 3b723c0bfea..a027c60e1ac 100644 --- a/lib/Cake/Test/Case/View/MediaViewTest.php +++ b/lib/Cake/Test/Case/View/MediaViewTest.php @@ -36,7 +36,10 @@ class MediaViewTest extends CakeTestCase { public function setUp() { parent::setUp(); $this->MediaView = $this->getMock('MediaView', array('_isActive', '_clearBuffer', '_flushBuffer')); - $this->MediaView->response = $this->getMock('CakeResponse'); + $this->MediaView->response = $this->getMock( + 'CakeResponse', + array('send', 'cache', 'type', 'download', 'statusCode') + ); } /** @@ -83,20 +86,6 @@ public function testRender() { ->with('css') ->will($this->returnArgument(0)); - $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - - $this->MediaView->response->expects($this->at(2)) - ->method('header') - ->with(array( - 'Content-Length' => 31 - )); $this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->expects($this->once())->method('_flushBuffer'); @@ -106,6 +95,16 @@ public function testRender() { $output = ob_get_clean(); $this->assertEquals('this is the test asset css file', $output); $this->assertTrue($result !== false); + + $headers = $this->MediaView->response->header(); + $this->assertEquals(31, $headers['Content-Length']); + $this->assertEquals(0, $headers['Expires']); + $this->assertEquals( + 'private, must-revalidate, post-check=0, pre-check=0', + $headers['Cache-Control'] + ); + $this->assertEquals('no-cache', $headers['Pragma']); + $this->assertContains(gmdate('D, d M Y H:i', time()), $headers['Date']); } /** @@ -130,29 +129,10 @@ public function testRenderWithUnknownFileTypeGeneric() { ->with('ini') ->will($this->returnValue(false)); - $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - $this->MediaView->response->expects($this->once()) ->method('download') ->with('no_section.ini'); - $this->MediaView->response->expects($this->at(3)) - ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); - - $this->MediaView->response->expects($this->at(4)) - ->method('header') - ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->expects($this->once())->method('_flushBuffer'); @@ -165,6 +145,17 @@ public function testRenderWithUnknownFileTypeGeneric() { if ($currentUserAgent !== null) { $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; } + + $headers = $this->MediaView->response->header(); + $this->assertEquals(35, $headers['Content-Length']); + $this->assertEquals(0, $headers['Expires']); + $this->assertEquals('bytes', $headers['Accept-Ranges']); + $this->assertEquals( + 'private, must-revalidate, post-check=0, pre-check=0', + $headers['Cache-Control'] + ); + $this->assertEquals('no-cache', $headers['Pragma']); + $this->assertContains(gmdate('D, d M Y H:i', time()), $headers['Date']); } /** @@ -190,15 +181,6 @@ public function testRenderWithUnknownFileTypeOpera() { ->will($this->returnValue(false)); $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - - $this->MediaView->response->expects($this->at(2)) ->method('type') ->with('application/octetstream') ->will($this->returnValue(false)); @@ -207,16 +189,6 @@ public function testRenderWithUnknownFileTypeOpera() { ->method('download') ->with('no_section.ini'); - $this->MediaView->response->expects($this->at(4)) - ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); - - $this->MediaView->response->expects($this->at(5)) - ->method('header') - ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->expects($this->once())->method('_flushBuffer'); @@ -229,6 +201,17 @@ public function testRenderWithUnknownFileTypeOpera() { if ($currentUserAgent !== null) { $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; } + + $headers = $this->MediaView->response->header(); + $this->assertEquals(35, $headers['Content-Length']); + $this->assertEquals(0, $headers['Expires']); + $this->assertEquals('bytes', $headers['Accept-Ranges']); + $this->assertEquals( + 'private, must-revalidate, post-check=0, pre-check=0', + $headers['Cache-Control'] + ); + $this->assertEquals('no-cache', $headers['Pragma']); + $this->assertContains(gmdate('D, d M Y H:i', time()), $headers['Date']); } /** @@ -255,15 +238,6 @@ public function testRenderWithUnknownFileTypeIE() { ->will($this->returnValue(false)); $this->MediaView->response->expects($this->at(1)) - ->method('header') - ->with(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - - $this->MediaView->response->expects($this->at(2)) ->method('type') ->with('application/force-download') ->will($this->returnValue(false)); @@ -272,16 +246,6 @@ public function testRenderWithUnknownFileTypeIE() { ->method('download') ->with('config.ini'); - $this->MediaView->response->expects($this->at(4)) - ->method('header') - ->with(array( - 'Accept-Ranges' => 'bytes' - )); - - $this->MediaView->response->expects($this->at(5)) - ->method('header') - ->with('Content-Length', 35); - $this->MediaView->response->expects($this->once())->method('send'); $this->MediaView->expects($this->once())->method('_clearBuffer'); $this->MediaView->expects($this->once())->method('_flushBuffer'); @@ -294,6 +258,17 @@ public function testRenderWithUnknownFileTypeIE() { if ($currentUserAgent !== null) { $_SERVER['HTTP_USER_AGENT'] = $currentUserAgent; } + + $headers = $this->MediaView->response->header(); + $this->assertEquals(35, $headers['Content-Length']); + $this->assertEquals(0, $headers['Expires']); + $this->assertEquals('bytes', $headers['Accept-Ranges']); + $this->assertEquals( + 'private, must-revalidate, post-check=0, pre-check=0', + $headers['Cache-Control'] + ); + $this->assertEquals('no-cache', $headers['Pragma']); + $this->assertContains(gmdate('D, d M Y H:i', time()), $headers['Date']); } /** diff --git a/lib/Cake/Test/Case/View/ScaffoldViewTest.php b/lib/Cake/Test/Case/View/ScaffoldViewTest.php index a5f2d85c093..87bf9bd9abe 100644 --- a/lib/Cake/Test/Case/View/ScaffoldViewTest.php +++ b/lib/Cake/Test/Case/View/ScaffoldViewTest.php @@ -279,7 +279,7 @@ public function testViewScaffold() { $this->assertRegExp('/

View Scaffold Mock<\/h2>/', $result); $this->assertRegExp('/
/', $result); - //TODO: add specific tests for fields. + $this->assertRegExp('/1<\/a>/', $result); //belongsTo links $this->assertRegExp('/
  • Edit Scaffold Mock<\/a>\s<\/li>/', $result); $this->assertRegExp('//', $result); - //TODO: add testing for table generation + $this->assertRegExp('/
  • New Scaffold Mock<\/a><\/li>/', $result); Configure::write('Routing.prefixes', $_backAdmin); @@ -454,7 +454,7 @@ public function testMultiplePrefixScaffold() { $this->assertRegExp('/

    Scaffold Mock<\/h2>/', $result); $this->assertRegExp('//', $result); - //TODO: add testing for table generation + $this->assertRegExp('/
  • New Scaffold Mock<\/a><\/li>/', $result); Configure::write('Routing.prefixes', $_backAdmin); diff --git a/lib/Cake/Test/Case/View/ViewTest.php b/lib/Cake/Test/Case/View/ViewTest.php index 75875349d7d..f7048ffe469 100644 --- a/lib/Cake/Test/Case/View/ViewTest.php +++ b/lib/Cake/Test/Case/View/ViewTest.php @@ -300,7 +300,7 @@ public function testGetTemplate() { $this->Controller->params['pass'] = array('home'); $ThemeView = new TestThemeView($this->Controller); - $ThemeView->theme = 'TestTheme'; + $ThemeView->theme = 'test_theme'; $expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Pages' . DS . 'home.ctp'; $result = $ThemeView->getViewFileName('home'); $this->assertEquals($expected, $result); @@ -309,6 +309,7 @@ public function testGetTemplate() { $result = $ThemeView->getViewFileName('/Posts/index'); $this->assertEquals($expected, $result); + $ThemeView->theme = 'TestTheme'; $expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Themed' . DS . 'TestTheme' . DS . 'Layouts' . DS . 'default.ctp'; $result = $ThemeView->getLayoutFileName(); $this->assertEquals($expected, $result); @@ -546,11 +547,7 @@ public function testMissingView() { $this->ThemeController->params['pass'] = array('home'); $View = new TestThemeView($this->ThemeController); - ob_start(); - $result = $View->getViewFileName('does_not_exist'); - $expected = ob_get_clean(); - $this->assertRegExp("/PagesController::/", $expected); - $this->assertRegExp("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)pages(\/|\\\)does_not_exist.ctp/", $expected); + $View->getViewFileName('does_not_exist'); } /** @@ -577,11 +574,7 @@ public function testMissingLayout() { $this->ThemeController->theme = 'my_theme'; $View = new TestThemeView($this->ThemeController); - ob_start(); $result = $View->getLayoutFileName(); - $expected = ob_get_clean(); - $this->assertRegExp("/Missing Layout/", $expected); - $this->assertRegExp("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)layouts(\/|\\\)whatever.ctp/", $expected); } /** @@ -1101,7 +1094,9 @@ public function testRenderCache() { $this->assertRegExp('/^some cacheText/', $result); - @unlink($path); + if (file_exists($path)) { + unlink($path); + } } /** diff --git a/lib/Cake/Test/Fixture/FlagTreeFixture.php b/lib/Cake/Test/Fixture/FlagTreeFixture.php index e775e8fa708..99937e39981 100644 --- a/lib/Cake/Test/Fixture/FlagTreeFixture.php +++ b/lib/Cake/Test/Fixture/FlagTreeFixture.php @@ -41,11 +41,11 @@ class FlagTreeFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'integer','key' => 'primary'), - 'name' => array('type' => 'string','null' => false), + 'id' => array('type' => 'integer','key' => 'primary'), + 'name' => array('type' => 'string','null' => false), 'parent_id' => 'integer', - 'lft' => array('type' => 'integer','null' => false), - 'rght' => array('type' => 'integer','null' => false), - 'flag' => array('type' => 'integer','null' => false, 'length' => 1, 'default' => 0) + 'lft' => array('type' => 'integer','null' => false), + 'rght' => array('type' => 'integer','null' => false), + 'flag' => array('type' => 'integer','null' => false, 'length' => 1, 'default' => 0) ); } diff --git a/lib/Cake/Test/test_app/Plugin/TestPlugin/Controller/TestPluginAppController.php b/lib/Cake/Test/test_app/Plugin/TestPlugin/Controller/TestPluginAppController.php index 93a498147ae..23ab63b3abb 100644 --- a/lib/Cake/Test/test_app/Plugin/TestPlugin/Controller/TestPluginAppController.php +++ b/lib/Cake/Test/test_app/Plugin/TestPlugin/Controller/TestPluginAppController.php @@ -16,5 +16,8 @@ * @since CakePHP(tm) v 1.2.0.5432 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ + +App::uses('AppController', 'Controller'); + class TestPluginAppController extends AppController { } diff --git a/lib/Cake/TestSuite/CakeTestCase.php b/lib/Cake/TestSuite/CakeTestCase.php index 35092438952..d624ced7042 100644 --- a/lib/Cake/TestSuite/CakeTestCase.php +++ b/lib/Cake/TestSuite/CakeTestCase.php @@ -674,6 +674,6 @@ protected function skipUnless($condition, $message = '') { } return $condition; } - // @codingStandardsIgnoreStop + // @codingStandardsIgnoreEnd } diff --git a/lib/Cake/TestSuite/CakeTestSuiteCommand.php b/lib/Cake/TestSuite/CakeTestSuiteCommand.php index 6d410ec7979..f655c7fda76 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteCommand.php +++ b/lib/Cake/TestSuite/CakeTestSuiteCommand.php @@ -82,7 +82,9 @@ public function run(array $argv, $exit = true) { $result = $skeleton->generate(true); if (!$result['incomplete']) { + //@codingStandardsIgnoreStart eval(str_replace(array(''), '', $result['code'])); + //@codingStandardsIgnoreEnd $suite = new PHPUnit_Framework_TestSuite( $this->arguments['test'] . 'Test' ); diff --git a/lib/Cake/TestSuite/ControllerTestCase.php b/lib/Cake/TestSuite/ControllerTestCase.php index d989553f7e8..fd44b8cb5be 100644 --- a/lib/Cake/TestSuite/ControllerTestCase.php +++ b/lib/Cake/TestSuite/ControllerTestCase.php @@ -173,7 +173,7 @@ abstract class ControllerTestCase extends CakeTestCase { * * @var boolean */ - private $__dirtyController = false; + protected $_dirtyController = false; /** * Used to enable calling ControllerTestCase::testAction() without the testing @@ -252,7 +252,7 @@ protected function _testAction($url = '', $options = array()) { $this->headers = Router::currentRoute()->response->header(); return; } - if ($this->__dirtyController) { + if ($this->_dirtyController) { $this->controller = null; } @@ -275,7 +275,7 @@ protected function _testAction($url = '', $options = array()) { if (isset($this->controller->View)) { $this->view = $this->controller->View->fetch('__view_no_layout__'); } - $this->__dirtyController = true; + $this->_dirtyController = true; $this->headers = $Dispatch->response->header(); $_GET = $restore['get']; @@ -371,7 +371,7 @@ public function generate($controller, $mocks = array()) { } $_controller->constructClasses(); - $this->__dirtyController = false; + $this->_dirtyController = false; $this->controller = $_controller; return $this->controller; diff --git a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php index d5998ecda9b..4d726efbbb8 100644 --- a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php +++ b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php @@ -204,19 +204,16 @@ public function load(CakeTestCase $test) { return; } - $nested = $test->db->useNestedTransactions; - $test->db->useNestedTransactions = false; - $test->db->begin(); foreach ($fixtures as $f) { if (!empty($this->_loaded[$f])) { $fixture = $this->_loaded[$f]; $db = ConnectionManager::getDataSource($fixture->useDbConfig); + $db->begin(); $this->_setupTable($fixture, $db, $test->dropTables); $fixture->insert($db); + $db->commit(); } } - $test->db->commit(); - $test->db->useNestedTransactions = $nested; } /** diff --git a/lib/Cake/TestSuite/templates/menu.php b/lib/Cake/TestSuite/templates/menu.php index df6ec413f81..f41cfa69ddd 100644 --- a/lib/Cake/TestSuite/templates/menu.php +++ b/lib/Cake/TestSuite/templates/menu.php @@ -1,4 +1,5 @@
  • Plugins - +
    • diff --git a/lib/Cake/Utility/CakeNumber.php b/lib/Cake/Utility/CakeNumber.php index 3b21cbc3666..7f61ec077a8 100644 --- a/lib/Cake/Utility/CakeNumber.php +++ b/lib/Cake/Utility/CakeNumber.php @@ -211,7 +211,10 @@ protected static function _numberFormat($number, $places = 0, $decimals = '.', $ * - `decimals` - Decimal separator symbol ie. '.' * - `negative` - Symbol for negative numbers. If equal to '()', * the number will be wrapped with ( and ) - * - `escape` - Should the output be htmlentity escaped? Defaults to true + * - `escape` - Should the output be escaped for html special characters. + * The default value for this option is controlled by the currency settings. + * By default the EUR, and GBP contain HTML encoded symbols. If you require non HTML + * encoded symbols you will need to update the settings with the correct bytes. * * @param float $number * @param string $currency Shortcut to default options. Valid values are diff --git a/lib/Cake/Utility/Debugger.php b/lib/Cake/Utility/Debugger.php index 7c6b2ba1221..5d57766362c 100644 --- a/lib/Cake/Utility/Debugger.php +++ b/lib/Cake/Utility/Debugger.php @@ -395,10 +395,15 @@ public static function excerpt($file, $line, $context = 2) { if (!file_exists($file)) { return array(); } - $data = @explode("\n", file_get_contents($file)); - - if (empty($data) || !isset($data[$line])) { - return; + $data = file_get_contents($file); + if (empty($data)) { + return $lines; + } + if (strpos($data, "\n") !== false) { + $data = explode("\n", $data); + } + if (!isset($data[$line])) { + return $lines; } for ($i = $line - ($context + 1); $i < $line + $context; $i++) { if (!isset($data[$i])) { @@ -422,13 +427,23 @@ public static function excerpt($file, $line, $context = 2) { * @return string */ protected static function _highlight($str) { - static $supportHighlight = null; - if (!$supportHighlight && function_exists('hphp_log')) { - $supportHighlight = false; + if (function_exists('hphp_log') || function_exists('hphp_gettid')) { return htmlentities($str); } - $supportHighlight = true; - return highlight_string($str, true); + $added = false; + if (strpos($str, '', + '', + $highlight + ); + } + return $highlight; } /** diff --git a/lib/Cake/Utility/Folder.php b/lib/Cake/Utility/Folder.php index bda4858695d..c673177abea 100644 --- a/lib/Cake/Utility/Folder.php +++ b/lib/Cake/Utility/Folder.php @@ -354,7 +354,9 @@ public function chmod($path, $mode = false, $recursive = true, $exceptions = arr } if ($recursive === false && is_dir($path)) { + //@codingStandardsIgnoreStart if (@chmod($path, intval($mode, 8))) { + //@codingStandardsIgnoreEnd $this->_messages[] = __d('cake_dev', '%s changed to %s', $path, $mode); return true; } @@ -375,7 +377,9 @@ public function chmod($path, $mode = false, $recursive = true, $exceptions = arr continue; } + //@codingStandardsIgnoreStart if (@chmod($fullpath, intval($mode, 8))) { + //@codingStandardsIgnoreEnd $this->_messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode); } else { $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode); @@ -559,13 +563,17 @@ public function delete($path = null) { foreach ($iterator as $item) { $filePath = $item->getPathname(); if ($item->isFile() || $item->isLink()) { + //@codingStandardsIgnoreStart if (@unlink($filePath)) { + //@codingStandardsIgnoreEnd $this->_messages[] = __d('cake_dev', '%s removed', $filePath); } else { $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath); } } elseif ($item->isDir() && !$item->isDot()) { + //@codingStandardsIgnoreStart if (@rmdir($filePath)) { + //@codingStandardsIgnoreEnd $this->_messages[] = __d('cake_dev', '%s removed', $filePath); } else { $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath); @@ -575,7 +583,9 @@ public function delete($path = null) { } $path = rtrim($path, DS); + //@codingStandardsIgnoreStart if (@rmdir($path)) { + //@codingStandardsIgnoreEnd $this->_messages[] = __d('cake_dev', '%s removed', $path); } else { $this->_errors[] = __d('cake_dev', '%s NOT removed', $path); @@ -629,7 +639,9 @@ public function copy($options = array()) { } $exceptions = array_merge(array('.', '..', '.svn'), $options['skip']); + //@codingStandardsIgnoreStart if ($handle = @opendir($fromDir)) { + //@codingStandardsIgnoreEnd while (false !== ($item = readdir($handle))) { if (!in_array($item, $exceptions)) { $from = Folder::addPathElement($fromDir, $item); diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index fc133ff5ff1..278a01aa9cb 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -96,7 +96,7 @@ public static function extract(array $data, $path) { return (array)self::get($data, $path); } - if (strpos('[', $path) === false) { + if (strpos($path, '[') === false) { $tokens = explode('.', $path); } else { $tokens = String::tokenize($path, '.', '[', ']'); @@ -527,11 +527,12 @@ public static function flatten(array $data, $separator = '.') { $element = $data[$key]; unset($data[$key]); - if (is_array($element)) { + if (is_array($element) && !empty($element)) { if (!empty($data)) { $stack[] = array($data, $path); } $data = $element; + reset($data); $path .= $key . $separator; } else { $result[$path . $key] = $element; @@ -539,6 +540,7 @@ public static function flatten(array $data, $separator = '.') { if (empty($data) && !empty($stack)) { list($data, $path) = array_pop($stack); + reset($data); } } return $result; @@ -698,6 +700,17 @@ public static function reduce(array $data, $path, $function) { * Apply a callback to a set of extracted values using `$function`. * The function will get the extracted values as the first argument. * + * ### Example + * + * You can easily count the results of an extract using apply(). + * For example to count the comments on an Article: + * + * `$count = Hash::apply($data, 'Article.Comment.{n}', 'count');` + * + * You could also use a function like `array_sum` to sum the results. + * + * `$total = Hash::apply($data, '{n}.Item.price', 'array_sum');` + * * @param array $data The data to reduce. * @param string $path The path to extract from $data. * @return mixed The results of the applied method. @@ -730,6 +743,9 @@ public static function apply(array $data, $path, $function) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::sort */ public static function sort(array $data, $path, $dir, $type = 'regular') { + if (empty($data)) { + return array(); + } $originalKeys = array_keys($data); $numeric = is_numeric(implode('', $originalKeys)); if ($numeric) { diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index 31829d6218a..b724d421818 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -55,7 +55,7 @@ class Inflector { '/$/' => 's', ), 'uninflected' => array( - '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people' + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie' ), 'irregular' => array( 'atlas' => 'atlases', @@ -63,6 +63,7 @@ class Inflector { 'brother' => 'brothers', 'cafe' => 'cafes', 'child' => 'children', + 'cookie' => 'cookies', 'corpus' => 'corpuses', 'cow' => 'cows', 'ganglion' => 'ganglions', @@ -176,7 +177,7 @@ class Inflector { '/Ä/' => 'Ae', '/Ü/' => 'Ue', '/Ö/' => 'Oe', - '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', + '/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', '/ç|ć|ĉ|ċ|č/' => 'c', diff --git a/lib/Cake/Utility/ObjectCollection.php b/lib/Cake/Utility/ObjectCollection.php index 96ec3d55532..00db1253907 100644 --- a/lib/Cake/Utility/ObjectCollection.php +++ b/lib/Cake/Utility/ObjectCollection.php @@ -103,7 +103,7 @@ public function trigger($callback, $params = array(), $options = array()) { if (empty($event->omitSubject)) { $subject = $event->subject(); } - //TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager + foreach (array('break', 'breakOn', 'collectReturn', 'modParams') as $opt) { if (isset($event->{$opt})) { $options[$opt] = $event->{$opt}; diff --git a/lib/Cake/Utility/Security.php b/lib/Cake/Utility/Security.php index 857415c9a47..766be8ae566 100644 --- a/lib/Cake/Utility/Security.php +++ b/lib/Cake/Utility/Security.php @@ -64,7 +64,6 @@ public static function generateAuthKey() { * * @param string $authKey Authorization hash * @return boolean Success - * @todo Complete implementation */ public static function validateAuthKey($authKey) { return true; diff --git a/lib/Cake/Utility/Set.php b/lib/Cake/Utility/Set.php index 7ac63cfbe7c..10c937f12ff 100644 --- a/lib/Cake/Utility/Set.php +++ b/lib/Cake/Utility/Set.php @@ -45,7 +45,7 @@ class Set { */ public static function merge($data, $merge = null) { $args = func_get_args(); - if (empty($args[1])) { + if (empty($args[1]) && count($args) <= 2) { return (array)$args[0]; } if (!is_array($args[0])) { @@ -956,6 +956,9 @@ protected static function _flatten($results, $key = null) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::sort */ public static function sort($data, $path, $dir) { + if (empty($data)) { + return $data; + } $originalKeys = array_keys($data); $numeric = false; if (is_numeric(implode('', $originalKeys))) { diff --git a/lib/Cake/Utility/String.php b/lib/Cake/Utility/String.php index 01f9acbbbd1..65969b69cc4 100644 --- a/lib/Cake/Utility/String.php +++ b/lib/Cake/Utility/String.php @@ -523,7 +523,7 @@ class_exists('Multibyte'); } } else { foreach ($droppedTags as $closingTag) { - array_push($openTags, $closingTag[1]); + $openTags[] = $closingTag[1]; } } } diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 43347e37df1..875f1dd1ea5 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -38,7 +38,7 @@ class Validation { * @var array */ protected static $_pattern = array( - 'hostname' => '(?:[-_a-z0-9][-_a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)' + 'hostname' => '(?:[-_a-z0-9][-_a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})' ); /** diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index f8662b282fc..cf257b49454 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -18,6 +18,7 @@ * @since CakePHP v .0.10.3.1400 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +App::uses('HttpSocket', 'Network/Http'); /** * XML handling for Cake. @@ -97,9 +98,15 @@ public static function build($input, $options = array()) { return self::fromArray((array)$input, $options); } elseif (strpos($input, '<') !== false) { return self::_loadXml($input, $options); - } elseif (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { - $input = file_get_contents($input); - return self::_loadXml($input, $options); + } elseif (file_exists($input)) { + return self::_loadXml(file_get_contents($input), $options); + } elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { + $socket = new HttpSocket(); + $response = $socket->get($input); + if (!$response->isOk()) { + throw new XmlException(__d('cake_dev', 'XML cannot be read.')); + } + return self::_loadXml($response->body, $options); } elseif (!is_string($input)) { throw new XmlException(__d('cake_dev', 'Invalid input.')); } diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index 702a3d46a69..4f9358865f4 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license MIT License (http://www.opensource.org/licenses/mit-license.php) // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.2.3 +2.2.5 diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index bb16ec6d583..55a6dec68ef 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -292,39 +292,55 @@ public function webroot($file) { */ public function assetUrl($path, $options = array()) { if (is_array($path)) { - $path = $this->url($path, !empty($options['fullBase'])); - } elseif (strpos($path, '://') === false) { - if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) { - list($plugin, $path) = $this->_View->pluginSplit($path, false); - } - if (!empty($options['pathPrefix']) && $path[0] !== '/') { - $path = $options['pathPrefix'] . $path; - } - if ( - !empty($options['ext']) && - strpos($path, '?') === false && - substr($path, -strlen($options['ext'])) !== $options['ext'] - ) { - $path .= $options['ext']; - } - if (isset($plugin)) { - $path = Inflector::underscore($plugin) . '/' . $path; - } - $path = h($this->assetTimestamp($this->webroot($path))); + return $this->url($path, !empty($options['fullBase'])); + } + if (strpos($path, '://') !== false) { + return $path; + } + if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) { + list($plugin, $path) = $this->_View->pluginSplit($path, false); + } + if (!empty($options['pathPrefix']) && $path[0] !== '/') { + $path = $options['pathPrefix'] . $path; + } + if ( + !empty($options['ext']) && + strpos($path, '?') === false && + substr($path, -strlen($options['ext'])) !== $options['ext'] + ) { + $path .= $options['ext']; + } + if (isset($plugin)) { + $path = Inflector::underscore($plugin) . '/' . $path; + } + $path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path))); - if (!empty($options['fullBase'])) { - $base = $this->url('/', true); - $len = strlen($this->request->webroot); - if ($len) { - $base = substr($base, 0, -$len); - } - $path = $base . $path; + if (!empty($options['fullBase'])) { + $base = $this->url('/', true); + $len = strlen($this->request->webroot); + if ($len) { + $base = substr($base, 0, -$len); } + $path = $base . $path; } - return $path; } +/** + * Encodes a URL for use in HTML attributes. + * + * @param string $url The url to encode. + * @return string The url encoded for both URL & HTML contexts. + */ + protected function _encodeUrl($url) { + $path = parse_url($url, PHP_URL_PATH); + $encoded = implode('/', array_map( + 'rawurlencode', + explode('/', $path) + )); + return h(str_replace($path, $encoded, $url)); + } + /** * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force' @@ -340,20 +356,26 @@ public function assetTimestamp($path) { $filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path); $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath); if (file_exists($webrootPath)) { + //@codingStandardsIgnoreStart return $path . '?' . @filemtime($webrootPath); + //@codingStandardsIgnoreEnd } $segments = explode('/', ltrim($filepath, '/')); if ($segments[0] === 'theme') { $theme = $segments[1]; unset($segments[0], $segments[1]); $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments); + //@codingStandardsIgnoreStart return $path . '?' . @filemtime($themePath); + //@codingStandardsIgnoreEnd } else { $plugin = Inflector::camelize($segments[0]); if (CakePlugin::loaded($plugin)) { unset($segments[0]); $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments); + //@codingStandardsIgnoreStart return $path . '?' . @filemtime($pluginPath); + //@codingStandardsIgnoreEnd } } } @@ -593,7 +615,6 @@ public function field() { * @param string $id The name of the 'id' attribute. * @return mixed If $options was an array, an array will be returned with $id set. If a string * was supplied, a string will be returned. - * @todo Refactor this method to not have as many input/output options. */ public function domId($options = null, $id = 'id') { if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) { @@ -626,7 +647,6 @@ public function domId($options = null, $id = 'id') { * @param string $key The name of the attribute to be set, defaults to 'name' * @return mixed If an array was given for $options, an array with $key set will be returned. * If a string was supplied a string will be returned. - * @todo Refactor this method to not have as many input/output options. */ protected function _name($options = array(), $field = null, $key = 'name') { if ($options === null) { @@ -670,7 +690,6 @@ protected function _name($options = array(), $field = null, $key = 'name') { * @param string $key The name of the attribute to be set, defaults to 'value' * @return mixed If an array was given for $options, an array with $key set will be returned. * If a string was supplied a string will be returned. - * @todo Refactor this method to not have as many input/output options. */ public function value($options = array(), $field = null, $key = 'value') { if ($options === null) { @@ -722,8 +741,7 @@ public function value($options = array(), $field = null, $key = 'value') { /** * Sets the defaults for an input tag. Will set the - * name, value, and id attributes for an array of html attributes. Will also - * add a 'form-error' class if the field contains validation errors. + * name, value, and id attributes for an array of html attributes. * * @param string $field The field name to initialize. * @param array $options Array of options to use while initializing an input field. diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 301acaaec06..b7961142f3a 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -199,7 +199,7 @@ protected function _introspectModel($model, $key, $field = null) { $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple'); } } - if (empty($field)) { + if ($field === null || $field === false) { return $this->fieldset[$model]['fields']; } elseif (isset($this->fieldset[$model]['fields'][$field])) { return $this->fieldset[$model]['fields'][$field]; @@ -247,13 +247,17 @@ protected function _isRequiredField($validationRules) { if (empty($validationRules) || count($validationRules) === 0) { return false; } + + $isUpdate = $this->requestType === 'put'; foreach ($validationRules as $rule) { - $rule->isUpdate($this->requestType === 'put'); - if ($rule->isEmptyAllowed()) { - return false; + $rule->isUpdate($isUpdate); + if ($rule->skip()) { + continue; } + + return !$rule->allowEmpty; } - return true; + return false; } /** @@ -299,9 +303,10 @@ public function tagIsInvalid() { * can be overridden when calling input() * - `encoding` Set the accept-charset encoding for the form. Defaults to `Configure::read('App.encoding')` * - * @param string|array $model The model object which the form is being defined for. Should - * include the plugin name for plugin forms. e.g. `ContactManager.Contact`. + * @param mixed $model The model name for which the form is being defined. Should + * include the plugin name for plugin models. e.g. `ContactManager.Contact`. * If an array is passed and $options argument is empty, the array will be used as options. + * If `false` no model is used. * @param array $options An array of html attributes and options. * @return string An formatted opening FORM tag. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#options-for-create @@ -769,7 +774,7 @@ public function error($field, $text = null, $options = array()) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::label */ public function label($fieldName = null, $text = null, $options = array()) { - if (empty($fieldName)) { + if ($fieldName === null) { $fieldName = implode('.', $this->entity()); } @@ -994,7 +999,7 @@ public function input($fieldName, $options = array()) { if ( (!isset($options['options']) && in_array($options['type'], $types)) || - (isset($magicType) && $options['type'] == 'text') + (isset($magicType) && in_array($options['type'], array('text', 'number'))) ) { $varName = Inflector::variable( Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey)) @@ -1006,12 +1011,17 @@ public function input($fieldName, $options = array()) { } $options['options'] = $varOptions; } + + if ($options['type'] === 'select' && array_key_exists('step', $options)) { + unset($options['step']); + } } $autoLength = ( !array_key_exists('maxlength', $options) && isset($fieldDef['length']) && - is_scalar($fieldDef['length']) + is_scalar($fieldDef['length']) && + $options['type'] !== 'select' ); if ($autoLength && $options['type'] == 'text') { $options['maxlength'] = $fieldDef['length']; @@ -1327,7 +1337,7 @@ public function radio($fieldName, $options = array(), $attributes = array()) { $showEmpty = $this->_extractOption('empty', $attributes); if ($showEmpty) { - $showEmpty = ($showEmpty === true) ? __('empty') : $showEmpty; + $showEmpty = ($showEmpty === true) ? __d('cake', 'empty') : $showEmpty; $options = array('' => $showEmpty) + $options; } unset($attributes['empty']); @@ -1375,6 +1385,10 @@ public function radio($fieldName, $options = array(), $attributes = array()) { $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true; unset($attributes['hiddenField']); + if (isset($value) && is_bool($value)) { + $value = $value ? 1 : 0; + } + foreach ($options as $optValue => $optTitle) { $optionsHere = array('value' => $optValue); @@ -1522,7 +1536,8 @@ public function file($fieldName, $options = array()) { $this->_secure($secure, array_merge($field, array($suffix))); } - return $this->Html->useTag('file', $options['name'], array_diff_key($options, array('name' => ''))); + $exclude = array('name' => '', 'value' => ''); + return $this->Html->useTag('file', $options['name'], array_diff_key($options, $exclude)); } /** @@ -2042,6 +2057,11 @@ public function hour($fieldName, $format24Hours = false, $attributes = array()) } elseif ($attributes['value'] === false) { $attributes['value'] = null; } + + if ($attributes['value'] > 12 && !$format24Hours) { + $attributes['value'] -= 12; + } + return $this->select( $fieldName . ".hour", $this->_generateOptions($format24Hours ? 'hour24' : 'hour'), @@ -2185,50 +2205,12 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a } if (!empty($attributes['value'])) { - if (is_array($attributes['value'])) { - extract($attributes['value']); - } else { - if (is_numeric($attributes['value'])) { - $attributes['value'] = strftime('%Y-%m-%d %H:%M:%S', $attributes['value']); - } - $meridian = 'am'; - $pos = strpos($attributes['value'], '-'); - if ($pos !== false) { - $date = explode('-', $attributes['value']); - $days = explode(' ', $date[2]); - $day = $days[0]; - $month = $date[1]; - $year = $date[0]; - } else { - $days[1] = $attributes['value']; - } - - if (!empty($timeFormat)) { - $time = explode(':', $days[1]); - - if (($time[0] > 12) && $timeFormat == '12') { - $time[0] = $time[0] - 12; - $meridian = 'pm'; - } elseif ($time[0] == '12' && $timeFormat == '12') { - $meridian = 'pm'; - } elseif ($time[0] == '00' && $timeFormat == '12') { - $time[0] = 12; - } elseif ($time[0] >= 12) { - $meridian = 'pm'; - } - if ($time[0] == 0 && $timeFormat == '12') { - $time[0] = 12; - } - $hour = $min = null; - if (isset($time[1])) { - $hour = $time[0]; - $min = $time[1]; - } - } - } + list($year, $month, $day, $hour, $min, $meridian) = $this->_getDateTimeValue( + $attributes['value'], + $timeFormat + ); } - $elements = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian'); $defaults = array( 'minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true @@ -2259,42 +2241,42 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a list($year, $month, $day, $hour, $min, $meridian) = $newTime; } - if (isset($attributes['id'])) { - if (is_string($attributes['id'])) { - // build out an array version - foreach ($elements as $element) { - $selectAttrName = 'select' . $element . 'Attr'; - ${$selectAttrName} = $attributes; - ${$selectAttrName}['id'] = $attributes['id'] . $element; - } - } elseif (is_array($attributes['id'])) { - // check for missing ones and build selectAttr for each element - $attributes['id'] += array( - 'month' => '', 'year' => '', 'day' => '', - 'hour' => '', 'minute' => '', 'meridian' => '' - ); - foreach ($elements as $element) { - $selectAttrName = 'select' . $element . 'Attr'; - ${$selectAttrName} = $attributes; - ${$selectAttrName}['id'] = $attributes['id'][strtolower($element)]; - } + $keys = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian'); + $attrs = array_fill_keys($keys, $attributes); + + $hasId = isset($attributes['id']); + if ($hasId && is_array($attributes['id'])) { + // check for missing ones and build selectAttr for each element + $attributes['id'] += array( + 'month' => '', + 'year' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'meridian' => '' + ); + foreach ($keys as $key) { + $attrs[$key]['id'] = $attributes['id'][strtolower($key)]; } - } else { - // build the selectAttrName with empty id's to pass - foreach ($elements as $element) { - $selectAttrName = 'select' . $element . 'Attr'; - ${$selectAttrName} = $attributes; + } + if ($hasId && is_string($attributes['id'])) { + // build out an array version + foreach ($keys as $key) { + $attrs[$key]['id'] = $attributes['id'] . $key; } } if (is_array($attributes['empty'])) { $attributes['empty'] += array( - 'month' => true, 'year' => true, 'day' => true, - 'hour' => true, 'minute' => true, 'meridian' => true + 'month' => true, + 'year' => true, + 'day' => true, + 'hour' => true, + 'minute' => true, + 'meridian' => true ); - foreach ($elements as $element) { - $selectAttrName = 'select' . $element . 'Attr'; - ${$selectAttrName}['empty'] = $attributes['empty'][strtolower($element)]; + foreach ($keys as $key) { + $attrs[$key]['empty'] = $attributes['empty'][strtolower($key)]; } } @@ -2302,47 +2284,98 @@ public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $a foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) { switch ($char) { case 'Y': - $selectYearAttr['value'] = $year; + $attrs['Year']['value'] = $year; $selects[] = $this->year( - $fieldName, $minYear, $maxYear, $selectYearAttr + $fieldName, $minYear, $maxYear, $attrs['Year'] ); break; case 'M': - $selectMonthAttr['value'] = $month; - $selectMonthAttr['monthNames'] = $monthNames; - $selects[] = $this->month($fieldName, $selectMonthAttr); + $attrs['Month']['value'] = $month; + $attrs['Month']['monthNames'] = $monthNames; + $selects[] = $this->month($fieldName, $attrs['Month']); break; case 'D': - $selectDayAttr['value'] = $day; - $selects[] = $this->day($fieldName, $selectDayAttr); + $attrs['Day']['value'] = $day; + $selects[] = $this->day($fieldName, $attrs['Day']); break; } } $opt = implode($separator, $selects); - $selectMinuteAttr['interval'] = $interval; + $attrs['Minute']['interval'] = $interval; switch ($timeFormat) { case '24': - $selectHourAttr['value'] = $hour; - $selectMinuteAttr['value'] = $min; - $opt .= $this->hour($fieldName, true, $selectHourAttr) . ':' . - $this->minute($fieldName, $selectMinuteAttr); + $attrs['Hour']['value'] = $hour; + $attrs['Minute']['value'] = $min; + $opt .= $this->hour($fieldName, true, $attrs['Hour']) . ':' . + $this->minute($fieldName, $attrs['Minute']); break; case '12': - $selectHourAttr['value'] = $hour; - $selectMinuteAttr['value'] = $min; - $selectMeridianAttr['value'] = $meridian; - $opt .= $this->hour($fieldName, false, $selectHourAttr) . ':' . - $this->minute($fieldName, $selectMinuteAttr) . ' ' . - $this->meridian($fieldName, $selectMeridianAttr); - break; - default: - $opt .= ''; + $attrs['Hour']['value'] = $hour; + $attrs['Minute']['value'] = $min; + $attrs['Meridian']['value'] = $meridian; + $opt .= $this->hour($fieldName, false, $attrs['Hour']) . ':' . + $this->minute($fieldName, $attrs['Minute']) . ' ' . + $this->meridian($fieldName, $attrs['Meridian']); break; } return $opt; } +/** + * Parse the value for a datetime selected value + * + * @param string|array $value The selected value. + * @param integer $timeFormat The time format + * @return array Array of selected value. + */ + protected function _getDateTimeValue($value, $timeFormat) { + $year = $month = $day = $hour = $min = $meridian = null; + if (is_array($value)) { + extract($value); + if ($meridian === 'pm') { + $hour += 12; + } + return array($year, $month, $day, $hour, $min, $meridian); + } + + if (is_numeric($value)) { + $value = strftime('%Y-%m-%d %H:%M:%S', $value); + } + $meridian = 'am'; + $pos = strpos($value, '-'); + if ($pos !== false) { + $date = explode('-', $value); + $days = explode(' ', $date[2]); + $day = $days[0]; + $month = $date[1]; + $year = $date[0]; + } else { + $days[1] = $value; + } + + if (!empty($timeFormat)) { + $time = explode(':', $days[1]); + + if ($time[0] >= '12' && $timeFormat == '12') { + $meridian = 'pm'; + } elseif ($time[0] == '00' && $timeFormat == '12') { + $time[0] = 12; + } elseif ($time[0] >= 12) { + $meridian = 'pm'; + } + if ($time[0] == 0 && $timeFormat == '12') { + $time[0] = 12; + } + $hour = $min = null; + if (isset($time[1])) { + $hour = $time[0]; + $min = $time[1]; + } + } + return array($year, $month, $day, $hour, $min, $meridian); + } + /** * Gets the input field name for the current tag * @@ -2579,7 +2612,8 @@ protected function _generateOptions($name, $options = array()) { } /** - * Sets field defaults and adds field to form security input hash + * Sets field defaults and adds field to form security input hash. + * Will also add a 'form-error' class if the field contains validation errors. * * ### Options * diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php index fb53cf340c1..7bfe5247af2 100644 --- a/lib/Cake/View/Helper/HtmlHelper.php +++ b/lib/Cake/View/Helper/HtmlHelper.php @@ -728,7 +728,7 @@ protected function _prepareCrumbs($startText) { 'text' => $startText ); } - $startText += array('url' => '/', 'text' => __('Home')); + $startText += array('url' => '/', 'text' => __d('cake', 'Home')); list($url, $text) = array($startText['url'], $startText['text']); unset($startText['url'], $startText['text']); array_unshift($crumbs, array($text, $url, $startText)); diff --git a/lib/Cake/View/Helper/JqueryEngineHelper.php b/lib/Cake/View/Helper/JqueryEngineHelper.php index d79a8ef884a..bfa029c2483 100644 --- a/lib/Cake/View/Helper/JqueryEngineHelper.php +++ b/lib/Cake/View/Helper/JqueryEngineHelper.php @@ -250,7 +250,7 @@ public function effect($name, $options = array()) { * @see JsBaseEngineHelper::request() for options list. */ public function request($url, $options = array()) { - $url = $this->url($url); + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); $options = $this->_mapOptions('request', $options); if (isset($options['data']) && is_array($options['data'])) { $options['data'] = $this->_toQuerystring($options['data']); diff --git a/lib/Cake/View/Helper/MootoolsEngineHelper.php b/lib/Cake/View/Helper/MootoolsEngineHelper.php index cc0340b6e54..69e886b13ef 100644 --- a/lib/Cake/View/Helper/MootoolsEngineHelper.php +++ b/lib/Cake/View/Helper/MootoolsEngineHelper.php @@ -234,7 +234,7 @@ public function effect($name, $options = array()) { * @return string The completed ajax call. */ public function request($url, $options = array()) { - $url = $this->url($url); + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); $options = $this->_mapOptions('request', $options); $type = $data = null; if (isset($options['type']) || isset($options['update'])) { diff --git a/lib/Cake/View/Helper/PrototypeEngineHelper.php b/lib/Cake/View/Helper/PrototypeEngineHelper.php index 6c483aa132b..4dd2c4e0395 100644 --- a/lib/Cake/View/Helper/PrototypeEngineHelper.php +++ b/lib/Cake/View/Helper/PrototypeEngineHelper.php @@ -234,7 +234,8 @@ public function effect($name, $options = array()) { * @return string The completed ajax call. */ public function request($url, $options = array()) { - $url = '"' . $this->url($url) . '"'; + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); + $url = '"' . $url . '"'; $options = $this->_mapOptions('request', $options); $type = '.Request'; if (isset($options['type']) && strtolower($options['type']) == 'json') { diff --git a/lib/Cake/View/MediaView.php b/lib/Cake/View/MediaView.php index 9dec162f05b..8ed331837c5 100644 --- a/lib/Cake/View/MediaView.php +++ b/lib/Cake/View/MediaView.php @@ -101,7 +101,9 @@ public function render($view = null, $layout = null) { if ($this->_isActive()) { $extension = strtolower($extension); + //@codingStandardsIgnoreStart $fileSize = @filesize($path); + //@codingStandardsIgnoreEnd $handle = fopen($path, 'rb'); if ($handle === false) { @@ -220,7 +222,9 @@ protected function _isActive() { * @return boolean */ protected function _clearBuffer() { + //@codingStandardsIgnoreStart return @ob_end_clean(); + //@codingStandardsIgnoreEnd } /** @@ -229,8 +233,10 @@ protected function _clearBuffer() { * @return void */ protected function _flushBuffer() { + //@codingStandardsIgnoreStart @flush(); @ob_flush(); + //@codingStandardsIgnoreEnd } } diff --git a/lib/Cake/View/ScaffoldView.php b/lib/Cake/View/ScaffoldView.php index 5cb9fb67767..04674a3389d 100644 --- a/lib/Cake/View/ScaffoldView.php +++ b/lib/Cake/View/ScaffoldView.php @@ -69,7 +69,7 @@ protected function _getViewFileName($name = null) { $paths = $this->_paths($this->plugin); $exts = array($this->ext); if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); + $exts[] = '.ctp'; } foreach ($exts as $ext) { foreach ($paths as $path) { diff --git a/lib/Cake/View/Scaffolds/form.ctp b/lib/Cake/View/Scaffolds/form.ctp index b6725953a6b..0463bcc5f55 100644 --- a/lib/Cake/View/Scaffolds/form.ctp +++ b/lib/Cake/View/Scaffolds/form.ctp @@ -40,12 +40,18 @@ foreach ($associations as $_type => $_data) { foreach ($_data as $_alias => $_details) { if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "\t\t
    • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
    • \n"; + echo "\t\t
    • " . $this->Html->link( + __d('cake', 'List %s', Inflector::humanize($_details['controller'])), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'index') + ) . "
    • \n"; + echo "\t\t
    • " . $this->Html->link( + __d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'add') + ) . "
    • \n"; $done[] = $_details['controller']; } } } ?>
    - \ No newline at end of file + diff --git a/lib/Cake/View/Scaffolds/index.ctp b/lib/Cake/View/Scaffolds/index.ctp index fffec7fa70a..010327fd03c 100644 --- a/lib/Cake/View/Scaffolds/index.ctp +++ b/lib/Cake/View/Scaffolds/index.ctp @@ -26,29 +26,28 @@
  • "; + echo ''; foreach ($scaffoldFields as $_field) { $isKey = false; if (!empty($associations['belongsTo'])) { foreach ($associations['belongsTo'] as $_alias => $_details) { if ($_field === $_details['foreignKey']) { $isKey = true; - echo ""; + echo ''; break; } } } if ($isKey !== true) { - echo ""; + echo ''; } } echo '\n"; echo "\t\t\n"; endforeach; @@ -139,7 +188,10 @@ $otherSingularVar = Inflector::variable($_alias);
      -
    • Html->link(__d('cake', "New %s", Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')); ?>
    • +
    • Html->link( + __d('cake', "New %s", Inflector::humanize(Inflector::underscore($_alias))), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'add') + ); ?>
    diff --git a/lib/Cake/View/ThemeView.php b/lib/Cake/View/ThemeView.php index 6b3617c98ca..7605d89b144 100644 --- a/lib/Cake/View/ThemeView.php +++ b/lib/Cake/View/ThemeView.php @@ -25,6 +25,7 @@ * Stub class for 2.1 Compatibility * * @package Cake.View + * @deprecated Deprecated since 2.1, use View class instead */ class ThemeView extends View { diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index aedddd90f97..6c08cd067ee 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -562,7 +562,9 @@ public function renderCache($filename, $timeStart) { if (preg_match('/^/', $out, $match)) { if (time() >= $match['1']) { + //@codingStandardsIgnoreStart @unlink($filename); + //@codingStandardsIgnoreEnd unset ($out); return false; } else { @@ -883,7 +885,7 @@ protected function _render($viewFile, $data = array()) { $this->getEventManager()->dispatch(new CakeEvent('View.beforeRenderFile', $this, array($viewFile))); $content = $this->_evaluate($viewFile, $data); $afterEvent = new CakeEvent('View.afterRenderFile', $this, array($viewFile, $content)); - //TODO: For BC puporses, set extra info in the event object. Remove when appropriate + $afterEvent->modParams = 1; $this->getEventManager()->dispatch($afterEvent); $content = $afterEvent->data[1]; @@ -1056,7 +1058,7 @@ protected function _getLayoutFileName($name = null) { protected function _getExtensions() { $exts = array($this->ext); if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); + $exts[] = '.ctp'; } return $exts; } @@ -1109,13 +1111,14 @@ protected function _paths($plugin = null, $cached = true) { $paths = array_unique(array_merge($paths, $viewPaths)); if (!empty($this->theme)) { + $theme = Inflector::camelize($this->theme); $themePaths = array(); foreach ($paths as $path) { if (strpos($path, DS . 'Plugin' . DS) === false) { if ($plugin) { - $themePaths[] = $path . 'Themed' . DS . $this->theme . DS . 'Plugin' . DS . $plugin . DS; + $themePaths[] = $path . 'Themed' . DS . $theme . DS . 'Plugin' . DS . $plugin . DS; } - $themePaths[] = $path . 'Themed' . DS . $this->theme . DS; + $themePaths[] = $path . 'Themed' . DS . $theme . DS; } } $paths = array_merge($themePaths, $paths); diff --git a/lib/Cake/basics.php b/lib/Cake/basics.php index 1dc81245e83..7908525262e 100644 --- a/lib/Cake/basics.php +++ b/lib/Cake/basics.php @@ -311,13 +311,10 @@ function env($key) { $offset = 4; } return substr($filename, 0, -(strlen($name) + $offset)); - break; case 'PHP_SELF': return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME')); - break; case 'CGI_MODE': return (PHP_SAPI === 'cgi'); - break; case 'HTTP_BASE': $host = env('HTTP_HOST'); $parts = explode('.', $host); @@ -357,7 +354,6 @@ function env($key) { } array_shift($parts); return '.' . implode('.', $parts); - break; } return null; } @@ -397,19 +393,27 @@ function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { $filetime = false; if (file_exists($filename)) { + //@codingStandardsIgnoreStart $filetime = @filemtime($filename); + //@codingStandardsIgnoreEnd } if ($data === null) { if (file_exists($filename) && $filetime !== false) { if ($filetime + $timediff < $now) { + //@codingStandardsIgnoreStart @unlink($filename); + //@codingStandardsIgnoreEnd } else { + //@codingStandardsIgnoreStart $data = @file_get_contents($filename); + //@codingStandardsIgnoreEnd } } } elseif (is_writable(dirname($filename))) { + //@codingStandardsIgnoreStart @file_put_contents($filename, $data, LOCK_EX); + //@codingStandardsIgnoreEnd } return $data; } @@ -430,7 +434,9 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { $cache = CACHE . $type . DS . $params; if (is_file($cache . $ext)) { + //@codingStandardsIgnoreStart @unlink($cache . $ext); + //@codingStandardsIgnoreEnd return true; } elseif (is_dir($cache)) { $files = glob($cache . '*'); @@ -441,7 +447,9 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { foreach ($files as $file) { if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { + //@codingStandardsIgnoreStart @unlink($file); + //@codingStandardsIgnoreEnd } } return true; @@ -462,7 +470,9 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { } foreach ($files as $file) { if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { + //@codingStandardsIgnoreStart @unlink($file); + //@codingStandardsIgnoreEnd } } return true; diff --git a/lib/Cake/bootstrap.php b/lib/Cake/bootstrap.php index 2c013b2d5e8..cd537cb5c71 100644 --- a/lib/Cake/bootstrap.php +++ b/lib/Cake/bootstrap.php @@ -145,6 +145,12 @@ Configure::bootstrap(isset($boot) ? $boot : true); +if (function_exists('mb_internal_encoding')) { + $encoding = Configure::read('App.encoding'); + if (!empty($encoding)) { + mb_internal_encoding($encoding); + } +} /** * Full url prefix
    " . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "' . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . '" . h(${$singularVar}[$modelClass][$_field]) . "' . h(${$singularVar}[$modelClass][$_field]) . ''; echo $this->Html->link(__d('cake', 'View'), array('action' => 'view', ${$singularVar}[$modelClass][$primaryKey])); - echo $this->Html->link(__d('cake', 'Edit'), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])); - echo $this->Form->postLink( + echo ' ' . $this->Html->link(__d('cake', 'Edit'), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])); + echo ' ' . $this->Form->postLink( __d('cake', 'Delete'), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, @@ -83,8 +82,19 @@ endforeach; foreach ($associations as $_type => $_data) { foreach ($_data as $_alias => $_details) { if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "
  • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
  • "; - echo "
  • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
  • "; + echo '
  • '; + echo $this->Html->link( + __d('cake', 'List %s', Inflector::humanize($_details['controller'])), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'index') + ); + echo '
  • '; + + echo '
  • '; + echo $this->Html->link( + __d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'add') + ); + echo '
  • '; $done[] = $_details['controller']; } } diff --git a/lib/Cake/View/Scaffolds/view.ctp b/lib/Cake/View/Scaffolds/view.ctp index c383d12fd8e..c7c58cf82ee 100644 --- a/lib/Cake/View/Scaffolds/view.ctp +++ b/lib/Cake/View/Scaffolds/view.ctp @@ -28,7 +28,12 @@ foreach ($scaffoldFields as $_field) { if ($_field === $_details['foreignKey']) { $isKey = true; echo "\t\t
    " . Inflector::humanize($_alias) . "
    \n"; - echo "\t\t
    \n\t\t\t" . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t 
    \n"; + echo "\t\t
    \n\t\t\t"; + echo $this->Html->link( + ${$singularVar}[$_alias][$_details['displayField']], + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']]) + ); + echo "\n\t\t 
    \n"; break; } } @@ -45,17 +50,38 @@ foreach ($scaffoldFields as $_field) {

      " . $this->Html->link(__d('cake', 'Edit %s', $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])) . " \n"; - echo "\t\t
    • " . $this->Form->postLink(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?') . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'List %s', $pluralHumanName), array('action' => 'index')) . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')) . "
    • \n"; + echo "\t\t
    • "; + echo $this->Html->link(__d('cake', 'Edit %s', $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])); + echo "
    • \n"; + + echo "\t\t
    • "; + echo $this->Form->postLink(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?'); + echo "
    • \n"; + + echo "\t\t
    • "; + echo $this->Html->link(__d('cake', 'List %s', $pluralHumanName), array('action' => 'index')); + echo "
    • \n"; + + echo "\t\t
    • "; + echo $this->Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')); + echo "
    • \n"; $done = array(); foreach ($associations as $_type => $_data) { foreach ($_data as $_alias => $_details) { if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "\t\t
    • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
    • \n"; + echo "\t\t
    • "; + echo $this->Html->link( + __d('cake', 'List %s', Inflector::humanize($_details['controller'])), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'index') + ); + echo "
    • \n"; + echo "\t\t
    • "; + echo $this->Html->link( + __d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'add') + ); + echo "
    • \n"; $done[] = $_details['controller']; } } @@ -82,7 +108,13 @@ foreach ($associations['hasOne'] as $_alias => $_details): ?>
        -
      • Html->link(__d('cake', 'Edit %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'edit', ${$singularVar}[$_alias][$_details['primaryKey']]))."
      • \n"; ?> +
      • Html->link( + __d('cake', 'Edit %s', Inflector::humanize(Inflector::underscore($_alias))), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'edit', ${$singularVar}[$_alias][$_details['primaryKey']]) + ); + echo "
      • \n"; + ?>
      @@ -128,9 +160,26 @@ $otherSingularVar = Inflector::variable($_alias); } echo "\t\t\t
    \n"; - echo "\t\t\t\t" . $this->Html->link(__d('cake', 'View'), array('controller' => $_details['controller'], 'action' => 'view', ${$otherSingularVar}[$_details['primaryKey']])). "\n"; - echo "\t\t\t\t" . $this->Html->link(__d('cake', 'Edit'), array('controller' => $_details['controller'], 'action' => 'edit', ${$otherSingularVar}[$_details['primaryKey']])). "\n"; - echo "\t\t\t\t" . $this->Form->postLink(__d('cake', 'Delete'), array('controller' => $_details['controller'], 'action' => 'delete', ${$otherSingularVar}[$_details['primaryKey']]), null, __d('cake', 'Are you sure you want to delete', true).' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?'). "\n"; + echo "\t\t\t\t"; + echo $this->Html->link( + __d('cake', 'View'), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'view', ${$otherSingularVar}[$_details['primaryKey']]) + ); + echo "\n"; + echo "\t\t\t\t"; + echo $this->Html->link( + __d('cake', 'Edit'), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'edit', ${$otherSingularVar}[$_details['primaryKey']]) + ); + echo "\n"; + echo "\t\t\t\t"; + echo $this->Form->postLink( + __d('cake', 'Delete'), + array('plugin' => $_details['plugin'], 'controller' => $_details['controller'], 'action' => 'delete', ${$otherSingularVar}[$_details['primaryKey']]), + null, + __d('cake', 'Are you sure you want to delete', true) .' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?' + ); + echo "\n"; echo "\t\t\t