Merge branch '2.0' into 2.0-api-doc

Conflicts:
	lib/Cake/Test/Case/View/Helper/CacheHelperTest.php
	lib/Cake/Utility/Debugger.php
This commit is contained in:
Juan Basso 2011-08-14 21:12:05 -04:00
commit 620a65b2fc
74 changed files with 1173 additions and 355 deletions

View file

@ -247,7 +247,6 @@
* 'serialize' => true, [optional] * 'serialize' => true, [optional]
* )); * ));
* *
*
* APC (http://pecl.php.net/package/APC) * APC (http://pecl.php.net/package/APC)
* *
* Cache::config('default', array( * Cache::config('default', array(
@ -263,12 +262,11 @@
* 'engine' => 'Xcache', //[required] * 'engine' => 'Xcache', //[required]
* 'duration'=> 3600, //[optional] * 'duration'=> 3600, //[optional]
* 'probability'=> 100, //[optional] * 'probability'=> 100, //[optional]
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
* 'user' => 'user', //user from xcache.admin.user settings * 'user' => 'user', //user from xcache.admin.user settings
* 'password' => 'password', //plaintext password (xcache.admin.pass) * 'password' => 'password', //plaintext password (xcache.admin.pass)
* )); * ));
* *
*
* Memcache (http://www.danga.com/memcached/) * Memcache (http://www.danga.com/memcached/)
* *
* Cache::config('default', array( * Cache::config('default', array(
@ -281,9 +279,16 @@
* ), //[optional] * ), //[optional]
* 'persistent' => true, // [optional] set this to false for non-persistent connections * 'persistent' => true, // [optional] set this to false for non-persistent connections
* 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory)
* 'persistent' => true, // [optional] set this to false for non-persistent connections
* )); * ));
* *
* Wincache (http://php.net/wincache)
*
* Cache::config('default', array(
* 'engine' => 'Wincache', //[required]
* 'duration'=> 3600, //[optional]
* 'probability'=> 100, //[optional]
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
* ));
*/ */
/** /**
@ -315,7 +320,7 @@ Cache::config('_cake_core_', array(
)); ));
/** /**
* Configure the cache for model, and datasource caches. This cache configuration * Configure the cache for model and datasource caches. This cache configuration
* is used to store schema descriptions, and table listings in connections. * is used to store schema descriptions, and table listings in connections.
*/ */
Cache::config('_cake_model_', array( Cache::config('_cake_model_', array(

View file

@ -66,6 +66,7 @@ class DATABASE_CONFIG {
'password' => 'password', 'password' => 'password',
'database' => 'database_name', 'database' => 'database_name',
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8',
); );
public $test = array( public $test = array(
@ -76,5 +77,6 @@ class DATABASE_CONFIG {
'password' => 'password', 'password' => 'password',
'database' => 'test_database_name', 'database' => 'test_database_name',
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8',
); );
} }

View file

@ -3,8 +3,6 @@
/** /**
* Command-line code generation utility to automate programmer chores. * Command-line code generation utility to automate programmer chores.
* *
* Shell dispatcher class
*
* PHP 5 * PHP 5
* *
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
@ -15,10 +13,29 @@
* *
* @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project * @link http://cakephp.org CakePHP(tm) Project
* @package app.console * @package app.Console
* @since CakePHP(tm) v 1.2.0.5012 * @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
require_once(dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'lib'. DIRECTORY_SEPARATOR . 'Cake' . DIRECTORY_SEPARATOR . 'Console' . DIRECTORY_SEPARATOR . 'ShellDispatcher.php'); $ds = DIRECTORY_SEPARATOR;
$dispatcher = 'Cake' . $ds . 'Console' . $ds . 'ShellDispatcher.php';
$found = false;
$paths = explode(PATH_SEPARATOR, ini_get('include_path'));
foreach ($paths as $path) {
if (file_exists($path . $ds . $dispatcher)) {
$found = $path;
}
}
if (!$found && function_exists('ini_set')) {
$root = dirname(dirname(dirname(__FILE__)));
ini_set('include_path', $root . $ds. 'lib' . PATH_SEPARATOR . ini_get('include_path'));
}
if (!include($dispatcher)) {
trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
}
unset($paths, $path, $found, $dispatcher, $root, $ds);
return ShellDispatcher::run($argv); return ShellDispatcher::run($argv);

View file

@ -44,13 +44,19 @@
if (!defined('APP_DIR')) { if (!defined('APP_DIR')) {
define('APP_DIR', basename(dirname(dirname(__FILE__)))); define('APP_DIR', basename(dirname(dirname(__FILE__))));
} }
/** /**
* The absolute path to the "cake" directory, WITHOUT a trailing DS. * The absolute path to the "cake" directory, WITHOUT a trailing DS.
* *
* Un-comment this line to specify a fixed path to CakePHP.
* This should point at the directory containg `Cake`.
*
* For ease of development CakePHP uses PHP's include_path. If you
* cannot modify your include_path set this value.
*
* Leaving this constant undefined will result in it being defined in Cake/bootstrap.php
*/ */
if (!defined('CAKE_CORE_INCLUDE_PATH')) { //define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
}
/** /**
* Editing below this line should NOT be necessary. * Editing below this line should NOT be necessary.
@ -63,11 +69,20 @@
if (!defined('WWW_ROOT')) { if (!defined('WWW_ROOT')) {
define('WWW_ROOT', dirname(__FILE__) . DS); define('WWW_ROOT', dirname(__FILE__) . DS);
} }
if (!defined('CORE_PATH')) {
define('APP_PATH', ROOT . DS . APP_DIR . DS); if (!defined('CAKE_CORE_INCLUDE_PATH')) {
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); if (function_exists('ini_set')) {
ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path'));
}
if (!include('Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} else {
if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} }
if (!include(CORE_PATH . 'Cake' . DS . 'bootstrap.php')) { if (!empty($failed)) {
trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
} }

View file

@ -44,13 +44,16 @@ ini_set('display_errors', 1);
if (!defined('APP_DIR')) { if (!defined('APP_DIR')) {
define('APP_DIR', basename(dirname(dirname(__FILE__)))); define('APP_DIR', basename(dirname(dirname(__FILE__))));
} }
/** /**
* The absolute path to the "Cake" directory, WITHOUT a trailing DS. * The absolute path to the "Cake" directory, WITHOUT a trailing DS.
* *
* For ease of development CakePHP uses PHP's include_path. If you
* need to cannot modify your include_path, you can set this path.
*
* Leaving this constant undefined will result in it being defined in Cake/bootstrap.php
*/ */
if (!defined('CAKE_CORE_INCLUDE_PATH')) { //define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
}
/** /**
* Editing below this line should not be necessary. * Editing below this line should not be necessary.
@ -63,11 +66,20 @@ if (!defined('WEBROOT_DIR')) {
if (!defined('WWW_ROOT')) { if (!defined('WWW_ROOT')) {
define('WWW_ROOT', dirname(__FILE__) . DS); define('WWW_ROOT', dirname(__FILE__) . DS);
} }
if (!defined('CORE_PATH')) {
define('APP_PATH', ROOT . DS . APP_DIR . DS); if (!defined('CAKE_CORE_INCLUDE_PATH')) {
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); if (function_exists('ini_set')) {
ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path'));
}
if (!include('Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} else {
if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} }
if (!include(CORE_PATH . 'Cake' . DS . 'bootstrap.php')) { if (!empty($failed)) {
trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
} }

View file

@ -37,10 +37,4 @@
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib'); define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
} }
/**
* Set the include path or define app and core path
*/
define('APP_PATH', ROOT . DS . APP_DIR . DS);
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
require APP_DIR . DS . WEBROOT_DIR . DS . 'index.php'; require APP_DIR . DS . WEBROOT_DIR . DS . 'index.php';

View file

@ -82,7 +82,7 @@ class Cache {
* *
* `Cache::config('default');` * `Cache::config('default');`
* *
* There are 4 built-in caching engines: * There are 5 built-in caching engines:
* *
* - `FileEngine` - Uses simple files to store content. Poor performance, but good for * - `FileEngine` - Uses simple files to store content. Poor performance, but good for
* storing large objects, or things that are not IO sensitive. * storing large objects, or things that are not IO sensitive.
@ -90,6 +90,7 @@ class Cache {
* - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage. * - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage.
* Fast reads/writes, and benefits from memcache being distributed. * Fast reads/writes, and benefits from memcache being distributed.
* - `XcacheEngine` - Uses the Xcache extension, an alternative to APC. * - `XcacheEngine` - Uses the Xcache extension, an alternative to APC.
* - `WincacheEngine` - Uses Windows Cache Extension for PHP. Supports wincache 1.1.0 and higher.
* *
* The following keys are used in core cache engines: * The following keys are used in core cache engines:
* *

View file

@ -121,19 +121,24 @@ class FileEngine extends CacheEngine {
} }
} }
if ($this->settings['lock']) {
$this->_File->flock(LOCK_EX);
}
$expires = time() + $duration; $expires = time() + $duration;
$contents = $expires . $lineBreak . $data . $lineBreak; $contents = $expires . $lineBreak . $data . $lineBreak;
$success = $this->_File->ftruncate(0) && $this->_File->fwrite($contents);
if (!$handle = fopen($this->_File->getPathName(), 'c')) {
return false;
}
if ($this->settings['lock']) { if ($this->settings['lock']) {
$this->_File->flock(LOCK_UN); flock($handle, LOCK_EX);
} }
$this->_File = null;
$success = ftruncate($handle, 0) && fwrite($handle, $contents) && fflush($handle);
if ($this->settings['lock']) {
flock($handle, LOCK_UN);
}
fclose($handle);
return $success; return $success;
} }

View file

