Merge branch '2.4' making 2.4 into the master branch.

Conflicts:
	lib/Cake/VERSION.txt
This commit is contained in:
mark_story 2013-08-30 16:02:42 -04:00
commit 4f237e6a93
184 changed files with 5267 additions and 1671 deletions

View file

@ -34,6 +34,6 @@ Get Support!
[Lighthouse](https://cakephp.lighthouseapp.com/) - Got issues? Please tell us!
[![Bake Status](https://secure.travis-ci.org/cakephp/cakephp.png?branch=master)](http://travis-ci.org/cakephp/cakephp)
[![Bake Status](https://secure.travis-ci.org/cakephp/cakephp.png?branch=2.4)](http://travis-ci.org/cakephp/cakephp)
![Cake Power](https://raw.github.com/cakephp/cakephp/master/lib/Cake/Console/Templates/skel/webroot/img/cake.power.gif)

View file

@ -28,8 +28,6 @@
*/
class DbAclSchema extends CakeSchema {
public $name = 'DbAcl';
public function before($event = array()) {
return true;
}

View file

@ -70,6 +70,9 @@
* - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
* should place the file for that class in app/Lib/Error. This class needs to implement a render method.
* - `log` - boolean - Should Exceptions be logged?
* - `skipLog` - array - list of exceptions to skip for logging. Exceptions that
* extend one of the listed exceptions will also be skipped for logging.
* Example: `'skipLog' => array('NotFoundException', 'UnauthorizedException')`
*
* @see ErrorHandler for more information on exception handling and configuration.
*/
@ -105,6 +108,33 @@
*/
//Configure::write('App.baseUrl', env('SCRIPT_NAME'));
/**
* To configure CakePHP to use a particular domain URL
* for any URL generation inside the application, set the following
* configuration variable to the http(s) address to your domain. This
* will override the automatic detection of full base URL and can be
* useful when generating links from the CLI (e.g. sending emails)
*/
//Configure::write('App.fullBaseUrl', 'http://example.com');
/**
* Web path to the public images directory under webroot.
* If not set defaults to 'img/'
*/
//Configure::write('App.imageBaseUrl', 'img/');
/**
* Web path to the CSS files directory under webroot.
* If not set defaults to 'css/'
*/
//Configure::write('App.cssBaseUrl', 'css/');
/**
* Web path to the js files directory under webroot.
* If not set defaults to 'js/'
*/
//Configure::write('App.jsBaseUrl', 'js/');
/**
* Uncomment the define below to use CakePHP prefix routes.
*

View file

@ -52,6 +52,12 @@
*
* unix_socket =>
* For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port`
*
* settings =>
* Array of key/value pairs, on connection it executes SET statements for each pair
* For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html
* For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html
* For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx
*/
class DATABASE_CONFIG {

View file

@ -31,13 +31,6 @@ App::uses('AppController', 'Controller');
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/**
* This controller does not use a model
*
@ -50,6 +43,8 @@ class PagesController extends AppController {
*
* @param mixed What page to display
* @return void
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
*/
public function display() {
$path = func_get_args();
@ -70,6 +65,14 @@ class PagesController extends AppController {
$title_for_layout = Inflector::humanize($path[$count - 1]);
}
$this->set(compact('page', 'subpage', 'title_for_layout'));
$this->render(implode('/', $path));
try {
$this->render(implode('/', $path));
} catch (MissingViewException $e) {
if (Configure::read('debug')) {
throw $e;
}
throw new NotFoundException();
}
}
}

View file

@ -2,5 +2,5 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
RewriteRule ^ index.php [L]
</IfModule>

View file

@ -51,6 +51,13 @@ class Cache {
*/
protected static $_config = array();
/**
* Group to Config mapping
*
* @var array
*/
protected static $_groups = array();
/**
* Whether to reset the settings with the next call to Cache::set();
*
@ -130,6 +137,14 @@ class Cache {
return false;
}
if (!empty(self::$_config[$name]['groups'])) {
foreach (self::$_config[$name]['groups'] as $group) {
self::$_groups[$group][] = $name;
sort(self::$_groups[$group]);
self::$_groups[$group] = array_unique(self::$_groups[$group]);
}
}
$engine = self::$_config[$name]['engine'];
if (!isset(self::$_engines[$name])) {
@ -159,7 +174,7 @@ class Cache {
}
$cacheClass = $class . 'Engine';
if (!is_subclass_of($cacheClass, 'CacheEngine')) {
throw new CacheException(__d('cake_dev', 'Cache engines must use CacheEngine as a base class.'));
throw new CacheException(__d('cake_dev', 'Cache engines must use %s as a base class.', 'CacheEngine'));
}
self::$_engines[$name] = new $cacheClass();
if (!self::$_engines[$name]->init($config)) {
@ -498,4 +513,33 @@ class Cache {
return array();
}
/**
* Retrieve group names to config mapping.
*
* {{{
* Cache::config('daily', array(
* 'duration' => '1 day', 'groups' => array('posts')
* ));
* Cache::config('weekly', array(
* 'duration' => '1 week', 'groups' => array('posts', 'archive')
* ));
* $configs = Cache::groupConfigs('posts');
* }}}
*
* $config will equal to `array('posts' => array('daily', 'weekly'))`
*
* @param string $group group name or null to retrieve all group mappings
* @return array map of group and all configuration that has the same group
* @throws CacheException
*/
public static function groupConfigs($group = null) {
if ($group == null) {
return self::$_groups;
}
if (isset(self::$_groups[$group])) {
return array($group => self::$_groups[$group]);
}
throw new CacheException(__d('cake_dev', 'Invalid cache group %s', $group));
}
}

View file

@ -336,7 +336,7 @@ class FileEngine extends CacheEngine {
$dir = $this->settings['path'] . $groups;
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
mkdir($dir, 0775, true);
}
$path = new SplFileInfo($dir . $key);
@ -369,6 +369,12 @@ class FileEngine extends CacheEngine {
*/
protected function _active() {
$dir = new SplFileInfo($this->settings['path']);
if (Configure::read('debug')) {
$path = $dir->getPathname();
if (!is_dir($path)) {
mkdir($path, 0775, true);
}
}
if ($this->_init && !($dir->isDir() && $dir->isWritable())) {
$this->_init = false;
trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING);

View file

@ -165,7 +165,7 @@ class MemcacheEngine extends CacheEngine {
public function increment($key, $offset = 1) {
if ($this->settings['compress']) {
throw new CacheException(
__d('cake_dev', 'Method increment() not implemented for compressed cache in %s', __CLASS__)
__d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'increment()', __CLASS__)
);
}
return $this->_Memcache->increment($key, $offset);
@ -182,7 +182,7 @@ class MemcacheEngine extends CacheEngine {
public function decrement($key, $offset = 1) {
if ($this->settings['compress']) {
throw new CacheException(
__d('cake_dev', 'Method decrement() not implemented for compressed cache in %s', __CLASS__)
__d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'decrement()', __CLASS__)
);
}
return $this->_Memcache->decrement($key, $offset);

View file

@ -71,7 +71,7 @@ class PhpReader implements ConfigReaderInterface {
include $file;
if (!isset($config)) {
throw new ConfigureException(__d('cake_dev', 'No variable $config found in %s', $file));
throw new ConfigureException(__d('cake_dev', 'No variable %s found in %s', '$config', $file));
}
return $config;
}

View file

@ -245,6 +245,9 @@ class BakeShell extends AppShell {
'help' => __d('cake_console', 'Database connection to use in conjunction with `bake all`.'),
'short' => 'c',
'default' => 'default'
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
));
}

View file

@ -19,6 +19,7 @@ App::uses('AppShell', 'Console/Command');
* Provides a very basic 'interactive' console for CakePHP apps.
*
* @package Cake.Console.Command
* @deprecated Deprecated since version 2.4, will be removed in 3.0
*/
class ConsoleShell extends AppShell {
@ -43,6 +44,35 @@ class ConsoleShell extends AppShell {
*/
public $models = array();
/**
* _finished
*
* This shell is perpetual, setting this property to true exits the process
*
* @var mixed
*/
protected $_finished = false;
/**
* _methodPatterns
*
* @var array
*/
protected $_methodPatterns = array(
'help' => '/^(help|\?)/',
'_exit' => '/^(quit|exit)/',
'_models' => '/^models/i',
'_bind' => '/^(\w+) bind (\w+) (\w+)/',
'_unbind' => '/^(\w+) unbind (\w+) (\w+)/',
'_find' => '/.+->find/',
'_save' => '/.+->save/',
'_columns' => '/^(\w+) columns/',
'_routesReload' => '/^routes\s+reload/i',
'_routesShow' => '/^routes\s+show/i',
'_routeToString' => '/^route\s+(\(.*\))$/i',
'_routeToArray' => '/^route\s+(.*)$/i',
);
/**
* Override startup of the Shell
*
@ -74,6 +104,11 @@ class ConsoleShell extends AppShell {
}
}
/**
* getOptionParser
*
* @return void
*/
public function getOptionParser() {
$description = array(
'The interactive console is a tool for testing parts of your',
@ -163,191 +198,289 @@ class ConsoleShell extends AppShell {
* @return void
*/
public function main($command = null) {
while (true) {
$this->_finished = false;
while (!$this->_finished) {
if (empty($command)) {
$command = trim($this->in(''));
}
switch (true) {
case $command == 'help':
$this->help();
break;
case $command == 'quit':
case $command == 'exit':
return true;
case $command == 'models':
$this->out(__d('cake_console', 'Model classes:'));
$this->hr();
foreach ($this->models as $model) {
$this->out(" - {$model}");
}
break;
case preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp):
foreach ($tmp as $data) {
$data = strip_tags($data);
$data = str_replace($this->badCommandChars, "", $data);
}
$modelA = $tmp[1];
$association = $tmp[2];
$modelB = $tmp[3];
$method = $this->_method($command);
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) {
$this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false);
$this->out(__d('cake_console', "Created %s association between %s and %s",
$association, $modelA, $modelB));
} else {
$this->out(__d('cake_console', "Please verify you are using valid models and association types"));
}
break;
case preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp):
foreach ($tmp as $data) {
$data = strip_tags($data);
$data = str_replace($this->badCommandChars, "", $data);
}
$modelA = $tmp[1];
$association = $tmp[2];
$modelB = $tmp[3];
// Verify that there is actually an association to unbind
$currentAssociations = $this->{$modelA}->getAssociated();
$validCurrentAssociation = false;
foreach ($currentAssociations as $model => $currentAssociation) {
if ($model == $modelB && $association == $currentAssociation) {
$validCurrentAssociation = true;
}
}
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) {
$this->{$modelA}->unbindModel(array($association => array($modelB)));
$this->out(__d('cake_console', "Removed %s association between %s and %s",
$association, $modelA, $modelB));
} else {
$this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types"));
}
break;
case (strpos($command, "->find") > 0):
// Remove any bad info
$command = strip_tags($command);
$command = str_replace($this->badCommandChars, "", $command);
// Do we have a valid model?
list($modelToCheck, $tmp) = explode('->', $command);
if ($this->_isValidModel($modelToCheck)) {
$findCommand = "\$data = \$this->$command;";
//@codingStandardsIgnoreStart
@eval($findCommand);
//@codingStandardsIgnoreEnd
if (is_array($data)) {
foreach ($data as $idx => $results) {
if (is_numeric($idx)) { // findAll() output
foreach ($results as $modelName => $result) {
$this->out("$modelName");
foreach ($result as $field => $value) {
if (is_array($value)) {
foreach ($value as $field2 => $value2) {
$this->out("\t$field2: $value2");
}
$this->out();
} else {
$this->out("\t$field: $value");
}
}
}
} else { // find() output
$this->out($idx);
foreach ($results as $field => $value) {
if (is_array($value)) {
foreach ($value as $field2 => $value2) {
$this->out("\t$field2: $value2");
}
$this->out();
} else {
$this->out("\t$field: $value");
}
}
}
}
} else {
$this->out();
$this->out(__d('cake_console', "No result set found"));
}
} else {
$this->out(__d('cake_console', "%s is not a valid model", $modelToCheck));
}
break;
case (strpos($command, '->save') > 0):
// Validate the model we're trying to save here
$command = strip_tags($command);
$command = str_replace($this->badCommandChars, "", $command);
list($modelToSave, $tmp) = explode("->", $command);
if ($this->_isValidModel($modelToSave)) {
// Extract the array of data we are trying to build
list(, $data) = explode("->save", $command);
$data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data);
$saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));";
//@codingStandardsIgnoreStart
@eval($saveCommand);
//@codingStandardsIgnoreEnd
$this->out(__d('cake_console', 'Saved record for %s', $modelToSave));
}
break;
case preg_match("/^(\w+) columns/", $command, $tmp):
$modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1]));
if ($this->_isValidModel($modelToCheck)) {
// Get the column info for this model
$fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
//@codingStandardsIgnoreStart
@eval($fieldsCommand);
//@codingStandardsIgnoreEnd
if (is_array($data)) {
foreach ($data as $field => $type) {
$this->out("\t{$field}: {$type}");
}
}
} else {
$this->out(__d('cake_console', "Please verify that you selected a valid model"));
}
break;
case preg_match("/^routes\s+reload/i", $command, $tmp):
if (!$this->_loadRoutes()) {
$this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors."));
break;
}
$this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes)));
break;
case preg_match("/^routes\s+show/i", $command, $tmp):
$this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true));
break;
case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true):
//@codingStandardsIgnoreStart
if ($url = eval('return array' . $tmp[1] . ';')) {
//@codingStandardsIgnoreEnd
$this->out(Router::url($url));
}
break;
case preg_match("/^route\s+(.*)/i", $command, $tmp):
$this->out(var_export(Router::parse($tmp[1]), true));
break;
default:
$this->out(__d('cake_console', "Invalid command"));
$this->out();
if ($method) {
$this->$method($command);
} else {
$this->out(__d('cake_console', "Invalid command"));
$this->out();
}
$command = '';
}
}
/**
* Determine the method to process the current command
*
* @param string $command
* @return string or false
*/
protected function _method($command) {
foreach ($this->_methodPatterns as $method => $pattern) {
if (preg_match($pattern, $command)) {
return $method;
}
}
return false;
}
/**
* Set the finiished property so that the loop in main method ends
*
* @return void
*/
protected function _exit() {
$this->_finished = true;
}
/**
* List all models
*
* @return void
*/
protected function _models() {
$this->out(__d('cake_console', 'Model classes:'));
$this->hr();
foreach ($this->models as $model) {
$this->out(" - {$model}");
}
}
/**
* Bind an association
*
* @param mixed $command
* @return void
*/
protected function _bind($command) {
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
foreach ($tmp as $data) {
$data = strip_tags($data);
$data = str_replace($this->badCommandChars, "", $data);
}
$modelA = $tmp[1];
$association = $tmp[2];
$modelB = $tmp[3];
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) {
$this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false);
$this->out(__d('cake_console', "Created %s association between %s and %s",
$association, $modelA, $modelB));
} else {
$this->out(__d('cake_console', "Please verify you are using valid models and association types"));
}
}
/**
* Unbind an association
*
* @param mixed $command
* @return void
*/
protected function _unbind($command) {
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
foreach ($tmp as $data) {
$data = strip_tags($data);
$data = str_replace($this->badCommandChars, "", $data);
}
$modelA = $tmp[1];
$association = $tmp[2];
$modelB = $tmp[3];
// Verify that there is actually an association to unbind
$currentAssociations = $this->{$modelA}->getAssociated();
$validCurrentAssociation = false;
foreach ($currentAssociations as $model => $currentAssociation) {
if ($model == $modelB && $association == $currentAssociation) {
$validCurrentAssociation = true;
}
}
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) {
$this->{$modelA}->unbindModel(array($association => array($modelB)));
$this->out(__d('cake_console', "Removed %s association between %s and %s",
$association, $modelA, $modelB));
} else {
$this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types"));
}
}
/**
* Perform a find
*
* @param mixed $command
* @return void
*/
protected function _find($command) {
$command = strip_tags($command);
$command = str_replace($this->badCommandChars, "", $command);
// Do we have a valid model?
list($modelToCheck, $tmp) = explode('->', $command);
if ($this->_isValidModel($modelToCheck)) {
$findCommand = "\$data = \$this->$command;";
//@codingStandardsIgnoreStart
@eval($findCommand);
//@codingStandardsIgnoreEnd
if (is_array($data)) {
foreach ($data as $idx => $results) {
if (is_numeric($idx)) { // findAll() output
foreach ($results as $modelName => $result) {
$this->out("$modelName");
foreach ($result as $field => $value) {
if (is_array($value)) {
foreach ($value as $field2 => $value2) {
$this->out("\t$field2: $value2");
}
$this->out();
} else {
$this->out("\t$field: $value");
}
}
}
} else { // find() output
$this->out($idx);
foreach ($results as $field => $value) {
if (is_array($value)) {
foreach ($value as $field2 => $value2) {
$this->out("\t$field2: $value2");
}
$this->out();
} else {
$this->out("\t$field: $value");
}
}
}
}
} else {
$this->out();
$this->out(__d('cake_console', "No result set found"));
}
} else {
$this->out(__d('cake_console', "%s is not a valid model", $modelToCheck));
}
}
/**
* Save a record
*
* @param mixed $command
* @return void
*/
protected function _save($command) {
// Validate the model we're trying to save here
$command = strip_tags($command);
$command = str_replace($this->badCommandChars, "", $command);
list($modelToSave, $tmp) = explode("->", $command);
if ($this->_isValidModel($modelToSave)) {
// Extract the array of data we are trying to build
list(, $data) = explode("->save", $command);
$data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data);
$saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));";
//@codingStandardsIgnoreStart
@eval($saveCommand);
//@codingStandardsIgnoreEnd
$this->out(__d('cake_console', 'Saved record for %s', $modelToSave));
}
}
/**
* Show the columns for a model
*
* @param mixed $command
* @return void
*/
protected function _columns($command) {
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
$modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1]));
if ($this->_isValidModel($modelToCheck)) {
// Get the column info for this model
$fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
//@codingStandardsIgnoreStart
@eval($fieldsCommand);
//@codingStandardsIgnoreEnd
if (is_array($data)) {
foreach ($data as $field => $type) {
$this->out("\t{$field}: {$type}");
}
}
} else {
$this->out(__d('cake_console', "Please verify that you selected a valid model"));
}
}
/**
* Reload route definitions
*
* @return void
*/
protected function _routesReload() {
if (!$this->_loadRoutes()) {
return $this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors."));
}
$this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes)));
}
/**
* Show all routes
*
* @return void
*/
protected function _routesShow() {
$this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true));
}
/**
* Parse an array url and show the equivalent url as a string
*
* @param mixed $command
* @return void
*/
protected function _routeToString($command) {
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
//@codingStandardsIgnoreStart
if ($url = eval('return array' . $tmp[1] . ';')) {
//@codingStandardsIgnoreEnd
$this->out(Router::url($url));
}
}
/**
* Parse a string url and show as an array
*
* @param mixed $command
* @return void
*/
protected function _routeToArray($command) {
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
$this->out(var_export(Router::parse($tmp[1]), true));
}
/**
* Tells if the specified model is included in the list of available models
*

View file

@ -153,6 +153,13 @@ class SchemaShell extends AppShell {
Configure::write('Cache.disable', $cacheDisable);
if (!empty($this->params['exclude']) && !empty($content)) {
$excluded = String::tokenize($this->params['exclude']);
foreach ($excluded as $table) {
unset($content['tables'][$table]);
}
}
if ($snapshot === true) {
$fileName = rtrim($this->params['file'], '.php');
$Folder = new Folder($this->Schema->path);
@ -228,10 +235,9 @@ class SchemaShell extends AppShell {
if ($File->write($contents)) {
$this->out(__d('cake_console', 'SQL dump file created in %s', $File->pwd()));
return $this->_stop();
} else {
$this->err(__d('cake_console', 'SQL dump could not be created'));
return $this->_stop();
}
$this->err(__d('cake_console', 'SQL dump could not be created'));
return $this->_stop();
}
$this->out($contents);
return $contents;
@ -365,10 +371,18 @@ class SchemaShell extends AppShell {
if (empty($table)) {
foreach ($compare as $table => $changes) {
$contents[$table] = $db->alterSchema(array($table => $changes), $table);
if (isset($compare[$table]['create'])) {
$contents[$table] = $db->createSchema($Schema, $table);
} else {
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
}
}
} elseif (isset($compare[$table])) {
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
if (isset($compare[$table]['create'])) {
$contents[$table] = $db->createSchema($Schema, $table);
} else {
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
}
}
if (empty($contents)) {
@ -479,6 +493,9 @@ class SchemaShell extends AppShell {
$write = array(
'help' => __d('cake_console', 'Write the dumped SQL to a file.')
);
$exclude = array(
'help' => __d('cake_console', 'Tables to exclude as comma separated list.')
);
$parser = parent::getOptionParser();
$parser->description(
@ -492,7 +509,7 @@ class SchemaShell extends AppShell {
))->addSubcommand('generate', array(
'help' => __d('cake_console', 'Reads from --connection and writes to --path. Generate snapshots with -s'),
'parser' => array(
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'snapshot', 'force', 'models'),
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'snapshot', 'force', 'models', 'exclude'),
'arguments' => array(
'snapshot' => array('help' => __d('cake_console', 'Generate a snapshot.'))
)

View file

@ -122,7 +122,7 @@ class ServerShell extends AppShell {
*/
public function main() {
if (version_compare(PHP_VERSION, '5.4.0') < 0) {
$this->out(__d('cake_console', '<warning>This command is available on PHP5.4 or above</warning>'));
$this->out(__d('cake_console', '<warning>This command is available on %s or above</warning>', 'PHP5.4'));
return;
}

View file

@ -480,6 +480,12 @@ class ControllerTask extends BakeTask {
))->addOption('connection', array(
'short' => 'c',
'help' => __d('cake_console', 'The connection the controller\'s model is on.')
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('force', array(
'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all controllers with CRUD methods.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));

View file

@ -83,8 +83,18 @@ class FixtureTask extends BakeTask {
))->addOption('plugin', array(
'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'),
'short' => 'p',
))->addOption('schema', array(
'help' => __d('cake_console', 'Importing schema for fixtures rather than hardcoding it.'),
'short' => 's',
'boolean' => true
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('force', array(
'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addOption('records', array(
'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10'),
'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10.'),
'short' => 'r',
'boolean' => true
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
@ -124,9 +134,14 @@ class FixtureTask extends BakeTask {
$this->interactive = false;
$this->Model->interactive = false;
$tables = $this->Model->listAll($this->connection, false);
foreach ($tables as $table) {
$model = $this->_modelName($table);
$this->bake($model);
$importOptions = array();
if (!empty($this->params['schema'])) {
$importOptions['schema'] = $model;
}
$this->bake($model, false, $importOptions);
}
}
@ -158,11 +173,20 @@ class FixtureTask extends BakeTask {
*/
public function importOptions($modelName) {
$options = array();
$doSchema = $this->in(__d('cake_console', 'Would you like to import schema for this fixture?'), array('y', 'n'), 'n');
if ($doSchema === 'y') {
if (!empty($this->params['schema'])) {
$options['schema'] = $modelName;
} else {
$doSchema = $this->in(__d('cake_console', 'Would you like to import schema for this fixture?'), array('y', 'n'), 'n');
if ($doSchema === 'y') {
$options['schema'] = $modelName;
}
}
if (!empty($this->params['records'])) {
$doRecords = 'y';
} else {
$doRecords = $this->in(__d('cake_console', 'Would you like to use record importing for this fixture?'), array('y', 'n'), 'n');
}
$doRecords = $this->in(__d('cake_console', 'Would you like to use record importing for this fixture?'), array('y', 'n'), 'n');
if ($doRecords === 'y') {
$options['records'] = true;
}

View file

@ -164,7 +164,7 @@ class ModelTask extends BakeTask {
* @param array $options Array of options to use for the selections. indexes must start at 0
* @param string $prompt Prompt to use for options list.
* @param integer $default The default option for the given prompt.
* @return integer result of user choice.
* @return integer Result of user choice.
*/
public function inOptions($options, $prompt = null, $default = null) {
$valid = false;
@ -347,7 +347,7 @@ class ModelTask extends BakeTask {
* @return array $validate Array of user selected validations.
*/
public function doValidation($model) {
if (!is_object($model)) {
if (!$model instanceof Model) {
return false;
}
$fields = $model->schema();
@ -490,10 +490,10 @@ class ModelTask extends BakeTask {
* Handles associations
*
* @param Model $model
* @return array $associations
* @return array Associations
*/
public function doAssociations($model) {
if (!is_object($model)) {
if (!$model instanceof Model) {
return false;
}
if ($this->interactive === true) {
@ -538,12 +538,36 @@ class ModelTask extends BakeTask {
return $associations;
}
/**
* Handles behaviors
*
* @param Model $model
* @return array Behaviors
*/
public function doActsAs($model) {
if (!$model instanceof Model) {
return false;
}
$behaviors = array();
$fields = $model->schema(true);
if (empty($fields)) {
return array();
}
if (isset($fields['lft']) && $fields['lft']['type'] === 'integer' &&
isset($fields['rght']) && $fields['rght']['type'] === 'integer' &&
isset($fields['parent_id'])) {
$behaviors[] = 'Tree';
}
return $behaviors;
}
/**
* Find belongsTo relations and add them to the associations list.
*
* @param Model $model Model instance of model being generated.
* @param array $associations Array of in progress associations
* @return array $associations with belongsTo added in.
* @return array Associations with belongsTo added in.
*/
public function findBelongsTo(Model $model, $associations) {
$fieldNames = array_keys($model->schema(true));
@ -572,7 +596,7 @@ class ModelTask extends BakeTask {
*
* @param Model $model Model instance being generated
* @param array $associations Array of in progress associations
* @return array $associations with hasOne and hasMany added in.
* @return array Associations with hasOne and hasMany added in.
*/
public function findHasOneAndMany(Model $model, $associations) {
$foreignKey = $this->_modelKey($model->name);
@ -615,7 +639,7 @@ class ModelTask extends BakeTask {
*
* @param Model $model Model instance being generated
* @param array $associations Array of in-progress associations
* @return array $associations with hasAndBelongsToMany added in.
* @return array Associations with hasAndBelongsToMany added in.
*/
public function findHasAndBelongsToMany(Model $model, $associations) {
$foreignKey = $this->_modelKey($model->name);
@ -747,7 +771,7 @@ class ModelTask extends BakeTask {
/**
* Finds all possible keys to use on custom associations.
*
* @return array array of tables and possible keys
* @return array Array of tables and possible keys
*/
protected function _generatePossibleKeys() {
$possible = array();
@ -771,11 +795,12 @@ class ModelTask extends BakeTask {
* @return string
*/
public function bake($name, $data = array()) {
if (is_object($name)) {
if ($name instanceof Model) {
if (!$data) {
$data = array();
$data['associations'] = $this->doAssociations($name);
$data['validate'] = $this->doValidation($name);
$data['actsAs'] = $this->doActsAs($name);
}
$data['primaryKey'] = $name->primaryKey;
$data['useTable'] = $name->table;
@ -784,8 +809,10 @@ class ModelTask extends BakeTask {
} else {
$data['name'] = $name;
}
$defaults = array(
'associations' => array(),
'actsAs' => array(),
'validate' => array(),
'primaryKey' => 'id',
'useTable' => null,
@ -920,7 +947,7 @@ class ModelTask extends BakeTask {
* Forces the user to specify the model he wants to bake, and returns the selected model name.
*
* @param string $useDbConfig Database config name
* @return string the model name
* @return string The model name
*/
public function getName($useDbConfig = null) {
$this->listAll($useDbConfig);
@ -965,9 +992,15 @@ class ModelTask extends BakeTask {
))->addOption('plugin', array(
'short' => 'p',
'help' => __d('cake_console', 'Plugin to bake the model into.')
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('connection', array(
'short' => 'c',
'help' => __d('cake_console', 'The connection the model table is on.')
))->addOption('force', array(
'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
}

View file

@ -117,8 +117,8 @@ class ProjectTask extends AppShell {
}
$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));
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in %s', CAKE_CORE_INCLUDE_PATH, 'webroot/index.php'));
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in %s', CAKE_CORE_INCLUDE_PATH, 'webroot/test.php'));
} 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;
@ -435,6 +435,9 @@ class ProjectTask extends AppShell {
))->addOption('empty', array(
'boolean' => true,
'help' => __d('cake_console', 'Create empty files in each of the directories. Good if you are using git')
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('skel', array(
'default' => current(App::core('Console')) . 'Templates' . DS . 'skel',
'help' => __d('cake_console', 'The directory layout to use for the new application skeleton. Defaults to cake/Console/Templates/skel of CakePHP used to create the project.')

View file

@ -563,9 +563,15 @@ class TestTask extends BakeTask {
)
))->addArgument('name', array(
'help' => __d('cake_console', 'An existing class to bake tests for.')
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('plugin', array(
'short' => 'p',
'help' => __d('cake_console', 'CamelCased name of the plugin to bake tests for.')
))->addOption('force', array(
'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
}

View file

@ -434,9 +434,15 @@ class ViewTask extends BakeTask {
))->addOption('admin', array(
'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'),
'boolean' => true
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('connection', array(
'short' => 'c',
'help' => __d('cake_console', 'The connection the connected model is on.')
))->addOption('force', array(
'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));

View file

@ -42,8 +42,7 @@ class TestsuiteShell extends TestShell {
$parser = parent::getOptionParser();
$parser->description(array(
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'),
__d('cake_console', '<warning>This shell is for backwards-compatibility only</warning>'),
__d('cake_console', 'use the test shell instead')
__d('cake_console', "<warning>This shell is for backwards-compatibility only</warning>\nuse the test shell instead"),
));
return $parser;

View file

@ -95,7 +95,7 @@ class ConsoleErrorHandler {
/**
* Wrapper for exit(), used for testing.
*
* @param int $code The exit code.
* @param integer $code The exit code.
* @return void
*/
protected function _stop($code = 0) {

View file

@ -50,4 +50,16 @@ class ConsoleInput {
return fgets($this->_input);
}
/**
* Checks if data is available on the stream
*
* @param integer $timeout An optional time to wait for data
* @return bool True for data available, false otherwise
*/
public function dataAvailable($timeout = 0) {
$readFds = array($this->_input);
$readyFds = stream_select($readFds, $writeFds, $errorFds, $timeout);
return ($readyFds > 0);
}
}

View file

@ -141,6 +141,7 @@ class ConsoleOutput {
'success' => array('text' => 'green'),
'comment' => array('text' => 'blue'),
'question' => array('text' => 'magenta'),
'notice' => array('text' => 'cyan')
);
/**

View file

@ -24,6 +24,7 @@ App::uses('ConsoleInputSubcommand', 'Console');
App::uses('ConsoleOptionParser', 'Console');
App::uses('ClassRegistry', 'Utility');
App::uses('File', 'Utility');
App::uses('ClassRegistry', 'Utility');
/**
* Base class for command-line utilities for automating programmer chores.
@ -120,6 +121,13 @@ class Shell extends Object {
*/
public $uses = array();
/**
* This shell's primary model class name, the first model in the $uses property
*
* @var string
*/
public $modelClass = null;
/**
* Task Collection for the command, used to create Tasks.
*
@ -178,7 +186,7 @@ class Shell extends Object {
if ($this->tasks !== null && $this->tasks !== false) {
$this->_mergeVars(array('tasks'), $parent, true);
}
if ($this->uses !== null && $this->uses !== false) {
if (!empty($this->uses)) {
$this->_mergeVars(array('uses'), $parent, false);
}
}
@ -224,31 +232,66 @@ class Shell extends Object {
}
/**
* If $uses = true
* Loads AppModel file and constructs AppModel class
* makes $this->AppModel available to subclasses
* If public $uses is an array of models will load those models
* If $uses is an array load each of the models in the array
*
* @return boolean
*/
protected function _loadModels() {
if (empty($this->uses)) {
return false;
if (is_array($this->uses)) {
list(, $this->modelClass) = pluginSplit(current($this->uses));
foreach ($this->uses as $modelClass) {
$this->loadModel($modelClass);
}
}
return true;
}
/**
* Lazy loads models using the loadModel() method if declared in $uses
*
* @param string $name
* @return void
*/
public function __isset($name) {
if (is_array($this->uses)) {
foreach ($this->uses as $modelClass) {
list(, $class) = pluginSplit($modelClass);
if ($name === $class) {
return $this->loadModel($modelClass);
}
}
}
}
/**
* Loads and instantiates models required by this shell.
*
* @param string $modelClass Name of model class to load
* @param mixed $id Initial ID the instanced model class should have
* @return mixed true when single model found and instance created, error returned if model not found.
* @throws MissingModelException if the model class cannot be found.
*/
public function loadModel($modelClass = null, $id = null) {
if ($modelClass === null) {
$modelClass = $this->modelClass;
}
$uses = is_array($this->uses) ? $this->uses : array($this->uses);
$modelClassName = $uses[0];
if (strpos($uses[0], '.') !== false) {
list($plugin, $modelClassName) = explode('.', $uses[0]);
}
$this->modelClass = $modelClassName;
foreach ($uses as $modelClass) {
list($plugin, $modelClass) = pluginSplit($modelClass, true);
$this->{$modelClass} = ClassRegistry::init($plugin . $modelClass);
$this->uses = ($this->uses) ? (array)$this->uses : array();
if (!in_array($modelClass, $this->uses)) {
$this->uses[] = $modelClass;
}
list($plugin, $modelClass) = pluginSplit($modelClass, true);
if (!isset($this->modelClass)) {
$this->modelClass = $modelClass;
}
$this->{$modelClass} = ClassRegistry::init(array(
'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id
));
if (!$this->{$modelClass}) {
throw new MissingModelException($modelClass);
}
return true;
}
@ -650,7 +693,7 @@ class Shell extends Object {
$this->out();
if (is_file($path) && $this->interactive === true) {
if (is_file($path) && empty($this->params['force']) && $this->interactive === true) {
$this->out(__d('cake_console', '<warning>File `%s` exists</warning>', $path));
$key = $this->in(__d('cake_console', 'Do you want to overwrite?'), array('y', 'n', 'q'), 'n');
@ -835,12 +878,12 @@ class Shell extends Object {
return;
}
CakeLog::config('stdout', array(
'engine' => 'ConsoleLog',
'engine' => 'Console',
'types' => array('notice', 'info'),
'stream' => $this->stdout,
));
CakeLog::config('stderr', array(
'engine' => 'ConsoleLog',
'engine' => 'Console',
'types' => array('emergency', 'alert', 'critical', 'error', 'warning', 'debug'),
'stream' => $this->stderr,
));

View file

@ -145,7 +145,9 @@ class ShellDispatcher {
$this->setErrorHandlers();
if (!defined('FULL_BASE_URL')) {
define('FULL_BASE_URL', 'http://localhost');
$url = Configure::read('App.fullBaseUrl');
define('FULL_BASE_URL', $url ? $url : 'http://localhost');
Configure::write('App.fullBaseUrl', FULL_BASE_URL);
}
return true;

View file

@ -50,8 +50,17 @@ class TaskCollection extends ObjectCollection {
}
/**
* Loads/constructs a task. Will return the instance in the collection
* if it already exists.
* Loads/constructs a task. Will return the instance in the registry if it already exists.
*
* You can alias your task as an existing task by setting the 'className' key, i.e.,
* {{{
* public $tasks = array(
* 'DbConfig' => array(
* 'className' => 'Bakeplus.DbConfigure'
* );
* );
* }}}
* All calls to the `DbConfig` task would use `DbConfigure` found in the `Bakeplus` plugin instead.
*
* @param string $task Task name to load
* @param array $settings Settings for the task.
@ -59,26 +68,33 @@ class TaskCollection extends ObjectCollection {
* @throws MissingTaskException when the task could not be found
*/
public function load($task, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) {
$alias = $task;
$task = $settings['className'];
}
list($plugin, $name) = pluginSplit($task, true);
if (isset($this->_loaded[$name])) {
return $this->_loaded[$name];
if (!isset($alias)) {
$alias = $name;
}
if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
$taskClass = $name . 'Task';
App::uses($taskClass, $plugin . 'Console/Command/Task');
$exists = class_exists($taskClass);
if (!$exists) {
throw new MissingTaskException(array(
'class' => $taskClass
'class' => $taskClass,
'plugin' => substr($plugin, 0, -1)
));
}
$this->_loaded[$name] = new $taskClass(
$this->_loaded[$alias] = new $taskClass(
$this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin
);
return $this->_loaded[$name];
return $this->_loaded[$alias];
}
}

View file

@ -74,6 +74,16 @@ if ($displayField): ?>
<?php endif;
if (!empty($actsAs)): ?>
/**
* Behaviors
*
* @var array
*/
public $actsAs = array(<?php echo "\n\t"; foreach ($actsAs as $behavior): echo "\t"; var_export($behavior); echo ",\n\t"; endforeach; ?>);
<?php endif;
if (!empty($validate)):
echo "/**\n * Validation rules\n *\n * @var array\n */\n";
echo "\tpublic \$validate = array(\n";

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/*
@ -28,8 +19,6 @@
*/
class DbAclSchema extends CakeSchema {
public $name = 'DbAcl';
public function before($event = array()) {
return true;
}

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/*

View file

@ -5,17 +5,9 @@
; *
; * PHP 5
; *
; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
; * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
; *
; * Licensed under The MIT License
; * Redistributions of files must retain the above copyright notice.
; *
; * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
; * @link http://cakephp.org CakePHP(tm) Project
; * @package app.Config
; * @since CakePHP(tm) v 0.10.0.1076
; * @license http://www.opensource.org/licenses/mit-license.php MIT License
; */
; acl.ini.php - Cake ACL Configuration

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 2.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**

View file

@ -10,18 +10,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 0.10.8.2117
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
// Setup a 'default' cache configuration for use in the application.
@ -98,12 +89,12 @@ Configure::write('Dispatcher.filters', array(
*/
App::uses('CakeLog', 'Log');
CakeLog::config('debug', array(
'engine' => 'FileLog',
'engine' => 'File',
'types' => array('notice', 'info', 'debug'),
'file' => 'debug',
));
CakeLog::config('error', array(
'engine' => 'FileLog',
'engine' => 'File',
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
'file' => 'error',
));

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
@ -70,6 +61,9 @@
* - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
* should place the file for that class in app/Lib/Error. This class needs to implement a render method.
* - `log` - boolean - Should Exceptions be logged?
* - `skipLog` - array - list of exceptions to skip for logging. Exceptions that
* extend one of the listed exceptions will also be skipped for logging.
* Example: `'skipLog' => array('NotFoundException', 'UnauthorizedException')`
*
* @see ErrorHandler for more information on exception handling and configuration.
*/
@ -105,6 +99,33 @@
*/
//Configure::write('App.baseUrl', env('SCRIPT_NAME'));
/**
* To configure CakePHP to use a particular domain URL
* for any URL generation inside the application, set the following
* configuration variable to the http(s) address to your domain. This
* will override the automatic detection of full base URL and can be
* useful when generating links from the CLI (e.g. sending emails)
*/
//Configure::write('App.fullBaseUrl', 'http://example.com');
/**
* Web path to the public images directory under webroot.
* If not set defaults to 'img/'
*/
//Configure::write('App.imageBaseUrl', 'img/');
/**
* Web path to the CSS files directory under webroot.
* If not set defaults to 'css/'
*/
//Configure::write('App.cssBaseUrl', 'css/');
/**
* Web path to the js files directory under webroot.
* If not set defaults to 'js/'
*/
//Configure::write('App.jsBaseUrl', 'js/');
/**
* Uncomment the define below to use CakePHP prefix routes.
*

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
@ -25,7 +16,7 @@
* You can specify multiple configurations for production, development and testing.
*
* datasource => The name of a supported datasource; valid options are as follows:
* Database/Mysql - MySQL 4 & 5,
* Database/Mysql - MySQL 4 & 5,
* Database/Sqlite - SQLite (PHP5 only),
* Database/Postgres - PostgreSQL 7 and higher,
* Database/Sqlserver - Microsoft SQL Server 2005 and higher

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 2.0.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
@ -25,7 +16,7 @@
* You can specify multiple configurations for production, development and testing.
*
* transport => The name of a supported transport; valid options are as follows:
* Mail - Send using PHP mail function
* Mail - Send using PHP mail function
* Smtp - Send using SMTP
* Debug - Do not send the email, just return the result
*

View file

@ -8,18 +8,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Config
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**

View file

@ -4,17 +4,8 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('Shell', 'Console');

View file

@ -13,7 +13,6 @@
:: @link http://cakephp.org CakePHP(tm) Project
:: @package app.Console
:: @since CakePHP(tm) v 2.0
:: @license http://www.opensource.org/licenses/mit-license.php MIT License
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

View file

@ -16,7 +16,6 @@
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Console
* @since CakePHP(tm) v 2.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
$ds = DIRECTORY_SEPARATOR;

View file

@ -7,18 +7,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Controller
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('Controller', 'Controller');

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Controller
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('AppController', 'Controller');
@ -32,13 +23,6 @@ App::uses('AppController', 'Controller');
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/**
* This controller does not use a model
*
@ -51,6 +35,8 @@ class PagesController extends AppController {
*
* @param mixed What page to display
* @return void
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
*/
public function display() {
$path = func_get_args();
@ -71,6 +57,14 @@ class PagesController extends AppController {
$title_for_layout = Inflector::humanize($path[$count - 1]);
}
$this->set(compact('page', 'subpage', 'title_for_layout'));
$this->render(implode('/', $path));
try {
$this->render(implode('/', $path));
} catch (MissingViewException $e) {
if (Configure::read('debug')) {
throw $e;
}
throw new NotFoundException();
}
}
}

View file

@ -7,18 +7,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Model
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('Model', 'Model');

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Errors
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
?>
<h2><?php echo $name; ?></h2>

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Errors
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
?>
<h2><?php echo $name; ?></h2>

View file

@ -7,18 +7,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Helper
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('Helper', 'View');

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts.Email.html
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
?>
<?php echo $this->fetch('content'); ?>

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
$cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
$cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
?>
<!DOCTYPE html>

View file

@ -3,18 +3,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Pages
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
if (!Configure::read('debug')):
@ -68,11 +59,11 @@ endif;
$settings = Cache::settings();
if (!empty($settings)):
echo '<span class="notice success">';
echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit APP/Config/core.php ', '<em>'. $settings['engine'] . 'Engine</em>');
echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', '<em>'. $settings['engine'] . 'Engine</em>', 'APP/Config/core.php');
echo '</span>';
else:
echo '<span class="notice">';
echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in APP/Config/core.php');
echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in %s', 'APP/Config/core.php');
echo '</span>';
endif;
?>
@ -89,7 +80,7 @@ endif;
echo '<span class="notice">';
echo __d('cake_dev', 'Your database configuration file is NOT present.');
echo '<br/>';
echo __d('cake_dev', 'Rename APP/Config/database.php.default to APP/Config/database.php');
echo __d('cake_dev', 'Rename %s to %s', 'APP/Config/database.php.default', 'APP/Config/database.php');
echo '</span>';
endif;
?>
@ -132,7 +123,7 @@ if (isset($filePresent)):
echo '<p><span class="notice">';
echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.');
echo '<br/>';
echo __d('cake_dev', 'Recompile PCRE with Unicode support by adding <code>--enable-unicode-properties</code> when configuring');
echo __d('cake_dev', 'Recompile PCRE with Unicode support by adding %s when configuring', '<code>--enable-unicode-properties</code>');
echo '</span></p>';
}
?>
@ -156,9 +147,10 @@ if (isset($filePresent)):
<h3><?php echo __d('cake_dev', 'Editing this Page'); ?></h3>
<p>
<?php
echo __d('cake_dev', 'To change the content of this page, edit: APP/View/Pages/home.ctp.<br />
To change its layout, edit: APP/View/Layouts/default.ctp.<br />
You can also add some CSS styles for your pages at: APP/webroot/css.');
echo __d('cake_dev', 'To change the content of this page, edit: %s.<br />
To change its layout, edit: %s.<br />
You can also add some CSS styles for your pages at: %s.',
'APP/View/Pages/home.ctp', 'APP/View/Layouts/default.ctp', 'APP/webroot/css');
?>
</p>

View file

@ -2,18 +2,9 @@
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app
* @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

View file

@ -2,5 +2,5 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
RewriteRule ^ index.php [L]
</IfModule>

View file

@ -6,18 +6,9 @@
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.webroot
* @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**

View file

@ -4,18 +4,9 @@
*
* PHP 5
*
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://book.cakephp.org/2.0/en/development/testing.html
* @package app.webroot
* @since CakePHP(tm) v 1.2.0.4433
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
set_time_limit(0);

View file

@ -31,13 +31,6 @@ App::uses('AppController', 'Controller');
*/
class CakeErrorController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'CakeError';
/**
* Uses Property
*

View file

@ -156,10 +156,10 @@ class AclComponent extends Component {
* @param array|string|Model $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return boolean Success
* @deprecated
* @deprecated Will be removed in 3.0.
*/
public function grant($aro, $aco, $action = "*") {
trigger_error(__d('cake_dev', 'AclComponent::grant() is deprecated, use allow() instead'), E_USER_WARNING);
trigger_error(__d('cake_dev', '%s is deprecated, use %s instead', 'AclComponent::grant()', 'allow()'), E_USER_WARNING);
return $this->_Instance->allow($aro, $aco, $action);
}
@ -170,10 +170,10 @@ class AclComponent extends Component {
* @param array|string|Model $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return boolean Success
* @deprecated
* @deprecated Will be removed in 3.0.
*/
public function revoke($aro, $aco, $action = "*") {
trigger_error(__d('cake_dev', 'AclComponent::revoke() is deprecated, use deny() instead'), E_USER_WARNING);
trigger_error(__d('cake_dev', '%s is deprecated, use %s instead', 'AclComponent::revoke()', 'deny()'), E_USER_WARNING);
return $this->_Instance->deny($aro, $aco, $action);
}

View file

@ -0,0 +1,74 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.4.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Abstract password hashing class
*
* @package Cake.Controller.Component.Auth
*/
abstract class AbstractPasswordHasher {
/**
* Configurations for this object. Settings passed from authenticator class to
* the constructor are merged with this property.
*
* @var array
*/
protected $_config = array();
/**
* Constructor
*
* @param array $config Array of config.
*/
public function __construct($config = array()) {
$this->config($config);
}
/**
* Get/Set the config
*
* @param array $config Sets config, if null returns existing config
* @return array Returns configs
*/
public function config($config = null) {
if (is_array($config)) {
$this->_config = array_merge($this->_config, $config);
}
return $this->_config;
}
/**
* Generates password hash.
*
* @param string|array $password Plain text password to hash or array of data
* required to generate password hash.
* @return string Password hash
*/
abstract public function hash($password);
/**
* Check hash. Generate hash from user provided password string or data array
* and check against existing hash.
*
* @param string|array $password Plain text password to hash or data array.
* @param string Existing hashed password.
* @return boolean True if hashes match else false.
*/
abstract public function check($password, $hashedPassword);
}

View file

@ -33,6 +33,9 @@ abstract class BaseAuthenticate {
* i.e. `array('User.is_active' => 1).`
* - `recursive` The value of the recursive key passed to find(). Defaults to 0.
* - `contain` Extra models to contain and store in session.
* - `passwordHasher` Password hasher class. Can be a string specifying class name
* or an array containing `className` key, any other keys will be passed as
* settings to the class. Defaults to 'Simple'.
*
* @var array
*/
@ -45,6 +48,7 @@ abstract class BaseAuthenticate {
'scope' => array(),
'recursive' => 0,
'contain' => null,
'passwordHasher' => 'Simple'
);
/**
@ -54,6 +58,13 @@ abstract class BaseAuthenticate {
*/
protected $_Collection;
/**
* Password hasher instance.
*
* @var AbstractPasswordHasher
*/
protected $_passwordHasher;
/**
* Constructor
*
@ -68,56 +79,96 @@ abstract class BaseAuthenticate {
/**
* Find a user record using the standard options.
*
* The $conditions parameter can be a (string)username or an array containing conditions for Model::find('first'). If
* the password field is not included in the conditions the password will be returned.
* The $username parameter can be a (string)username or an array containing
* conditions for Model::find('first'). If the $password param is not provided
* the password field will be present in returned array.
*
* @param Mixed $conditions The username/identifier, or an array of find conditions.
* @param Mixed $password The password, only use if passing as $conditions = 'username'.
* @return Mixed Either false on failure, or an array of user data.
* Input passwords will be hashed even when a user doesn't exist. This
* helps mitigate timing attacks that are attempting to find valid usernames.
*
* @param string|array $username The username/identifier, or an array of find conditions.
* @param string $password The password, only used if $username param is string.
* @return boolean|array Either false on failure, or an array of user data.
*/
protected function _findUser($conditions, $password = null) {
protected function _findUser($username, $password = null) {
$userModel = $this->settings['userModel'];
list(, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
if (!is_array($conditions)) {
if (!$password) {
return false;
}
$username = $conditions;
if (is_array($username)) {
$conditions = $username;
} else {
$conditions = array(
$model . '.' . $fields['username'] => $username,
$model . '.' . $fields['password'] => $this->_password($password),
$model . '.' . $fields['username'] => $username
);
}
if (!empty($this->settings['scope'])) {
$conditions = array_merge($conditions, $this->settings['scope']);
}
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => $this->settings['recursive'],
'contain' => $this->settings['contain'],
));
if (empty($result) || empty($result[$model])) {
if (empty($result[$model])) {
$this->passwordHasher()->hash($password);
return false;
}
$user = $result[$model];
if (
isset($conditions[$model . '.' . $fields['password']]) ||
isset($conditions[$fields['password']])
) {
if ($password) {
if (!$this->passwordHasher()->check($password, $user[$fields['password']])) {
return false;
}
unset($user[$fields['password']]);
}
unset($result[$model]);
return array_merge($user, $result);
}
/**
* Return password hasher object
*
* @return AbstractPasswordHasher Password hasher instance
* @throws CakeException If password hasher class not found or
* it does not extend AbstractPasswordHasher
*/
public function passwordHasher() {
if ($this->_passwordHasher) {
return $this->_passwordHasher;
}
$config = array();
if (is_string($this->settings['passwordHasher'])) {
$class = $this->settings['passwordHasher'];
} else {
$class = $this->settings['passwordHasher']['className'];
$config = $this->settings['passwordHasher'];
unset($config['className']);
}
list($plugin, $class) = pluginSplit($class, true);
$className = $class . 'PasswordHasher';
App::uses($className, $plugin . 'Controller/Component/Auth');
if (!class_exists($className)) {
throw new CakeException(__d('cake_dev', 'Password hasher class "%s" was not found.', $class));
}
if (!is_subclass_of($className, 'AbstractPasswordHasher')) {
throw new CakeException(__d('cake_dev', 'Password hasher must extend AbstractPasswordHasher class.'));
}
$this->_passwordHasher = new $className($config);
return $this->_passwordHasher;
}
/**
* Hash the plain text password so that it matches the hashed/encrypted password
* in the datasource.
*
* @param string $password The plain text password.
* @return string The hashed form of the password.
* @deprecated Since 2.4. Use a PasswordHasher class instead.
*/
protected function _password($password) {
return Security::hash($password, null, true);
@ -156,4 +207,15 @@ abstract class BaseAuthenticate {
return false;
}
/**
* Handle unauthenticated access attempt.
*
* @param CakeRequest $request A request object.
* @param CakeResponse $response A response object.
* @return mixed Either true to indicate the unauthenticated request has been
* dealt with and no more action is required by AuthComponent or void (default).
*/
public function unauthenticated(CakeRequest $request, CakeResponse $response) {
}
}

View file

@ -43,31 +43,6 @@ App::uses('BaseAuthenticate', 'Controller/Component/Auth');
*/
class BasicAuthenticate extends BaseAuthenticate {
/**
* Settings for this object.
*
* - `fields` The fields to use to identify a user by.
* - `userModel` The model name of the User, defaults to User.
* - `scope` Additional conditions to use when looking up and authenticating users,
* i.e. `array('User.is_active' => 1).`
* - `recursive` The value of the recursive key passed to find(). Defaults to 0.
* - `contain` Extra models to contain and store in session.
* - `realm` The realm authentication is for. Defaults the server name.
*
* @var array
*/
public $settings = array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
'userModel' => 'User',
'scope' => array(),
'recursive' => 0,
'contain' => null,
'realm' => '',
);
/**
* Constructor, completes configuration for basic authentication.
*
@ -82,23 +57,15 @@ class BasicAuthenticate extends BaseAuthenticate {
}
/**
* Authenticate a user using basic HTTP auth. Will use the configured User model and attempt a
* login using basic HTTP auth.
* Authenticate a user using HTTP auth. Will use the configured User model and attempt a
* login using HTTP auth.
*
* @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $response The response to add headers to.
* @return mixed Either false on failure, or an array of user data on success.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$result = $this->getUser($request);
if (empty($result)) {
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
}
return $result;
return $this->getUser($request);
}
/**
@ -117,6 +84,20 @@ class BasicAuthenticate extends BaseAuthenticate {
return $this->_findUser($username, $pass);
}
/**
* Handles an unauthenticated access attempt by sending appropriate login headers
*
* @param CakeRequest $request A request object.
* @param CakeResponse $response A response object.
* @return void
* @throws UnauthorizedException
*/
public function unauthenticated(CakeRequest $request, CakeResponse $response) {
$Exception = new UnauthorizedException();
$Exception->responseHeader(array($this->loginHeaders()));
throw $Exception;
}
/**
* Generate the login headers
*

View file

@ -34,46 +34,22 @@ App::uses('FormAuthenticate', 'Controller/Component/Auth');
* For initial password hashing/creation see Security::hash(). Other than how the password is initially hashed,
* BlowfishAuthenticate works exactly the same way as FormAuthenticate.
*
* @package Cake.Controller.Component.Auth
* @package Cake.Controller.Component.Auth
* @since CakePHP(tm) v 2.3
* @see AuthComponent::$authenticate
* @see AuthComponent::$authenticate
* @deprecated Since 2.4. Just use FormAuthenticate with 'passwordHasher' setting set to 'Blowfish'
*/
class BlowfishAuthenticate extends FormAuthenticate {
/**
* Authenticates the identity contained in a request. Will use the `settings.userModel`, and `settings.fields`
* to find POST data that is used to find a matching record in the`settings.userModel`. Will return false if
* there is no post data, either username or password is missing, or if the scope conditions have not been met.
* Constructor. Sets default passwordHasher to Blowfish
*
* @param CakeRequest $request The request that contains login information.
* @param CakeResponse $response Unused response object.
* @return mixed False on login failure. An array of User data on success.
* @param ComponentCollection $collection The Component collection used on this request.
* @param array $settings Array of settings to use.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$userModel = $this->settings['userModel'];
list(, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
if (!$this->_checkFields($request, $model, $fields)) {
return false;
}
$user = $this->_findUser(
array(
$model . '.' . $fields['username'] => $request->data[$model][$fields['username']],
)
);
if (!$user) {
return false;
}
$password = Security::hash(
$request->data[$model][$fields['password']],
'blowfish',
$user[$fields['password']]
);
if ($password === $user[$fields['password']]) {
unset($user[$fields['password']]);
return $user;
}
return false;
public function __construct(ComponentCollection $collection, $settings) {
$this->settings['passwordHasher'] = 'Blowfish';
parent::__construct($collection, $settings);
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.4.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
App::uses('Security', 'Utility');
/**
* Blowfish password hashing class.
*
* @package Cake.Controller.Component.Auth
*/
class BlowfishPasswordHasher extends AbstractPasswordHasher {
/**
* Generates password hash.
*
* @param string $password Plain text password to hash.
* @return string Password hash
*/
public function hash($password) {
return Security::hash($password, 'blowfish', false);
}
/**
* Check hash. Generate hash for user provided password and check against existing hash.
*
* @param string $password Plain text password to hash.
* @param string Existing hashed password.
* @return boolean True if hashes match else false.
*/
public function check($password, $hashedPassword) {
return $hashedPassword === Security::hash($password, 'blowfish', $hashedPassword);
}
}

View file

@ -48,7 +48,7 @@ class ControllerAuthorize extends BaseAuthorize {
public function controller(Controller $controller = null) {
if ($controller) {
if (!method_exists($controller, 'isAuthorized')) {
throw new CakeException(__d('cake_dev', '$controller does not implement an isAuthorized() method.'));
throw new CakeException(__d('cake_dev', '$controller does not implement an %s method.', 'isAuthorized()'));
}
}
return parent::controller($controller);

View file

@ -14,7 +14,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
App::uses('BasicAuthenticate', 'Controller/Component/Auth');
/**
* Digest Authentication adapter for AuthComponent.
@ -55,7 +55,7 @@ App::uses('BaseAuthenticate', 'Controller/Component/Auth');
* @package Cake.Controller.Component.Auth
* @since 2.0
*/
class DigestAuthenticate extends BaseAuthenticate {
class DigestAuthenticate extends BasicAuthenticate {
/**
* Settings for this object.
@ -86,7 +86,8 @@ class DigestAuthenticate extends BaseAuthenticate {
'realm' => '',
'qop' => 'auth',
'nonce' => '',
'opaque' => ''
'opaque' => '',
'passwordHasher' => 'Simple',
);
/**
@ -97,9 +98,6 @@ class DigestAuthenticate extends BaseAuthenticate {
*/
public function __construct(ComponentCollection $collection, $settings) {
parent::__construct($collection, $settings);
if (empty($this->settings['realm'])) {
$this->settings['realm'] = env('SERVER_NAME');
}
if (empty($this->settings['nonce'])) {
$this->settings['nonce'] = uniqid('');
}
@ -108,26 +106,6 @@ class DigestAuthenticate extends BaseAuthenticate {
}
}
/**
* Authenticate a user using Digest HTTP auth. Will use the configured User model and attempt a
* login using Digest HTTP auth.
*
* @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $response The response to add headers to.
* @return mixed Either false on failure, or an array of user data on success.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$user = $this->getUser($request);
if (empty($user)) {
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
}
return $user;
}
/**
* Get a user based on information in the request. Used by cookie-less auth for stateless clients.
*
@ -139,7 +117,11 @@ class DigestAuthenticate extends BaseAuthenticate {
if (empty($digest)) {
return false;
}
$user = $this->_findUser($digest['username']);
list(, $model) = pluginSplit($this->settings['userModel']);
$user = $this->_findUser(array(
$model . '.' . $this->settings['fields']['username'] => $digest['username']
));
if (empty($user)) {
return false;
}
@ -151,34 +133,6 @@ class DigestAuthenticate extends BaseAuthenticate {
return false;
}
/**
* Find a user record using the standard options.
*
* @param string $username The username/identifier.
* @param string $password Unused password, digest doesn't require passwords.
* @return Mixed Either false on failure, or an array of user data.
*/
protected function _findUser($username, $password = null) {
$userModel = $this->settings['userModel'];
list(, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
$conditions = array(
$model . '.' . $fields['username'] => $username,
);
if (!empty($this->settings['scope'])) {
$conditions = array_merge($conditions, $this->settings['scope']);
}
$result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions,
'recursive' => $this->settings['recursive']
));
if (empty($result) || empty($result[$model])) {
return false;
}
return $result[$model];
}
/**
* Gets the digest headers from the request/environment.
*

View file

@ -0,0 +1,55 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.4.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
App::uses('Security', 'Utility');
/**
* Simple password hashing class.
*
* @package Cake.Controller.Component.Auth
*/
class SimplePasswordHasher extends AbstractPasswordHasher {
/**
* Config for this object.
*
* @var array
*/
protected $_config = array('hashType' => null);
/**
* Generates password hash.
*
* @param string $password Plain text password to hash.
* @return string Password hash
*/
public function hash($password) {
return Security::hash($password, $this->_config['hashType'], true);
}
/**
* Check hash. Generate hash for user provided password and check against existing hash.
*
* @param string $password Plain text password to hash.
* @param string Existing hashed password.
* @return boolean True if hashes match else false.
*/
public function check($password, $hashedPassword) {
return $hashedPassword === $this->hash($password);
}
}

View file

@ -157,8 +157,9 @@ class AuthComponent extends Component {
);
/**
* The session key name where the record of the current user is stored. If
* unspecified, it will be "Auth.User".
* The session key name where the record of the current user is stored. Default
* key is "Auth.User". If you are using only stateless authenticators set this
* to false to ensure session is not started.
*
* @var string
*/
@ -188,7 +189,7 @@ class AuthComponent extends Component {
* Normally, if a user is redirected to the $loginAction page, the location they
* were redirected from will be stored in the session so that they can be
* redirected back after a successful login. If this session value is not
* set, the user will be redirected to the page specified in $loginRedirect.
* set, redirectUrl() method will return the url specified in $loginRedirect.
*
* @var mixed
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$loginRedirect
@ -210,7 +211,7 @@ class AuthComponent extends Component {
* Error to display when user attempts to access an object or action to which they do not have
* access.
*
* @var string
* @var string|bool Error message or boolean false to suppress flash message
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$authError
*/
public $authError = null;
@ -294,43 +295,13 @@ class AuthComponent extends Component {
if (!$this->_setDefaults()) {
return false;
}
$request = $controller->request;
$url = '';
if (isset($request->url)) {
$url = $request->url;
}
$url = Router::normalize($url);
$loginAction = Router::normalize($this->loginAction);
if ($loginAction != $url && in_array($action, array_map('strtolower', $this->allowedActions))) {
return true;
}
if ($loginAction == $url) {
if (empty($request->data)) {
if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) {
$referer = $request->referer(true);
$this->Session->write('Auth.redirect', $referer);
}
}
if ($this->_isAllowed($controller)) {
return true;
}
if (!$this->_getUser()) {
if (!$request->is('ajax')) {
$this->flash($this->authError);
$this->Session->write('Auth.redirect', $request->here(false));
$controller->redirect($loginAction);
return false;
}
if (!empty($this->ajaxLogin)) {
$controller->viewPath = 'Elements';
echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$this->_stop();
return false;
}
$controller->redirect(null, 403);
return $this->_unauthenticated($controller);
}
if (empty($this->authorize) || $this->isAuthorized($this->user())) {
@ -340,12 +311,95 @@ class AuthComponent extends Component {
return $this->_unauthorized($controller);
}
/**
* Checks whether current action is accessible without authentication.
*
* @param Controller $controller A reference to the instantiating controller object
* @return boolean True if action is accessible without authentication else false
*/
protected function _isAllowed(Controller $controller) {
$action = strtolower($controller->request->params['action']);
if (in_array($action, array_map('strtolower', $this->allowedActions))) {
return true;
}
return false;
}
/**
* Handles unauthenticated access attempt. First the `unathenticated()` method
* of the last authenticator in the chain will be called. The authenticator can
* handle sending response or redirection as appropriate and return `true` to
* indicate no furthur action is necessary. If authenticator returns null this
* method redirects user to login action. If it's an ajax request and
* $ajaxLogin is specified that element is rendered else a 403 http status code
* is returned.
*
* @param Controller $controller A reference to the controller object.
* @return boolean True if current action is login action else false.
*/
protected function _unauthenticated(Controller $controller) {
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
}
$auth = $this->_authenticateObjects[count($this->_authenticateObjects) - 1];
if ($auth->unauthenticated($this->request, $this->response)) {
return false;
}
if ($this->_isLoginAction($controller)) {
return true;
}
if (!$controller->request->is('ajax')) {
$this->flash($this->authError);
$this->Session->write('Auth.redirect', $controller->request->here(false));
$controller->redirect($this->loginAction);
return false;
}
if (!empty($this->ajaxLogin)) {
$controller->viewPath = 'Elements';
echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$this->_stop();
return false;
}
$controller->redirect(null, 403);
return false;
}
/**
* Normalizes $loginAction and checks if current request url is same as login
* action. If current url is same as login action, referrer url is saved in session
* which is later accessible using redirectUrl().
*
* @param Controller $controller A reference to the controller object.
* @return boolean True if current action is login action else false.
*/
protected function _isLoginAction(Controller $controller) {
$url = '';
if (isset($controller->request->url)) {
$url = $controller->request->url;
}
$url = Router::normalize($url);
$loginAction = Router::normalize($this->loginAction);
if ($loginAction == $url) {
if (empty($controller->request->data)) {
if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) {
$this->Session->write('Auth.redirect', $controller->referer(null, true));
}
}
return true;
}
return false;
}
/**
* Handle unauthorized access attempt
*
* @param Controller $controller A reference to the controller object
* @return boolean Returns false
* @throws ForbiddenException
* @see AuthComponent::$unauthorizedRedirect
*/
protected function _unauthorized(Controller $controller) {
if ($this->unauthorizedRedirect === false) {
@ -369,7 +423,7 @@ class AuthComponent extends Component {
/**
* Attempts to introspect the correct values for object properties.
*
* @return boolean
* @return boolean True
*/
protected function _setDefaults() {
$defaults = array(
@ -377,7 +431,7 @@ class AuthComponent extends Component {
'authError' => __d('cake', 'You are not authorized to access that location.')
);
foreach ($defaults as $key => $value) {
if (empty($this->{$key})) {
if (!isset($this->{$key}) || $this->{$key} === true) {
$this->{$key} = $value;
}
}
@ -441,7 +495,7 @@ class AuthComponent extends Component {
throw new CakeException(__d('cake_dev', 'Authorization adapter "%s" was not found.', $class));
}
if (!method_exists($className, 'authorize')) {
throw new CakeException(__d('cake_dev', 'Authorization objects must implement an authorize method.'));
throw new CakeException(__d('cake_dev', 'Authorization objects must implement an %s method.', 'authorize()'));
}
$settings = array_merge($global, (array)$settings);
$this->_authorizeObjects[] = new $className($this->_Collection, $settings);
@ -593,13 +647,12 @@ class AuthComponent extends Component {
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user
*/
public static function user($key = null) {
if (empty(self::$_user) && !CakeSession::check(self::$sessionKey)) {
return null;
}
if (!empty(self::$_user)) {
$user = self::$_user;
} else {
} elseif (self::$sessionKey && CakeSession::check(self::$sessionKey)) {
$user = CakeSession::read(self::$sessionKey);
} else {
return null;
}
if ($key === null) {
return $user;
@ -616,8 +669,10 @@ class AuthComponent extends Component {
protected function _getUser() {
$user = $this->user();
if ($user) {
$this->Session->delete('Auth.redirect');
return true;
}
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
}
@ -628,6 +683,7 @@ class AuthComponent extends Component {
return true;
}
}
return false;
}
@ -728,7 +784,7 @@ class AuthComponent extends Component {
throw new CakeException(__d('cake_dev', 'Authentication adapter "%s" was not found.', $class));
}
if (!method_exists($className, 'authenticate')) {
throw new CakeException(__d('cake_dev', 'Authentication objects must implement an authenticate method.'));
throw new CakeException(__d('cake_dev', 'Authentication objects must implement an %s method.', 'authenticate()'));
}
$settings = array_merge($global, (array)$settings);
$this->_authenticateObjects[] = new $className($this->_Collection, $settings);
@ -744,24 +800,12 @@ class AuthComponent extends Component {
*
* @param string $password Password to hash
* @return string Hashed password
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#hashing-passwords
* @deprecated Since 2.4. Use Security::hash() directly or a password hasher object.
*/
public static function password($password) {
return Security::hash($password, null, true);
}
/**
* Component shutdown. If user is logged in, wipe out redirect.
*
* @param Controller $controller Instantiating controller
* @return void
*/
public function shutdown(Controller $controller) {
if ($this->loggedIn()) {
$this->Session->delete('Auth.redirect');
}
}
/**
* Check whether or not the current user has data in the session, and is considered logged in.
*
@ -778,6 +822,9 @@ class AuthComponent extends Component {
* @return void
*/
public function flash($message) {
if ($message === false) {
return;
}
$this->Session->setFlash($message, $this->flash['element'], $this->flash['params'], $this->flash['key']);
}

View file

@ -144,6 +144,9 @@ class RequestHandlerComponent extends Component {
* Compares the accepted types and configured extensions.
* If there is one common type, that is assigned as the ext/content type
* for the response.
* Type with the highest weight will be set. If the highest weight has more
* then one type matching the extensions, the order in which extensions are specified
* determines which type will be set.
*
* If html is one of the preferred types, no content type will be set, this
* is to avoid issues with browsers that prefer html and several other content types.
@ -155,13 +158,19 @@ class RequestHandlerComponent extends Component {
if (empty($accept)) {
return;
}
$accepts = $this->response->mapType($this->request->parseAccept());
$preferedTypes = current($accepts);
if (array_intersect($preferedTypes, array('html', 'xhtml'))) {
return null;
}
$extensions = Router::extensions();
$preferred = array_shift($accept);
$preferredTypes = $this->response->mapType($preferred);
if (!in_array('xhtml', $preferredTypes) && !in_array('html', $preferredTypes)) {
$similarTypes = array_intersect($extensions, $preferredTypes);
if (count($similarTypes) === 1) {
$this->ext = array_shift($similarTypes);
foreach ($accepts as $types) {
$ext = array_intersect($extensions, $types);
if ($ext) {
$this->ext = current($ext);
break;
}
}
}

View file

@ -94,7 +94,7 @@ class ComponentCollection extends ObjectCollection implements CakeEventListener
* @throws MissingComponentException when the component could not be found
*/
public function load($component, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) {
if (isset($settings['className'])) {
$alias = $component;
$component = $settings['className'];
}

View file

@ -705,7 +705,7 @@ class Controller extends Object implements CakeEventListener {
*
* @return array Associative array of the HTTP codes as keys, and the message
* strings as values, or null of the given $code does not exist.
* @deprecated Use CakeResponse::httpCodes();
* @deprecated Since 2.4. Will be removed in 3.0. Use CakeResponse::httpCodes().
*/
public function httpCodes($code = null) {
return $this->response->httpCodes($code);
@ -820,7 +820,7 @@ class Controller extends Object implements CakeEventListener {
*
* @param string $status The header message that is being set.
* @return void
* @deprecated Use CakeResponse::header()
* @deprecated Will be removed in 3.0. Use CakeResponse::header().
*/
public function header($status) {
$this->response->header($status);
@ -978,7 +978,7 @@ class Controller extends Object implements CakeEventListener {
*
* @return void
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache
* @deprecated Use CakeResponse::disableCache()
* @deprecated Will be removed in 3.0. Use CakeResponse::disableCache().
*/
public function disableCache() {
$this->response->disableCache();
@ -995,6 +995,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $layout Layout you want to use, defaults to 'flash'
* @return void
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::flash
* @deprecated Will be removed in 3.0. Use Session::setFlash().
*/
public function flash($message, $url, $pause = 1, $layout = 'flash') {
$this->autoRender = false;
@ -1015,7 +1016,7 @@ class Controller extends Object implements CakeEventListener {
* @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be
* included in the returned conditions
* @return array An array of model conditions
* @deprecated Will be removed in 3.0
* @deprecated Will be removed in 3.0.
*/
public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
if (!is_array($data) || empty($data)) {
@ -1072,7 +1073,7 @@ class Controller extends Object implements CakeEventListener {
* @param array $whitelist List of allowed options for paging
* @return array Model query results
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::paginate
* @deprecated Use PaginatorComponent instead
* @deprecated Will be removed in 3.0. Use PaginatorComponent instead.
*/
public function paginate($object = null, $scope = array(), $whitelist = array()) {
return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
@ -1146,7 +1147,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method
* @return boolean
* @see Controller::beforeScaffold()
* @deprecated
* @deprecated Will be removed in 3.0.
*/
protected function _beforeScaffold($method) {
return $this->beforeScaffold($method);
@ -1169,7 +1170,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method
* @return boolean
* @see Controller::afterScaffoldSave()
* @deprecated
* @deprecated Will be removed in 3.0.
*/
protected function _afterScaffoldSave($method) {
return $this->afterScaffoldSave($method);
@ -1192,7 +1193,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method
* @return boolean
* @see Controller::afterScaffoldSaveError()
* @deprecated
* @deprecated Will be removed in 3.0.
*/
protected function _afterScaffoldSaveError($method) {
return $this->afterScaffoldSaveError($method);
@ -1217,7 +1218,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method
* @return boolean
* @see Controller::scaffoldError()
* @deprecated
* @deprecated Will be removed in 3.0.
*/
protected function _scaffoldError($method) {
return $this->scaffoldError($method);

View file

@ -67,16 +67,14 @@ class Configure {
*/
public static function bootstrap($boot = true) {
if ($boot) {
self::write('App', array(
'base' => false,
'baseUrl' => false,
'dir' => APP_DIR,
'webroot' => WEBROOT_DIR,
'www_root' => WWW_ROOT
));
self::_appDefaults();
if (!include APP . 'Config' . DS . 'core.php') {
trigger_error(__d('cake_dev', "Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", APP . 'Config' . DS), E_USER_ERROR);
trigger_error(__d('cake_dev',
"Can't find application core file. Please create %s, and make sure it is readable by PHP.",
APP . 'Config' . DS . 'core.php'),
E_USER_ERROR
);
}
App::init();
App::$bootstrapping = false;
@ -92,7 +90,11 @@ class Configure {
self::_setErrorHandlers($error, $exception);
if (!include APP . 'Config' . DS . 'bootstrap.php') {
trigger_error(__d('cake_dev', "Can't find application bootstrap file. Please create %sbootstrap.php, and make sure it is readable by PHP.", APP . 'Config' . DS), E_USER_ERROR);
trigger_error(__d('cake_dev',
"Can't find application bootstrap file. Please create %s, and make sure it is readable by PHP.",
APP . 'Config' . DS . 'bootstrap.php'),
E_USER_ERROR
);
}
restore_error_handler();
@ -109,6 +111,20 @@ class Configure {
}
}
/**
* Set app's default configs
* @return void
*/
protected static function _appDefaults() {
self::write('App', (array)self::read('App') + array(
'base' => false,
'baseUrl' => false,
'dir' => APP_DIR,
'webroot' => WEBROOT_DIR,
'www_root' => WWW_ROOT
));
}
/**
* Used to store a dynamic variable in Configure.
*
@ -128,7 +144,8 @@ class Configure {
* }}}
*
* @link http://book.cakephp.org/2.0/en/development/configuration.html#Configure::write
* @param array $config Name of var to write
* @param string|array $config The key to write, can be a dot notation value.
* Alternatively can be an array containing key(s) and value(s).
* @param mixed $value Value to set for var
* @return boolean True if write was successful
*/
@ -323,7 +340,7 @@ class Configure {
throw new ConfigureException(__d('cake_dev', 'There is no "%s" adapter.', $config));
}
if (!method_exists($reader, 'dump')) {
throw new ConfigureException(__d('cake_dev', 'The "%s" adapter, does not have a dump() method.', $config));
throw new ConfigureException(__d('cake_dev', 'The "%s" adapter, does not have a %s method.', $config, 'dump()'));
}
$values = self::$_values;
if (!empty($keys) && is_array($keys)) {

View file

@ -16,7 +16,9 @@
App::uses('CakeLog', 'Log');
App::uses('Dispatcher', 'Routing');
App::uses('Router', 'Routing');
App::uses('Set', 'Utility');
App::uses('CakeLog', 'Log');
/**
* Object class provides a few generic methods used in several subclasses.
@ -86,8 +88,8 @@ class Object {
$data = isset($extra['data']) ? $extra['data'] : null;
unset($extra['data']);
if (is_string($url) && strpos($url, FULL_BASE_URL) === 0) {
$url = Router::normalize(str_replace(FULL_BASE_URL, '', $url));
if (is_string($url) && strpos($url, Router::fullBaseUrl()) === 0) {
$url = Router::normalize(str_replace(Router::fullBaseUrl(), '', $url));
}
if (is_string($url)) {
$request = new CakeRequest($url);
@ -148,17 +150,16 @@ class Object {
* Convenience method to write a message to CakeLog. See CakeLog::write()
* for more information on writing to logs.
*
* @param string $msg Log message.
* @param integer|string $type Type of message being written. Either a valid
* LOG_* constant or a string matching the recognized levels.
* @return boolean Success of log write.
* @see CakeLog::write()
* @param string $msg Log message
* @param integer $type Error type constant. Defined in app/Config/core.php.
* @return boolean Success of log write
*/
public function log($msg, $type = LOG_ERR) {
public function log($msg, $type = LOG_ERR, $scope = null) {
if (!is_string($msg)) {
$msg = print_r($msg, true);
}
return CakeLog::write($type, $msg);
return CakeLog::write($type, $msg, $scope);
}
/**

View file

@ -110,9 +110,8 @@ class ErrorHandler {
*/
public static function handleException(Exception $exception) {
$config = Configure::read('Exception');
if (!empty($config['log'])) {
CakeLog::write(LOG_ERR, self::_getMessage($exception));
}
self::_log($exception, $config);
$renderer = isset($config['renderer']) ? $config['renderer'] : 'ExceptionRenderer';
if ($renderer !== 'ExceptionRenderer') {
list($plugin, $renderer) = pluginSplit($renderer, true);
@ -159,6 +158,28 @@ class ErrorHandler {
return $message;
}
/**
* Handles exception logging
*
* @param Exception $exception
* @param array $config
* @return boolean
*/
protected static function _log(Exception $exception, $config) {
if (empty($config['log'])) {
return false;
}
if (!empty($config['skipLog'])) {
foreach ((array)$config['skipLog'] as $class) {
if ($exception instanceof $class) {
return false;
}
}
}
return CakeLog::write(LOG_ERR, self::_getMessage($exception));
}
/**
* Set as the default error handler by CakePHP. Use Configure::write('Error.handler', $callback), to use your own
* error handling methods. This function will use Debugger to display errors when debug > 0. And

View file

@ -39,7 +39,7 @@ class CakeBaseException extends RuntimeException {
* @param string|array $header. An array of header strings or a single header string
* - an associative array of "header name" => "header value"
* - an array of string headers is also accepted
* @param string $value. The header value.
* @param string $value The header value.
* @return array
* @see CakeResponse::header()
*/
@ -78,7 +78,7 @@ class BadRequestException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Bad Request' will be the message
* @param int $code Status code, defaults to 400
* @param integer $code Status code, defaults to 400
*/
public function __construct($message = null, $code = 400) {
if (empty($message)) {
@ -100,7 +100,7 @@ class UnauthorizedException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Unauthorized' will be the message
* @param int $code Status code, defaults to 401
* @param integer $code Status code, defaults to 401
*/
public function __construct($message = null, $code = 401) {
if (empty($message)) {
@ -122,7 +122,7 @@ class ForbiddenException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Forbidden' will be the message
* @param int $code Status code, defaults to 403
* @param integer $code Status code, defaults to 403
*/
public function __construct($message = null, $code = 403) {
if (empty($message)) {
@ -144,7 +144,7 @@ class NotFoundException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Not Found' will be the message
* @param int $code Status code, defaults to 404
* @param integer $code Status code, defaults to 404
*/
public function __construct($message = null, $code = 404) {
if (empty($message)) {
@ -166,7 +166,7 @@ class MethodNotAllowedException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Method Not Allowed' will be the message
* @param int $code Status code, defaults to 405
* @param integer $code Status code, defaults to 405
*/
public function __construct($message = null, $code = 405) {
if (empty($message)) {
@ -188,7 +188,7 @@ class InternalErrorException extends HttpException {
* Constructor
*
* @param string $message If no message is given 'Internal Server Error' will be the message
* @param int $code Status code, defaults to 500
* @param integer $code Status code, defaults to 500
*/
public function __construct($message = null, $code = 500) {
if (empty($message)) {
@ -230,7 +230,7 @@ class CakeException extends CakeBaseException {
*
* @param string|array $message Either the string of the error message, or an array of attributes
* that are made available in the view, and sprintf()'d into CakeException::$_messageTemplate
* @param int $code The code of the error, is also the HTTP status code for the error.
* @param integer $code The code of the error, is also the HTTP status code for the error.
*/
public function __construct($message, $code = 500) {
if (is_array($message)) {

View file

@ -39,7 +39,7 @@ class L10n {
*
* @var array
*/
public $languagePath = array('eng');
public $languagePath = array('en_us', 'eng');
/**
* ISO 639-3 for current locale
@ -56,9 +56,11 @@ class L10n {
public $locale = 'en_us';
/**
* Default ISO 639-3 language.
* Default language.
*
* DEFAULT_LANGUAGE is defined in an application this will be set as a fall back
* If config value 'Config.language' is set in an application this will be set
* as a fall back else if DEFAULT_LANGUAGE it defined it will be used.
* Constant DEFAULT_LANGUAGE has been deprecated in 2.4
*
* @var string
*/
@ -78,13 +80,6 @@ class L10n {
*/
public $direction = 'ltr';
/**
* Set to true if a locale is found
*
* @var string
*/
public $found = false;
/**
* Maps ISO 639-3 to I10n::_l10nCatalog
* The terminological codes (first one per language) should be used if possible.
@ -138,6 +133,8 @@ class L10n {
/* Irish */ 'gle' => 'ga',
/* Italian */ 'ita' => 'it',
/* Japanese */ 'jpn' => 'ja',
/* Kazakh */ 'kaz' => 'kk',
/* Kalaallisut (Greenlandic) */ 'kal' => 'kl',
/* Korean */ 'kor' => 'ko',
/* Latvian */ 'lav' => 'lv',
/* Lithuanian */ 'lit' => 'lt',
@ -155,7 +152,7 @@ class L10n {
/* Romanian */ 'ron' => 'ro',
/* Romanian - bibliographic */ 'rum' => 'ro',
/* Russian */ 'rus' => 'ru',
/* Sami (Lappish) */ 'smi' => 'sz',
/* Sami */ 'sme' => 'se',
/* Serbian */ 'srp' => 'sr',
/* Slovak */ 'slk' => 'sk',
/* Slovak - bibliographic */ 'slo' => 'sk',
@ -219,8 +216,7 @@ class L10n {
'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'el' => array('language' => 'Greek', 'locale' => 'ell', 'localeFallback' => 'ell', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -254,7 +250,7 @@ class L10n {
'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr'),
'eu' => array('language' => 'Basque', 'locale' => 'eus', 'localeFallback' => 'eus', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl'),
'fa' => array('language' => 'Farsi', 'locale' => 'fas', 'localeFallback' => 'fas', 'charset' => 'utf-8', 'direction' => 'rtl'),
'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr' => array('language' => 'French (Standard)', 'locale' => 'fra', 'localeFallback' => 'fra', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -273,28 +269,27 @@ class L10n {
'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'),
'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
'is' => array('language' => 'Icelandic', 'locale' => 'isl', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'),
'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'),
'kk' => array('language' => 'Kazakh', 'locale' => 'kaz', 'localeFallback' => 'kaz', 'charset' => 'utf-8', 'direction' => 'ltr'),
'kl' => array('language' => 'Kalaallisut (Greenlandic)', 'locale' => 'kal', 'localeFallback' => 'kal', 'charset' => 'kl', 'direction' => 'ltr'),
'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r', 'direction' => 'ltr'),
'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr'),
'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mkd', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mkd', 'localeFallback' => 'mkd', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mkd', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ms' => array('language' => 'Malaysian', 'locale' => 'msa', 'localeFallback' => 'msa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8', 'direction' => 'ltr'),
'n' => array('language' => 'Dutch (Standard)', 'locale' => 'nld', 'localeFallback' => 'nld', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'nld', 'localeFallback' => 'nld', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'nld', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -310,8 +305,7 @@ class L10n {
'sr' => array('language' => 'Serbian', 'locale' => 'srp', 'localeFallback' => 'srp', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sv-fi' => array('language' => 'Swedish (Finland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'se' => array('language' => 'Sami', 'locale' => 'sme', 'localeFallback' => 'sme', 'charset' => 'utf-8', 'direction' => 'ltr'),
'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8', 'direction' => 'ltr'),
'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8', 'direction' => 'ltr'),
'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -338,6 +332,10 @@ class L10n {
if (defined('DEFAULT_LANGUAGE')) {
$this->default = DEFAULT_LANGUAGE;
}
$default = Configure::read('Config.language');
if ($default) {
$this->default = $default;
}
}
/**
@ -361,44 +359,44 @@ class L10n {
/**
* Sets the class vars to correct values for $language.
* If $language is null it will use the DEFAULT_LANGUAGE if defined
* If $language is null it will use the L10n::$default if defined
*
* @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
* @param string $language Language (if null will use L10n::$default if defined)
* @return mixed
*/
protected function _setLanguage($language = null) {
$langKey = null;
if ($language !== null && isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) {
$langKey = $this->_l10nMap[$language];
} elseif ($language !== null && isset($this->_l10nCatalog[$language])) {
$langKey = $language;
} elseif (defined('DEFAULT_LANGUAGE')) {
$langKey = $language = DEFAULT_LANGUAGE;
$catalog = false;
if ($language !== null) {
$catalog = $this->catalog($language);
}
if ($langKey !== null && isset($this->_l10nCatalog[$langKey])) {
$this->language = $this->_l10nCatalog[$langKey]['language'];
$this->languagePath = array(
$this->_l10nCatalog[$langKey]['locale'],
$this->_l10nCatalog[$langKey]['localeFallback']
);
if (!$catalog && $this->default) {
$language = $this->default;
$catalog = $this->catalog($language);
}
if ($catalog) {
$this->language = $catalog['language'];
$this->languagePath = array_unique(array(
$catalog['locale'],
$catalog['localeFallback']
));
$this->lang = $language;
$this->locale = $this->_l10nCatalog[$langKey]['locale'];
$this->charset = $this->_l10nCatalog[$langKey]['charset'];
$this->direction = $this->_l10nCatalog[$langKey]['direction'];
} else {
$this->locale = $catalog['locale'];
$this->charset = $catalog['charset'];
$this->direction = $catalog['direction'];
} elseif ($language) {
$this->lang = $language;
$this->languagePath = array($language);
}
if ($this->default) {
if (isset($this->_l10nMap[$this->default]) && isset($this->_l10nCatalog[$this->_l10nMap[$this->default]])) {
$this->languagePath[] = $this->_l10nCatalog[$this->_l10nMap[$this->default]]['localeFallback'];
} elseif (isset($this->_l10nCatalog[$this->default])) {
$this->languagePath[] = $this->_l10nCatalog[$this->default]['localeFallback'];
if ($this->default && $language !== $this->default) {
$catalog = $this->catalog($this->default);
$fallback = $catalog['localeFallback'];
if (!in_array($fallback, $this->languagePath)) {
$this->languagePath[] = $fallback;
}
}
$this->found = true;
if (Configure::read('Config.language') === null) {
Configure::write('Config.language', $this->lang);
@ -420,7 +418,8 @@ class L10n {
if (isset($this->_l10nCatalog[$langKey])) {
$this->_setLanguage($langKey);
return true;
} elseif (strpos($langKey, '-') !== false) {
}
if (strpos($langKey, '-') !== false) {
$langKey = substr($langKey, 0, 2);
if (isset($this->_l10nCatalog[$langKey])) {
$this->_setLanguage($langKey);
@ -447,10 +446,12 @@ class L10n {
}
}
return $result;
} elseif (is_string($mixed)) {
}
if (is_string($mixed)) {
if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) {
return array_search($mixed, $this->_l10nMap);
} elseif (isset($this->_l10nMap[$mixed])) {
}
if (isset($this->_l10nMap[$mixed])) {
return $this->_l10nMap[$mixed];
}
return false;
@ -474,10 +475,12 @@ class L10n {
}
}
return $result;
} elseif (is_string($language)) {
}
if (is_string($language)) {
if (isset($this->_l10nCatalog[$language])) {
return $this->_l10nCatalog[$language];
} elseif (isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) {
}
if (isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) {
return $this->_l10nCatalog[$this->_l10nMap[$language]];
}
return false;

View file

@ -34,7 +34,7 @@ App::uses('LogEngineCollection', 'Log');
* A sample configuration would look like:
*
* {{{
* CakeLog::config('my_log', array('engine' => 'FileLog'));
* CakeLog::config('my_log', array('engine' => 'File'));
* }}}
*
* See the documentation on CakeLog::config() for more detail.
@ -133,7 +133,7 @@ class CakeLog {
*
* {{{
* CakeLog::config('second_file', array(
* 'engine' => 'FileLog',
* 'engine' => 'File',
* 'path' => '/var/logs/my_app/'
* ));
* }}}
@ -378,7 +378,7 @@ class CakeLog {
*/
protected static function _autoConfig() {
self::$_Collection->load('default', array(
'engine' => 'FileLog',
'engine' => 'File',
'path' => LOGS,
));
}

View file

@ -20,6 +20,7 @@
App::uses('BaseLog', 'Log/Engine');
App::uses('Hash', 'Utility');
App::uses('CakeNumber', 'Utility');
/**
* File Storage stream for Logging. Writes logs to different files
@ -29,6 +30,22 @@ App::uses('Hash', 'Utility');
*/
class FileLog extends BaseLog {
/**
* Default configuration values
*
* @var array
* @see FileLog::__construct()
*/
protected $_defaults = array(
'path' => LOGS,
'file' => null,
'types' => null,
'scopes' => array(),
'rotate' => 10,
'size' => 10485760, // 10MB
'mask' => null,
);
/**
* Path to save log files on.
*
@ -36,6 +53,20 @@ class FileLog extends BaseLog {
*/
protected $_path = null;
/**
* Log file name
*
* @var string
*/
protected $_file = null;
/**
* Max file size, used for log file rotation.
*
* @var integer
*/
protected $_size = null;
/**
* Constructs a new File Logger.
*
@ -43,25 +74,55 @@ class FileLog extends BaseLog {
*
* - `types` string or array, levels the engine is interested in
* - `scopes` string or array, scopes the engine is interested in
* - `file` log file name
* - `path` the path to save logs on.
* - `file` Log file name
* - `path` The path to save logs on.
* - `size` Used to implement basic log file rotation. If log file size
* reaches specified size the existing file is renamed by appending timestamp
* to filename and new log file is created. Can be integer bytes value or
* human reabable string values like '10MB', '100KB' etc.
* - `rotate` Log files are rotated specified times before being removed.
* If value is 0, old versions are removed rather then rotated.
* - `mask` A mask is applied when log files are created. Left empty no chmod
* is made.
*
* @param array $options Options for the FileLog, see above.
*/
public function __construct($config = array()) {
$config = Hash::merge($this->_defaults, $config);
parent::__construct($config);
$config = Hash::merge(array(
'path' => LOGS,
'file' => null,
'types' => null,
'scopes' => array(),
), $this->_config);
$config = $this->config($config);
$this->_path = $config['path'];
$this->_file = $config['file'];
if (!empty($this->_file) && substr($this->_file, -4) !== '.log') {
$this->_file .= '.log';
}
/**
* Sets protected properties based on config provided
*
* @param array $config Engine configuration
* @return array
*/
public function config($config = array()) {
parent::config($config);
if (!empty($config['path'])) {
$this->_path = $config['path'];
}
if (Configure::read('debug') && !is_dir($this->_path)) {
mkdir($this->_path, 0775, true);
}
if (!empty($config['file'])) {
$this->_file = $config['file'];
if (substr($this->_file, -4) !== '.log') {
$this->_file .= '.log';
}
}
if (!empty($config['size'])) {
if (is_numeric($config['size'])) {
$this->_size = (int)$config['size'];
} else {
$this->_size = CakeNumber::fromReadableSize($config['size']);
}
}
return $this->_config;
}
/**
@ -72,19 +133,85 @@ class FileLog extends BaseLog {
* @return boolean success of write.
*/
public function write($type, $message) {
$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
$filename = $this->_getFilename($type);
if (!empty($this->_size)) {
$this->_rotateFile($filename);
}
$pathname = $this->_path . $filename;
if (empty($this->_config['mask'])) {
return file_put_contents($pathname, $output, FILE_APPEND);
}
$exists = file_exists($pathname);
$result = file_put_contents($pathname, $output, FILE_APPEND);
static $selfError = false;
if (!$selfError && !$exists && !chmod($pathname, (int)$this->_config['mask'])) {
$selfError = true;
trigger_error(__d(
'cake_dev', 'Could not apply permission mask "%s" on log file "%s"',
array($this->_config['mask'], $pathname)), E_USER_WARNING);
$selfError = false;
}
return $result;
}
/**
* Get filename
* @param string $type The type of log.
* @return string File name
*/
protected function _getFilename($type) {
$debugTypes = array('notice', 'info', 'debug');
if (!empty($this->_file)) {
$filename = $this->_path . $this->_file;
} elseif ($type === 'error' || $type === 'warning') {
$filename = $this->_path . 'error.log';
$filename = $this->_file;
} elseif ($type == 'error' || $type == 'warning') {
$filename = 'error.log';
} elseif (in_array($type, $debugTypes)) {
$filename = $this->_path . 'debug.log';
$filename = 'debug.log';
} else {
$filename = $this->_path . $type . '.log';
$filename = $type . '.log';
}
$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
return file_put_contents($filename, $output, FILE_APPEND);
return $filename;
}
/**
* Rotate log file if size specified in config is reached.
* Also if `rotate` count is reached oldest file is removed.
*
* @param string $filename Log file name
* @return mixed True if rotated successfully or false in case of error.
* Void if file doesn't need to be rotated.
*/
protected function _rotateFile($filename) {
$filepath = $this->_path . $filename;
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
clearstatcache(true, $filepath);
} else {
clearstatcache();
}
if (!file_exists($filepath) ||
filesize($filepath) < $this->_size
) {
return;
}
if ($this->_config['rotate'] === 0) {
return unlink($filepath);
}
if ($this->_config['rotate']) {
$files = glob($filepath . '.*');
if (count($files) === $this->_config['rotate']) {
unlink(array_shift($files));
}
}
return rename($filepath, $filepath . '.' . time());
}
}

View file

@ -0,0 +1,164 @@
<?php
/**
* Syslog logger engine for CakePHP
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package Cake.Log.Engine
* @since CakePHP(tm) v 2.4
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::uses('BaseLog', 'Log/Engine');
/**
* Syslog stream for Logging. Writes logs to the system logger
*
* @package Cake.Log.Engine
*/
class SyslogLog extends BaseLog {
/**
*
* By default messages are formatted as:
* type: message
*
* To override the log format (e.g. to add your own info) define the format key when configuring
* this logger
*
* If you wish to include a prefix to all messages, for instance to identify the
* application or the web server, then use the prefix option. Please keep in mind
* the prefix is shared by all streams using syslog, as it is dependent of
* the running process. For a local prefix, to be used only by one stream, you
* can use the format key.
*
* ## Example:
*
* {{{
* CakeLog::config('error', array(
* 'engine' => 'Syslog',
* 'types' => array('emergency', 'alert', 'critical', 'error'),
* 'format' => "%s: My-App - %s",
* 'prefix' => 'Web Server 01'
* ));
* }}}
*
* @var array
*/
protected $_defaults = array(
'format' => '%s: %s',
'flag' => LOG_ODELAY,
'prefix' => '',
'facility' => LOG_USER
);
/**
*
* Used to map the string names back to their LOG_* constants
*
* @var array
*/
protected $_priorityMap = array(
'emergency' => LOG_EMERG,
'alert' => LOG_ALERT,
'critical' => LOG_CRIT,
'error' => LOG_ERR,
'warning' => LOG_WARNING,
'notice' => LOG_NOTICE,
'info' => LOG_INFO,
'debug' => LOG_DEBUG
);
/**
* Whether the logger connection is open or not
*
* @var boolean
*/
protected $_open = false;
/**
* Make sure the configuration contains the format parameter, by default it uses
* the error number and the type as a prefix to the message
*
* @param array $config
*/
public function __construct($config = array()) {
$config += $this->_defaults;
parent::__construct($config);
}
/**
* Writes a message to syslog
*
* Map the $type back to a LOG_ constant value, split multi-line messages into multiple
* log messages, pass all messages through the format defined in the configuration
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
* @return boolean success of write.
*/
public function write($type, $message) {
if (!$this->_open) {
$config = $this->_config;
$this->_open($config['prefix'], $config['flag'], $config['facility']);
$this->_open = true;
}
$priority = LOG_DEBUG;
if (isset($this->_priorityMap[$type])) {
$priority = $this->_priorityMap[$type];
}
$messages = explode("\n", $message);
foreach ($messages as $message) {
$message = sprintf($this->_config['format'], $type, $message);
$this->_write($priority, $message);
}
return true;
}
/**
* Extracts the call to openlog() in order to run unit tests on it. This function
* will initialize the connection to the system logger
*
* @param string $ident the prefix to add to all messages logged
* @param integer $options the options flags to be used for logged messages
* @param integer $facility the stream or facility to log to
* @return void
*/
protected function _open($ident, $options, $facility) {
openlog($ident, $options, $facility);
}
/**
* Extracts the call to syslog() in order to run unit tests on it. This function
* will perform the actual write in the system logger
*
* @param integer $priority
* @param string $message
* @return bool
*/
protected function _write($priority, $message) {
return syslog($priority, $message);
}
/**
* Closes the logger connection
*
* @return void
**/
public function __destruct() {
closelog();
}
}

View file

@ -42,9 +42,9 @@ class LogEngineCollection extends ObjectCollection {
$className = $this->_getLogger($loggerName);
$logger = new $className($options);
if (!$logger instanceof CakeLogInterface) {
throw new CakeLogException(sprintf(
__d('cake_dev', 'logger class %s does not implement a write method.'), $loggerName
));
throw new CakeLogException(
__d('cake_dev', 'logger class %s does not implement a %s method.', $loggerName, 'write()')
);
}
$this->_loaded[$name] = $logger;
if ($enable) {
@ -63,7 +63,9 @@ class LogEngineCollection extends ObjectCollection {
*/
protected static function _getLogger($loggerName) {
list($plugin, $loggerName) = pluginSplit($loggerName, true);
if (substr($loggerName, -3) !== 'Log') {
$loggerName .= 'Log';
}
App::uses($loggerName, $plugin . 'Log/Engine');
if (!class_exists($loggerName)) {
throw new CakeLogException(__d('cake_dev', 'Could not load class %s', $loggerName));

View file

@ -65,7 +65,7 @@ class AclBehavior extends ModelBehavior {
$model->{$type} = ClassRegistry::init($type);
}
if (!method_exists($model, 'parentNode')) {
trigger_error(__d('cake_dev', 'Callback parentNode() not defined in %s', $model->alias), E_USER_WARNING);
trigger_error(__d('cake_dev', 'Callback %s not defined in %s', 'parentNode()', $model->alias), E_USER_WARNING);
}
}

View file

@ -628,19 +628,8 @@ class TreeBehavior extends ModelBehavior {
$Model->updateAll(array($Model->escapeField($parent) => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)));
}
}
$count = 1;
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey), 'order' => $left)) as $array) {
$lft = $count++;
$rght = $count++;
$Model->create(false);
$Model->id = $array[$Model->alias][$Model->primaryKey];
$Model->save(array($left => $lft, $right => $rght), array('callbacks' => false, 'validate' => false));
}
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
$Model->create(false);
$Model->id = $array[$Model->alias][$Model->primaryKey];
$this->_setParent($Model, $array[$Model->alias][$parent]);
}
$this->_recoverByParentId($Model);
} else {
$db = ConnectionManager::getDataSource($Model->useDbConfig);
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
@ -655,6 +644,77 @@ class TreeBehavior extends ModelBehavior {
return true;
}
/**
* _recoverByParentId
*
* Recursive helper function used by recover
*
* @param Model $Model
* @param integer $counter
* @param mixed $parentId
* @return integer $counter
*/
protected function _recoverByParentId(Model $Model, $counter = 1, $parentId = null) {
$params = array(
'conditions' => array(
$this->settings[$Model->alias]['parent'] => $parentId
),
'fields' => array($Model->primaryKey),
'page' => 1,
'limit' => 100,
'order' => array($Model->primaryKey)
);
$scope = $this->settings[$Model->alias]['scope'];
if ($scope && ($scope !== '1 = 1' && $scope !== true)) {
$conditions[] = $scope;
}
$children = $Model->find('all', $params);
$hasChildren = (bool)$children;
if (!is_null($parentId)) {
if ($hasChildren) {
$Model->updateAll(
array($this->settings[$Model->alias]['left'] => $counter),
array($Model->escapeField() => $parentId)
);
$counter++;
} else {
$Model->updateAll(
array(
$this->settings[$Model->alias]['left'] => $counter,
$this->settings[$Model->alias]['right'] => $counter + 1
),
array($Model->escapeField() => $parentId)
);
$counter += 2;
}
}
while ($children) {
foreach ($children as $row) {
$counter = $this->_recoverByParentId($Model, $counter, $row[$Model->alias][$Model->primaryKey]);
}
if (count($children) !== $params['limit']) {
break;
}
$params['page']++;
$children = $Model->find('all', $params);
}
if (!is_null($parentId) && $hasChildren) {
$Model->updateAll(
array($this->settings[$Model->alias]['right'] => $counter),
array($Model->escapeField() => $parentId)
);
$counter++;
}
return $counter;
}
/**
* Reorder method.
*
@ -729,10 +789,9 @@ class TreeBehavior extends ModelBehavior {
if ($node[$right] == $node[$left] + 1) {
if ($delete) {
return $Model->delete($id);
} else {
$Model->id = $id;
return $Model->saveField($parent, null);
}
$Model->id = $id;
return $Model->saveField($parent, null);
} elseif ($node[$parent]) {
list($parentNode) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $node[$parent]),

View file

@ -76,7 +76,7 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
* @param string $behavior
* @param array $config
* @return void
* @deprecated Replaced with load()
* @deprecated Will be removed in 3.0. Replaced with load().
*/
public function attach($behavior, $config = array()) {
return $this->load($behavior, $config);
@ -103,7 +103,7 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
* @throws MissingBehaviorException when a behavior could not be found.
*/
public function load($behavior, $config = array()) {
if (is_array($config) && isset($config['className'])) {
if (isset($config['className'])) {
$alias = $behavior;
$behavior = $config['className'];
}
@ -206,7 +206,7 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
*
* @param string $name Name of behavior
* @return void
* @deprecated Use unload instead.
* @deprecated Will be removed in 3.0. Use unload instead.
*/
public function detach($name) {
return $this->unload($name);
@ -228,7 +228,7 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
$method = $this->hasMethod($method, true);
if ($strict && empty($method)) {
trigger_error(__d('cake_dev', "BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", $method), E_USER_WARNING);
trigger_error(__d('cake_dev', '%s - Method %s not found in any attached behavior', 'BehaviorCollection::dispatchMethod()', $method), E_USER_WARNING);
return null;
}
if (empty($method)) {

View file

@ -476,7 +476,7 @@ class CakeSchema extends Object {
continue;
}
if (!array_key_exists($table, $old)) {
$tables[$table]['add'] = $fields;
$tables[$table]['create'] = $fields;
} else {
$diff = $this->_arrayDiffAssoc($fields, $old[$table]);
if (!empty($diff)) {

View file

@ -132,7 +132,7 @@ class CakeSession {
self::$time = time();
$checkAgent = Configure::read('Session.checkAgent');
if (($checkAgent === true || $checkAgent === null) && env('HTTP_USER_AGENT')) {
if (env('HTTP_USER_AGENT')) {
self::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
}
self::_setPath($base);
@ -486,10 +486,7 @@ class CakeSession {
if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
foreach ($sessionConfig['ini'] as $setting => $value) {
if (ini_set($setting, $value) === false) {
throw new CakeSessionException(sprintf(
__d('cake_dev', 'Unable to configure the session, setting %s failed.'),
$setting
));
throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
}
}
}

View file

@ -130,6 +130,15 @@ class Mysql extends DboSource {
/**
* Connects to the database using options in the given configuration array.
*
* MySQL supports a few additional options that other drivers do not:
*
* - `unix_socket` Set to the path of the MySQL sock file. Can be used in place
* of host + port.
* - `ssl_key` SSL key file for connecting via SSL. Must be combined with `ssl_cert`.
* - `ssl_cert` The SSL certificate to use when connecting via SSL. Must be
* combined with `ssl_key`.
* - `ssl_ca` The certificate authority for SSL connections.
*
* @return boolean True if the database could be connected, else false
* @throws MissingConnectionException
*/
@ -146,7 +155,13 @@ class Mysql extends DboSource {
if (!empty($config['encoding'])) {
$flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['encoding'];
}
if (!empty($config['ssl_key']) && !empty($config['ssl_cert'])) {
$flags[PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key'];
$flags[PDO::MYSQL_ATTR_SSL_CERT] = $config['ssl_cert'];
}
if (!empty($config['ssl_ca'])) {
$flags[PDO::MYSQL_ATTR_SSL_CA] = $config['ssl_ca'];
}
if (empty($config['unix_socket'])) {
$dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
} else {
@ -161,6 +176,11 @@ class Mysql extends DboSource {
$flags
);
$this->connected = true;
if (!empty($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->_execute("SET $key=$value");
}
}
} catch (PDOException $e) {
throw new MissingConnectionException(array(
'class' => get_class($this),

View file

@ -131,6 +131,11 @@ class Postgres extends DboSource {
if (!empty($config['schema'])) {
$this->_execute('SET search_path TO ' . $config['schema']);
}
if (!empty($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->_execute("SET $key TO $value");
}
}
} catch (PDOException $e) {
throw new MissingConnectionException(array(
'class' => get_class($this),

View file

@ -135,6 +135,11 @@ class Sqlserver extends DboSource {
$flags
);
$this->connected = true;
if (!empty($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->_execute("SET $key $value");
}
}
} catch (PDOException $e) {
throw new MissingConnectionException(array(
'class' => get_class($this),

View file

@ -898,7 +898,7 @@ class DboSource extends DataSource {
if (PHP_SAPI !== 'cli') {
$controller = null;
$View = new View($controller, false);
$View->set('logs', array($this->configKeyName => $log));
$View->set('sqlLogs', array($this->configKeyName => $log));
echo $View->element('sql_dump', array('_forced_from_dbo_' => true));
} else {
foreach ($log['log'] as $k => $i) {
@ -2489,7 +2489,7 @@ class DboSource extends DataSource {
$keys = array_keys($value);
if ($keys === array_values($keys)) {
$count = count($value);
if ($count === 1 && !preg_match("/\s+NOT$/", $key)) {
if ($count === 1 && !preg_match('/\s+(?:NOT|\!=)$/', $key)) {
$data = $this->_quoteFields($key) . ' = (';
if ($quoteValues) {
if (is_object($model)) {

View file

@ -1497,6 +1497,17 @@ class Model extends Object implements CakeEventListener {
return $this->data;
}
/**
* This function is a convenient wrapper class to create(false) and, as the name suggests, clears the id, data, and validation errors.
*
* @return always boolean TRUE upon success
* @see Model::create()
*/
public function clear() {
$this->create(false);
return true;
}
/**
* Returns a list of fields from the database, and sets the current model
* data (Model::$data) with the record found.
@ -1575,7 +1586,8 @@ class Model extends Object implements CakeEventListener {
* @param mixed $value Value of the field
* @param boolean|array $validate Either a boolean, or an array.
* If a boolean, indicates whether or not to validate before saving.
* If an array, allows control of 'validate' and 'callbacks' options.
* If an array, allows control of 'validate', 'callbacks' and 'counterCache' options.
* See Model::save() for details of each options.
* @return boolean See Model::save()
* @see Model::save()
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false
@ -1604,13 +1616,17 @@ class Model extends Object implements CakeEventListener {
* - fieldList: An array of fields you want to allow for saving.
* - callbacks: Set to false to disable callbacks. Using 'before' or 'after'
* will enable only those callbacks.
* - `counterCache`: Boolean to control updating of counter caches (if any)
*
* @param array $fieldList List of fields to allow to be saved
* @return mixed On success Model::$data if its not empty or true, false on failure
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html
*/
public function save($data = null, $validate = true, $fieldList = array()) {
$defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true);
$defaults = array(
'validate' => true, 'fieldList' => array(),
'callbacks' => true, 'counterCache' => true
);
$_whitelist = $this->whitelist;
$fields = array();
@ -1749,7 +1765,7 @@ class Model extends Object implements CakeEventListener {
}
}
if ($success && !empty($this->belongsTo)) {
if ($success && $options['counterCache'] && !empty($this->belongsTo)) {
$this->updateCounterCache($cache, $created);
}
}
@ -2030,7 +2046,9 @@ class Model extends Object implements CakeEventListener {
* 'AssociatedModel' => array('field', 'otherfield')
* )
* }}}
* - `deep`: see saveMany/saveAssociated
* - `deep`: See saveMany/saveAssociated
* - `callbacks`: See Model::save()
* - `counterCache`: See Model::save()
*
* @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple
* records of the same type), or an array indexed by association name.
@ -2066,6 +2084,8 @@ class Model extends Object implements CakeEventListener {
* Should be set to false if database/table does not support transactions.
* - `fieldList`: Equivalent to the $fieldList parameter in Model::save()
* - `deep`: If set to true, all associated data will be saved as well.
* - `callbacks`: See Model::save()
* - `counterCache`: See Model::save()
*
* @param array $data Record data to save. This should be a numerically-indexed array
* @param array $options Options to use when saving record data, See $options above.
@ -2179,6 +2199,8 @@ class Model extends Object implements CakeEventListener {
* )
* }}}
* - `deep`: If set to true, not only directly associated data is saved, but deeper nested associated data as well.
* - `callbacks`: See Model::save()
* - `counterCache`: See Model::save()
*
* @param array $data Record data to save. This should be an array indexed by association name.
* @param array $options Options to use when saving record data, See $options above.
@ -2697,6 +2719,34 @@ class Model extends Object implements CakeEventListener {
return null;
}
return $this->_readDataSource($type, $query);
}
/**
* Read from the datasource
*
* Model::_readDataSource() is used by all find() calls to read from the data source and can be overloaded to allow
* caching of datasource calls.
*
* {{{
* protected function _readDataSource($type, $query) {
* $cacheName = md5(json_encode($query));
* $cache = Cache::read($cacheName, 'cache-config-name');
* if ($cache !== false) {
* return $cache;
* }
*
* $results = parent::_readDataSource($type, $query);
* Cache::write($cacheName, $results, 'cache-config-name');
* return $results;
* }
* }}}
*
* @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
* @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
* @return array
*/
protected function _readDataSource($type, $query) {
$results = $this->getDataSource()->read($this, $query);
$this->resetAssociations();
@ -2706,10 +2756,6 @@ class Model extends Object implements CakeEventListener {
$this->findQueryType = null;
if ($type === 'all') {
return $results;
}
if ($this->findMethods[$type] === true) {
return $this->{'_find' . ucfirst($type)}('after', $query, $results);
}
@ -2732,7 +2778,7 @@ class Model extends Object implements CakeEventListener {
(array)$query
);
if ($type !== 'all' && $this->findMethods[$type] === true) {
if ($this->findMethods[$type] === true) {
$query = $this->{'_find' . ucfirst($type)}('before', $query);
}
@ -2760,6 +2806,23 @@ class Model extends Object implements CakeEventListener {
return $query;
}
/**
* Handles the before/after filter logic for find('all') operations. Only called by Model::find().
*
* @param string $state Either "before" or "after"
* @param array $query
* @param array $results
* @return array
* @see Model::find()
*/
protected function _findAll($state, $query, $results = array()) {
if ($state === 'before') {
return $query;
} elseif ($state === 'after') {
return $results;
}
}
/**
* Handles the before/after filter logic for find('first') operations. Only called by Model::find().
*

View file

@ -26,13 +26,6 @@ App::uses('AppModel', 'Model');
*/
class Permission extends AppModel {
/**
* Model name
*
* @var string
*/
public $name = 'Permission';
/**
* Explicitly disable in-memory query caching
*
@ -81,7 +74,7 @@ class Permission extends AppModel {
* @param string $action Action (defaults to *)
* @return boolean Success (true if ARO has access to action in ACO, false otherwise)
*/
public function check($aro, $aco, $action = "*") {
public function check($aro, $aco, $action = '*') {
if (!$aro || !$aco) {
return false;
}
@ -91,17 +84,29 @@ class Permission extends AppModel {
$acoPath = $this->Aco->node($aco);
if (!$aroPath || !$acoPath) {
trigger_error(__d('cake_dev', "DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
trigger_error(__d('cake_dev',
"%s - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: %s\nAco: %s",
'DbAcl::check()',
print_r($aro, true),
print_r($aco, true)),
E_USER_WARNING
);
return false;
}
if (!$acoPath) {
trigger_error(__d('cake_dev', "DbAcl::check() - Failed ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
trigger_error(__d('cake_dev',
"%s - Failed ACO node lookup in permissions check. Node references:\nAro: %s\nAco: %s",
'DbAcl::check()',
print_r($aro, true),
print_r($aco, true)),
E_USER_WARNING
);
return false;
}
if ($action !== '*' && !in_array('_' . $action, $permKeys)) {
trigger_error(__d('cake_dev', "ACO permissions key %s does not exist in DbAcl::check()", $action), E_USER_NOTICE);
trigger_error(__d('cake_dev', "ACO permissions key %s does not exist in %s", $action, 'DbAcl::check()'), E_USER_NOTICE);
return false;
}
@ -166,20 +171,20 @@ class Permission extends AppModel {
* @return boolean Success
* @throws AclException on Invalid permission key.
*/
public function allow($aro, $aco, $actions = "*", $value = 1) {
public function allow($aro, $aco, $actions = '*', $value = 1) {
$perms = $this->getAclLink($aro, $aco);
$permKeys = $this->getAcoKeys($this->schema());
$save = array();
if (!$perms) {
trigger_error(__d('cake_dev', 'DbAcl::allow() - Invalid node'), E_USER_WARNING);
trigger_error(__d('cake_dev', '%s - Invalid node', 'DbAcl::allow()'), E_USER_WARNING);
return false;
}
if (isset($perms[0])) {
$save = $perms[0][$this->alias];
}
if ($actions === "*") {
if ($actions === '*') {
$save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
} else {
if (!is_array($actions)) {

View file

@ -238,7 +238,7 @@ class CakeRequest implements ArrayAccess {
if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) {
$uri = $_SERVER['REQUEST_URI'];
} else {
$uri = substr($_SERVER['REQUEST_URI'], strlen(FULL_BASE_URL));
$uri = substr($_SERVER['REQUEST_URI'], strlen(Configure::read('App.fullBaseUrl')));
}
} elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) {
$uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']);
@ -424,10 +424,7 @@ class CakeRequest implements ArrayAccess {
$ref = $forwarded;
}
$base = '';
if (defined('FULL_BASE_URL')) {
$base = FULL_BASE_URL . $this->webroot;
}
$base = Configure::read('App.fullBaseUrl') . $this->webroot;
if (!empty($ref) && !empty($base)) {
if ($local && strpos($ref, $base) === 0) {
$ref = substr($ref, strlen($base));
@ -485,14 +482,21 @@ class CakeRequest implements ArrayAccess {
}
/**
* Check whether or not a Request is a certain type. Uses the built in detection rules
* as well as additional rules defined with CakeRequest::addDetector(). Any detector can be called
* Check whether or not a Request is a certain type.
*
* Uses the built in detection rules as well as additional rules
* defined with CakeRequest::addDetector(). Any detector can be called
* as `is($type)` or `is$Type()`.
*
* @param string $type The type of request you want to check.
* @param string|array $type The type of request you want to check. If an array
* this method will return true if the request matches any type.
* @return boolean Whether or not the request is the type you are checking.
*/
public function is($type) {
if (is_array($type)) {
$result = array_map(array($this, 'is'), $type);
return count(array_filter($result)) > 0;
}
$type = strtolower($type);
if (!isset($this->_detectors[$type])) {
return false;
@ -521,6 +525,22 @@ class CakeRequest implements ArrayAccess {
return false;
}
/**
* Check that a request matches all the given types.
*
* Allows you to test multiple types and union the results.
* See CakeRequest::is() for how to add additional types and the
* built-in types.
*
* @param array $types The types to check.
* @return boolean Success.
* @see CakeRequest::is()
*/
public function isAll(array $types) {
$result = array_filter(array_map(array($this, 'is'), $types));
return count($result) === count($types);
}
/**
* Add a new detector to the list of detectors that a request can use.
* There are several different formats and types of detectors that can be set.
@ -825,6 +845,20 @@ class CakeRequest implements ArrayAccess {
return Hash::get($this->data, $name);
}
/**
* Safely access the values in $this->params.
*
* @param string $name The name of the parameter to get.
* @return mixed The value of the provided parameter. Will
* return false if the parameter doesn't exist or is falsey.
*/
public function param($name) {
if (!isset($this->params[$name])) {
return false;
}
return $this->params[$name];
}
/**
* Read data from `php://input`. Useful when interacting with XML or JSON
* request body content.

View file

@ -416,8 +416,10 @@ class CakeResponse {
$this->_setContent();
$this->_setContentLength();
$this->_setContentType();
foreach ($this->_headers as $header => $value) {
$this->_sendHeader($header, $value);
foreach ($this->_headers as $header => $values) {
foreach ((array)$values as $value) {
$this->_sendHeader($header, $value);
}
}
if ($this->_file) {
$this->_sendFile($this->_file, $this->_fileRange);
@ -556,34 +558,42 @@ class CakeResponse {
* @param string|array $header. An array of header strings or a single header string
* - an associative array of "header name" => "header value" is also accepted
* - an array of string headers is also accepted
* @param string $value. The header value.
* @param string|array $value. The header value(s)
* @return array list of headers to be sent
*/
public function header($header = null, $value = null) {
if ($header === null) {
return $this->_headers;
}
if (is_array($header)) {
foreach ($header as $h => $v) {
if (is_numeric($h)) {
$this->header($v);
continue;
}
$this->_headers[$h] = trim($v);
$headers = is_array($header) ? $header : array($header => $value);
foreach ($headers as $header => $value) {
if (is_numeric($header)) {
list($header, $value) = array($value, null);
}
return $this->_headers;
if (is_null($value)) {
list($header, $value) = explode(':', $header, 2);
}
$this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value);
}
if ($value !== null) {
$this->_headers[$header] = $value;
return $this->_headers;
}
list($header, $value) = explode(':', $header, 2);
$this->_headers[$header] = trim($value);
return $this->_headers;
}
/**
* Acccessor for the location header.
*
* Get/Set the Location header value.
* @param null|string $url Either null to get the current location, or a string to set one.
* @return string|null When setting the location null will be returned. When reading the location
* a string of the current location header value (if any) will be returned.
*/
public function location($url = null) {
if ($url === null) {
$headers = $this->header();
return isset($headers['Location']) ? $headers['Location'] : null;
}
$this->header('Location', $url);
}
/**
* Buffers the response message to be sent
* if $content is null the current buffer is returned
@ -602,7 +612,7 @@ class CakeResponse {
* Sets the HTTP status code to be sent
* if $code is null the current code is returned
*
* @param integer $code
* @param integer $code the HTTP status code
* @return integer current status code
* @throws CakeException When an unknown status code is reached.
*/
@ -619,31 +629,47 @@ class CakeResponse {
/**
* Queries & sets valid HTTP response codes & messages.
*
* @param integer|array $code If $code is an integer, then the corresponding code/message is
* returned if it exists, null if it does not exist. If $code is an array,
* then the 'code' and 'message' keys of each nested array are added to the default
* HTTP codes. Example:
* @param integer|array $code If $code is an integer, then the corresponding code/message is
* returned if it exists, null if it does not exist. If $code is an array, then the
* keys are used as codes and the values as messages to add to the default HTTP
* codes. The codes must be integers greater than 99 and less than 1000. Keep in
* mind that the HTTP specification outlines that status codes begin with a digit
* between 1 and 5, which defines the class of response the client is to expect.
* Example:
*
* httpCodes(404); // returns array(404 => 'Not Found')
*
* httpCodes(array(
* 701 => 'Unicorn Moved',
* 800 => 'Unexpected Minotaur'
* 381 => 'Unicorn Moved',
* 555 => 'Unexpected Minotaur'
* )); // sets these new values, and returns true
*
* httpCodes(array(
* 0 => 'Nothing Here',
* -1 => 'Reverse Infinity',
* 12345 => 'Universal Password',
* 'Hello' => 'World'
* )); // throws an exception due to invalid codes
*
* For more on HTTP status codes see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
*
* @return mixed associative array of the HTTP codes as keys, and the message
* strings as values, or null of the given $code does not exist.
* @throws CakeException If an attempt is made to add an invalid status code
*/
public function httpCodes($code = null) {
if (empty($code)) {
return $this->_statusCodes;
}
if (is_array($code)) {
$codes = array_keys($code);
$min = min($codes);
if (!is_int($min) || $min < 100 || max($codes) > 999) {
throw new CakeException(__d('cake_dev', 'Invalid status code'));
}
$this->_statusCodes = $code + $this->_statusCodes;
return true;
}
if (!isset($this->_statusCodes[$code])) {
return null;
}
@ -724,9 +750,7 @@ class CakeResponse {
}
foreach ($this->_mimeTypes as $alias => $types) {
if (is_array($types) && in_array($ctype, $types)) {
return $alias;
} elseif (is_string($types) && $types == $ctype) {
if (in_array($ctype, (array)$types)) {
return $alias;
}
}

View file

@ -184,7 +184,7 @@ class CakeSocket {
*
* Instead we need to handle those errors manually.
*
* @param int $code
* @param integer $code
* @param string $message
* @return void
*/

View file

@ -318,6 +318,14 @@ class CakeEmail {
'ISO-2022-JP-MS' => 'ISO-2022-JP'
);
/**
* Regex for email validation
* If null, it will use built in regex
*
* @var string
*/
protected $_emailPattern = null;
/**
* Constructor
*
@ -521,6 +529,20 @@ class CakeEmail {
return $this->headerCharset = $charset;
}
/**
* EmailPattern setter/getter
*
* @param string $regex for email address validation
* @return string|CakeEmail
*/
public function emailPattern($regex = null) {
if ($regex === null) {
return $this->_emailPattern;
}
$this->_emailPattern = $regex;
return $this;
}
/**
* Set email
*
@ -532,7 +554,7 @@ class CakeEmail {
*/
protected function _setEmail($varName, $email, $name) {
if (!is_array($email)) {
if (!Validation::email($email)) {
if (!Validation::email($email, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
}
if ($name === null) {
@ -546,7 +568,7 @@ class CakeEmail {
if (is_int($key)) {
$key = $value;
}
if (!Validation::email($key)) {
if (!Validation::email($key, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
}
$list[$key] = $value;
@ -586,7 +608,7 @@ class CakeEmail {
*/
protected function _addEmail($varName, $email, $name) {
if (!is_array($email)) {
if (!Validation::email($email)) {
if (!Validation::email($email, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
}
if ($name === null) {
@ -600,7 +622,7 @@ class CakeEmail {
if (is_int($key)) {
$key = $value;
}
if (!Validation::email($key)) {
if (!Validation::email($key, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
}
$list[$key] = $value;
@ -884,7 +906,7 @@ class CakeEmail {
if (!class_exists($transportClassname)) {
throw new SocketException(__d('cake_dev', 'Class "%s" not found.', $transportClassname));
} elseif (!method_exists($transportClassname, 'send')) {
throw new SocketException(__d('cake_dev', 'The "%s" do not have send method.', $transportClassname));
throw new SocketException(__d('cake_dev', 'The "%s" does not have a %s method.', $transportClassname, 'send()'));
}
return $this->_transportClass = new $transportClassname();
@ -953,6 +975,15 @@ class CakeEmail {
* 'contentDisposition' => false
* ));
* }}}
*
* Attach a file from string and specify additional properties:
*
* {{{
* $email->attachments(array('custom_name.png' => array(
* 'data' => file_get_contents('path/to/file'),
* 'mimetype' => 'image/png'
* ));
* }}}
*
* The `contentId` key allows you to specify an inline attachment. In your email text, you
* can use `<img src="cid:abc123" />` to display the image inline.
@ -974,14 +1005,21 @@ class CakeEmail {
$fileInfo = array('file' => $fileInfo);
}
if (!isset($fileInfo['file'])) {
throw new SocketException(__d('cake_dev', 'File not specified.'));
}
$fileInfo['file'] = realpath($fileInfo['file']);
if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileInfo['file']));
}
if (is_int($name)) {
$name = basename($fileInfo['file']);
if (!isset($fileInfo['data'])) {
throw new SocketException(__d('cake_dev', 'No file or data specified.'));
}
if (is_int($name)) {
throw new SocketException(__d('cake_dev', 'No filename specified.'));
}
$fileInfo['data'] = chunk_split(base64_encode($fileInfo['data']), 76, "\r\n");
} else {
$fileInfo['file'] = realpath($fileInfo['file']);
if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileInfo['file']));
}
if (is_int($name)) {
$name = basename($fileInfo['file']);
}
}
if (!isset($fileInfo['mimetype'])) {
$fileInfo['mimetype'] = 'application/octet-stream';
@ -1074,11 +1112,21 @@ class CakeEmail {
$contents = $this->transportClass()->send($this);
if (!empty($this->_config['log'])) {
$level = LOG_DEBUG;
$config = array(
'level' => LOG_DEBUG,
'scope' => 'email'
);
if ($this->_config['log'] !== true) {
$level = $this->_config['log'];
if (!is_array($this->_config['log'])) {
$this->_config['log'] = array('level' => $this->_config['log']);
}
$config = array_merge($config, $this->_config['log']);
}
CakeLog::write($level, PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message']);
CakeLog::write(
$config['level'],
PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message'],
$config['scope']
);
}
return $contents;
}
@ -1149,7 +1197,7 @@ class CakeEmail {
$simpleMethods = array(
'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments',
'transport', 'emailFormat', 'theme', 'helpers'
'transport', 'emailFormat', 'theme', 'helpers', 'emailPattern'
);
foreach ($simpleMethods as $method) {
if (isset($config[$method])) {
@ -1206,6 +1254,7 @@ class CakeEmail {
$this->headerCharset = null;
$this->_attachments = array();
$this->_config = array();
$this->_emailPattern = null;
return $this;
}
@ -1376,7 +1425,7 @@ class CakeEmail {
if (!empty($fileInfo['contentId'])) {
continue;
}
$data = $this->_readFile($fileInfo['file']);
$data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
@ -1421,7 +1470,7 @@ class CakeEmail {
if (empty($fileInfo['contentId'])) {
continue;
}
$data = $this->_readFile($fileInfo['file']);
$data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];

View file

@ -96,6 +96,7 @@ class HttpSocket extends CakeSocket {
'port' => 80,
'timeout' => 30,
'ssl_verify_peer' => true,
'ssl_allow_self_signed' => false,
'ssl_verify_depth' => 5,
'ssl_verify_host' => true,
'request' => array(
@ -495,6 +496,19 @@ class HttpSocket extends CakeSocket {
return $this->request($request);
}
/**
* Issues a PATCH request to the specified URI, query, and request.
*
* @param string|array $uri URI to request, See HttpSocket::_parseUri()
* @param array $data Array of PATCH data keys and values.
* @param array $request An indexed array with indexes such as 'method' or uri
* @return mixed Result of request
*/
public function patch($uri = null, $data = array(), $request = array()) {
$request = Hash::merge(array('method' => 'PATCH', 'uri' => $uri, 'body' => $data), $request);
return $this->request($request);
}
/**
* Issues a DELETE request to the specified URI, query, and request.
*
@ -590,7 +604,7 @@ class HttpSocket extends CakeSocket {
throw new SocketException(__d('cake_dev', 'Unknown authentication method.'));
}
if (!method_exists($authClass, 'authentication')) {
throw new SocketException(sprintf(__d('cake_dev', 'The %s do not support authentication.'), $authClass));
throw new SocketException(__d('cake_dev', 'The %s does not support authentication.', $authClass));
}
call_user_func_array("$authClass::authentication", array($this, &$this->_auth[$method]));
}
@ -619,7 +633,7 @@ class HttpSocket extends CakeSocket {
throw new SocketException(__d('cake_dev', 'Unknown authentication method for proxy.'));
}
if (!method_exists($authClass, 'proxyAuthentication')) {
throw new SocketException(sprintf(__d('cake_dev', 'The %s do not support proxy authentication.'), $authClass));
throw new SocketException(__d('cake_dev', 'The %s does not support proxy authentication.', $authClass));
}
call_user_func_array("$authClass::proxyAuthentication", array($this, &$this->_proxy));
}

View file

@ -56,6 +56,14 @@ class Router {
*/
public static $initialized = false;
/**
* Contains the base string that will be applied to all generated URLs
* For example `https://example.com`
*
* @var string
*/
protected static $_fullBaseUrl;
/**
* List of action prefixes used in connected routes.
* Includes admin prefix
@ -551,7 +559,8 @@ class Router {
$url = '/' . $url;
}
if (strpos($url, '?') !== false) {
$url = substr($url, 0, strpos($url, '?'));
list($url, $queryParameters) = explode('?', $url, 2);
parse_str($queryParameters, $queryParameters);
}
extract(self::_parseExtension($url));
@ -572,6 +581,10 @@ class Router {
if (!empty($ext) && !isset($out['ext'])) {
$out['ext'] = $ext;
}
if (!empty($queryParameters) && !isset($out['?'])) {
$out['?'] = $queryParameters;
}
return $out;
}
@ -759,7 +772,7 @@ class Router {
* cake relative URLs are required when using requestAction.
* - `?` - Takes an array of query string parameters
* - `#` - Allows you to set URL hash fragments.
* - `full_base` - If true the `FULL_BASE_URL` constant will be prepended to generated URLs.
* - `full_base` - If true the `Router::fullBaseUrl()` value will be prepended to generated URLs.
*
* @param string|array $url Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4"
* or an array specifying any of the following: 'controller', 'action',
@ -796,8 +809,8 @@ class Router {
if (empty($url)) {
$output = isset($path['here']) ? $path['here'] : '/';
if ($full && defined('FULL_BASE_URL')) {
$output = FULL_BASE_URL . $output;
if ($full) {
$output = self::fullBaseUrl() . $output;
}
return $output;
} elseif (is_array($url)) {
@ -860,7 +873,7 @@ class Router {
$output = self::_handleNoRoute($url);
}
} else {
if (preg_match('/:\/\/|^(javascript|mailto|tel|sms):|^\#/i', $url)) {
if (preg_match('/^([a-z][a-z0-9.+\-]+:|:?\/\/|[#?])/i', $url)) {
return $url;
}
if (substr($url, 0, 1) === '/') {
@ -878,12 +891,12 @@ class Router {
$output .= Inflector::underscore($params['controller']) . '/' . $url;
}
}
$protocol = preg_match('#^[a-z][a-z0-9+-.]*\://#i', $output);
$protocol = preg_match('#^[a-z][a-z0-9+\-.]*\://#i', $output);
if ($protocol === 0) {
$output = str_replace('//', '/', $base . '/' . $output);
if ($full && defined('FULL_BASE_URL')) {
$output = FULL_BASE_URL . $output;
if ($full) {
$output = self::fullBaseUrl() . $output;
}
if (!empty($extension)) {
$output = rtrim($output, '/');
@ -892,6 +905,32 @@ class Router {
return $output . $extension . self::queryString($q, array(), $escape) . $frag;
}
/**
* Sets the full base url that will be used as a prefix for generating
* fully qualified URLs for this application. If not parameters are passed,
* the currently configured value is returned.
*
* ## Note:
*
* If you change the configuration value ``App.fullBaseUrl`` during runtime
* and expect the router to produce links using the new setting, you are
* required to call this method passing such value again.
*
* @param string $base the prefix for URLs generated containing the domain.
* For example: ``http://example.com``
* @return string
*/
public static function fullBaseUrl($base = null) {
if ($base !== null) {
self::$_fullBaseUrl = $base;
Configure::write('App.fullBaseUrl', $base);
}
if (empty(self::$_fullBaseUrl)) {
self::$_fullBaseUrl = Configure::read('App.fullBaseUrl');
}
return self::$_fullBaseUrl;
}
/**
* A special fallback method that handles URL arrays that cannot match
* any defined routes.

View file

@ -703,8 +703,9 @@ class BasicsTest extends CakeTestCase {
########## DEBUG ##########
'this-is-a-test'
###########################
EXPECTED;
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 8);
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 9);
$this->assertEquals($expected, $result);
@ -766,9 +767,10 @@ EXPECTED;
########## DEBUG ##########
'<div>this-is-a-test</div>'
###########################
EXPECTED;
if (php_sapi_name() === 'cli') {
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 17);
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 18);
} else {
$expected = sprintf($expectedHtml, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 19);
}
@ -790,9 +792,10 @@ EXPECTED;
########## DEBUG ##########
'<div>this-is-a-test</div>'
###########################
EXPECTED;
if (php_sapi_name() === 'cli') {
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 17);
$expected = sprintf($expectedText, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 18);
} else {
$expected = sprintf($expectedHtml, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 19);
}
@ -806,8 +809,9 @@ EXPECTED;
########## DEBUG ##########
'<div>this-is-a-test</div>'
###########################
EXPECTED;
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 8);
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 9);
$this->assertEquals($expected, $result);
ob_start();
@ -818,8 +822,9 @@ EXPECTED;
########## DEBUG ##########
'<div>this-is-a-test</div>'
###########################
EXPECTED;
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 8);
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 9);
$this->assertEquals($expected, $result);
ob_start();
@ -830,8 +835,9 @@ EXPECTED;
########## DEBUG ##########
'<div>this-is-a-test</div>'
###########################
EXPECTED;
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 8);
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 9);
$this->assertEquals($expected, $result);
ob_start();
@ -842,8 +848,9 @@ EXPECTED;
########## DEBUG ##########
false
###########################
EXPECTED;
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 8);
$expected = sprintf($expected, str_replace(CAKE_CORE_INCLUDE_PATH, '', __FILE__), __LINE__ - 9);
$this->assertEquals($expected, $result);
}
@ -853,6 +860,7 @@ EXPECTED;
* @return void
*/
public function testPr() {
$this->skipIf(php_sapi_name() == 'cli', 'Skipping web test in cli mode');
ob_start();
pr('this is a test');
$result = ob_get_clean();
@ -866,6 +874,26 @@ EXPECTED;
$this->assertEquals($expected, $result);
}
/**
* test pr()
*
* @return void
*/
public function testPrCli() {
$this->skipIf(php_sapi_name() != 'cli', 'Skipping cli test in web mode');
ob_start();
pr('this is a test');
$result = ob_get_clean();
$expected = "\nthis is a test\n";
$this->assertEquals($expected, $result);
ob_start();
pr(array('this' => 'is', 'a' => 'test'));
$result = ob_get_clean();
$expected = "\nArray\n(\n [this] => is\n [a] => test\n)\n\n";
$this->assertEquals($expected, $result);
}
/**
* test stripslashes_deep()
*

View file

@ -48,6 +48,9 @@ class CacheTest extends CakeTestCase {
*/
public function tearDown() {
parent::tearDown();
Cache::drop('latest');
Cache::drop('page');
Cache::drop('archive');
Configure::write('Cache.disable', $this->_cacheDisable);
Cache::config('default', $this->_defaultCacheConfig['settings']);
}
@ -129,6 +132,10 @@ class CacheTest extends CakeTestCase {
* @return void
*/
public function testInvalidConfig() {
// In debug mode it would auto create the folder.
$debug = Configure::read('debug');
Configure::write('debug', 0);
Cache::config('invalid', array(
'engine' => 'File',
'duration' => '+1 year',
@ -138,6 +145,8 @@ class CacheTest extends CakeTestCase {
'random' => 'wii'
));
Cache::read('Test', 'invalid');
Configure::write('debug', $debug);
}
/**
@ -237,6 +246,67 @@ class CacheTest extends CakeTestCase {
Cache::config('sessions', $_cacheConfigSessions['settings']);
}
/**
* testGroupConfigs method
*/
public function testGroupConfigs() {
Cache::config('latest', array(
'duration' => 300,
'engine' => 'File',
'groups' => array(
'posts', 'comments',
),
));
$expected = array(
'posts' => array('latest'),
'comments' => array('latest'),
);
$result = Cache::groupConfigs();
$this->assertEquals($expected, $result);
$result = Cache::groupConfigs('posts');
$this->assertEquals(array('posts' => array('latest')), $result);
Cache::config('page', array(
'duration' => 86400,
'engine' => 'File',
'groups' => array(
'posts', 'archive'
),
));
$result = Cache::groupConfigs();
$expected = array(
'posts' => array('latest', 'page'),
'comments' => array('latest'),
'archive' => array('page'),
);
$this->assertEquals($expected, $result);
$result = Cache::groupConfigs('archive');
$this->assertEquals(array('archive' => array('page')), $result);
Cache::config('archive', array(
'duration' => 86400 * 30,
'engine' => 'File',
'groups' => array(
'posts', 'archive', 'comments',
),
));
$result = Cache::groupConfigs('archive');
$this->assertEquals(array('archive' => array('archive', 'page')), $result);
}
/**
* testGroupConfigsThrowsException method
* @expectedException CacheException
*/
public function testGroupConfigsThrowsException() {
Cache::groupConfigs('bogus');
}
/**
* test that configured returns an array of the currently configured cache
* settings

View file

@ -381,20 +381,19 @@ class FileEngineTest extends CakeTestCase {
}
/**
* check that FileEngine generates an error when a configured Path does not exist.
* check that FileEngine does not generate an error when a configured Path does not exist in debug mode.
*
* @expectedException PHPUnit_Framework_Error_Warning
* @return void
*/
public function testErrorWhenPathDoesNotExist() {
$this->skipIf(is_dir(TMP . 'tests' . DS . 'file_failure'), 'Cannot run test directory exists.');
public function testPathDoesNotExist() {
$this->skipIf(is_dir(TMP . 'tests' . DS . 'autocreate'), 'Cannot run if test directory exists.');
Cache::config('failure', array(
Cache::config('autocreate', array(
'engine' => 'File',
'path' => TMP . 'tests' . DS . 'file_failure'
'path' => TMP . 'tests' . DS . 'autocreate'
));
Cache::drop('failure');
Cache::drop('autocreate');
}
/**

Some files were not shown because too many files have changed in this diff Show more