@ -46,7 +46,7 @@ class CommandListShell extends Shell {
if (empty($this->params['xml'])) { if (empty($this->params['xml'])) {
$this->out(__d('cake_console', "<info>Current Paths:</info>"), 2); $this->out(__d('cake_console', "<info>Current Paths:</info>"), 2);
$this->out(" -app: ". APP_DIR); $this->out(" -app: ". APP_DIR);
$this->out(" -working: " . rtrim(APP_PATH, DS)); $this->out(" -working: " . rtrim(APP, DS));
$this->out(" -root: " . rtrim(ROOT, DS)); $this->out(" -root: " . rtrim(ROOT, DS));
$this->out(" -core: " . rtrim(CORE_PATH, DS)); $this->out(" -core: " . rtrim(CORE_PATH, DS));
$this->out(""); $this->out("");

View file

@ -390,7 +390,12 @@ class ExtractTask extends Shell {
foreach ($rules as $rule => $validateProp) { foreach ($rules as $rule => $validateProp) {
if (isset($validateProp['message'])) { if (isset($validateProp['message'])) {
$this->_strings[$domain][$validateProp['message']][$file][] = 'validation for field ' . $field; if (is_array($validateProp['message'])) {
$message = $validateProp['message'][0];
} else {
$message = $validateProp['message'];
}
$this->_strings[$domain][$message][$file][] = 'validation for field ' . $field;
} }
} }
} }

View file

@ -50,9 +50,10 @@ class ProjectTask extends Shell {
while (!$project) { while (!$project) {
$prompt = __d('cake_console', "What is the path to the project you want to bake?"); $prompt = __d('cake_console', "What is the path to the project you want to bake?");
$project = $this->in($prompt, null, APP_PATH . 'myapp'); $project = $this->in($prompt, null, APP . 'myapp');
} }
if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) { if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) {
$project = $_SERVER['PWD'] . DS . $project; $project = $_SERVER['PWD'] . DS . $project;
} }
@ -97,20 +98,24 @@ class ProjectTask extends Shell {
$success = false; $success = false;
} }
$this->out(__d('cake_console', 'The value for CAKE_CORE_INCLUDE_PATH can be hardcoded set to %s in webroot/index.php', CAKE_CORE_INCLUDE_PATH)); $hardCode = false;
$this->out(__d('cake_console', '<warning>If you hard code it, the project will possibly run only in your computer.</warning>')); if ($this->cakeOnIncludePath()) {
$setConstants = $this->in(__d('cake_console', 'Do you want to set CAKE_CORE_INCLUDE_PATH in webroot/index.php?'), array('y', 'n'), 'n'); $this->out(__d('cake_console', '<info>CakePHP is on your `include_path`. CAKE_CORE_INCLUDE_PATH will be set, but commented out.</info>'));
if (strtolower($setConstants) === 'y') {
if ($this->corePath($path) === true) {
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', CAKE_CORE_INCLUDE_PATH));
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', CAKE_CORE_INCLUDE_PATH));
$this->out(__d('cake_console', ' * <warning>Remember to check these values after moving to production server</warning>'));
} else {
$this->err(__d('cake_console', 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', $path . 'webroot' .DS .'index.php'));
$success = false;
}
} else { } else {
$this->out(__d('cake_console', '<warning>Please make sure your cake core is accessible, if you have problems edit CAKE_CORE_INCLUDE_PATH in webroot/index.php</warning>')); $this->out(__d('cake_console', '<warning>CakePHP is not on your `include_path`, CAKE_CORE_INCLUDE_PATH will be hard coded.</warning>'));
$this->out(__d('cake_console', 'You can fix this by adding CakePHP to your `include_path`.'));
$hardCode = true;
}
$success = $this->corePath($path, $hardCode) === true;
if ($success) {
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', CAKE_CORE_INCLUDE_PATH));
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', CAKE_CORE_INCLUDE_PATH));
} else {
$this->err(__d('cake_console', 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', $path . 'webroot' .DS .'index.php'));
$success = false;
}
if ($success && $hardCode) {
$this->out(__d('cake_console', ' * <warning>Remember to check these values after moving to production server</warning>'));
} }
$Folder = new Folder($path); $Folder = new Folder($path);
@ -128,6 +133,21 @@ class ProjectTask extends Shell {
} }
} }
/**
* Checks PHP's include_path for CakePHP.
*
* @return bool Indicates whether or not CakePHP exists on include_path
*/
public function cakeOnIncludePath() {
$paths = explode(PATH_SEPARATOR, ini_get('include_path'));
foreach ($paths as $path) {
if (file_exists($path . DS . 'Cake' . DS . 'bootstrap.php')) {
return true;
}
}
return false;
}
/** /**
* Looks for a skeleton template of a Cake application, * Looks for a skeleton template of a Cake application,
* and if not found asks the user for a path. When there is a path * and if not found asks the user for a path. When there is a path
@ -226,8 +246,8 @@ class ProjectTask extends Shell {
$File = new File($path . 'Console' . DS . 'cake.php'); $File = new File($path . 'Console' . DS . 'cake.php');
$contents = $File->read(); $contents = $File->read();
if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) { if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) {
$path = CAKE . 'Console' . DS; $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " \$ds . '" : "'";
$replacement = "'" . str_replace(DS, "' . DIRECTORY_SEPARATOR . '", $path) . "'"; $replacement = $root . str_replace(DS, "' . \$ds . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
$result = str_replace($match[0], $replacement, $contents); $result = str_replace($match[0], $replacement, $contents);
if ($File->write($result)) { if ($File->write($result)) {
return true; return true;
@ -284,36 +304,49 @@ class ProjectTask extends Shell {
* Generates and writes CAKE_CORE_INCLUDE_PATH * Generates and writes CAKE_CORE_INCLUDE_PATH
* *
* @param string $path Project path * @param string $path Project path
* @param bool $hardCode Wether or not define calls should be hardcoded.
* @return boolean Success * @return boolean Success
*/ */
public function corePath($path) { public function corePath($path, $hardCode = true) {
if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) { if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
$File = new File($path . 'webroot' . DS . 'index.php'); $filename = $path . 'webroot' . DS . 'index.php';
$contents = $File->read(); if (!$this->_replaceCorePath($filename, $hardCode)) {
if (preg_match('/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9\.]*\);)/', $contents, $match)) {
$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
$result = str_replace($match[0], "\n\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
if (!$File->write($result)) {
return false;
}
} else {
return false; return false;
} }
$filename = $path . 'webroot' . DS . 'test.php';
$File = new File($path . 'webroot' . DS . 'test.php'); if (!$this->_replaceCorePath($filename, $hardCode)) {
$contents = $File->read();
if (preg_match('/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9\.]*\);)/', $contents, $match)) {
$result = str_replace($match[0], "\n\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
if (!$File->write($result)) {
return false;
}
} else {
return false; return false;
} }
return true; return true;
} }
} }
/**
* Replaces the __CAKE_PATH__ placeholder in the template files.
*
* @param string $filename The filename to operate on.
* @param boolean $hardCode Whether or not the define should be uncommented.
* @retun bool Success
*/
protected function _replaceCorePath($filename, $hardCode) {
$contents = file_get_contents($filename);
$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
$corePath = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
$result = str_replace('__CAKE_PATH__', $corePath, $contents, $count);
if ($hardCode) {
$result = str_replace('//define(\'CAKE_CORE', 'define(\'CAKE_CORE', $result);
}
if (!file_put_contents($filename, $result)) {
return false;
}
if ($count == 0) {
return false;
}
return true;
}
/** /**
* Enables Configure::read('Routing.prefixes') in /app/Config/core.php * Enables Configure::read('Routing.prefixes') in /app/Config/core.php
* *

View file

@ -104,6 +104,10 @@ class UpgradeShell extends Shell {
public function locations() { public function locations() {
$cwd = getcwd(); $cwd = getcwd();
if (!empty($this->params['plugin'])) {
chdir(App::pluginPath($this->params['plugin']));
}
if (is_dir('plugins')) { if (is_dir('plugins')) {
$Folder = new Folder('plugins'); $Folder = new Folder('plugins');

View file

@ -89,6 +89,11 @@ class ConsoleInputOption {
$this->_default = $default; $this->_default = $default;
$this->_choices = $choices; $this->_choices = $choices;
} }
if (strlen($this->_short) > 1) {
throw new ConsoleException(
__d('cake_console', 'Short options must be one letter.')
);
}
} }
/** /**

View file

@ -41,7 +41,8 @@ App::uses('HelpFormatter', 'Console');
* *
* Options can be defined with both long and short forms. By using `$parser->addOption()` * Options can be defined with both long and short forms. By using `$parser->addOption()`
* you can define new options. The name of the option is used as its long form, and you * you can define new options. The name of the option is used as its long form, and you
* can supply an additional short form, with the `short` option. * can supply an additional short form, with the `short` option. Short options should
* only be one letter long. Using more than one letter for a short option will raise an exception.
* *
* Calling options can be done using syntax similar to most *nix command line tools. Long options * Calling options can be done using syntax similar to most *nix command line tools. Long options
* cane either include an `=` or leave it out. * cane either include an `=` or leave it out.
@ -52,6 +53,15 @@ App::uses('HelpFormatter', 'Console');
* *
* `cake myshell command -cn` * `cake myshell command -cn`
* *
* Short options can be combined into groups as seen above. Each letter in a group
* will be treated as a separate option. The previous example is equivalent to:
*
* `cake myshell command -c -n`
*
* Short options can also accept values:
*
* `cake myshell command -c default`
*
* ### Positional arguments * ### Positional arguments
* *
* If no positional arguments are defined, all of them will be parsed. If you define positional * If no positional arguments are defined, all of them will be parsed. If you define positional
@ -116,6 +126,13 @@ class ConsoleOptionParser {
*/ */
protected $_subcommands = array(); protected $_subcommands = array();
/**
* Command name.
*
* @var string
*/
protected $_command = '';
/** /**
* Construct an OptionParser so you can define its behavior * Construct an OptionParser so you can define its behavior
* *
@ -124,7 +141,7 @@ class ConsoleOptionParser {
* this to false will prevent the addition of `--verbose` & `--quiet` options. * this to false will prevent the addition of `--verbose` & `--quiet` options.
*/ */
public function __construct($command = null, $defaultOptions = true) { public function __construct($command = null, $defaultOptions = true) {
$this->_command = $command; $this->command($command);
$this->addOption('help', array( $this->addOption('help', array(
'short' => 'h', 'short' => 'h',
@ -206,7 +223,7 @@ class ConsoleOptionParser {
*/ */
public function command($text = null) { public function command($text = null) {
if ($text !== null) { if ($text !== null) {
$this->_command = $text; $this->_command = Inflector::underscore($text);
return $this; return $this;
} }
return $this->_command; return $this->_command;
@ -563,7 +580,7 @@ class ConsoleOptionParser {
$option = $this->_options[$name]; $option = $this->_options[$name];
$isBoolean = $option->isBoolean(); $isBoolean = $option->isBoolean();
$nextValue = $this->_nextToken(); $nextValue = $this->_nextToken();
if (!$isBoolean && !empty($nextValue) && $nextValue{0} != '-') { if (!$isBoolean && !empty($nextValue) && !$this->_optionExists($nextValue)) {
array_shift($this->_tokens); array_shift($this->_tokens);
$value = $nextValue; $value = $nextValue;
} elseif ($isBoolean) { } elseif ($isBoolean) {
@ -577,6 +594,23 @@ class ConsoleOptionParser {
} }
} }
/**
* Check to see if $name has an option (short/long) defined for it.
*
* @param string $name The name of the option.
* @return boolean
*/
protected function _optionExists($name) {
if (substr($name, 0, 2) === '--') {
return isset($this->_options[substr($name, 2)]);
}
if ($name{0} === '-' && $name{1} !== '-') {
return isset($this->_shortOptions[$name{1}]);
}
return false;
}
/** /**
* Parse an argument, and ensure that the argument doesn't exceed the number of arguments * Parse an argument, and ensure that the argument doesn't exceed the number of arguments
* and that the argument is a valid choice. * and that the argument is a valid choice.

View file

@ -29,6 +29,20 @@ App::uses('String', 'Utility');
* @since CakePHP(tm) v 2.0 * @since CakePHP(tm) v 2.0
*/ */
class HelpFormatter { class HelpFormatter {
/**
* The maximum number of arguments shown when generating usage.
*
* @var integer
*/
protected $_maxArgs = 6;
/**
* The maximum number of options shown when generating usage.
*
* @var integer
*/
protected $_maxOptions = 6;
/** /**
* Build the help formatter for a an OptionParser * Build the help formatter for a an OptionParser
* *
@ -122,12 +136,22 @@ class HelpFormatter {
if (!empty($subcommands)) { if (!empty($subcommands)) {
$usage[] = '[subcommand]'; $usage[] = '[subcommand]';
} }
$options = array();
foreach ($this->_parser->options() as $option) { foreach ($this->_parser->options() as $option) {
$usage[] = $option->usage(); $options[] = $option->usage();
} }
if (count($options) > $this->_maxOptions){
$options = array('[options]');
}
$usage = array_merge($usage, $options);
$args = array();
foreach ($this->_parser->arguments() as $argument) { foreach ($this->_parser->arguments() as $argument) {
$usage[] = $argument->usage(); $args[] = $argument->usage();
} }
if (count($args) > $this->_maxArgs) {
$args = array('[arguments]');
}
$usage = array_merge($usage, $args);
return implode(' ', $usage); return implode(' ', $usage);
} }

View file

@ -204,7 +204,7 @@ class Shell extends Object {
$this->out(__d('cake_console', '<info>Welcome to CakePHP %s Console</info>', 'v' . Configure::version())); $this->out(__d('cake_console', '<info>Welcome to CakePHP %s Console</info>', 'v' . Configure::version()));
$this->hr(); $this->hr();
$this->out(__d('cake_console', 'App : %s', APP_DIR)); $this->out(__d('cake_console', 'App : %s', APP_DIR));
$this->out(__d('cake_console', 'Path: %s', APP_PATH)); $this->out(__d('cake_console', 'Path: %s', APP));
$this->hr(); $this->hr();
} }
@ -386,7 +386,7 @@ class Shell extends Object {
$format = 'text'; $format = 'text';
if (!empty($this->args[0]) && $this->args[0] == 'xml') { if (!empty($this->args[0]) && $this->args[0] == 'xml') {
$format = 'xml'; $format = 'xml';
$this->output->outputAs(ConsoleOutput::RAW); $this->stdout->outputAs(ConsoleOutput::RAW);
} else { } else {
$this->_welcome(); $this->_welcome();
} }

View file

@ -122,15 +122,15 @@ class ShellDispatcher {
private function __bootstrap() { private function __bootstrap() {
define('ROOT', $this->params['root']); define('ROOT', $this->params['root']);
define('APP_DIR', $this->params['app']); define('APP_DIR', $this->params['app']);
define('APP_PATH', $this->params['working'] . DS); define('APP', $this->params['working'] . DS);
define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS); define('WWW_ROOT', APP . $this->params['webroot'] . DS);
if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) { if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) {
define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'tmp' . DS); define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'tmp' . DS);
} }
$boot = file_exists(ROOT . DS . APP_DIR . DS . 'Config' . DS . 'bootstrap.php'); $boot = file_exists(ROOT . DS . APP_DIR . DS . 'Config' . DS . 'bootstrap.php');
require CORE_PATH . 'Cake' . DS . 'bootstrap.php'; require CORE_PATH . 'Cake' . DS . 'bootstrap.php';
if (!file_exists(APP_PATH . 'Config' . DS . 'core.php')) { if (!file_exists(APP . 'Config' . DS . 'core.php')) {
include_once CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'Config' . DS . 'core.php'; include_once CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'Config' . DS . 'core.php';
App::build(); App::build();
} }

View file

@ -247,7 +247,6 @@
* 'serialize' => true, [optional] * 'serialize' => true, [optional]
* )); * ));
* *
*
* APC (http://pecl.php.net/package/APC) * APC (http://pecl.php.net/package/APC)
* *
* Cache::config('default', array( * Cache::config('default', array(
@ -263,12 +262,11 @@
* 'engine' => 'Xcache', //[required] * 'engine' => 'Xcache', //[required]
* 'duration'=> 3600, //[optional] * 'duration'=> 3600, //[optional]
* 'probability'=> 100, //[optional] * 'probability'=> 100, //[optional]
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
* 'user' => 'user', //user from xcache.admin.user settings * 'user' => 'user', //user from xcache.admin.user settings
* 'password' => 'password', //plaintext password (xcache.admin.pass) * 'password' => 'password', //plaintext password (xcache.admin.pass)
* )); * ));
* *
*
* Memcache (http://www.danga.com/memcached/) * Memcache (http://www.danga.com/memcached/)
* *
* Cache::config('default', array( * Cache::config('default', array(
@ -281,9 +279,16 @@
* ), //[optional] * ), //[optional]
* 'persistent' => true, // [optional] set this to false for non-persistent connections * 'persistent' => true, // [optional] set this to false for non-persistent connections
* 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory)
* 'persistent' => true, // [optional] set this to false for non-persistent connections
* )); * ));
* *
* Wincache (http://php.net/wincache)
*
* Cache::config('default', array(
* 'engine' => 'Wincache', //[required]
* 'duration'=> 3600, //[optional]
* 'probability'=> 100, //[optional]
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
* ));
*/ */
/** /**
@ -315,7 +320,7 @@ Cache::config('_cake_core_', array(
)); ));
/** /**
* Configure the cache for model, and datasource caches. This cache configuration * Configure the cache for model and datasource caches. This cache configuration
* is used to store schema descriptions, and table listings in connections. * is used to store schema descriptions, and table listings in connections.
*/ */
Cache::config('_cake_model_', array( Cache::config('_cake_model_', array(

View file

@ -66,6 +66,7 @@ class DATABASE_CONFIG {
'password' => 'password', 'password' => 'password',
'database' => 'database_name', 'database' => 'database_name',
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8',
); );
public $test = array( public $test = array(
@ -76,5 +77,6 @@ class DATABASE_CONFIG {
'password' => 'password', 'password' => 'password',
'database' => 'test_database_name', 'database' => 'test_database_name',
'prefix' => '', 'prefix' => '',
//'encoding' => 'utf8',
); );
} }

View file

@ -3,8 +3,6 @@
/** /**
* Command-line code generation utility to automate programmer chores. * Command-line code generation utility to automate programmer chores.
* *
* Shell dispatcher class
*
* PHP 5 * PHP 5
* *
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
@ -16,9 +14,28 @@
* @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project * @link http://cakephp.org CakePHP(tm) Project
* @package app.Console * @package app.Console
* @since CakePHP(tm) v 1.2.0.5012 * @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
require_once(__CAKE_PATH__ . 'ShellDispatcher.php'); $ds = DIRECTORY_SEPARATOR;
$dispatcher = 'Cake' . $ds . 'Console' . $ds . 'ShellDispatcher.php';
$found = false;
$paths = explode(PATH_SEPARATOR, ini_get('include_path'));
foreach ($paths as $path) {
if (file_exists($path . $ds . $dispatcher)) {
$found = $path;
}
}
if (!$found && function_exists('ini_set')) {
$root = dirname(dirname(dirname(__FILE__)));
ini_set('include_path', __CAKE_PATH__ . PATH_SEPARATOR . ini_get('include_path'));
}
if (!include($dispatcher)) {
trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
}
unset($paths, $path, $found, $dispatcher, $root, $ds);
return ShellDispatcher::run($argv); return ShellDispatcher::run($argv);

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 496 B

View file

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 783 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -44,13 +44,19 @@
if (!defined('APP_DIR')) { if (!defined('APP_DIR')) {
define('APP_DIR', basename(dirname(dirname(__FILE__)))); define('APP_DIR', basename(dirname(dirname(__FILE__))));
} }
/** /**
* The absolute path to the "cake" directory, WITHOUT a trailing DS. * The absolute path to the "cake" directory, WITHOUT a trailing DS.
* *
* Un-comment this line to specify a fixed path to CakePHP.
* This should point at the directory containg `Cake`.
*
* For ease of development CakePHP uses PHP's include_path. If you
* need to squeeze a bit more performance you can set this path.
*
* Leaving this constant undefined will result in it being defined in Cake/bootstrap.php
*/ */
if (!defined('CAKE_CORE_INCLUDE_PATH')) { //define('CAKE_CORE_INCLUDE_PATH', __CAKE_PATH__);
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
}
/** /**
* Editing below this line should NOT be necessary. * Editing below this line should NOT be necessary.
@ -63,11 +69,17 @@
if (!defined('WWW_ROOT')) { if (!defined('WWW_ROOT')) {
define('WWW_ROOT', dirname(__FILE__) . DS); define('WWW_ROOT', dirname(__FILE__) . DS);
} }
if (!defined('CORE_PATH')) {
define('APP_PATH', ROOT . DS . APP_DIR . DS); if (!defined('CAKE_CORE_INCLUDE_PATH')) {
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); if (!include('Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} else {
if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} }
if (!include(CORE_PATH . 'Cake' . DS . 'bootstrap.php')) { if (!empty($failed)) {
trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
} }
@ -76,5 +88,6 @@
} }
App::uses('Dispatcher', 'Routing'); App::uses('Dispatcher', 'Routing');
$Dispatcher = new Dispatcher(); $Dispatcher = new Dispatcher();
$Dispatcher->dispatch(new CakeRequest(), new CakeResponse(array('charset' => Configure::read('App.encoding')))); $Dispatcher->dispatch(new CakeRequest(), new CakeResponse(array('charset' => Configure::read('App.encoding'))));

View file

@ -44,13 +44,16 @@ ini_set('display_errors', 1);
if (!defined('APP_DIR')) { if (!defined('APP_DIR')) {
define('APP_DIR', basename(dirname(dirname(__FILE__)))); define('APP_DIR', basename(dirname(dirname(__FILE__))));
} }
/** /**
* The absolute path to the "Cake" directory, WITHOUT a trailing DS. * The absolute path to the "Cake" directory, WITHOUT a trailing DS.
* *
* For ease of development CakePHP uses PHP's include_path. If you
* need to cannot modify your include_path, you can set this path.
*
* Leaving this constant undefined will result in it being defined in Cake/bootstrap.php
*/ */
if (!defined('CAKE_CORE_INCLUDE_PATH')) { //define('CAKE_CORE_INCLUDE_PATH', __CAKE_PATH__);
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
}
/** /**
* Editing below this line should not be necessary. * Editing below this line should not be necessary.
@ -63,11 +66,17 @@ if (!defined('WEBROOT_DIR')) {
if (!defined('WWW_ROOT')) { if (!defined('WWW_ROOT')) {
define('WWW_ROOT', dirname(__FILE__) . DS); define('WWW_ROOT', dirname(__FILE__) . DS);
} }
if (!defined('CORE_PATH')) {
define('APP_PATH', ROOT . DS . APP_DIR . DS); if (!defined('CAKE_CORE_INCLUDE_PATH')) {
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); if (!include('Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} else {
if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) {
$failed = true;
}
} }
if (!include(CORE_PATH . 'Cake' . DS . 'bootstrap.php')) { if (!empty($failed)) {
trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
} }

View file

@ -19,6 +19,6 @@
* @since CakePHP(tm) v 1.2.0.5012 * @since CakePHP(tm) v 1.2.0.5012
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR. 'ShellDispatcher.php'); require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ShellDispatcher.php');
return ShellDispatcher::run($argv); return ShellDispatcher::run($argv);

View file

@ -192,17 +192,17 @@ class SecurityComponent extends Component {
$this->_authRequired($controller); $this->_authRequired($controller);
$isPost = ($this->request->is('post') || $this->request->is('put')); $isPost = ($this->request->is('post') || $this->request->is('put'));
$isRequestAction = ( $isNotRequestAction = (
!isset($controller->request->params['requested']) || !isset($controller->request->params['requested']) ||
$controller->request->params['requested'] != 1 $controller->request->params['requested'] != 1
); );
if ($isPost && $isRequestAction && $this->validatePost) { if ($isPost && $isNotRequestAction && $this->validatePost) {
if ($this->_validatePost($controller) === false) { if ($this->_validatePost($controller) === false) {
return $this->blackHole($controller, 'auth'); return $this->blackHole($controller, 'auth');
} }
} }
if ($isPost && $this->csrfCheck) { if ($isPost && $isNotRequestAction && $this->csrfCheck) {
if ($this->_validateCsrf($controller) === false) { if ($this->_validateCsrf($controller) === false) {
return $this->blackHole($controller, 'csrf'); return $this->blackHole($controller, 'csrf');
} }
@ -499,7 +499,7 @@ class SecurityComponent extends Component {
$token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']); $token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']);
} }
} }
if ($this->csrfCheck && ($this->csrfUseOnce || empty($tokenData['csrfTokens'])) ) { if ($this->csrfCheck && ($this->csrfUseOnce || empty($token['csrfTokens'])) ) {
$token['csrfTokens'][$authKey] = strtotime($this->csrfExpires); $token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
} }
if ($this->csrfCheck && $this->csrfUseOnce == false) { if ($this->csrfCheck && $this->csrfUseOnce == false) {

View file

@ -504,7 +504,7 @@ class Controller extends Object {
if (!$privateAction && !empty($prefixes)) { if (!$privateAction && !empty($prefixes)) {
if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) { if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
list($prefix, $action) = explode('_', $request->params['action']); list($prefix) = explode('_', $request->params['action']);
$privateAction = in_array($prefix, $prefixes); $privateAction = in_array($prefix, $prefixes);
} }
} }
@ -890,7 +890,7 @@ class Controller extends Object {
$currentObject = ClassRegistry::getObject($currentModel); $currentObject = ClassRegistry::getObject($currentModel);
if (is_a($currentObject, 'Model')) { if (is_a($currentObject, 'Model')) {
$className = get_class($currentObject); $className = get_class($currentObject);
list($plugin, $package) = pluginSplit(App::location($className)); list($plugin) = pluginSplit(App::location($className));
$this->request->params['models'][$currentObject->alias] = compact('plugin', 'className'); $this->request->params['models'][$currentObject->alias] = compact('plugin', 'className');
$View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors; $View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors;
} }

View file

@ -322,7 +322,7 @@ class Configure {
*/ */
public static function version() { public static function version() {
if (!isset(self::$_values['Cake']['version'])) { if (!isset(self::$_values['Cake']['version'])) {
require(CAKE . 'Config' . DS . 'config.php'); require CAKE . 'Config' . DS . 'config.php';
self::write($config); self::write($config);
} }
return self::$_values['Cake']['version']; return self::$_values['Cake']['version'];

View file

@ -85,7 +85,9 @@ class Object {
} }
$dispatcher = new Dispatcher(); $dispatcher = new Dispatcher();
return $dispatcher->dispatch($request, new CakeResponse(), $extra); $result = $dispatcher->dispatch($request, new CakeResponse(), $extra);
Router::popRequest();
return $result;
} }
/** /**

View file

@ -42,6 +42,19 @@
* using CakeLogs's methods. If you don't configure any adapters, and write to the logs * using CakeLogs's methods. If you don't configure any adapters, and write to the logs
* a default FileLog will be autoconfigured for you. * a default FileLog will be autoconfigured for you.
* *
* ### Configuring Log adapters
*
* You can configure log adapters in your applications `bootstrap.php` file. A sample configuration
* would look like:
*
* `CakeLog::config('my_log', array('engine' => 'FileLog'));`
*
* See the documentation on CakeLog::config() for more detail.
*
* ### Writing to the log
*
* You write to the logs using CakeLog::write(). See its documentation for more information.
*
* @package Cake.Log * @package Cake.Log
*/ */
class CakeLog { class CakeLog {
@ -69,11 +82,7 @@ class CakeLog {
* *
* Will configure a FileLog instance to use the specified path. All options that are not `engine` * Will configure a FileLog instance to use the specified path. All options that are not `engine`
* are passed onto the logging adapter, and handled there. Any class can be configured as a logging * are passed onto the logging adapter, and handled there. Any class can be configured as a logging
* adapter as long as it implements a `write` method with the following signature. * adapter as long as it implements the methods in CakeLogInterface.
*
* `write($type, $message)`
*
* For an explaination of these parameters, see CakeLog::write()
* *
* @param string $key The keyname for this logger, used to remove the logger later. * @param string $key The keyname for this logger, used to remove the logger later.
* @param array $config Array of configuration information for the logger * @param array $config Array of configuration information for the logger
@ -191,7 +200,7 @@ class CakeLog {
if (empty(self::$_streams)) { if (empty(self::$_streams)) {
self::_autoConfig(); self::_autoConfig();
} }
foreach (self::$_streams as $key => $logger) { foreach (self::$_streams as $logger) {
$logger->write($type, $message); $logger->write($type, $message);
} }
return true; return true;

View file

@ -75,7 +75,7 @@ class AclBehavior extends ModelBehavior {
if (empty($type)) { if (empty($type)) {
$type = $this->__typeMaps[$this->settings[$model->name]['type']]; $type = $this->__typeMaps[$this->settings[$model->name]['type']];
if (is_array($type)) { if (is_array($type)) {
trigger_error(__('AclBehavior is setup with more then one type, please specify type parameter for node()', true), E_USER_WARNING); trigger_error(__d('cake_dev', 'AclBehavior is setup with more then one type, please specify type parameter for node()'), E_USER_WARNING);
return null; return null;
} }
} }

View file

@ -219,7 +219,7 @@ class TreeBehavior extends ModelBehavior {
if ($id === null) { if ($id === null) {
return $Model->find('count', array('conditions' => $scope)); return $Model->find('count', array('conditions' => $scope));
} elseif (isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) { } elseif ($Model->id === $id && isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) {
$data = $Model->data[$Model->alias]; $data = $Model->data[$Model->alias];
} else { } else {
$data = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'recursive' => $recursive)); $data = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'recursive' => $recursive));

View file

@ -299,7 +299,7 @@ class Postgres extends DboSource {
* and if 1, sequences are not modified * and if 1, sequences are not modified
* @return boolean SQL TRUNCATE TABLE statement, false if not applicable. * @return boolean SQL TRUNCATE TABLE statement, false if not applicable.
*/ */
public function truncate($table, $reset = true) { function truncate($table, $reset = 0) {
$table = $this->fullTableName($table, false); $table = $this->fullTableName($table, false);
if (!isset($this->_sequenceMap[$table])) { if (!isset($this->_sequenceMap[$table])) {
$cache = $this->cacheSources; $cache = $this->cacheSources;
@ -307,8 +307,9 @@ class Postgres extends DboSource {
$this->describe($table); $this->describe($table);
$this->cacheSources = $cache; $this->cacheSources = $cache;
} }
if (parent::truncate($table)) { if ($this->execute('DELETE FROM ' . $this->fullTableName($table))) {
if (isset($this->_sequenceMap[$table]) && $reset) { $table = $this->fullTableName($table, false);
if (isset($this->_sequenceMap[$table]) && $reset !== 1) {
foreach ($this->_sequenceMap[$table] as $field => $sequence) { foreach ($this->_sequenceMap[$table] as $field => $sequence) {
$this->_execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1"); $this->_execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1");
} }

View file

@ -1385,6 +1385,9 @@ class Model extends Object {
if ($success && $count > 0) { if ($success && $count > 0) {
if (!empty($this->data)) { if (!empty($this->data)) {
$success = $this->data; $success = $this->data;
if ($created) {
$this->data[$this->alias][$this->primaryKey] = $this->id;
}
} }
if ($options['callbacks'] === true || $options['callbacks'] === 'after') { if ($options['callbacks'] === true || $options['callbacks'] === 'after') {
$this->Behaviors->trigger('afterSave', array(&$this, $created, $options)); $this->Behaviors->trigger('afterSave', array(&$this, $created, $options));
@ -2282,10 +2285,14 @@ class Model extends Object {
$query['order'] = false; $query['order'] = false;
return $query; return $query;
} elseif ($state === 'after') { } elseif ($state === 'after') {
if (isset($results[0][0]['count'])) { foreach (array(0, $this->alias) as $key) {
return intval($results[0][0]['count']); if (isset($results[0][$key]['count'])) {
} elseif (isset($results[0][$this->alias]['count'])) { if (count($results) > 1) {
return intval($results[0][$this->alias]['count']); return intval(array_sum(Set::extract('/' . $key . '/count', $results)));
} else {
return intval($results[0][$key]['count']);
}
}
} }
return false; return false;
} }
@ -2715,6 +2722,13 @@ class Model extends Object {
} else { } else {
$validator['message'] = __d($validationDomain, $message); $validator['message'] = __d($validationDomain, $message);
} }
} elseif (is_array($validator['message'])) {
if (count($validator['message']) > 1) {
$args = array_slice($validator['message'], 1);
} else {
$args = $validator['rule'];
}
$validator['message'] = __d($validationDomain, $validator['message'][0], $args);
} }
$this->invalidate($fieldName, $validator['message']); $this->invalidate($fieldName, $validator['message']);

View file

@ -612,7 +612,7 @@ class CakeRequest implements ArrayAccess {
$acceptTypes = explode(',', $this->header('accept')); $acceptTypes = explode(',', $this->header('accept'));
foreach ($acceptTypes as $i => $accepted) { foreach ($acceptTypes as $i => $accepted) {
if (strpos($accepted, ';') !== false) { if (strpos($accepted, ';') !== false) {
list($accepted, $prefValue) = explode(';', $accepted); list($accepted) = explode(';', $accepted);
$acceptTypes[$i] = $accepted; $acceptTypes[$i] = $accepted;
} }
} }

View file

@ -571,7 +571,6 @@ class CakeResponse {
if (is_array($ctype)) { if (is_array($ctype)) {
return array_map(array($this, 'mapType'), $ctype); return array_map(array($this, 'mapType'), $ctype);
} }
$keys = array_keys($this->_mimeTypes);
foreach ($this->_mimeTypes as $alias => $types) { foreach ($this->_mimeTypes as $alias => $types) {
if (is_array($types) && in_array($ctype, $types)) { if (is_array($types) && in_array($ctype, $types)) {

View file

@ -148,7 +148,7 @@ class CakeRoute {
} }
$names[] = $name; $names[] = $name;
} }
if (preg_match('#\/\*$#', $route, $m)) { if (preg_match('#\/\*$#', $route)) {
$parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed); $parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed);
$this->_greedy = true; $this->_greedy = true;
} }
@ -273,7 +273,7 @@ class CakeRoute {
if ((!isset($this->options['named']) || !empty($this->options['named'])) && $separatorIsPresent) { if ((!isset($this->options['named']) || !empty($this->options['named'])) && $separatorIsPresent) {
list($key, $val) = explode($namedConfig['separator'], $param, 2); list($key, $val) = explode($namedConfig['separator'], $param, 2);
$hasRule = isset($rules[$key]); $hasRule = isset($rules[$key]);
$passIt = (!$hasRule && !$greedy) || ($hasRule && !$this->_matchNamed($key, $val, $rules[$key], $context)); $passIt = (!$hasRule && !$greedy) || ($hasRule && !$this->_matchNamed($val, $rules[$key], $context));
if ($passIt) { if ($passIt) {
$pass[] = $param; $pass[] = $param;
} else { } else {
@ -306,13 +306,12 @@ class CakeRoute {
* Return true if a given named $param's $val matches a given $rule depending on $context. Currently implemented * Return true if a given named $param's $val matches a given $rule depending on $context. Currently implemented
* rule types are controller, action and match that can be combined with each other. * rule types are controller, action and match that can be combined with each other.
* *
* @param string $param The name of the named parameter
* @param string $val The value of the named parameter * @param string $val The value of the named parameter
* @param array $rule The rule(s) to apply, can also be a match string * @param array $rule The rule(s) to apply, can also be a match string
* @param string $context An array with additional context information (controller / action) * @param string $context An array with additional context information (controller / action)
* @return boolean * @return boolean
*/ */
protected function _matchNamed($param, $val, $rule, $context) { protected function _matchNamed($val, $rule, $context) {
if ($rule === true || $rule === false) { if ($rule === true || $rule === false) {
return $rule; return $rule;
} }

View file

@ -513,6 +513,9 @@ class Router {
* parameters as the current request parameters that are merged with url arrays * parameters as the current request parameters that are merged with url arrays
* created later in the request. * created later in the request.
* *
* Nested requests will create a stack of requests. You can remove requests using
* Router::popRequest(). This is done automatically when using Object::requestAction().
*
* Will accept either a CakeRequest object or an array of arrays. Support for * Will accept either a CakeRequest object or an array of arrays. Support for
* accepting arrays may be removed in the future. * accepting arrays may be removed in the future.
* *
@ -531,6 +534,17 @@ class Router {
} }
} }
/**
* Pops a request off of the request stack. Used when doing requestAction
*
* @return CakeRequest The request removed from the stack.
* @see Router::setRequestInfo()
* @see Object::requestAction()
*/
public static function popRequest() {
return array_pop(self::$_requests);
}
/** /**
* Get the either the current request object, or the first one. * Get the either the current request object, or the first one.
* *
@ -671,12 +685,7 @@ class Router {
$path = array('base' => null); $path = array('base' => null);
if (!empty(self::$_requests)) { if (!empty(self::$_requests)) {
$currentRequest = self::$_requests[count(self::$_requests) - 1]; $request = self::$_requests[count(self::$_requests) - 1];
if (!empty($currentRequest->params['requested'])) {
$request = self::$_requests[0];
} else {
$request = $currentRequest;
}
$params = $request->params; $params = $request->params;
$path = array('base' => $request->base, 'here' => $request->here); $path = array('base' => $request->base, 'here' => $request->here);
} }

View file

@ -293,6 +293,9 @@ class ExtractTaskTest extends CakeTestCase {
$pattern = '#msgid "Post title is required"#'; $pattern = '#msgid "Post title is required"#';
$this->assertPattern($pattern, $result); $this->assertPattern($pattern, $result);
$pattern = '#msgid "You may enter up to %s chars \(minimum is %s chars\)"#';
$this->assertPattern($pattern, $result);
$pattern = '#msgid "Post body is required"#'; $pattern = '#msgid "Post body is required"#';
$this->assertPattern($pattern, $result); $this->assertPattern($pattern, $result);

View file

@ -118,37 +118,42 @@ class ProjectTaskTest extends CakeTestCase {
$path = $this->Task->args[0] = TMP . 'tests' . DS . 'bake_test_app'; $path = $this->Task->args[0] = TMP . 'tests' . DS . 'bake_test_app';
$this->Task->params['skel'] = CAKE . 'Console' . DS . 'Templates' . DS . 'skel'; $this->Task->params['skel'] = CAKE . 'Console' . DS . 'Templates' . DS . 'skel';
$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y')); $this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y'));
$this->Task->expects($this->at(3))->method('in')->will($this->returnValue('n'));
$this->Task->execute(); $this->Task->execute();
$this->assertTrue(is_dir($this->Task->args[0]), 'No project dir'); $this->assertTrue(is_dir($this->Task->args[0]), 'No project dir');
$file = new File($path . DS . 'webroot' . DS . 'index.php'); $file = new File($path . DS . 'webroot' . DS . 'index.php');
$contents = $file->read(); $contents = $file->read();
$this->assertPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', ROOT/', $contents); $this->assertRegExp('/define\(\'CAKE_CORE_INCLUDE_PATH\', .*?DS/', $contents);
$file = new File($path . DS . 'webroot' . DS . 'test.php'); $file = new File($path . DS . 'webroot' . DS . 'test.php');
$contents = $file->read(); $contents = $file->read();
$this->assertPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', ROOT/', $contents); $this->assertRegExp('/define\(\'CAKE_CORE_INCLUDE_PATH\', .*?DS/', $contents);
} }
/** /**
* test bake with setting CAKE_CORE_INCLUDE_PATH in webroot/index.php * test bake with CakePHP on the include path. The constants should remain commented out.
* *
* @return void * @return void
*/ */
public function testExecuteWithSettingIncludePath() { public function testExecuteWithCakeOnIncludePath() {
if (!function_exists('ini_set')) {
$this->markTestAsSkipped('Not access to ini_set, cannot proceed.');
}
$restore = ini_get('include_path');
ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . $restore);
$path = $this->Task->args[0] = TMP . 'tests' . DS . 'bake_test_app'; $path = $this->Task->args[0] = TMP . 'tests' . DS . 'bake_test_app';
$this->Task->params['skel'] = CAKE . 'Console' . DS . 'Templates' . DS . 'skel'; $this->Task->params['skel'] = CAKE . 'Console' . DS . 'Templates' . DS . 'skel';
$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y')); $this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y'));
$this->Task->expects($this->at(3))->method('in')->will($this->returnValue('y'));
$this->Task->execute(); $this->Task->execute();
$this->assertTrue(is_dir($this->Task->args[0]), 'No project dir'); $this->assertTrue(is_dir($this->Task->args[0]), 'No project dir');
$file = new File($path . DS . 'webroot' . DS . 'index.php'); $contents = file_get_contents($path . DS . 'webroot' . DS . 'index.php');
$contents = $file->read(); $this->assertRegExp('#//define\(\'CAKE_CORE_INCLUDE_PATH#', $contents);
$this->assertNoPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', ROOT/', $contents);
$file = new File($path . DS . 'webroot' . DS . 'test.php'); $contents = file_get_contents($path . DS . 'webroot' . DS . 'test.php');
$contents = $file->read(); $this->assertRegExp('#//define\(\'CAKE_CORE_INCLUDE_PATH#', $contents);
$this->assertNoPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', ROOT/', $contents);
ini_set('include_path', $restore);
} }
/** /**

View file

@ -139,6 +139,18 @@ class ConsoleOptionParserTest extends CakeTestCase {
$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Short parameter did not parse out'); $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Short parameter did not parse out');
} }
/**
* Test that adding an option using a two letter short value causes an exception.
* As they will not parse correctly.
*
* @expectedException ConsoleException
* @return void
*/
public function testAddOptionShortOneLetter() {
$parser = new ConsoleOptionParser('test', false);
$parser->addOption('test', array('short' => 'te'));
}
/** /**
* test adding and using boolean options. * test adding and using boolean options.
* *
@ -255,6 +267,21 @@ class ConsoleOptionParserTest extends CakeTestCase {
$result = $parser->parse(array('--name', 'jimmy')); $result = $parser->parse(array('--name', 'jimmy'));
} }
/**
* Ensure that option values can start with -
*
* @return void
*/
public function testOptionWithValueStartingWithMinus() {
$parser = new ConsoleOptionParser('test', false);
$parser->addOption('name')
->addOption('age');
$result = $parser->parse(array('--name', '-foo', '--age', 'old'));
$expected = array('name' => '-foo', 'age' => 'old', 'help' => false);
$this->assertEquals($expected, $result[0], 'Option values starting with "-" are broken.');
}
/** /**
* test positional argument parsing. * test positional argument parsing.
* *
@ -496,6 +523,16 @@ TEXT;
$this->assertEquals('factory', $parser->command()); $this->assertEquals('factory', $parser->command());
} }
/**
* test that command() inflects the command name.
*
* @return void
*/
public function testCommandInflection() {
$parser = new ConsoleOptionParser('CommandLine');
$this->assertEquals('command_line', $parser->command());
}
/** /**
* test that parse() takes a subcommand argument, and that the subcommand parser * test that parse() takes a subcommand argument, and that the subcommand parser
* is used. * is used.

View file

@ -210,6 +210,54 @@ TEXT;
$this->assertEquals($expected, $result, 'Help does not match'); $this->assertEquals($expected, $result, 'Help does not match');
} }
/**
* Test that a long set of options doesn't make useless output.
*
* @return void
*/
public function testHelpWithLotsOfOptions() {
$parser = new ConsoleOptionParser('mycommand', false);
$parser
->addOption('test', array('help' => 'A test option.'))
->addOption('test2', array('help' => 'A test option.'))
->addOption('test3', array('help' => 'A test option.'))
->addOption('test4', array('help' => 'A test option.'))
->addOption('test5', array('help' => 'A test option.'))
->addOption('test6', array('help' => 'A test option.'))
->addOption('test7', array('help' => 'A test option.'))
->addArgument('model', array('help' => 'The model to make.', 'required' => true))
->addArgument('other_longer', array('help' => 'Another argument.'));
$formatter = new HelpFormatter($parser);
$result = $formatter->text();
$expected = 'cake mycommand [options] <model> [<other_longer>]';
$this->assertContains($expected, $result);
}
/**
* Test that a long set of arguments doesn't make useless output.
*
* @return void
*/
public function testHelpWithLotsOfArguments() {
$parser = new ConsoleOptionParser('mycommand', false);
$parser
->addArgument('test', array('help' => 'A test option.'))
->addArgument('test2', array('help' => 'A test option.'))
->addArgument('test3', array('help' => 'A test option.'))
->addArgument('test4', array('help' => 'A test option.'))
->addArgument('test5', array('help' => 'A test option.'))
->addArgument('test6', array('help' => 'A test option.'))
->addArgument('test7', array('help' => 'A test option.'))
->addArgument('model', array('help' => 'The model to make.', 'required' => true))
->addArgument('other_longer', array('help' => 'Another argument.'));
$formatter = new HelpFormatter($parser);
$result = $formatter->text();
$expected = 'cake mycommand [-h] [arguments]';
$this->assertContains($expected, $result);
}
/** /**
* test help() with options and arguments that have choices. * test help() with options and arguments that have choices.
* *

View file

@ -453,6 +453,25 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertTrue($this->Controller->Security->validatePost($this->Controller)); $this->assertTrue($this->Controller->Security->validatePost($this->Controller));
} }
/**
* Test that validatePost fails if you are missing the session information.
*
* @return void
*/
function testValidatePostNoSession() {
$this->Controller->Security->startup($this->Controller);
$this->Controller->Session->delete('_Token');
$key = $this->Controller->params['_Token']['key'];
$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
$this->Controller->data = array(
'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
'_Token' => compact('key', 'fields')
);
$this->assertFalse($this->Controller->Security->validatePost($this->Controller));
}
/** /**
* test that validatePost fails if any of its required fields are missing. * test that validatePost fails if any of its required fields are missing.
* *
@ -1036,6 +1055,23 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertTrue($this->Controller->Security->Session->check('_Token'), '_Token was deleted by blackHole %s'); $this->assertTrue($this->Controller->Security->Session->check('_Token'), '_Token was deleted by blackHole %s');
} }
/**
* test that csrf checks are skipped for request action.
*
* @return void
*/
public function testCsrfSkipRequestAction() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Security->validatePost = false;
$this->Security->csrfCheck = true;
$this->Security->csrfExpires = '+10 minutes';
$this->Controller->request->params['requested'] = 1;
$this->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed, 'fail() was called.');
}
/** /**
* test setting * test setting
* *

View file

@ -441,12 +441,14 @@ class ObjectTest extends CakeTestCase {
'views' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS), 'views' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS),
'controllers' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Controller' . DS) 'controllers' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Controller' . DS)
), true); ), true);
$this->assertNull(Router::getRequest(), 'request stack should be empty.');
$result = $this->object->requestAction(''); $result = $this->object->requestAction('');
$this->assertFalse($result); $this->assertFalse($result);
$result = $this->object->requestAction('/request_action/test_request_action'); $result = $this->object->requestAction('/request_action/test_request_action');
$expected = 'This is a test'; $expected = 'This is a test';
$this->assertEqual($expected, $result);; $this->assertEqual($expected, $result);
$result = $this->object->requestAction('/request_action/another_ra_test/2/5'); $result = $this->object->requestAction('/request_action/another_ra_test/2/5');
$expected = 7; $expected = 7;
@ -466,6 +468,8 @@ class ObjectTest extends CakeTestCase {
$result = $this->object->requestAction('/request_action/normal_request_action'); $result = $this->object->requestAction('/request_action/normal_request_action');
$expected = 'Hello World'; $expected = 'Hello World';
$this->assertEqual($expected, $result); $this->assertEqual($expected, $result);
$this->assertNull(Router::getRequest(), 'requests were not popped off the stack, this will break url generation');
} }
/** /**

View file

@ -65,6 +65,7 @@ class TreeBehaviorAfterTest extends CakeTestCase {
$expected = array('AfterTree' => array('name' => 'Six and One Half Changed in AfterTree::afterSave() but not in database', 'parent_id' => 6, 'lft' => 11, 'rght' => 12)); $expected = array('AfterTree' => array('name' => 'Six and One Half Changed in AfterTree::afterSave() but not in database', 'parent_id' => 6, 'lft' => 11, 'rght' => 12));
$result = $this->Tree->save(array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6))); $result = $this->Tree->save(array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6)));
$expected['AfterTree']['id'] = $this->Tree->id;
$this->assertEqual($expected, $result); $this->assertEqual($expected, $result);
$expected = array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6, 'lft' => 11, 'rght' => 12, 'id' => 8)); $expected = array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6, 'lft' => 11, 'rght' => 12, 'id' => 8));

View file

@ -1078,6 +1078,11 @@ class TreeBehaviorNumberTest extends CakeTestCase {
$total = $this->Tree->childCount(); $total = $this->Tree->childCount();
$this->assertEqual($total, 6); $this->assertEqual($total, 6);
$this->Tree->read(null, $data[$modelClass]['id']);
$id = $this->Tree->field('id', array($modelClass . '.name' => '1.2'));
$total = $this->Tree->childCount($id);
$this->assertEqual($total, 2);
} }
/** /**

View file

@ -862,16 +862,24 @@ class BehaviorCollectionTest extends CakeTestCase {
$Sample->Behaviors->attach('Test', array('beforeSave' => 'off')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off'));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $record); $result = $Sample->save($record);
$expected = $record;
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->attach('Test', array('beforeSave' => 'test')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'test'));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $record); $result = $Sample->save($record);
$expected = $record;
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->attach('Test', array('beforeSave' => 'modify')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'modify'));
$expected = Set::insert($record, 'Sample.name', 'sample99 modified before'); $expected = Set::insert($record, 'Sample.name', 'sample99 modified before');
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $expected); $result = $Sample->save($record);
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->disable('Test'); $Sample->Behaviors->disable('Test');
$this->assertIdentical($Sample->save($record), $record); $this->assertIdentical($Sample->save($record), $record);
@ -879,20 +887,30 @@ class BehaviorCollectionTest extends CakeTestCase {
$Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'on')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'on'));
$expected = Set::merge($record, array('Sample' => array('aftersave' => 'modified after on create'))); $expected = Set::merge($record, array('Sample' => array('aftersave' => 'modified after on create')));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $expected); $result = $Sample->save($record);
$expected['Sample']['id'] = $Sample->id;
$this->assertEqual($result, $expected);
$Sample->Behaviors->attach('Test', array('beforeSave' => 'modify', 'afterSave' => 'modify')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'modify', 'afterSave' => 'modify'));
$expected = Set::merge($record, array('Sample' => array('name' => 'sample99 modified before modified after on create'))); $expected = Set::merge($record, array('Sample' => array('name' => 'sample99 modified before modified after on create')));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $expected); $result = $Sample->save($record);
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'test')); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'test'));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $record); $expected = $record;
$result = $Sample->save($record);
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->attach('Test', array('afterSave' => 'test2')); $Sample->Behaviors->attach('Test', array('afterSave' => 'test2'));
$Sample->create(); $Sample->create();
$this->assertIdentical($Sample->save($record), $record); $expected = $record;
$result = $Sample->save($record);
$expected['Sample']['id'] = $Sample->id;
$this->assertIdentical($result, $expected);
$Sample->Behaviors->attach('Test', array('beforeFind' => 'off', 'afterFind' => 'off')); $Sample->Behaviors->attach('Test', array('beforeFind' => 'off', 'afterFind' => 'off'));
$Sample->recursive = -1; $Sample->recursive = -1;
@ -996,7 +1014,7 @@ class BehaviorCollectionTest extends CakeTestCase {
$Apple->validate['color'] = 'validateField'; $Apple->validate['color'] = 'validateField';
$result = $Apple->save(array('name' => 'Genetically Modified Apple', 'color' => 'Orange')); $result = $Apple->save(array('name' => 'Genetically Modified Apple', 'color' => 'Orange'));
$this->assertEqual(array_keys($result['Apple']), array('name', 'color', 'modified', 'created')); $this->assertEqual(array_keys($result['Apple']), array('name', 'color', 'modified', 'created', 'id'));
$Apple->create(); $Apple->create();
$result = $Apple->save(array('name' => 'Regular Apple', 'color' => 'Red')); $result = $Apple->save(array('name' => 'Regular Apple', 'color' => 'Red'));

View file

@ -277,10 +277,10 @@ class AclNodeTest extends CakeTestCase {
$this->assertEqual($expected, $result); $this->assertEqual($expected, $result);
$result = Set::extract($Aco->node('Controller2/action3'), '{n}.DbAcoTest.id'); $result = Set::extract($Aco->node('Controller2/action3'), '{n}.DbAcoTest.id');
$this->assertFalse($result); $this->assertNull($result);
$result = Set::extract($Aco->node('Controller2/action3/record5'), '{n}.DbAcoTest.id'); $result = Set::extract($Aco->node('Controller2/action3/record5'), '{n}.DbAcoTest.id');
$this->assertFalse($result); $this->assertNull($result);
$result = $Aco->node(''); $result = $Aco->node('');
$this->assertEqual($result, null); $this->assertEqual($result, null);

View file

@ -6567,7 +6567,7 @@ class ModelReadTest extends BaseModelTest {
* @return void * @return void
*/ */
public function testFindCount() { public function testFindCount() {
$this->loadFixtures('User', 'Project'); $this->loadFixtures('User', 'Article');
$TestModel = new User(); $TestModel = new User();
$this->db->getLog(false, true); $this->db->getLog(false, true);
@ -6583,6 +6583,10 @@ class ModelReadTest extends BaseModelTest {
$log = $this->db->getLog(); $log = $this->db->getLog();
$this->assertTrue(isset($log['log'][0]['query'])); $this->assertTrue(isset($log['log'][0]['query']));
$this->assertNoPattern('/ORDER\s+BY/', $log['log'][0]['query']); $this->assertNoPattern('/ORDER\s+BY/', $log['log'][0]['query']);
$Article = new Article();
$result = $Article->find('count', array('group' => 'Article.user_id'));
$this->assertEqual($result, 3);
} }
/** /**

View file

@ -679,4 +679,35 @@ class ModelValidationTest extends BaseModelTest {
$this->assertEquals($TestModel->validationErrors, array()); $this->assertEquals($TestModel->validationErrors, array());
} }
/**
* Test placeholder replacement when validation message is an array
*
* @return void
*/
public function testValidationMessageAsArray() {
$TestModel = new ValidationTest1();
$TestModel->create(array('title' => 'foo'));
$TestModel->validate = array(
'title' => array(
'minLength' => array(
'rule' => array('minLength', 6),
'message' => array('Minimum length allowed is %d chars'),
'last' => false
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => array('You may enter up to %s chars (minimum is %s chars)', 14, 6)
)
)
);
$TestModel->invalidFields();
$expected = array(
'title' => array(
'Minimum length allowed is 6 chars',
'You may enter up to 14 chars (minimum is 6 chars)'
)
);
$this->assertEquals($TestModel->validationErrors, $expected);
}
} }

View file

@ -55,8 +55,9 @@ class ModelWriteTest extends BaseModelTest {
'updated' => '2008-01-03 10:56:44' 'updated' => '2008-01-03 10:56:44'
); );
$result = $TestModel->JoinAsJoinB->save($data); $result = $TestModel->JoinAsJoinB->save($data);
$this->assertEquals($result, array('JoinAsJoinB' => $data));
$lastInsertId = $TestModel->JoinAsJoinB->getLastInsertID(); $lastInsertId = $TestModel->JoinAsJoinB->getLastInsertID();
$data['id'] = $lastInsertId;
$this->assertEquals($result, array('JoinAsJoinB' => $data));
$this->assertTrue($lastInsertId != null); $this->assertTrue($lastInsertId != null);
$result = $TestModel->JoinAsJoinB->findById(1); $result = $TestModel->JoinAsJoinB->findById(1);
@ -5227,4 +5228,23 @@ class ModelWriteTest extends BaseModelTest {
setlocale(LC_ALL, $restore); setlocale(LC_ALL, $restore);
} }
/**
* Test returned array contains primary key when save creates a new record
*
* @return void
*/
public function testPkInReturnArrayForCreate() {
$this->loadFixtures('Article');
$TestModel = new Article();
$data = array('Article' => array(
'user_id' => '1',
'title' => 'Fourth Article',
'body' => 'Fourth Article Body',
'published' => 'Y'
));
$result = $TestModel->save($data);
$this->assertIdentical($result['Article']['id'], $TestModel->id);
}
} }

View file

@ -2271,6 +2271,35 @@ class RouterTest extends CakeTestCase {
$this->assertEqual($result->webroot, '/'); $this->assertEqual($result->webroot, '/');
} }
/**
* Test that Router::url() uses the first request
*/
public function testUrlWithRequestAction() {
$firstRequest = new CakeRequest('/posts/index');
$firstRequest->addParams(array(
'plugin' => null,
'controller' => 'posts',
'action' => 'index'
))->addPaths(array('base' => ''));
$secondRequest = new CakeRequest('/posts/index');
$secondRequest->addParams(array(
'requested' => 1,
'plugin' => null,
'controller' => 'comments',
'action' => 'listing'
))->addPaths(array('base' => ''));
Router::setRequestInfo($firstRequest);
Router::setRequestInfo($secondRequest);
$result = Router::url(array('base' => false));
$this->assertEquals('/comments/listing', $result, 'with second requests, the last should win.');
Router::popRequest();
$result = Router::url(array('base' => false));
$this->assertEquals('/posts', $result, 'with second requests, the last should win.');
}
/** /**
* test that a route object returning a full url is not modified. * test that a route object returning a full url is not modified.
* *

View file

@ -98,6 +98,10 @@ class ControllerTestCaseTestController extends AppController {
} }
/**
* Used to get a testable concrete class of the test subject
*/
class TestingControllerTestCase extends ControllerTestCase {}
/** /**
* ControllerTestCaseTest * ControllerTestCaseTest
@ -127,7 +131,7 @@ class ControllerTestCaseTest extends CakeTestCase {
'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS) 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS)
)); ));
CakePlugin::loadAll(); CakePlugin::loadAll();
$this->Case = new ControllerTestCase(); $this->Case = new TestingControllerTestCase();
Router::reload(); Router::reload();
} }

View file

@ -244,7 +244,12 @@ class SanitizeTest extends CakeTestCase {
$string = "This sentence \t\t\t has lots of \n\n white\nspace \rthat \r\n needs to be \t \n trimmed."; $string = "This sentence \t\t\t has lots of \n\n white\nspace \rthat \r\n needs to be \t \n trimmed.";
$expected = "This sentence has lots of whitespace that needs to be trimmed."; $expected = "This sentence has lots of whitespace that needs to be trimmed.";
$result = Sanitize::stripWhitespace($string); $result = Sanitize::stripWhitespace($string);
$this->assertEqual($expected, $result); $this->assertEquals($expected, $result);
$text = 'I love ßá†ö√ letters.';
$result = Sanitize::stripWhitespace($text);
$expected = 'I love ßá†ö√ letters.';
$this->assertEqual($result, $expected);
} }
/** /**

View file

@ -948,7 +948,7 @@ class SetTest extends CakeTestCase {
); );
$result = Set::extract('/ParentNode/name', $hasMany); $result = Set::extract('/ParentNode/name', $hasMany);
$expected = array('Second'); $expected = array('Second');
$this->assertEqual($expected, $result); $this->assertEquals($expected, $result);
$data = array( $data = array(
array( array(
@ -982,7 +982,33 @@ class SetTest extends CakeTestCase {
) )
); );
$result = Set::extract('/Category[id=1]/..', $data); $result = Set::extract('/Category[id=1]/..', $data);
$this->assertEqual($expected, $result); $this->assertEquals($expected, $result);
$data = array(
array(
'ChildNode' => array('id' => 1),
array('name' => 'Item 1')
),
array(
'ChildNode' => array('id' => 2),
array('name' => 'Item 2')
),
);
$expected = array(
'Item 1',
'Item 2'
);
$result = Set::extract('/0/name', $data);
$this->assertEquals($expected, $result);
$data = array(
array('A1', 'B1'),
array('A2', 'B2')
);
$expected = array('A1', 'A2');
$result = Set::extract('/0', $data);
$this->assertEquals($expected, $result);
} }
/** /**
@ -1582,6 +1608,25 @@ class SetTest extends CakeTestCase {
$result = Set::extract($a, 'articles.{n}.Article.title'); $result = Set::extract($a, 'articles.{n}.Article.title');
$expected = array('Article 1', 'Article 2', 'Article 3'); $expected = array('Article 1', 'Article 2', 'Article 3');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$a = new ArrayObject();
$a['articles'] = array(
array('Article' => array('id' => 1, 'title' => 'Article 1')),
array('Article' => array('id' => 2, 'title' => 'Article 2')),
array('Article' => array('id' => 3, 'title' => 'Article 3'))
);
$result = Set::extract($a, 'articles.{n}.Article.id');
$expected = array(1, 2, 3);
$this->assertEquals($expected, $result);
$result = Set::extract($a, 'articles.{n}.Article.title');
$expected = array('Article 1', 'Article 2', 'Article 3');
$this->assertEquals($expected, $result);
$result = Set::extract($a, 'articles.0.Article.title');
$expected = 'Article 1';
$this->assertEquals($expected, $result);
} }
/** /**

View file

@ -318,6 +318,37 @@ class CacheHelperTest extends CakeTestCase {
@unlink($filename); @unlink($filename);
} }
function testCacheCallbacks() {
$this->Controller->cache_parsing();
$this->Controller->params = array(
'controller' => 'cache_test',
'action' => 'cache_parsing',
'url' => array(),
'pass' => array(),
'named' => array()
);
$this->Controller->cacheAction = array(
'cache_parsing' => array(
'duration' => 21600,
'callbacks' => true
)
);
$this->Controller->here = '/cacheTest/cache_parsing';
$this->Controller->action = 'cache_parsing';
$View = new View($this->Controller);
$result = $View->render('index');
$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
$this->assertTrue(file_exists($filename));
$contents = file_get_contents($filename);
$this->assertPattern('/\$controller->startupProcess\(\);/', $contents);
@unlink($filename);
}
/** /**
* test cacheAction set to a boolean * test cacheAction set to a boolean
* *
@ -551,10 +582,17 @@ class CacheHelperTest extends CakeTestCase {
* This test must be uncommented/fixed in next release (1.2+) * This test must be uncommented/fixed in next release (1.2+)
* *
* @return void * @return void
* */
public function testCacheEmptySections () { public function testCacheEmptySections() {
$this->Controller->cache_parsing(); $this->Controller->cache_parsing();
$this->Controller->cacheAction = array('cacheTest' => 21600); $this->Controller->params = array(
'controller' => 'cacheTest',
'action' => 'cache_empty_sections',
'url' => array(),
'pass' => array(),
'named' => array()
);
$this->Controller->cacheAction = array('cache_empty_sections' => 21600);
$this->Controller->here = '/cacheTest/cache_empty_sections'; $this->Controller->here = '/cacheTest/cache_empty_sections';
$this->Controller->action = 'cache_empty_sections'; $this->Controller->action = 'cache_empty_sections';
$this->Controller->layout = 'cache_empty_sections'; $this->Controller->layout = 'cache_empty_sections';
@ -562,7 +600,7 @@ class CacheHelperTest extends CakeTestCase {
$View = new View($this->Controller); $View = new View($this->Controller);
$result = $View->render('cache_empty_sections'); $result = $View->render('cache_empty_sections');
$this->assertNoPattern('/cake:nocache/', $result); $this->assertNoPattern('/nocache/', $result);
$this->assertNoPattern('/php echo/', $result); $this->assertNoPattern('/php echo/', $result);
$this->assertPattern( $this->assertPattern(
'@</title>\s*</head>\s*' . '@</title>\s*</head>\s*' .
@ -574,19 +612,18 @@ class CacheHelperTest extends CakeTestCase {
$filename = CACHE . 'views' . DS . 'cachetest_cache_empty_sections.php'; $filename = CACHE . 'views' . DS . 'cachetest_cache_empty_sections.php';
$this->assertTrue(file_exists($filename)); $this->assertTrue(file_exists($filename));
$contents = file_get_contents($filename); $contents = file_get_contents($filename);
$this->assertNoPattern('/cake:nocache/', $contents); $this->assertNoPattern('/nocache/', $contents);
$this->assertPattern( $this->assertPattern(
'@<head>\s*<title>Posts</title>\s*' . '@<head>\s*<title>Posts</title>\s*' .
"<\?php \$x = 1; \?>\s*" . '<\?php \$x \= 1; \?>\s*' .
'</head>\s*' . '</head>\s*' .
'<body>\s*' . '<body>\s*' .
"<\?php \$x\+\+; \?>\s*" . '<\?php \$x\+\+; \?>\s*' .
"<\?php \$x\+\+; \?>\s*" . '<\?php \$x\+\+; \?>\s*' .
'View Content\s*' . 'View Content\s*' .
"<\?php \$y = 1; \?>\s*" . '<\?php \$y = 1; \?>\s*' .
"<\?php echo 'cached count is:' . \$x; \?>\s*" . '<\?php echo \'cached count is: \' . \$x; \?>\s*' .
'@', $contents); '@', $contents);
@unlink($filename); @unlink($filename);
} }
*/
} }

View file

@ -91,7 +91,8 @@ class Contact extends CakeTestModel {
'password' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'), 'password' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
'published' => array('type' => 'date', 'null' => true, 'default' => null, 'length' => null), 'published' => array('type' => 'date', 'null' => true, 'default' => null, 'length' => null),
'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''), 'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null) 'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null),
'age' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => null)
); );
/** /**
@ -826,6 +827,36 @@ class FormHelperTest extends CakeTestCase {
); );
} }
/**
* Tests correct generation of text fields for double and float fields
*
* @access public
* @return void
*/
public function testTextFieldTypeNumberGenerationForIntegers() {
$model = ClassRegistry::getObject('Contact');
$model->setSchema(array('foo' => array(
'type' => 'integer',
'null' => false,
'default' => null,
'length' => null
)));
$this->Form->create('Contact');
$result = $this->Form->input('foo');
$expected = array(
'div' => array('class' => 'input text'),
'label' => array('for' => 'ContactFoo'),
'Foo',
'/label',
array('input' => array(
'type' => 'number', 'name' => 'data[Contact][foo]',
'id' => 'ContactFoo'
)),
'/div'
);
}
/** /**
* testFormSecurityMultipleFields method * testFormSecurityMultipleFields method
* *
@ -1596,7 +1627,7 @@ class FormHelperTest extends CakeTestCase {
'label' => array('for'), 'label' => array('for'),
'Balance', 'Balance',
'/label', '/label',
'input' => array('name', 'type' => 'text', 'maxlength' => 8, 'id'), 'input' => array('name', 'type' => 'number', 'maxlength' => 8, 'id'),
'/div', '/div',
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
@ -2400,6 +2431,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
); );
@ -2423,6 +2456,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
); );
@ -2447,6 +2482,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
'/fieldset' '/fieldset'
@ -2471,6 +2508,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
); );
@ -2498,6 +2537,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
'/fieldset' '/fieldset'
@ -2526,6 +2567,8 @@ class FormHelperTest extends CakeTestCase {
'*/div', '*/div',
array('div' => array('class' => 'input datetime')), array('div' => array('class' => 'input datetime')),
'*/div', '*/div',
array('div' => array('class' => 'input number')),
'*/div',
array('div' => array('class' => 'input select')), array('div' => array('class' => 'input select')),
'*/div', '*/div',
'/fieldset' '/fieldset'
@ -4582,13 +4625,14 @@ class FormHelperTest extends CakeTestCase {
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$selected = strtotime('2008-10-26 10:33:00'); $selected = strtotime('2008-10-26 12:33:00');
$result = $this->Form->dateTime('Model.field', 'DMY', '12', array('value' => $selected)); $result = $this->Form->dateTime('Model.field', 'DMY', '12', array('value' => $selected));
$this->assertPattern('/<option[^<>]+value="2008"[^<>]+selected="selected"[^>]*>2008<\/option>/', $result); $this->assertPattern('/<option[^<>]+value="2008"[^<>]+selected="selected"[^>]*>2008<\/option>/', $result);
$this->assertPattern('/<option[^<>]+value="10"[^<>]+selected="selected"[^>]*>10<\/option>/', $result); $this->assertPattern('/<option[^<>]+value="10"[^<>]+selected="selected"[^>]*>October<\/option>/', $result);
$this->assertPattern('/<option[^<>]+value="26"[^<>]+selected="selected"[^>]*>26<\/option>/', $result); $this->assertPattern('/<option[^<>]+value="26"[^<>]+selected="selected"[^>]*>26<\/option>/', $result);
$this->assertPattern('/<option[^<>]+value="10"[^<>]+selected="selected"[^>]*>10<\/option>/', $result); $this->assertPattern('/<option[^<>]+value="12"[^<>]+selected="selected"[^>]*>12<\/option>/', $result);
$this->assertPattern('/<option[^<>]+value="33"[^<>]+selected="selected"[^>]*>33<\/option>/', $result); $this->assertPattern('/<option[^<>]+value="33"[^<>]+selected="selected"[^>]*>33<\/option>/', $result);
$this->assertPattern('/<option[^<>]+value="pm"[^<>]+selected="selected"[^>]*>pm<\/option>/', $result);
$this->Form->create('Contact'); $this->Form->create('Contact');
$result = $this->Form->input('published'); $result = $this->Form->input('published');
@ -5552,6 +5596,35 @@ class FormHelperTest extends CakeTestCase {
$this->assertTrue(strpos($result, '<input type="hidden" name="data[extra]" value="value"/>') !== false); $this->assertTrue(strpos($result, '<input type="hidden" name="data[extra]" value="value"/>') !== false);
} }
/**
* Test that postButton adds _Token fields.
*
* @return void
*/
public function testSecurePostButton() {
$this->Form->request->params['_Token'] = array('key' => 'testkey');
$result = $this->Form->postButton('Delete', '/posts/delete/1');
$expected = array(
'form' => array(
'method' => 'post', 'action' => '/posts/delete/1', 'accept-charset' => 'utf-8',
'style' => 'display:none;'
),
array('div' => array('style' => 'display:none;')),
array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][key]', 'value' => 'testkey', 'id' => 'preg:/Token\d+/')),
'/div',
'button' => array('type' => 'submit'),
'Delete',
'/button',
array('div' => array('style' => 'display:none;')),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][fields]', 'value' => 'preg:/[\w\d%]+/', 'id' => 'preg:/TokenFields\d+/')),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][unlocked]', 'value' => '', 'id' => 'preg:/TokenUnlocked\d+/')),
'/div',
'/form',
);
$this->assertTags($result, $expected);
}
/** /**
* testPostLink method * testPostLink method
* *
@ -5564,9 +5637,7 @@ class FormHelperTest extends CakeTestCase {
'method' => 'post', 'action' => '/posts/delete/1', 'method' => 'post', 'action' => '/posts/delete/1',
'name' => 'preg:/post_\w+/', 'id' => 'preg:/post_\w+/', 'style' => 'display:none;' 'name' => 'preg:/post_\w+/', 'id' => 'preg:/post_\w+/', 'style' => 'display:none;'
), ),
'div' => array('style' => 'display:none;'),
'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'), 'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
'/div',
'/form', '/form',
'a' => array('href' => '#', 'onclick' => 'preg:/document\.post_\w+\.submit\(\); event\.returnValue = false; return false;/'), 'a' => array('href' => '#', 'onclick' => 'preg:/document\.post_\w+\.submit\(\); event\.returnValue = false; return false;/'),
'Delete', 'Delete',
@ -5579,9 +5650,7 @@ class FormHelperTest extends CakeTestCase {
'method' => 'post', 'action' => '/posts/delete/1', 'method' => 'post', 'action' => '/posts/delete/1',
'name' => 'preg:/post_\w+/', 'id' => 'preg:/post_\w+/', 'style' => 'display:none;' 'name' => 'preg:/post_\w+/', 'id' => 'preg:/post_\w+/', 'style' => 'display:none;'
), ),
'div' => array('style' => 'display:none;'),
'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'), 'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
'/div',
'/form', '/form',
'a' => array('href' => '#', 'onclick' => 'preg:/if \(confirm\(&#039;Confirm\?&#039;\)\) \{ document\.post_\w+\.submit\(\); \} event\.returnValue = false; return false;/'), 'a' => array('href' => '#', 'onclick' => 'preg:/if \(confirm\(&#039;Confirm\?&#039;\)\) \{ document\.post_\w+\.submit\(\); \} event\.returnValue = false; return false;/'),
'Delete', 'Delete',
@ -5589,7 +5658,35 @@ class FormHelperTest extends CakeTestCase {
)); ));
$result = $this->Form->postLink('Delete', '/posts/delete', array('data' => array('id' => 1))); $result = $this->Form->postLink('Delete', '/posts/delete', array('data' => array('id' => 1)));
$this->assertTrue(strpos($result, '<input type="hidden" name="data[id]" value="1"/>') !== false); $this->assertContains('<input type="hidden" name="data[id]" value="1"/>', $result);
}
/**
* Test that postLink adds _Token fields.
*
* @return void
*/
public function testSecurePostLink() {
$this->Form->request->params['_Token'] = array('key' => 'testkey');
$result = $this->Form->postLink('Delete', '/posts/delete/1');
$expected = array(
'form' => array(
'method' => 'post', 'action' => '/posts/delete/1',
'name' => 'preg:/post_\w+/', 'id' => 'preg:/post_\w+/', 'style' => 'display:none;'
),
array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][key]', 'value' => 'testkey', 'id' => 'preg:/Token\d+/')),
'div' => array('style' => 'display:none;'),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][fields]', 'value' => 'preg:/[\w\d%]+/', 'id' => 'preg:/TokenFields\d+/')),
array('input' => array('type' => 'hidden', 'name' => 'data[_Token][unlocked]', 'value' => '', 'id' => 'preg:/TokenUnlocked\d+/')),
'/div',
'/form',
'a' => array('href' => '#', 'onclick' => 'preg:/document\.post_\w+\.submit\(\); event\.returnValue = false; return false;/'),
'Delete',
'/a'
);
$this->assertTags($result, $expected);
} }
/** /**
@ -7170,6 +7267,12 @@ class FormHelperTest extends CakeTestCase {
'input' => array('type' => 'text', 'name' => 'data[User][query]', 'id' => 'UserQuery', 'value' => 'test') 'input' => array('type' => 'text', 'name' => 'data[User][query]', 'id' => 'UserQuery', 'value' => 'test')
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$result = $this->Form->input('User.website', array('type' => 'url', 'value' => 'http://domain.tld', 'div' => false, 'label' => false));
$expected = array(
'input' => array('type' => 'url', 'name' => 'data[User][website]', 'id' => 'UserWebsite', 'value' => 'http://domain.tld')
);
$this->assertTags($result, $expected);
} }
/** /**

View file

@ -601,6 +601,12 @@ class HtmlHelperTest extends CakeTestCase {
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$result = $this->Html->script('http://example.com/test.json');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'http://example.com/test.json')
);
$this->assertTags($result, $expected);
$result = $this->Html->script('/plugin/js/jquery-1.3.2.js?someparam=foo'); $result = $this->Html->script('/plugin/js/jquery-1.3.2.js?someparam=foo');
$expected = array( $expected = array(
'script' => array('type' => 'text/javascript', 'src' => '/plugin/js/jquery-1.3.2.js?someparam=foo') 'script' => array('type' => 'text/javascript', 'src' => '/plugin/js/jquery-1.3.2.js?someparam=foo')

View file

@ -452,8 +452,8 @@ class PaginatorHelperTest extends CakeTestCase {
$this->Paginator->request->params['paging']['Article']['page'] = 1; $this->Paginator->request->params['paging']['Article']['page'] = 1;
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/admin/users/index/page:2', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/admin/users/index/page:2', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/span' '/span'
@ -553,8 +553,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('next', array('url' => $options)); $result = $this->Paginator->next('next', array('url' => $options));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/members/posts/index/page:3', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/members/posts/index/page:3', 'rel' => 'next'),
'next', 'next',
'/a', '/a',
'/span' '/span'
@ -563,8 +563,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->prev('prev', array('url' => $options)); $result = $this->Paginator->prev('prev', array('url' => $options));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/members/posts/index/page:1', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/members/posts/index/page:1', 'rel' => 'prev'),
'prev', 'prev',
'/a', '/a',
'/span' '/span'
@ -688,8 +688,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/articles/index/2/page:2/foo:bar', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/articles/index/2/page:2/foo:bar', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/span' '/span'
@ -737,8 +737,8 @@ class PaginatorHelperTest extends CakeTestCase {
$this->Paginator->request->params['paging']['Client']['prevPage'] = true; $this->Paginator->request->params['paging']['Client']['prevPage'] = true;
$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled')); $result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled'));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/index/page:1', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/index/page:1', 'rel' => 'prev'),
'&lt;&lt; Previous', '&lt;&lt; Previous',
'/a', '/a',
'/span' '/span'
@ -747,8 +747,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/index/page:3', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/index/page:3', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/span' '/span'
@ -757,8 +757,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next', array('tag' => 'li')); $result = $this->Paginator->next('Next', array('tag' => 'li'));
$expected = array( $expected = array(
'<li', 'li' => array('class' => 'next'),
'a' => array('href' => '/index/page:3', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/index/page:3', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/li' '/li'
@ -767,8 +767,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->prev('<< Previous', array('escape' => true)); $result = $this->Paginator->prev('<< Previous', array('escape' => true));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/index/page:1', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/index/page:1', 'rel' => 'prev'),
'&lt;&lt; Previous', '&lt;&lt; Previous',
'/a', '/a',
'/span' '/span'
@ -777,8 +777,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->prev('<< Previous', array('escape' => false)); $result = $this->Paginator->prev('<< Previous', array('escape' => false));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/index/page:1', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/index/page:1', 'rel' => 'prev'),
'preg:/<< Previous/', 'preg:/<< Previous/',
'/a', '/a',
'/span' '/span'
@ -845,10 +845,9 @@ class PaginatorHelperTest extends CakeTestCase {
$this->Paginator->request->params['paging']['Client']['prevPage'] = true; $this->Paginator->request->params['paging']['Client']['prevPage'] = true;
$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled')); $result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled'));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array( 'a' => array(
'href' => '/index/page:1/limit:3/sort:Client.name/direction:DESC', 'href' => '/index/page:1/limit:3/sort:Client.name/direction:DESC',
'class' => 'prev',
'rel' => 'prev' 'rel' => 'prev'
), ),
'&lt;&lt; Previous', '&lt;&lt; Previous',
@ -859,10 +858,9 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array( 'a' => array(
'href' => '/index/page:3/limit:3/sort:Client.name/direction:DESC', 'href' => '/index/page:3/limit:3/sort:Client.name/direction:DESC',
'class' => 'next',
'rel' => 'next' 'rel' => 'next'
), ),
'Next', 'Next',
@ -890,8 +888,8 @@ class PaginatorHelperTest extends CakeTestCase {
); );
$result = $this->Paginator->prev('Prev'); $result = $this->Paginator->prev('Prev');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/index/page:1/limit:10', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/index/page:1/limit:10', 'rel' => 'prev'),
'Prev', 'Prev',
'/a', '/a',
'/span' '/span'
@ -912,8 +910,8 @@ class PaginatorHelperTest extends CakeTestCase {
$this->Paginator->options(array('url' => array(12, 'page' => 3))); $this->Paginator->options(array('url' => array(12, 'page' => 3)));
$result = $this->Paginator->prev('Prev', array('url' => array('foo' => 'bar'))); $result = $this->Paginator->prev('Prev', array('url' => array('foo' => 'bar')));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/index/12/page:1/limit:10/foo:bar', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/index/12/page:1/limit:10/foo:bar', 'rel' => 'prev'),
'Prev', 'Prev',
'/a', '/a',
'/span' '/span'
@ -952,8 +950,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next >>', array('escape' => false)); $result = $this->Paginator->next('Next >>', array('escape' => false));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/index/page:2', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/index/page:2', 'rel' => 'next'),
'preg:/Next >>/', 'preg:/Next >>/',
'/a', '/a',
'/span' '/span'
@ -998,8 +996,8 @@ class PaginatorHelperTest extends CakeTestCase {
); );
$result = $this->Paginator->next('Next', array('model' => 'Client')); $result = $this->Paginator->next('Next', array('model' => 'Client'));
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/index/page:2', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/index/page:2', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/span' '/span'
@ -1522,15 +1520,15 @@ class PaginatorHelperTest extends CakeTestCase {
'paramType' => 'named' 'paramType' => 'named'
) )
); );
$result = $this->Paginator->numbers(); $result = $this->Paginator->numbers(array('class' => 'page-link'));
$expected = array( $expected = array(
array('span' => array()), array('a' => array('href' => '/index/page:1/sort:Client.name/direction:DESC')), '1', '/a', '/span', array('span' => array('class' => 'page-link')), array('a' => array('href' => '/index/page:1/sort:Client.name/direction:DESC')), '1', '/a', '/span',
' | ', ' | ',
array('span' => array('class' => 'current')), '2', '/span', array('span' => array('class' => 'current page-link')), '2', '/span',
' | ', ' | ',
array('span' => array()), array('a' => array('href' => '/index/page:3/sort:Client.name/direction:DESC')), '3', '/a', '/span', array('span' => array('class' => 'page-link')), array('a' => array('href' => '/index/page:3/sort:Client.name/direction:DESC')), '3', '/a', '/span',
' | ', ' | ',
array('span' => array()), array('a' => array('href' => '/index/page:4/sort:Client.name/direction:DESC')), '4', '/a', '/span', array('span' => array('class' => 'page-link')), array('a' => array('href' => '/index/page:4/sort:Client.name/direction:DESC')), '4', '/a', '/span',
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
@ -1760,9 +1758,9 @@ class PaginatorHelperTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testFirstAndLastTag() { public function testFirstAndLastTag() {
$result = $this->Paginator->first('<<', array('tag' => 'li')); $result = $this->Paginator->first('<<', array('tag' => 'li', 'class' => 'first'));
$expected = array( $expected = array(
'<li', 'li' => array('class' => 'first'),
'a' => array('href' => '/index/page:1', 'rel' => 'first'), 'a' => array('href' => '/index/page:1', 'rel' => 'first'),
'&lt;&lt;', '&lt;&lt;',
'/a', '/a',
@ -1770,15 +1768,14 @@ class PaginatorHelperTest extends CakeTestCase {
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$result = $this->Paginator->last(2, array('tag' => 'li')); $result = $this->Paginator->last(2, array('tag' => 'li', 'class' => 'last'));
$expected = array( $expected = array(
'...', '...',
'<li', 'li' => array('class' => 'last'),
array('a' => array('href' => '/index/page:6')), '6', '/a', array('a' => array('href' => '/index/page:6')), '6', '/a',
'/li', '/li',
' | ', ' | ',
'<li', array('li' => array('class' => 'last')),
array('a' => array('href' => '/index/page:7')), '7', '/a', array('a' => array('href' => '/index/page:7')), '7', '/a',
'/li', '/li',
); );
@ -2132,10 +2129,9 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array( 'a' => array(
'href' => '/officespace/accounts/index/page:2/sort:Article.title/direction:asc', 'href' => '/officespace/accounts/index/page:2/sort:Article.title/direction:asc',
'class' => 'next',
'rel' => 'next' 'rel' => 'next'
), ),
'Next', 'Next',
@ -2230,8 +2226,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->next('Next'); $result = $this->Paginator->next('Next');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'next'),
'a' => array('href' => '/?page=3', 'class' => 'next', 'rel' => 'next'), 'a' => array('href' => '/?page=3', 'rel' => 'next'),
'Next', 'Next',
'/a', '/a',
'/span' '/span'
@ -2240,8 +2236,8 @@ class PaginatorHelperTest extends CakeTestCase {
$result = $this->Paginator->prev('Prev'); $result = $this->Paginator->prev('Prev');
$expected = array( $expected = array(
'<span', 'span' => array('class' => 'prev'),
'a' => array('href' => '/?page=1', 'class' => 'prev', 'rel' => 'prev'), 'a' => array('href' => '/?page=1', 'rel' => 'prev'),
'Prev', 'Prev',
'/a', '/a',
'/span' '/span'

View file

@ -27,10 +27,16 @@ class PersisterOne extends AppModel {
public $hasMany = array('Comment', 'TestPlugin.TestPluginComment'); public $hasMany = array('Comment', 'TestPlugin.TestPluginComment');
public $validate = array( public $validate = array(
'title' => array( 'title' => array(
'rule' => array('custom', '.*'), 'custom' => array(
'allowEmpty' => true, 'rule' => array('custom', '.*'),
'required' => false, 'allowEmpty' => true,
'message' => 'Post title is required' 'required' => false,
'message' => 'Post title is required'
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => array('You may enter up to %s chars (minimum is %s chars)', 14, 6)
)
), ),
'body' => array( 'body' => array(
'first_rule' => array( 'first_rule' => array(

View file

@ -98,7 +98,7 @@ class InterceptContentHelper extends Helper {
* *
* @package Cake.TestSuite * @package Cake.TestSuite
*/ */
class ControllerTestCase extends CakeTestCase { abstract class ControllerTestCase extends CakeTestCase {
/** /**
* The controller to test in testAction * The controller to test in testAction

View file

@ -16,7 +16,7 @@
* @since CakePHP(tm) v 2.0 * @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
require_once dirname(__FILE__) . '/base_coverage_report.php'; App::uses('BaseCoverageReport', 'TestSuite/Coverage');
PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist(__FILE__, 'DEFAULT'); PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist(__FILE__, 'DEFAULT');

View file

@ -17,7 +17,8 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
include_once dirname(__FILE__) . DS . 'cake_base_reporter.php'; App::uses('CakeBaseReporter', 'TestSuite/Reporter');
App::uses('TextCoverageReport', 'TestSuite/Coverage');
PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist(__FILE__, 'DEFAULT'); PHP_CodeCoverage_Filter::getInstance()->addFileToBlacklist(__FILE__, 'DEFAULT');
@ -91,7 +92,7 @@ class CakeTextReporter extends CakeBaseReporter {
echo 'Peak memory: ' . number_format(memory_get_peak_usage()) . " bytes\n"; echo 'Peak memory: ' . number_format(memory_get_peak_usage()) . " bytes\n";
if (isset($this->params['codeCoverage']) && $this->params['codeCoverage']) { if (isset($this->params['codeCoverage']) && $this->params['codeCoverage']) {
$coverage = $result->getCodeCoverageInformation(); $coverage = $result->getCodeCoverage()->getSummary();
echo $this->paintCoverage($coverage); echo $this->paintCoverage($coverage);
} }
} }
@ -184,8 +185,6 @@ class CakeTextReporter extends CakeBaseReporter {
* @return string * @return string
*/ */
public function paintCoverage($coverage) { public function paintCoverage($coverage) {
$file = dirname(dirname(__FILE__)) . '/coverage/text_coverage_report.php';
include $file;
$reporter = new TextCoverageReport($coverage, $this); $reporter = new TextCoverageReport($coverage, $this);
echo $reporter->report(); echo $reporter->report();
} }

View file

@ -186,13 +186,14 @@ class Debugger {
} }
/** /**
* Formats and outputs the contents of the supplied variable. * Recursively formats and outputs the contents of the supplied variable.
*
* *
* @param mixed $var the variable to dump * @param mixed $var the variable to dump
* @return void * @return void
* @see Debugger::exportVar() * @see Debugger::exportVar()
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/ */
public static function dump($var) { public static function dump($var) {
pr(self::exportVar($var)); pr(self::exportVar($var));
} }
@ -396,12 +397,22 @@ class Debugger {
} }
/** /**
* Grabs an excerpt from a file and highlights a given line of code * Grabs an excerpt from a file and highlights a given line of code.
*
* Usage:
*
* `Debugger::excerpt('/path/to/file', 100, 4);`
*
* The above would return an array of 8 items. The 4th item would be the provided line,
* and would be wrapped in `<span class="code-highlight"></span>`. All of the lines
* are processed with highlight_string() as well, so they have basic PHP syntax highlighting
* applied.
* *
* @param string $file Absolute path to a PHP file * @param string $file Absolute path to a PHP file
* @param integer $line Line number to highlight * @param integer $line Line number to highlight
* @param integer $context Number of lines of context to extract above and below $line * @param integer $context Number of lines of context to extract above and below $line
* @return array Set of lines highlighted * @return array Set of lines highlighted
* @see http://php.net/highlight_string
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/ */
public static function excerpt($file, $line, $context = 2) { public static function excerpt($file, $line, $context = 2) {
@ -431,6 +442,20 @@ class Debugger {
/** /**
* Converts a variable to a string for debug output. * Converts a variable to a string for debug output.
* *
* *Note:* The following keys will have their contents replaced with
* `*****`:
*
* - password
* - login
* - host
* - database
* - port
* - prefix
* - schema
*
* This is done to protect database credentials, which could be accidentally
* shown in an error message if CakePHP is deployed in development mode.
*
* @param string $var Variable to convert * @param string $var Variable to convert
* @param integer $recursion * @param integer $recursion
* @return string Variable as a formatted string * @return string Variable as a formatted string

View file

@ -360,7 +360,7 @@ class Folder {
if (@chmod($fullpath, intval($mode, 8))) { if (@chmod($fullpath, intval($mode, 8))) {
$this->__messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode); $this->__messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode);
} else { } else {
$this->__errors[] = __d('cake_deverloper', '%s NOT changed to %s', $fullpath, $mode); $this->__errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode);
} }
} }
} }

View file

@ -122,7 +122,7 @@ class Sanitize {
*/ */
public static function stripWhitespace($str) { public static function stripWhitespace($str) {
$r = preg_replace('/[\n\r\t]+/', '', $str); $r = preg_replace('/[\n\r\t]+/', '', $str);
return preg_replace('/\s{2,}/', ' ', $r); return preg_replace('/\s{2,}/u', ' ', $r);
} }
/** /**

View file

@ -129,27 +129,6 @@ class Set {
return Set::__map($val, $class); return Set::__map($val, $class);
} }
/**
* Get the array value of $array. If $array is null, it will return
* the current array Set holds. If it is an object of type Set, it
* will return its value. If it is another object, its object variables.
* If it is anything else but an array, it will return an array whose first
* element is $array.
*
* @param mixed $array Data from where to get the array.
* @return array Array from $array.
*/
private function __array($array) {
if (empty($array)) {
$array = array();
} elseif (is_object($array)) {
$array = get_object_vars($array);
} elseif (!is_array($array)) {
$array = array($array);
}
return $array;
}
/** /**
* Maps the given value as an object. If $value is an object, * Maps the given value as an object. If $value is an object,
* it returns $value. Otherwise it maps $value as an object of * it returns $value. Otherwise it maps $value as an object of
@ -420,14 +399,9 @@ class Set {
'key' => $key, 'key' => $key,
'item' => array_keys($context['item']), 'item' => array_keys($context['item']),
); );
} elseif (($key === $token || (ctype_digit($token) && $key == $token) || $token === '.')) { } elseif (is_array($context['item'])
$context['trace'][] = $key; && array_key_exists($token, $context['item'])
$matches[] = array( && !(strval($key) === strval($token) && count($tokens) == 1 && $tokens[0] === '.')) {
'trace' => $context['trace'],
'key' => $key,
'item' => $context['item'],
);
} elseif (is_array($context['item']) && array_key_exists($token, $context['item'])) {
$items = $context['item'][$token]; $items = $context['item'][$token];
if (!is_array($items)) { if (!is_array($items)) {
$items = array($items); $items = array($items);
@ -466,6 +440,13 @@ class Set {
'item' => $item, 'item' => $item,
); );
} }
} elseif ($key === $token || (ctype_digit($token) && $key == $token) || $token === '.') {
$context['trace'][] = $key;
$matches[] = array(
'trace' => $context['trace'],
'key' => $key,
'item' => $context['item'],
);
} }
} }
if ($conditions) { if ($conditions) {
@ -586,12 +567,13 @@ class Set {
return $data; return $data;
} }
if (is_object($data)) { if (is_object($data)) {
$data = get_object_vars($data); if (!($data instanceof ArrayAccess || $data instanceof Traversable)) {
$data = get_object_vars($data);
}
} }
if (!is_array($data)) { if (empty($data)) {
return $data; return null;
} }
if (is_string($path) && strpos($path, '{') !== false) { if (is_string($path) && strpos($path, '{') !== false) {
$path = String::tokenize($path, '.', '{', '}'); $path = String::tokenize($path, '.', '{', '}');
} elseif (is_string($path)) { } elseif (is_string($path)) {
@ -898,7 +880,9 @@ class Set {
} }
if (is_object($data)) { if (is_object($data)) {
$data = get_object_vars($data); if (!($data instanceof ArrayAccess || $data instanceof Traversable)) {
$data = get_object_vars($data);
}
} }
if (is_array($path1)) { if (is_array($path1)) {

View file

@ -55,7 +55,7 @@ class CacheHelper extends AppHelper {
public function afterRender($viewFile) { public function afterRender($viewFile) {
$caching = (($this->_View->cacheAction != false)) && (Configure::read('Cache.check') === true); $caching = (($this->_View->cacheAction != false)) && (Configure::read('Cache.check') === true);
if ($caching) { if ($caching) {
$this->cache($viewFile, $this->_View->output, false); $this->_View->output = $this->cache($viewFile, $this->_View->output, false);
} }
} }
@ -68,11 +68,18 @@ class CacheHelper extends AppHelper {
public function afterLayout($layoutFile) { public function afterLayout($layoutFile) {
$caching = (($this->_View->cacheAction != false)) && (Configure::read('Cache.check') === true); $caching = (($this->_View->cacheAction != false)) && (Configure::read('Cache.check') === true);
if ($caching) { if ($caching) {
$this->cache($layoutFile, $this->_View->output, true); $this->_View->output = $this->cache($layoutFile, $this->_View->output, true);
} }
$this->_View->output = preg_replace('/<!--\/?nocache-->/', '', $this->_View->output); $this->_View->output = preg_replace('/<!--\/?nocache-->/', '', $this->_View->output);
} }
/**
* Counter used for counting nocache section tags.
*
* @var integer
*/
var $_counter = 0;
/** /**
* Main method used to cache a view * Main method used to cache a view
* *
@ -120,10 +127,13 @@ class CacheHelper extends AppHelper {
} }
if ($cacheTime != '' && $cacheTime > 0) { if ($cacheTime != '' && $cacheTime > 0) {
$out = preg_replace_callback('/<!--nocache-->/', array($this, '_replaceSection'), $out);
$this->_parseFile($file, $out); $this->_parseFile($file, $out);
if ($cache === true) { if ($cache === true) {
$cached = $this->_parseOutput($out); $cached = $this->_parseOutput($out);
$this->_writeFile($cached, $cacheTime, $useCallbacks); $this->_writeFile($cached, $cacheTime, $useCallbacks);
$out = $this->_stripTags($out);
} }
return $out; return $out;
} else { } else {
@ -144,7 +154,7 @@ class CacheHelper extends AppHelper {
} elseif ($file = fileExistsInPath($file)) { } elseif ($file = fileExistsInPath($file)) {
$file = file_get_contents($file); $file = file_get_contents($file);
} }
preg_match_all('/(<!--nocache-->(?<=<!--nocache-->)[\\s\\S]*?(?=<!--\/nocache-->)<!--\/nocache-->)/i', $cache, $outputResult, PREG_PATTERN_ORDER); preg_match_all('/(<!--nocache:\d{3}-->(?<=<!--nocache:\d{3}-->)[\\s\\S]*?(?=<!--\/nocache-->)<!--\/nocache-->)/i', $cache, $outputResult, PREG_PATTERN_ORDER);
preg_match_all('/(?<=<!--nocache-->)([\\s\\S]*?)(?=<!--\/nocache-->)/i', $file, $fileResult, PREG_PATTERN_ORDER); preg_match_all('/(?<=<!--nocache-->)([\\s\\S]*?)(?=<!--\/nocache-->)/i', $file, $fileResult, PREG_PATTERN_ORDER);
$fileResult = $fileResult[0]; $fileResult = $fileResult[0];
$outputResult = $outputResult[0]; $outputResult = $outputResult[0];
@ -171,6 +181,30 @@ class CacheHelper extends AppHelper {
} }
} }
/**
* Munges the output from a view with cache tags, and numbers the sections.
* This helps solve issues with empty/duplicate content.
*
* @param string $content The content to munge.
* @return string The content with cake:nocache tags replaced.
*/
function _replaceSection($matches) {
$this->_counter += 1;
return sprintf('<!--nocache:%03d-->', $this->_counter);
}
/**
* Strip cake:nocache tags from a string. Since View::render()
* only removes un-numbered nocache tags, remove all the numbered ones.
* This is the complement to _replaceSection.
*
* @param string $content String to remove tags from.
* @return string String with tags removed.
*/
function _stripTags($content) {
return preg_replace('#<!--/?nocache(\:\d{3})?-->#', '', $content);
}
/** /**
* Parse the output and replace cache tags * Parse the output and replace cache tags
* *

View file

@ -198,7 +198,7 @@ class FormHelper extends AppHelper {
} }
if ($key === 'key') { if ($key === 'key') {
return $this->fieldset[$model]['key']; return $this->fieldset[$model]['key'] = $object->primaryKey;
} }
if ($key === 'fields') { if ($key === 'fields') {
@ -437,18 +437,7 @@ class FormHelper extends AppHelper {
$htmlAttributes = array_merge($options, $htmlAttributes); $htmlAttributes = array_merge($options, $htmlAttributes);
$this->fields = array(); $this->fields = array();
if (!empty($this->request->params['_Token'])) { $append .= $this->_csrfField();
$append .= $this->hidden('_Token.key', array(
'value' => $this->request->params['_Token']['key'], 'id' => 'Token' . mt_rand(),
'secure' => self::SECURE_SKIP
));
if (!empty($this->request['_Token']['unlockedFields'])) {
foreach ((array)$this->request['_Token']['unlockedFields'] as $unlocked) {
$this->_unlockedFields[] = $unlocked;
}
}
}
if (!empty($append)) { if (!empty($append)) {
$append = $this->Html->useTag('block', ' style="display:none;"', $append); $append = $this->Html->useTag('block', ' style="display:none;"', $append);
@ -460,6 +449,27 @@ class FormHelper extends AppHelper {
return $this->Html->useTag('form', $action, $htmlAttributes) . $append; return $this->Html->useTag('form', $action, $htmlAttributes) . $append;
} }
/**
* Return a CSRF input if the _Token is present.
* Used to secure forms in conjunction with SecurityComponent
*
* @return string
*/
protected function _csrfField() {
if (empty($this->request->params['_Token'])) {
return '';
}
if (!empty($this->request['_Token']['unlockedFields'])) {
foreach ((array)$this->request['_Token']['unlockedFields'] as $unlocked) {
$this->_unlockedFields[] = $unlocked;
}
}
return $this->hidden('_Token.key', array(
'value' => $this->request->params['_Token']['key'], 'id' => 'Token' . mt_rand(),
'secure' => self::SECURE_SKIP
));
}
/** /**
* Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
* input fields where appropriate. * input fields where appropriate.
@ -911,7 +921,8 @@ class FormHelper extends AppHelper {
'string' => 'text', 'datetime' => 'datetime', 'string' => 'text', 'datetime' => 'datetime',
'boolean' => 'checkbox', 'timestamp' => 'datetime', 'boolean' => 'checkbox', 'timestamp' => 'datetime',
'text' => 'textarea', 'time' => 'time', 'text' => 'textarea', 'time' => 'time',
'date' => 'date', 'float' => 'text' 'date' => 'date', 'float' => 'number',
'integer' => 'number'
); );
if (isset($this->map[$type])) { if (isset($this->map[$type])) {
@ -1065,6 +1076,9 @@ class FormHelper extends AppHelper {
case 'textarea': case 'textarea':
$input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6')); $input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
break; break;
case 'url':
$input = $this->text($fieldName, array('type' => 'url') + $options);
break;
default: default:
$input = $this->{$type}($fieldName, $options); $input = $this->{$type}($fieldName, $options);
} }
@ -1497,13 +1511,18 @@ class FormHelper extends AppHelper {
$formName = uniqid('post_'); $formName = uniqid('post_');
$formUrl = $this->url($url); $formUrl = $this->url($url);
$out = $this->Html->useTag('form', $formUrl, array('name' => $formName, 'id' => $formName, 'style' => 'display:none;', 'method' => 'post')); $out = $this->Html->useTag('form', $formUrl, array('name' => $formName, 'id' => $formName, 'style' => 'display:none;', 'method' => 'post'));
$out .= $this->Html->useTag('block', ' style="display:none;"', $this->Html->useTag('hidden', '_method', ' value="POST"')); $out .= $this->Html->useTag('hidden', '_method', ' value="POST"');
$out .= $this->_csrfField();
$fields = array();
if (isset($options['data']) && is_array($options['data'])) { if (isset($options['data']) && is_array($options['data'])) {
foreach ($options['data'] as $key => $value) { foreach ($options['data'] as $key => $value) {
$fields[$key] = $value;
$out .= $this->hidden($key, array('value' => $value, 'id' => false)); $out .= $this->hidden($key, array('value' => $value, 'id' => false));
} }
unset($options['data']); unset($options['data']);
} }
$out .= $this->secure($fields);
$out .= $this->Html->useTag('formend'); $out .= $this->Html->useTag('formend');
$url = '#'; $url = '#';
@ -2063,6 +2082,8 @@ class FormHelper extends AppHelper {
if (($time[0] > 12) && $timeFormat == '12') { if (($time[0] > 12) && $timeFormat == '12') {
$time[0] = $time[0] - 12; $time[0] = $time[0] - 12;
$meridian = 'pm'; $meridian = 'pm';
} elseif ($time[0] == '12' && $timeFormat == '12') {
$meridian = 'pm';
} elseif ($time[0] == '00' && $timeFormat == '12') { } elseif ($time[0] == '00' && $timeFormat == '12') {
$time[0] = 12; $time[0] = 12;
} elseif ($time[0] >= 12) { } elseif ($time[0] >= 12) {

View file

@ -152,7 +152,16 @@ class HtmlHelper extends AppHelper {
); );
/** /**
* Default Constructor * Constructor
*
* ### Settings
*
* - `configFile` A file containing an array of tags you wish to redefine.
*
* ### Customizing tag sets
*
* Using the `configFile` option you can redefine the tag HtmlHelper will use.
* The file named should be compatible with HtmlHelper::loadConfig().
* *
* @param View $View The View this helper is being attached to. * @param View $View The View this helper is being attached to.
* @param array $settings Configuration settings for the helper. * @param array $settings Configuration settings for the helper.
@ -349,6 +358,20 @@ class HtmlHelper extends AppHelper {
/** /**
* Creates a link element for CSS stylesheets. * Creates a link element for CSS stylesheets.
* *
* ### Usage
*
* Include one CSS file:
*
* `echo $this->Html->css('styles.css');`
*
* Include multiple CSS files:
*
* `echo $this->Html->css(array('one.css', 'two.css'));`
*
* Add the stylesheet to the `$scripts_for_layout` layout var:
*
* `$this->Html->css('styles.css', null, array('inline' => false));`
*
* ### Options * ### Options
* *
* - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true * - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true
@ -418,7 +441,20 @@ class HtmlHelper extends AppHelper {
* If the filename is prefixed with "/", the path will be relative to the base path of your * If the filename is prefixed with "/", the path will be relative to the base path of your
* application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js. * application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js.
* *
* Can include one or many Javascript files. *
* ### Usage
*
* Include one script file:
*
* `echo $this->Html->script('styles.js');`
*
* Include multiple script files:
*
* `echo $this->Html->script(array('one.js', 'two.js'));`
*
* Add the script file to the `$scripts_for_layout` layout var:
*
* `$this->Html->script('styles.js', null, array('inline' => false));`
* *
* ### Options * ### Options
* *
@ -900,7 +936,39 @@ class HtmlHelper extends AppHelper {
} }
/** /**
* Load Html configs * Load Html tag configuration.
*
* Loads a file from APP/Config that contains tag data. By default the file is expected
* to be compatible with PhpReader:
*
* `$this->Html->loadConfig('tags.php');`
*
* tags.php could look like:
*
* {{{
* $tags = array(
* 'meta' => '<meta %s>'
* );
* }}}
*
* If you wish to store tag definitions in another format you can give an array
* containing the file name, and reader class name:
*
* `$this->Html->loadConfig(array('tags.ini', 'ini'));`
*
* Its expected that the `tags` index will exist from any configuration file that is read.
* You can also specify the path to read the configuration file from, if APP/Config is not
* where the file is.
*
* `$this->Html->loadConfig('tags.php', APP . 'Lib' . DS);`
*
* Configuration files can define the following sections:
*
* - `tags` The tags to replace.
* - `minimizedAttributes` The attributes that are represented like `disabled="disabled"`
* - `docTypes` Additional doctypes to use.
* - `attributeFormat` Format for long attributes e.g. `'%s="%s"'`
* - `minimizedAttributeFormat` Format for minimized attributes e.g. `'%s="%s"'`
* *
* @param mixed $configFile String with the config file (load using PhpReader) or an array with file and reader name * @param mixed $configFile String with the config file (load using PhpReader) or an array with file and reader name
* @param string $path Path with config file * @param string $path Path with config file

View file

@ -461,7 +461,7 @@ class PaginatorHelper extends AppHelper {
$url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url); $url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url);
if ($this->{$check}($model)) { if ($this->{$check}($model)) {
return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'class')))); return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape'))), compact('class'));
} else { } else {
unset($options['rel']); unset($options['rel']);
return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class'))); return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));
@ -644,7 +644,7 @@ class PaginatorHelper extends AppHelper {
} }
$defaults = array( $defaults = array(
'tag' => 'span', 'before' => null, 'after' => null, 'model' => $this->defaultModel(), 'tag' => 'span', 'before' => null, 'after' => null, 'model' => $this->defaultModel(), 'class' => null,
'modulus' => '8', 'separator' => ' | ', 'first' => null, 'last' => null, 'ellipsis' => '...', 'modulus' => '8', 'separator' => ' | ', 'first' => null, 'last' => null, 'ellipsis' => '...',
); );
$options += $defaults; $options += $defaults;
@ -659,7 +659,7 @@ class PaginatorHelper extends AppHelper {
extract($options); extract($options);
unset($options['tag'], $options['before'], $options['after'], $options['model'], unset($options['tag'], $options['before'], $options['after'], $options['model'],
$options['modulus'], $options['separator'], $options['first'], $options['last'], $options['modulus'], $options['separator'], $options['first'], $options['last'],
$options['ellipsis'] $options['ellipsis'], $options['class']
); );
$out = ''; $out = '';
@ -680,32 +680,36 @@ class PaginatorHelper extends AppHelper {
if ($first && $start > 1) { if ($first && $start > 1) {
$offset = ($start <= (int)$first) ? $start - 1 : $first; $offset = ($start <= (int)$first) ? $start - 1 : $first;
if ($offset < $start - 1) { if ($offset < $start - 1) {
$out .= $this->first($offset, array('tag' => $tag, 'separator' => $separator, 'ellipsis' => $ellipsis)); $out .= $this->first($offset, compact('tag', 'separator', 'ellipsis', 'class'));
} else { } else {
$out .= $this->first($offset, array('tag' => $tag, 'after' => $separator, 'separator' => $separator)); $out .= $this->first($offset, compact('tag', 'separator', 'class') + array('after' => $separator));
} }
} }
$out .= $before; $out .= $before;
for ($i = $start; $i < $params['page']; $i++) { for ($i = $start; $i < $params['page']; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)) $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options), compact('class'))
. $separator; . $separator;
} }
$out .= $this->Html->tag($tag, $params['page'], array('class' => 'current')); $currentClass = 'current';
if ($class) {
$currentClass .= ' ' . $class;
}
$out .= $this->Html->tag($tag, $params['page'], array('class' => $currentClass));
if ($i != $params['pageCount']) { if ($i != $params['pageCount']) {
$out .= $separator; $out .= $separator;
} }
$start = $params['page'] + 1; $start = $params['page'] + 1;
for ($i = $start; $i < $end; $i++) { for ($i = $start; $i < $end; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)) $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options), compact('class'))
. $separator; . $separator;
} }
if ($end != $params['page']) { if ($end != $params['page']) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $end), $options)); $out .= $this->Html->tag($tag, $this->link($i, array('page' => $end), $options), compact('class'));
} }
$out .= $after; $out .= $after;
@ -713,9 +717,9 @@ class PaginatorHelper extends AppHelper {
if ($last && $end < $params['pageCount']) { if ($last && $end < $params['pageCount']) {
$offset = ($params['pageCount'] < $end + (int)$last) ? $params['pageCount'] - $end : $last; $offset = ($params['pageCount'] < $end + (int)$last) ? $params['pageCount'] - $end : $last;
if ($offset <= $last && $params['pageCount'] - $end > $offset) { if ($offset <= $last && $params['pageCount'] - $end > $offset) {
$out .= $this->last($offset, array('tag' => $tag, 'separator' => $separator, 'ellipsis' => $ellipsis)); $out .= $this->last($offset, compact('tag', 'separator', 'ellipsis', 'class'));
} else { } else {
$out .= $this->last($offset, array('tag' => $tag, 'before' => $separator, 'separator' => $separator)); $out .= $this->last($offset, compact('tag', 'separator', 'class') + array('before' => $separator));
} }
} }
@ -724,9 +728,13 @@ class PaginatorHelper extends AppHelper {
for ($i = 1; $i <= $params['pageCount']; $i++) { for ($i = 1; $i <= $params['pageCount']; $i++) {
if ($i == $params['page']) { if ($i == $params['page']) {
$out .= $this->Html->tag($tag, $i, array('class' => 'current')); $currentClass = 'current';
if ($class) {
$currentClass .= ' ' . $class;
}
$out .= $this->Html->tag($tag, $i, array('class' => $currentClass));
} else { } else {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)); $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options), compact('class'));
} }
if ($i != $params['pageCount']) { if ($i != $params['pageCount']) {
$out .= $separator; $out .= $separator;
@ -771,7 +779,8 @@ class PaginatorHelper extends AppHelper {
'after' => null, 'after' => null,
'model' => $this->defaultModel(), 'model' => $this->defaultModel(),
'separator' => ' | ', 'separator' => ' | ',
'ellipsis' => '...' 'ellipsis' => '...',
'class' => null
), ),
(array)$options); (array)$options);
@ -782,7 +791,7 @@ class PaginatorHelper extends AppHelper {
return false; return false;
} }
extract($options); extract($options);
unset($options['tag'], $options['after'], $options['model'], $options['separator'], $options['ellipsis']); unset($options['tag'], $options['after'], $options['model'], $options['separator'], $options['ellipsis'], $options['class']);
$out = ''; $out = '';
@ -791,7 +800,7 @@ class PaginatorHelper extends AppHelper {
$after = $ellipsis; $after = $ellipsis;
} }
for ($i = 1; $i <= $first; $i++) { for ($i = 1; $i <= $first; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)); $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options), compact('class'));
if ($i != $first) { if ($i != $first) {
$out .= $separator; $out .= $separator;
} }
@ -799,7 +808,7 @@ class PaginatorHelper extends AppHelper {
$out .= $after; $out .= $after;
} elseif ($params['page'] > 1 && is_string($first)) { } elseif ($params['page'] > 1 && is_string($first)) {
$options += array('rel' => 'first'); $options += array('rel' => 'first');
$out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options)) $out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options), compact('class'))
. $after; . $after;
} }
return $out; return $out;
@ -836,6 +845,7 @@ class PaginatorHelper extends AppHelper {
'model' => $this->defaultModel(), 'model' => $this->defaultModel(),
'separator' => ' | ', 'separator' => ' | ',
'ellipsis' => '...', 'ellipsis' => '...',
'class' => null
), ),
(array)$options); (array)$options);
@ -847,7 +857,7 @@ class PaginatorHelper extends AppHelper {
} }
extract($options); extract($options);
unset($options['tag'], $options['before'], $options['model'], $options['separator'], $options['ellipsis']); unset($options['tag'], $options['before'], $options['model'], $options['separator'], $options['ellipsis'], $options['class']);
$out = ''; $out = '';
$lower = $params['pageCount'] - $last + 1; $lower = $params['pageCount'] - $last + 1;
@ -857,7 +867,7 @@ class PaginatorHelper extends AppHelper {
$before = $ellipsis; $before = $ellipsis;
} }
for ($i = $lower; $i <= $params['pageCount']; $i++) { for ($i = $lower; $i <= $params['pageCount']; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)); $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options), compact('class'));
if ($i != $params['pageCount']) { if ($i != $params['pageCount']) {
$out .= $separator; $out .= $separator;
} }
@ -866,8 +876,8 @@ class PaginatorHelper extends AppHelper {
} elseif ($params['page'] < $params['pageCount'] && is_string($last)) { } elseif ($params['page'] < $params['pageCount'] && is_string($last)) {
$options += array('rel' => 'last'); $options += array('rel' => 'last');
$out = $before . $this->Html->tag( $out = $before . $this->Html->tag(
$tag, $this->link($last, array('page' => $params['pageCount']), $options $tag, $this->link($last, array('page' => $params['pageCount']), $options), compact('class')
)); );
} }
return $out; return $out;
} }

View file

@ -23,18 +23,17 @@ if (!defined('E_DEPRECATED')) {
} }
error_reporting(E_ALL & ~E_DEPRECATED); error_reporting(E_ALL & ~E_DEPRECATED);
/** if (!defined('CAKE_CORE_INCLUDE_PATH')) {
* If the index.php file is used instead of an .htaccess file define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(__FILE__)));
* or if the user can not set the web root to use the public }
* directory we will define ROOT there, otherwise we set it
* here. if (!defined('CORE_PATH')) {
*/ define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
if (!defined('ROOT')) { }
define('ROOT', '../');
} if (!defined('WEBROOT_DIR')) {
if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', 'webroot');
define('WEBROOT_DIR', 'webroot'); }
}
/** /**
* Path to the cake directory. * Path to the cake directory.