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! [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) ![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 { class DbAclSchema extends CakeSchema {
public $name = 'DbAcl';
public function before($event = array()) { public function before($event = array()) {
return true; return true;
} }

View file

@ -70,6 +70,9 @@
* - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you * - `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. * 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? * - `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. * @see ErrorHandler for more information on exception handling and configuration.
*/ */
@ -105,6 +108,33 @@
*/ */
//Configure::write('App.baseUrl', env('SCRIPT_NAME')); //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. * Uncomment the define below to use CakePHP prefix routes.
* *

View file

@ -52,6 +52,12 @@
* *
* unix_socket => * unix_socket =>
* For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port` * 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 { class DATABASE_CONFIG {

View file

@ -31,13 +31,6 @@ App::uses('AppController', 'Controller');
*/ */
class PagesController extends AppController { class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/** /**
* This controller does not use a model * This controller does not use a model
* *
@ -50,6 +43,8 @@ class PagesController extends AppController {
* *
* @param mixed What page to display * @param mixed What page to display
* @return void * @return void
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
*/ */
public function display() { public function display() {
$path = func_get_args(); $path = func_get_args();
@ -70,6 +65,14 @@ class PagesController extends AppController {
$title_for_layout = Inflector::humanize($path[$count - 1]); $title_for_layout = Inflector::humanize($path[$count - 1]);
} }
$this->set(compact('page', 'subpage', 'title_for_layout')); $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 RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L] RewriteRule ^ index.php [L]
</IfModule> </IfModule>

View file

@ -51,6 +51,13 @@ class Cache {
*/ */
protected static $_config = array(); 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(); * Whether to reset the settings with the next call to Cache::set();
* *
@ -130,6 +137,14 @@ class Cache {
return false; 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']; $engine = self::$_config[$name]['engine'];
if (!isset(self::$_engines[$name])) { if (!isset(self::$_engines[$name])) {
@ -159,7 +174,7 @@ class Cache {
} }
$cacheClass = $class . 'Engine'; $cacheClass = $class . 'Engine';
if (!is_subclass_of($cacheClass, 'CacheEngine')) { 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(); self::$_engines[$name] = new $cacheClass();
if (!self::$_engines[$name]->init($config)) { if (!self::$_engines[$name]->init($config)) {
@ -498,4 +513,33 @@ class Cache {
return array(); 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; $dir = $this->settings['path'] . $groups;
if (!is_dir($dir)) { if (!is_dir($dir)) {
mkdir($dir, 0777, true); mkdir($dir, 0775, true);
} }
$path = new SplFileInfo($dir . $key); $path = new SplFileInfo($dir . $key);
@ -369,6 +369,12 @@ class FileEngine extends CacheEngine {
*/ */
protected function _active() { protected function _active() {
$dir = new SplFileInfo($this->settings['path']); $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())) { if ($this->_init && !($dir->isDir() && $dir->isWritable())) {
$this->_init = false; $this->_init = false;
trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING); 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) { public function increment($key, $offset = 1) {
if ($this->settings['compress']) { if ($this->settings['compress']) {
throw new CacheException( 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); return $this->_Memcache->increment($key, $offset);
@ -182,7 +182,7 @@ class MemcacheEngine extends CacheEngine {
public function decrement($key, $offset = 1) { public function decrement($key, $offset = 1) {
if ($this->settings['compress']) { if ($this->settings['compress']) {
throw new CacheException( 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); return $this->_Memcache->decrement($key, $offset);

View file

@ -71,7 +71,7 @@ class PhpReader implements ConfigReaderInterface {
include $file; include $file;
if (!isset($config)) { 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; 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`.'), 'help' => __d('cake_console', 'Database connection to use in conjunction with `bake all`.'),
'short' => 'c', 'short' => 'c',
'default' => 'default' '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. * Provides a very basic 'interactive' console for CakePHP apps.
* *
* @package Cake.Console.Command * @package Cake.Console.Command
* @deprecated Deprecated since version 2.4, will be removed in 3.0
*/ */
class ConsoleShell extends AppShell { class ConsoleShell extends AppShell {
@ -43,6 +44,35 @@ class ConsoleShell extends AppShell {
*/ */
public $models = array(); 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 * Override startup of the Shell
* *
@ -74,6 +104,11 @@ class ConsoleShell extends AppShell {
} }
} }
/**
* getOptionParser
*
* @return void
*/
public function getOptionParser() { public function getOptionParser() {
$description = array( $description = array(
'The interactive console is a tool for testing parts of your', 'The interactive console is a tool for testing parts of your',
@ -163,191 +198,289 @@ class ConsoleShell extends AppShell {
* @return void * @return void
*/ */
public function main($command = null) { public function main($command = null) {
while (true) { $this->_finished = false;
while (!$this->_finished) {
if (empty($command)) { if (empty($command)) {
$command = trim($this->in('')); $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]; $method = $this->_method($command);
$association = $tmp[2];
$modelB = $tmp[3];
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) { if ($method) {
$this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); $this->$method($command);
$this->out(__d('cake_console', "Created %s association between %s and %s", } else {
$association, $modelA, $modelB)); $this->out(__d('cake_console', "Invalid command"));
} else { $this->out();
$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();
} }
$command = ''; $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 * 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); 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) { if ($snapshot === true) {
$fileName = rtrim($this->params['file'], '.php'); $fileName = rtrim($this->params['file'], '.php');
$Folder = new Folder($this->Schema->path); $Folder = new Folder($this->Schema->path);
@ -228,10 +235,9 @@ class SchemaShell extends AppShell {
if ($File->write($contents)) { if ($File->write($contents)) {
$this->out(__d('cake_console', 'SQL dump file created in %s', $File->pwd())); $this->out(__d('cake_console', 'SQL dump file created in %s', $File->pwd()));
return $this->_stop(); 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); $this->out($contents);
return $contents; return $contents;
@ -365,10 +371,18 @@ class SchemaShell extends AppShell {
if (empty($table)) { if (empty($table)) {
foreach ($compare as $table => $changes) { 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])) { } 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)) { if (empty($contents)) {
@ -479,6 +493,9 @@ class SchemaShell extends AppShell {
$write = array( $write = array(
'help' => __d('cake_console', 'Write the dumped SQL to a file.') '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 = parent::getOptionParser();
$parser->description( $parser->description(
@ -492,7 +509,7 @@ class SchemaShell extends AppShell {
))->addSubcommand('generate', array( ))->addSubcommand('generate', array(
'help' => __d('cake_console', 'Reads from --connection and writes to --path. Generate snapshots with -s'), 'help' => __d('cake_console', 'Reads from --connection and writes to --path. Generate snapshots with -s'),
'parser' => array( '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( 'arguments' => array(
'snapshot' => array('help' => __d('cake_console', 'Generate a snapshot.')) 'snapshot' => array('help' => __d('cake_console', 'Generate a snapshot.'))
) )

View file

@ -122,7 +122,7 @@ class ServerShell extends AppShell {
*/ */
public function main() { public function main() {
if (version_compare(PHP_VERSION, '5.4.0') < 0) { 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; return;
} }

View file

@ -480,6 +480,12 @@ class ControllerTask extends BakeTask {
))->addOption('connection', array( ))->addOption('connection', array(
'short' => 'c', 'short' => 'c',
'help' => __d('cake_console', 'The connection the controller\'s model is on.') '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( ))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all controllers with CRUD methods.') '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.')); ))->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( ))->addOption('plugin', array(
'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'), 'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'),
'short' => 'p', '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( ))->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', 'short' => 'r',
'boolean' => true 'boolean' => true
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->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->interactive = false;
$this->Model->interactive = false; $this->Model->interactive = false;
$tables = $this->Model->listAll($this->connection, false); $tables = $this->Model->listAll($this->connection, false);
foreach ($tables as $table) { foreach ($tables as $table) {
$model = $this->_modelName($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) { public function importOptions($modelName) {
$options = array(); $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; $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') { if ($doRecords === 'y') {
$options['records'] = true; $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 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 string $prompt Prompt to use for options list.
* @param integer $default The default option for the given prompt. * @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) { public function inOptions($options, $prompt = null, $default = null) {
$valid = false; $valid = false;
@ -347,7 +347,7 @@ class ModelTask extends BakeTask {
* @return array $validate Array of user selected validations. * @return array $validate Array of user selected validations.
*/ */
public function doValidation($model) { public function doValidation($model) {
if (!is_object($model)) { if (!$model instanceof Model) {
return false; return false;
} }
$fields = $model->schema(); $fields = $model->schema();
@ -490,10 +490,10 @@ class ModelTask extends BakeTask {
* Handles associations * Handles associations
* *
* @param Model $model * @param Model $model
* @return array $associations * @return array Associations
*/ */
public function doAssociations($model) { public function doAssociations($model) {
if (!is_object($model)) { if (!$model instanceof Model) {
return false; return false;
} }
if ($this->interactive === true) { if ($this->interactive === true) {
@ -538,12 +538,36 @@ class ModelTask extends BakeTask {
return $associations; 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. * Find belongsTo relations and add them to the associations list.
* *
* @param Model $model Model instance of model being generated. * @param Model $model Model instance of model being generated.
* @param array $associations Array of in progress associations * @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) { public function findBelongsTo(Model $model, $associations) {
$fieldNames = array_keys($model->schema(true)); $fieldNames = array_keys($model->schema(true));
@ -572,7 +596,7 @@ class ModelTask extends BakeTask {
* *
* @param Model $model Model instance being generated * @param Model $model Model instance being generated
* @param array $associations Array of in progress associations * @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) { public function findHasOneAndMany(Model $model, $associations) {
$foreignKey = $this->_modelKey($model->name); $foreignKey = $this->_modelKey($model->name);
@ -615,7 +639,7 @@ class ModelTask extends BakeTask {
* *
* @param Model $model Model instance being generated * @param Model $model Model instance being generated
* @param array $associations Array of in-progress associations * @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) { public function findHasAndBelongsToMany(Model $model, $associations) {
$foreignKey = $this->_modelKey($model->name); $foreignKey = $this->_modelKey($model->name);
@ -747,7 +771,7 @@ class ModelTask extends BakeTask {
/** /**
* Finds all possible keys to use on custom associations. * 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() { protected function _generatePossibleKeys() {
$possible = array(); $possible = array();
@ -771,11 +795,12 @@ class ModelTask extends BakeTask {
* @return string * @return string
*/ */
public function bake($name, $data = array()) { public function bake($name, $data = array()) {
if (is_object($name)) { if ($name instanceof Model) {
if (!$data) { if (!$data) {
$data = array(); $data = array();
$data['associations'] = $this->doAssociations($name); $data['associations'] = $this->doAssociations($name);
$data['validate'] = $this->doValidation($name); $data['validate'] = $this->doValidation($name);
$data['actsAs'] = $this->doActsAs($name);
} }
$data['primaryKey'] = $name->primaryKey; $data['primaryKey'] = $name->primaryKey;
$data['useTable'] = $name->table; $data['useTable'] = $name->table;
@ -784,8 +809,10 @@ class ModelTask extends BakeTask {
} else { } else {
$data['name'] = $name; $data['name'] = $name;
} }
$defaults = array( $defaults = array(
'associations' => array(), 'associations' => array(),
'actsAs' => array(),
'validate' => array(), 'validate' => array(),
'primaryKey' => 'id', 'primaryKey' => 'id',
'useTable' => null, '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. * Forces the user to specify the model he wants to bake, and returns the selected model name.
* *
* @param string $useDbConfig Database config name * @param string $useDbConfig Database config name
* @return string the model name * @return string The model name
*/ */
public function getName($useDbConfig = null) { public function getName($useDbConfig = null) {
$this->listAll($useDbConfig); $this->listAll($useDbConfig);
@ -965,9 +992,15 @@ class ModelTask extends BakeTask {
))->addOption('plugin', array( ))->addOption('plugin', array(
'short' => 'p', 'short' => 'p',
'help' => __d('cake_console', 'Plugin to bake the model into.') '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( ))->addOption('connection', array(
'short' => 'c', 'short' => 'c',
'help' => __d('cake_console', 'The connection the model table is on.') '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.')); ))->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; $success = $this->corePath($path, $hardCode) === true;
if ($success) { 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 %s', CAKE_CORE_INCLUDE_PATH, 'webroot/index.php'));
$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/test.php'));
} else { } else {
$this->err(__d('cake_console', 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', $path . 'webroot' . DS . 'index.php')); $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; $success = false;
@ -435,6 +435,9 @@ class ProjectTask extends AppShell {
))->addOption('empty', array( ))->addOption('empty', array(
'boolean' => true, 'boolean' => true,
'help' => __d('cake_console', 'Create empty files in each of the directories. Good if you are using git') '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( ))->addOption('skel', array(
'default' => current(App::core('Console')) . 'Templates' . DS . 'skel', '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.') '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( ))->addArgument('name', array(
'help' => __d('cake_console', 'An existing class to bake tests for.') '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( ))->addOption('plugin', array(
'short' => 'p', 'short' => 'p',
'help' => __d('cake_console', 'CamelCased name of the plugin to bake tests for.') '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.')); ))->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( ))->addOption('admin', array(
'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'), 'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'),
'boolean' => true 'boolean' => true
))->addOption('theme', array(
'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('connection', array( ))->addOption('connection', array(
'short' => 'c', 'short' => 'c',
'help' => __d('cake_console', 'The connection the connected model is on.') '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( ))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.') '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.')); ))->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 = parent::getOptionParser();
$parser->description(array( $parser->description(array(
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'), __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', "<warning>This shell is for backwards-compatibility only</warning>\nuse the test shell instead"),
__d('cake_console', 'use the test shell instead')
)); ));
return $parser; return $parser;

View file

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

View file

@ -50,4 +50,16 @@ class ConsoleInput {
return fgets($this->_input); 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'), 'success' => array('text' => 'green'),
'comment' => array('text' => 'blue'), 'comment' => array('text' => 'blue'),
'question' => array('text' => 'magenta'), 'question' => array('text' => 'magenta'),
'notice' => array('text' => 'cyan')
); );
/** /**

View file

@ -24,6 +24,7 @@ App::uses('ConsoleInputSubcommand', 'Console');
App::uses('ConsoleOptionParser', 'Console'); App::uses('ConsoleOptionParser', 'Console');
App::uses('ClassRegistry', 'Utility'); App::uses('ClassRegistry', 'Utility');
App::uses('File', 'Utility'); App::uses('File', 'Utility');
App::uses('ClassRegistry', 'Utility');
/** /**
* Base class for command-line utilities for automating programmer chores. * Base class for command-line utilities for automating programmer chores.
@ -120,6 +121,13 @@ class Shell extends Object {
*/ */
public $uses = array(); 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. * Task Collection for the command, used to create Tasks.
* *
@ -178,7 +186,7 @@ class Shell extends Object {
if ($this->tasks !== null && $this->tasks !== false) { if ($this->tasks !== null && $this->tasks !== false) {
$this->_mergeVars(array('tasks'), $parent, true); $this->_mergeVars(array('tasks'), $parent, true);
} }
if ($this->uses !== null && $this->uses !== false) { if (!empty($this->uses)) {
$this->_mergeVars(array('uses'), $parent, false); $this->_mergeVars(array('uses'), $parent, false);
} }
} }
@ -224,31 +232,66 @@ class Shell extends Object {
} }
/** /**
* If $uses = true * If $uses is an array load each of the models in the array
* 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
* *
* @return boolean * @return boolean
*/ */
protected function _loadModels() { protected function _loadModels() {
if (empty($this->uses)) { if (is_array($this->uses)) {
return false; 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); $this->uses = ($this->uses) ? (array)$this->uses : array();
if (!in_array($modelClass, $this->uses)) {
$modelClassName = $uses[0]; $this->uses[] = $modelClass;
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);
} }
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; return true;
} }
@ -650,7 +693,7 @@ class Shell extends Object {
$this->out(); $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)); $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'); $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; return;
} }
CakeLog::config('stdout', array( CakeLog::config('stdout', array(
'engine' => 'ConsoleLog', 'engine' => 'Console',
'types' => array('notice', 'info'), 'types' => array('notice', 'info'),
'stream' => $this->stdout, 'stream' => $this->stdout,
)); ));
CakeLog::config('stderr', array( CakeLog::config('stderr', array(
'engine' => 'ConsoleLog', 'engine' => 'Console',
'types' => array('emergency', 'alert', 'critical', 'error', 'warning', 'debug'), 'types' => array('emergency', 'alert', 'critical', 'error', 'warning', 'debug'),
'stream' => $this->stderr, 'stream' => $this->stderr,
)); ));

View file

@ -145,7 +145,9 @@ class ShellDispatcher {
$this->setErrorHandlers(); $this->setErrorHandlers();
if (!defined('FULL_BASE_URL')) { 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; return true;

View file

@ -50,8 +50,17 @@ class TaskCollection extends ObjectCollection {
} }
/** /**
* Loads/constructs a task. Will return the instance in the collection * Loads/constructs a task. Will return the instance in the registry if it already exists.
* 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 string $task Task name to load
* @param array $settings Settings for the task. * @param array $settings Settings for the task.
@ -59,26 +68,33 @@ class TaskCollection extends ObjectCollection {
* @throws MissingTaskException when the task could not be found * @throws MissingTaskException when the task could not be found
*/ */
public function load($task, $settings = array()) { public function load($task, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) {
$alias = $task;
$task = $settings['className'];
}
list($plugin, $name) = pluginSplit($task, true); list($plugin, $name) = pluginSplit($task, true);
if (!isset($alias)) {
if (isset($this->_loaded[$name])) { $alias = $name;
return $this->_loaded[$name];
} }
if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
$taskClass = $name . 'Task'; $taskClass = $name . 'Task';
App::uses($taskClass, $plugin . 'Console/Command/Task'); App::uses($taskClass, $plugin . 'Console/Command/Task');
$exists = class_exists($taskClass); $exists = class_exists($taskClass);
if (!$exists) { if (!$exists) {
throw new MissingTaskException(array( 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 $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; <?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)): if (!empty($validate)):
echo "/**\n * Validation rules\n *\n * @var array\n */\n"; echo "/**\n * Validation rules\n *\n * @var array\n */\n";
echo "\tpublic \$validate = array(\n"; echo "\tpublic \$validate = array(\n";

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema * @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9 * @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 { class DbAclSchema extends CakeSchema {
public $name = 'DbAcl';
public function before($event = array()) { public function before($event = array()) {
return true; return true;
} }

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema * @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9 * @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 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config.Schema * @package app.Config.Schema
* @since CakePHP(tm) v 0.2.9 * @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 ; * 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 ; * @link http://cakephp.org CakePHP(tm) Project
; * @package app.Config ; * @package app.Config
; * @since CakePHP(tm) v 0.10.0.1076 ; * @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 ; acl.ini.php - Cake ACL Configuration

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 2.1 * @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 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 0.10.8.2117 * @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. // Setup a 'default' cache configuration for use in the application.
@ -98,12 +89,12 @@ Configure::write('Dispatcher.filters', array(
*/ */
App::uses('CakeLog', 'Log'); App::uses('CakeLog', 'Log');
CakeLog::config('debug', array( CakeLog::config('debug', array(
'engine' => 'FileLog', 'engine' => 'File',
'types' => array('notice', 'info', 'debug'), 'types' => array('notice', 'info', 'debug'),
'file' => 'debug', 'file' => 'debug',
)); ));
CakeLog::config('error', array( CakeLog::config('error', array(
'engine' => 'FileLog', 'engine' => 'File',
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
'file' => 'error', 'file' => 'error',
)); ));

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 0.2.9 * @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 * - `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. * 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? * - `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. * @see ErrorHandler for more information on exception handling and configuration.
*/ */
@ -105,6 +99,33 @@
*/ */
//Configure::write('App.baseUrl', env('SCRIPT_NAME')); //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. * Uncomment the define below to use CakePHP prefix routes.
* *

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 0.2.9 * @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. * You can specify multiple configurations for production, development and testing.
* *
* datasource => The name of a supported datasource; valid options are as follows: * 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/Sqlite - SQLite (PHP5 only),
* Database/Postgres - PostgreSQL 7 and higher, * Database/Postgres - PostgreSQL 7 and higher,
* Database/Sqlserver - Microsoft SQL Server 2005 and higher * Database/Sqlserver - Microsoft SQL Server 2005 and higher

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 2.0.0 * @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. * You can specify multiple configurations for production, development and testing.
* *
* transport => The name of a supported transport; valid options are as follows: * 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 * Smtp - Send using SMTP
* Debug - Do not send the email, just return the result * Debug - Do not send the email, just return the result
* *

View file

@ -8,18 +8,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Config * @package app.Config
* @since CakePHP(tm) v 0.2.9 * @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 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.0 * @since CakePHP(tm) v 2.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('Shell', 'Console'); App::uses('Shell', 'Console');

View file

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

View file

@ -7,18 +7,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Controller * @package app.Controller
* @since CakePHP(tm) v 0.2.9 * @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('Controller', 'Controller'); App::uses('Controller', 'Controller');

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Controller * @package app.Controller
* @since CakePHP(tm) v 0.2.9 * @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('AppController', 'Controller'); App::uses('AppController', 'Controller');
@ -32,13 +23,6 @@ App::uses('AppController', 'Controller');
*/ */
class PagesController extends AppController { class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/** /**
* This controller does not use a model * This controller does not use a model
* *
@ -51,6 +35,8 @@ class PagesController extends AppController {
* *
* @param mixed What page to display * @param mixed What page to display
* @return void * @return void
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
*/ */
public function display() { public function display() {
$path = func_get_args(); $path = func_get_args();
@ -71,6 +57,14 @@ class PagesController extends AppController {
$title_for_layout = Inflector::humanize($path[$count - 1]); $title_for_layout = Inflector::humanize($path[$count - 1]);
} }
$this->set(compact('page', 'subpage', 'title_for_layout')); $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 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.Model * @package app.Model
* @since CakePHP(tm) v 0.2.9 * @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('Model', 'Model'); App::uses('Model', 'Model');

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Errors * @package app.View.Errors
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $name; ?></h2>

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Errors * @package app.View.Errors
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $name; ?></h2>

View file

@ -7,18 +7,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Helper * @package app.View.Helper
* @since CakePHP(tm) v 0.2.9 * @since CakePHP(tm) v 0.2.9
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('Helper', 'View'); App::uses('Helper', 'View');

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts.Email.html * @package app.View.Layouts.Email.html
* @since CakePHP(tm) v 0.10.0.1076 * @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"> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts * @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<?php echo $this->fetch('content'); ?> <?php echo $this->fetch('content'); ?>

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts * @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076 * @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'); $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts * @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076 * @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'); $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Layouts * @package app.View.Layouts
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<!DOCTYPE html> <!DOCTYPE html>

View file

@ -3,18 +3,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.View.Pages * @package app.View.Pages
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
if (!Configure::read('debug')): if (!Configure::read('debug')):
@ -68,11 +59,11 @@ endif;
$settings = Cache::settings(); $settings = Cache::settings();
if (!empty($settings)): if (!empty($settings)):
echo '<span class="notice success">'; 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>'; echo '</span>';
else: else:
echo '<span class="notice">'; 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>'; echo '</span>';
endif; endif;
?> ?>
@ -89,7 +80,7 @@ endif;
echo '<span class="notice">'; echo '<span class="notice">';
echo __d('cake_dev', 'Your database configuration file is NOT present.'); echo __d('cake_dev', 'Your database configuration file is NOT present.');
echo '<br/>'; 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>'; echo '</span>';
endif; endif;
?> ?>
@ -132,7 +123,7 @@ if (isset($filePresent)):
echo '<p><span class="notice">'; echo '<p><span class="notice">';
echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.'); echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.');
echo '<br/>'; 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>'; echo '</span></p>';
} }
?> ?>
@ -156,9 +147,10 @@ if (isset($filePresent)):
<h3><?php echo __d('cake_dev', 'Editing this Page'); ?></h3> <h3><?php echo __d('cake_dev', 'Editing this Page'); ?></h3>
<p> <p>
<?php <?php
echo __d('cake_dev', 'To change the content of this page, edit: APP/View/Pages/home.ctp.<br /> echo __d('cake_dev', 'To change the content of this page, edit: %s.<br />
To change its layout, edit: APP/View/Layouts/default.ctp.<br /> To change its layout, edit: %s.<br />
You can also add some CSS styles for your pages at: APP/webroot/css.'); 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> </p>

View file

@ -2,18 +2,9 @@
/** /**
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app * @package app
* @since CakePHP(tm) v 0.10.0.1076 * @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'; require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

View file

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

View file

@ -6,18 +6,9 @@
* *
* PHP 5 * 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 * @link http://cakephp.org CakePHP(tm) Project
* @package app.webroot * @package app.webroot
* @since CakePHP(tm) v 0.2.9 * @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 * 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 * @link http://book.cakephp.org/2.0/en/development/testing.html
* @package app.webroot * @package app.webroot
* @since CakePHP(tm) v 1.2.0.4433 * @since CakePHP(tm) v 1.2.0.4433
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
set_time_limit(0); set_time_limit(0);

View file

@ -31,13 +31,6 @@ App::uses('AppController', 'Controller');
*/ */
class CakeErrorController extends AppController { class CakeErrorController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'CakeError';
/** /**
* Uses Property * 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 array|string|Model $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *) * @param string $action Action (defaults to *)
* @return boolean Success * @return boolean Success
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
public function grant($aro, $aco, $action = "*") { 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); 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 array|string|Model $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *) * @param string $action Action (defaults to *)
* @return boolean Success * @return boolean Success
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
public function revoke($aro, $aco, $action = "*") { 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); 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).` * i.e. `array('User.is_active' => 1).`
* - `recursive` The value of the recursive key passed to find(). Defaults to 0. * - `recursive` The value of the recursive key passed to find(). Defaults to 0.
* - `contain` Extra models to contain and store in session. * - `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 * @var array
*/ */
@ -45,6 +48,7 @@ abstract class BaseAuthenticate {
'scope' => array(), 'scope' => array(),
'recursive' => 0, 'recursive' => 0,
'contain' => null, 'contain' => null,
'passwordHasher' => 'Simple'
); );
/** /**
@ -54,6 +58,13 @@ abstract class BaseAuthenticate {
*/ */
protected $_Collection; protected $_Collection;
/**
* Password hasher instance.
*
* @var AbstractPasswordHasher
*/
protected $_passwordHasher;
/** /**
* Constructor * Constructor
* *
@ -68,56 +79,96 @@ abstract class BaseAuthenticate {
/** /**
* Find a user record using the standard options. * 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 $username parameter can be a (string)username or an array containing
* the password field is not included in the conditions the password will be returned. * 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. * Input passwords will be hashed even when a user doesn't exist. This
* @param Mixed $password The password, only use if passing as $conditions = 'username'. * helps mitigate timing attacks that are attempting to find valid usernames.
* @return Mixed Either false on failure, or an array of user data. *
* @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']; $userModel = $this->settings['userModel'];
list(, $model) = pluginSplit($userModel); list(, $model) = pluginSplit($userModel);
$fields = $this->settings['fields']; $fields = $this->settings['fields'];
if (!is_array($conditions)) { if (is_array($username)) {
if (!$password) { $conditions = $username;
return false; } else {
}
$username = $conditions;
$conditions = array( $conditions = array(
$model . '.' . $fields['username'] => $username, $model . '.' . $fields['username'] => $username
$model . '.' . $fields['password'] => $this->_password($password),
); );
} }
if (!empty($this->settings['scope'])) { if (!empty($this->settings['scope'])) {
$conditions = array_merge($conditions, $this->settings['scope']); $conditions = array_merge($conditions, $this->settings['scope']);
} }
$result = ClassRegistry::init($userModel)->find('first', array( $result = ClassRegistry::init($userModel)->find('first', array(
'conditions' => $conditions, 'conditions' => $conditions,
'recursive' => $this->settings['recursive'], 'recursive' => $this->settings['recursive'],
'contain' => $this->settings['contain'], 'contain' => $this->settings['contain'],
)); ));
if (empty($result) || empty($result[$model])) { if (empty($result[$model])) {
$this->passwordHasher()->hash($password);
return false; return false;
} }
$user = $result[$model]; $user = $result[$model];
if ( if ($password) {
isset($conditions[$model . '.' . $fields['password']]) || if (!$this->passwordHasher()->check($password, $user[$fields['password']])) {
isset($conditions[$fields['password']]) return false;
) { }
unset($user[$fields['password']]); unset($user[$fields['password']]);
} }
unset($result[$model]); unset($result[$model]);
return array_merge($user, $result); 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 * Hash the plain text password so that it matches the hashed/encrypted password
* in the datasource. * in the datasource.
* *
* @param string $password The plain text password. * @param string $password The plain text password.
* @return string The hashed form of the password. * @return string The hashed form of the password.
* @deprecated Since 2.4. Use a PasswordHasher class instead.
*/ */
protected function _password($password) { protected function _password($password) {
return Security::hash($password, null, true); return Security::hash($password, null, true);
@ -156,4 +207,15 @@ abstract class BaseAuthenticate {
return false; 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 { 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. * 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 * Authenticate a user using HTTP auth. Will use the configured User model and attempt a
* login using basic HTTP auth. * login using HTTP auth.
* *
* @param CakeRequest $request The request to authenticate with. * @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $response The response to add headers to. * @param CakeResponse $response The response to add headers to.
* @return mixed Either false on failure, or an array of user data on success. * @return mixed Either false on failure, or an array of user data on success.
*/ */
public function authenticate(CakeRequest $request, CakeResponse $response) { public function authenticate(CakeRequest $request, CakeResponse $response) {
$result = $this->getUser($request); return $this->getUser($request);
if (empty($result)) {
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
}
return $result;
} }
/** /**
@ -117,6 +84,20 @@ class BasicAuthenticate extends BaseAuthenticate {
return $this->_findUser($username, $pass); 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 * 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, * For initial password hashing/creation see Security::hash(). Other than how the password is initially hashed,
* BlowfishAuthenticate works exactly the same way as FormAuthenticate. * BlowfishAuthenticate works exactly the same way as FormAuthenticate.
* *
* @package Cake.Controller.Component.Auth * @package Cake.Controller.Component.Auth
* @since CakePHP(tm) v 2.3 * @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 { class BlowfishAuthenticate extends FormAuthenticate {
/** /**
* Authenticates the identity contained in a request. Will use the `settings.userModel`, and `settings.fields` * Constructor. Sets default passwordHasher to Blowfish
* 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.
* *
* @param CakeRequest $request The request that contains login information. * @param ComponentCollection $collection The Component collection used on this request.
* @param CakeResponse $response Unused response object. * @param array $settings Array of settings to use.
* @return mixed False on login failure. An array of User data on success.
*/ */
public function authenticate(CakeRequest $request, CakeResponse $response) { public function __construct(ComponentCollection $collection, $settings) {
$userModel = $this->settings['userModel']; $this->settings['passwordHasher'] = 'Blowfish';
list(, $model) = pluginSplit($userModel); parent::__construct($collection, $settings);
$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;
} }
} }

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) { public function controller(Controller $controller = null) {
if ($controller) { if ($controller) {
if (!method_exists($controller, 'isAuthorized')) { 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); return parent::controller($controller);

View file

@ -14,7 +14,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @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. * Digest Authentication adapter for AuthComponent.
@ -55,7 +55,7 @@ App::uses('BaseAuthenticate', 'Controller/Component/Auth');
* @package Cake.Controller.Component.Auth * @package Cake.Controller.Component.Auth
* @since 2.0 * @since 2.0
*/ */
class DigestAuthenticate extends BaseAuthenticate { class DigestAuthenticate extends BasicAuthenticate {
/** /**
* Settings for this object. * Settings for this object.
@ -86,7 +86,8 @@ class DigestAuthenticate extends BaseAuthenticate {
'realm' => '', 'realm' => '',
'qop' => 'auth', 'qop' => 'auth',
'nonce' => '', 'nonce' => '',
'opaque' => '' 'opaque' => '',
'passwordHasher' => 'Simple',
); );
/** /**
@ -97,9 +98,6 @@ class DigestAuthenticate extends BaseAuthenticate {
*/ */
public function __construct(ComponentCollection $collection, $settings) { public function __construct(ComponentCollection $collection, $settings) {
parent::__construct($collection, $settings); parent::__construct($collection, $settings);
if (empty($this->settings['realm'])) {
$this->settings['realm'] = env('SERVER_NAME');
}
if (empty($this->settings['nonce'])) { if (empty($this->settings['nonce'])) {
$this->settings['nonce'] = uniqid(''); $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. * 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)) { if (empty($digest)) {
return false; 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)) { if (empty($user)) {
return false; return false;
} }
@ -151,34 +133,6 @@ class DigestAuthenticate extends BaseAuthenticate {
return false; 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. * 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 * The session key name where the record of the current user is stored. Default
* unspecified, it will be "Auth.User". * key is "Auth.User". If you are using only stateless authenticators set this
* to false to ensure session is not started.
* *
* @var string * @var string
*/ */
@ -188,7 +189,7 @@ class AuthComponent extends Component {
* Normally, if a user is redirected to the $loginAction page, the location they * 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 * 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 * 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 * @var mixed
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$loginRedirect * @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 * Error to display when user attempts to access an object or action to which they do not have
* access. * 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 * @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$authError
*/ */
public $authError = null; public $authError = null;
@ -294,43 +295,13 @@ class AuthComponent extends Component {
if (!$this->_setDefaults()) { if (!$this->_setDefaults()) {
return false; return false;
} }
$request = $controller->request;
$url = ''; if ($this->_isAllowed($controller)) {
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);
}
}
return true; return true;
} }
if (!$this->_getUser()) { if (!$this->_getUser()) {
if (!$request->is('ajax')) { return $this->_unauthenticated($controller);
$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);
} }
if (empty($this->authorize) || $this->isAuthorized($this->user())) { if (empty($this->authorize) || $this->isAuthorized($this->user())) {
@ -340,12 +311,95 @@ class AuthComponent extends Component {
return $this->_unauthorized($controller); 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 * Handle unauthorized access attempt
* *
* @param Controller $controller A reference to the controller object * @param Controller $controller A reference to the controller object
* @return boolean Returns false * @return boolean Returns false
* @throws ForbiddenException * @throws ForbiddenException
* @see AuthComponent::$unauthorizedRedirect
*/ */
protected function _unauthorized(Controller $controller) { protected function _unauthorized(Controller $controller) {
if ($this->unauthorizedRedirect === false) { if ($this->unauthorizedRedirect === false) {
@ -369,7 +423,7 @@ class AuthComponent extends Component {
/** /**
* Attempts to introspect the correct values for object properties. * Attempts to introspect the correct values for object properties.
* *
* @return boolean * @return boolean True
*/ */
protected function _setDefaults() { protected function _setDefaults() {
$defaults = array( $defaults = array(
@ -377,7 +431,7 @@ class AuthComponent extends Component {
'authError' => __d('cake', 'You are not authorized to access that location.') 'authError' => __d('cake', 'You are not authorized to access that location.')
); );
foreach ($defaults as $key => $value) { foreach ($defaults as $key => $value) {
if (empty($this->{$key})) { if (!isset($this->{$key}) || $this->{$key} === true) {
$this->{$key} = $value; $this->{$key} = $value;
} }
} }
@ -441,7 +495,7 @@ class AuthComponent extends Component {
throw new CakeException(__d('cake_dev', 'Authorization adapter "%s" was not found.', $class)); throw new CakeException(__d('cake_dev', 'Authorization adapter "%s" was not found.', $class));
} }
if (!method_exists($className, 'authorize')) { 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); $settings = array_merge($global, (array)$settings);
$this->_authorizeObjects[] = new $className($this->_Collection, $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 * @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user
*/ */
public static function user($key = null) { public static function user($key = null) {
if (empty(self::$_user) && !CakeSession::check(self::$sessionKey)) {
return null;
}
if (!empty(self::$_user)) { if (!empty(self::$_user)) {
$user = self::$_user; $user = self::$_user;
} else { } elseif (self::$sessionKey && CakeSession::check(self::$sessionKey)) {
$user = CakeSession::read(self::$sessionKey); $user = CakeSession::read(self::$sessionKey);
} else {
return null;
} }
if ($key === null) { if ($key === null) {
return $user; return $user;
@ -616,8 +669,10 @@ class AuthComponent extends Component {
protected function _getUser() { protected function _getUser() {
$user = $this->user(); $user = $this->user();
if ($user) { if ($user) {
$this->Session->delete('Auth.redirect');
return true; return true;
} }
if (empty($this->_authenticateObjects)) { if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate(); $this->constructAuthenticate();
} }
@ -628,6 +683,7 @@ class AuthComponent extends Component {
return true; return true;
} }
} }
return false; return false;
} }
@ -728,7 +784,7 @@ class AuthComponent extends Component {
throw new CakeException(__d('cake_dev', 'Authentication adapter "%s" was not found.', $class)); throw new CakeException(__d('cake_dev', 'Authentication adapter "%s" was not found.', $class));
} }
if (!method_exists($className, 'authenticate')) { 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); $settings = array_merge($global, (array)$settings);
$this->_authenticateObjects[] = new $className($this->_Collection, $settings); $this->_authenticateObjects[] = new $className($this->_Collection, $settings);
@ -744,24 +800,12 @@ class AuthComponent extends Component {
* *
* @param string $password Password to hash * @param string $password Password to hash
* @return string Hashed password * @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) { public static function password($password) {
return Security::hash($password, null, true); 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. * 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 * @return void
*/ */
public function flash($message) { public function flash($message) {
if ($message === false) {
return;
}
$this->Session->setFlash($message, $this->flash['element'], $this->flash['params'], $this->flash['key']); $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. * Compares the accepted types and configured extensions.
* If there is one common type, that is assigned as the ext/content type * If there is one common type, that is assigned as the ext/content type
* for the response. * 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 * 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. * 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)) { if (empty($accept)) {
return; return;
} }
$accepts = $this->response->mapType($this->request->parseAccept());
$preferedTypes = current($accepts);
if (array_intersect($preferedTypes, array('html', 'xhtml'))) {
return null;
}
$extensions = Router::extensions(); $extensions = Router::extensions();
$preferred = array_shift($accept); foreach ($accepts as $types) {
$preferredTypes = $this->response->mapType($preferred); $ext = array_intersect($extensions, $types);
if (!in_array('xhtml', $preferredTypes) && !in_array('html', $preferredTypes)) { if ($ext) {
$similarTypes = array_intersect($extensions, $preferredTypes); $this->ext = current($ext);
if (count($similarTypes) === 1) { break;
$this->ext = array_shift($similarTypes);
} }
} }
} }

View file

@ -94,7 +94,7 @@ class ComponentCollection extends ObjectCollection implements CakeEventListener
* @throws MissingComponentException when the component could not be found * @throws MissingComponentException when the component could not be found
*/ */
public function load($component, $settings = array()) { public function load($component, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) { if (isset($settings['className'])) {
$alias = $component; $alias = $component;
$component = $settings['className']; $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 * @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. * 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) { public function httpCodes($code = null) {
return $this->response->httpCodes($code); 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. * @param string $status The header message that is being set.
* @return void * @return void
* @deprecated Use CakeResponse::header() * @deprecated Will be removed in 3.0. Use CakeResponse::header().
*/ */
public function header($status) { public function header($status) {
$this->response->header($status); $this->response->header($status);
@ -978,7 +978,7 @@ class Controller extends Object implements CakeEventListener {
* *
* @return void * @return void
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache * @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() { public function disableCache() {
$this->response->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' * @param string $layout Layout you want to use, defaults to 'flash'
* @return void * @return void
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::flash * @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') { public function flash($message, $url, $pause = 1, $layout = 'flash') {
$this->autoRender = false; $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 * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be
* included in the returned conditions * included in the returned conditions
* @return array An array of model 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) { public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
if (!is_array($data) || empty($data)) { 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 * @param array $whitelist List of allowed options for paging
* @return array Model query results * @return array Model query results
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::paginate * @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()) { public function paginate($object = null, $scope = array(), $whitelist = array()) {
return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist); return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
@ -1146,7 +1147,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method * @param string $method
* @return boolean * @return boolean
* @see Controller::beforeScaffold() * @see Controller::beforeScaffold()
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
protected function _beforeScaffold($method) { protected function _beforeScaffold($method) {
return $this->beforeScaffold($method); return $this->beforeScaffold($method);
@ -1169,7 +1170,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method * @param string $method
* @return boolean * @return boolean
* @see Controller::afterScaffoldSave() * @see Controller::afterScaffoldSave()
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
protected function _afterScaffoldSave($method) { protected function _afterScaffoldSave($method) {
return $this->afterScaffoldSave($method); return $this->afterScaffoldSave($method);
@ -1192,7 +1193,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method * @param string $method
* @return boolean * @return boolean
* @see Controller::afterScaffoldSaveError() * @see Controller::afterScaffoldSaveError()
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
protected function _afterScaffoldSaveError($method) { protected function _afterScaffoldSaveError($method) {
return $this->afterScaffoldSaveError($method); return $this->afterScaffoldSaveError($method);
@ -1217,7 +1218,7 @@ class Controller extends Object implements CakeEventListener {
* @param string $method * @param string $method
* @return boolean * @return boolean
* @see Controller::scaffoldError() * @see Controller::scaffoldError()
* @deprecated * @deprecated Will be removed in 3.0.
*/ */
protected function _scaffoldError($method) { protected function _scaffoldError($method) {
return $this->scaffoldError($method); return $this->scaffoldError($method);

View file

@ -67,16 +67,14 @@ class Configure {
*/ */
public static function bootstrap($boot = true) { public static function bootstrap($boot = true) {
if ($boot) { if ($boot) {
self::write('App', array( self::_appDefaults();
'base' => false,
'baseUrl' => false,
'dir' => APP_DIR,
'webroot' => WEBROOT_DIR,
'www_root' => WWW_ROOT
));
if (!include APP . 'Config' . DS . 'core.php') { 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::init();
App::$bootstrapping = false; App::$bootstrapping = false;
@ -92,7 +90,11 @@ class Configure {
self::_setErrorHandlers($error, $exception); self::_setErrorHandlers($error, $exception);
if (!include APP . 'Config' . DS . 'bootstrap.php') { 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(); 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. * 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 * @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 * @param mixed $value Value to set for var
* @return boolean True if write was successful * @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)); throw new ConfigureException(__d('cake_dev', 'There is no "%s" adapter.', $config));
} }
if (!method_exists($reader, 'dump')) { 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; $values = self::$_values;
if (!empty($keys) && is_array($keys)) { if (!empty($keys) && is_array($keys)) {

View file

@ -16,7 +16,9 @@
App::uses('CakeLog', 'Log'); App::uses('CakeLog', 'Log');
App::uses('Dispatcher', 'Routing'); App::uses('Dispatcher', 'Routing');
App::uses('Router', 'Routing');
App::uses('Set', 'Utility'); App::uses('Set', 'Utility');
App::uses('CakeLog', 'Log');
/** /**
* Object class provides a few generic methods used in several subclasses. * Object class provides a few generic methods used in several subclasses.
@ -86,8 +88,8 @@ class Object {
$data = isset($extra['data']) ? $extra['data'] : null; $data = isset($extra['data']) ? $extra['data'] : null;
unset($extra['data']); unset($extra['data']);
if (is_string($url) && strpos($url, FULL_BASE_URL) === 0) { if (is_string($url) && strpos($url, Router::fullBaseUrl()) === 0) {
$url = Router::normalize(str_replace(FULL_BASE_URL, '', $url)); $url = Router::normalize(str_replace(Router::fullBaseUrl(), '', $url));
} }
if (is_string($url)) { if (is_string($url)) {
$request = new CakeRequest($url); $request = new CakeRequest($url);
@ -148,17 +150,16 @@ class Object {
* Convenience method to write a message to CakeLog. See CakeLog::write() * Convenience method to write a message to CakeLog. See CakeLog::write()
* for more information on writing to logs. * for more information on writing to logs.
* *
* @param string $msg Log message. * @param string $msg Log message
* @param integer|string $type Type of message being written. Either a valid * @param integer $type Error type constant. Defined in app/Config/core.php.
* LOG_* constant or a string matching the recognized levels. * @return boolean Success of log write
* @return boolean Success of log write.
* @see CakeLog::write()
*/ */
public function log($msg, $type = LOG_ERR) { public function log($msg, $type = LOG_ERR, $scope = null) {
if (!is_string($msg)) { if (!is_string($msg)) {
$msg = print_r($msg, true); $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) { public static function handleException(Exception $exception) {
$config = Configure::read('Exception'); $config = Configure::read('Exception');
if (!empty($config['log'])) { self::_log($exception, $config);
CakeLog::write(LOG_ERR, self::_getMessage($exception));
}
$renderer = isset($config['renderer']) ? $config['renderer'] : 'ExceptionRenderer'; $renderer = isset($config['renderer']) ? $config['renderer'] : 'ExceptionRenderer';
if ($renderer !== 'ExceptionRenderer') { if ($renderer !== 'ExceptionRenderer') {
list($plugin, $renderer) = pluginSplit($renderer, true); list($plugin, $renderer) = pluginSplit($renderer, true);
@ -159,6 +158,28 @@ class ErrorHandler {
return $message; 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 * 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 * 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 * @param string|array $header. An array of header strings or a single header string
* - an associative array of "header name" => "header value" * - an associative array of "header name" => "header value"
* - an array of string headers is also accepted * - an array of string headers is also accepted
* @param string $value. The header value. * @param string $value The header value.
* @return array * @return array
* @see CakeResponse::header() * @see CakeResponse::header()
*/ */
@ -78,7 +78,7 @@ class BadRequestException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Bad Request' will be the message * @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) { public function __construct($message = null, $code = 400) {
if (empty($message)) { if (empty($message)) {
@ -100,7 +100,7 @@ class UnauthorizedException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Unauthorized' will be the message * @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) { public function __construct($message = null, $code = 401) {
if (empty($message)) { if (empty($message)) {
@ -122,7 +122,7 @@ class ForbiddenException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Forbidden' will be the message * @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) { public function __construct($message = null, $code = 403) {
if (empty($message)) { if (empty($message)) {
@ -144,7 +144,7 @@ class NotFoundException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Not Found' will be the message * @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) { public function __construct($message = null, $code = 404) {
if (empty($message)) { if (empty($message)) {
@ -166,7 +166,7 @@ class MethodNotAllowedException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Method Not Allowed' will be the message * @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) { public function __construct($message = null, $code = 405) {
if (empty($message)) { if (empty($message)) {
@ -188,7 +188,7 @@ class InternalErrorException extends HttpException {
* Constructor * Constructor
* *
* @param string $message If no message is given 'Internal Server Error' will be the message * @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) { public function __construct($message = null, $code = 500) {
if (empty($message)) { 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 * @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 * 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) { public function __construct($message, $code = 500) {
if (is_array($message)) { if (is_array($message)) {

View file

@ -39,7 +39,7 @@ class L10n {
* *
* @var array * @var array
*/ */
public $languagePath = array('eng'); public $languagePath = array('en_us', 'eng');
/** /**
* ISO 639-3 for current locale * ISO 639-3 for current locale
@ -56,9 +56,11 @@ class L10n {
public $locale = 'en_us'; 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 * @var string
*/ */
@ -78,13 +80,6 @@ class L10n {
*/ */
public $direction = 'ltr'; public $direction = 'ltr';
/**
* Set to true if a locale is found
*
* @var string
*/
public $found = false;
/** /**
* Maps ISO 639-3 to I10n::_l10nCatalog * Maps ISO 639-3 to I10n::_l10nCatalog
* The terminological codes (first one per language) should be used if possible. * The terminological codes (first one per language) should be used if possible.
@ -138,6 +133,8 @@ class L10n {
/* Irish */ 'gle' => 'ga', /* Irish */ 'gle' => 'ga',
/* Italian */ 'ita' => 'it', /* Italian */ 'ita' => 'it',
/* Japanese */ 'jpn' => 'ja', /* Japanese */ 'jpn' => 'ja',
/* Kazakh */ 'kaz' => 'kk',
/* Kalaallisut (Greenlandic) */ 'kal' => 'kl',
/* Korean */ 'kor' => 'ko', /* Korean */ 'kor' => 'ko',
/* Latvian */ 'lav' => 'lv', /* Latvian */ 'lav' => 'lv',
/* Lithuanian */ 'lit' => 'lt', /* Lithuanian */ 'lit' => 'lt',
@ -155,7 +152,7 @@ class L10n {
/* Romanian */ 'ron' => 'ro', /* Romanian */ 'ron' => 'ro',
/* Romanian - bibliographic */ 'rum' => 'ro', /* Romanian - bibliographic */ 'rum' => 'ro',
/* Russian */ 'rus' => 'ru', /* Russian */ 'rus' => 'ru',
/* Sami (Lappish) */ 'smi' => 'sz', /* Sami */ 'sme' => 'se',
/* Serbian */ 'srp' => 'sr', /* Serbian */ 'srp' => 'sr',
/* Slovak */ 'slk' => 'sk', /* Slovak */ 'slk' => 'sk',
/* Slovak - bibliographic */ 'slo' => '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-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-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'), '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' => 'ell', 'localeFallback' => 'ell', 'charset' => 'utf-8', 'direction' => 'ltr'),
'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', '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-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'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), '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' => 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'), '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'), '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' => 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-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'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), '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' => 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'), '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'), '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'), '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'), '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' => 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'), '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'), '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' => 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'), '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'), 'se' => array('language' => 'Sami', 'locale' => 'sme', 'localeFallback' => 'sme', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', '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'), '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'), 'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -338,6 +332,10 @@ class L10n {
if (defined('DEFAULT_LANGUAGE')) { if (defined('DEFAULT_LANGUAGE')) {
$this->default = 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. * 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 * @return mixed
*/ */
protected function _setLanguage($language = null) { protected function _setLanguage($language = null) {
$langKey = null; $catalog = false;
if ($language !== null && isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { if ($language !== null) {
$langKey = $this->_l10nMap[$language]; $catalog = $this->catalog($language);
} elseif ($language !== null && isset($this->_l10nCatalog[$language])) {
$langKey = $language;
} elseif (defined('DEFAULT_LANGUAGE')) {
$langKey = $language = DEFAULT_LANGUAGE;
} }
if ($langKey !== null && isset($this->_l10nCatalog[$langKey])) { if (!$catalog && $this->default) {
$this->language = $this->_l10nCatalog[$langKey]['language']; $language = $this->default;
$this->languagePath = array( $catalog = $this->catalog($language);
$this->_l10nCatalog[$langKey]['locale'], }
$this->_l10nCatalog[$langKey]['localeFallback']
); if ($catalog) {
$this->language = $catalog['language'];
$this->languagePath = array_unique(array(
$catalog['locale'],
$catalog['localeFallback']
));
$this->lang = $language; $this->lang = $language;
$this->locale = $this->_l10nCatalog[$langKey]['locale']; $this->locale = $catalog['locale'];
$this->charset = $this->_l10nCatalog[$langKey]['charset']; $this->charset = $catalog['charset'];
$this->direction = $this->_l10nCatalog[$langKey]['direction']; $this->direction = $catalog['direction'];
} else { } elseif ($language) {
$this->lang = $language; $this->lang = $language;
$this->languagePath = array($language); $this->languagePath = array($language);
} }
if ($this->default) { if ($this->default && $language !== $this->default) {
if (isset($this->_l10nMap[$this->default]) && isset($this->_l10nCatalog[$this->_l10nMap[$this->default]])) { $catalog = $this->catalog($this->default);
$this->languagePath[] = $this->_l10nCatalog[$this->_l10nMap[$this->default]]['localeFallback']; $fallback = $catalog['localeFallback'];
} elseif (isset($this->_l10nCatalog[$this->default])) { if (!in_array($fallback, $this->languagePath)) {
$this->languagePath[] = $this->_l10nCatalog[$this->default]['localeFallback']; $this->languagePath[] = $fallback;
} }
} }
$this->found = true;
if (Configure::read('Config.language') === null) { if (Configure::read('Config.language') === null) {
Configure::write('Config.language', $this->lang); Configure::write('Config.language', $this->lang);
@ -420,7 +418,8 @@ class L10n {
if (isset($this->_l10nCatalog[$langKey])) { if (isset($this->_l10nCatalog[$langKey])) {
$this->_setLanguage($langKey); $this->_setLanguage($langKey);
return true; return true;
} elseif (strpos($langKey, '-') !== false) { }
if (strpos($langKey, '-') !== false) {
$langKey = substr($langKey, 0, 2); $langKey = substr($langKey, 0, 2);
if (isset($this->_l10nCatalog[$langKey])) { if (isset($this->_l10nCatalog[$langKey])) {
$this->_setLanguage($langKey); $this->_setLanguage($langKey);
@ -447,10 +446,12 @@ class L10n {
} }
} }
return $result; return $result;
} elseif (is_string($mixed)) { }
if (is_string($mixed)) {
if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) { if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) {
return array_search($mixed, $this->_l10nMap); return array_search($mixed, $this->_l10nMap);
} elseif (isset($this->_l10nMap[$mixed])) { }
if (isset($this->_l10nMap[$mixed])) {
return $this->_l10nMap[$mixed]; return $this->_l10nMap[$mixed];
} }
return false; return false;
@ -474,10 +475,12 @@ class L10n {
} }
} }
return $result; return $result;
} elseif (is_string($language)) { }
if (is_string($language)) {
if (isset($this->_l10nCatalog[$language])) { if (isset($this->_l10nCatalog[$language])) {
return $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 $this->_l10nCatalog[$this->_l10nMap[$language]];
} }
return false; return false;

View file

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

View file

@ -20,6 +20,7 @@
App::uses('BaseLog', 'Log/Engine'); App::uses('BaseLog', 'Log/Engine');
App::uses('Hash', 'Utility'); App::uses('Hash', 'Utility');
App::uses('CakeNumber', 'Utility');
/** /**
* File Storage stream for Logging. Writes logs to different files * File Storage stream for Logging. Writes logs to different files
@ -29,6 +30,22 @@ App::uses('Hash', 'Utility');
*/ */
class FileLog extends BaseLog { 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. * Path to save log files on.
* *
@ -36,6 +53,20 @@ class FileLog extends BaseLog {
*/ */
protected $_path = null; 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. * Constructs a new File Logger.
* *
@ -43,25 +74,55 @@ class FileLog extends BaseLog {
* *
* - `types` string or array, levels the engine is interested in * - `types` string or array, levels the engine is interested in
* - `scopes` string or array, scopes the engine is interested in * - `scopes` string or array, scopes the engine is interested in
* - `file` log file name * - `file` Log file name
* - `path` the path to save logs on. * - `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. * @param array $options Options for the FileLog, see above.
*/ */
public function __construct($config = array()) { public function __construct($config = array()) {
$config = Hash::merge($this->_defaults, $config);
parent::__construct($config); parent::__construct($config);
$config = Hash::merge(array( }
'path' => LOGS,
'file' => null, /**
'types' => null, * Sets protected properties based on config provided
'scopes' => array(), *
), $this->_config); * @param array $config Engine configuration
$config = $this->config($config); * @return array
$this->_path = $config['path']; */
$this->_file = $config['file']; public function config($config = array()) {
if (!empty($this->_file) && substr($this->_file, -4) !== '.log') { parent::config($config);
$this->_file .= '.log';
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. * @return boolean success of write.
*/ */
public function write($type, $message) { 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'); $debugTypes = array('notice', 'info', 'debug');
if (!empty($this->_file)) { if (!empty($this->_file)) {
$filename = $this->_path . $this->_file; $filename = $this->_file;
} elseif ($type === 'error' || $type === 'warning') { } elseif ($type == 'error' || $type == 'warning') {
$filename = $this->_path . 'error.log'; $filename = 'error.log';
} elseif (in_array($type, $debugTypes)) { } elseif (in_array($type, $debugTypes)) {
$filename = $this->_path . 'debug.log'; $filename = 'debug.log';
} else { } 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); $className = $this->_getLogger($loggerName);
$logger = new $className($options); $logger = new $className($options);
if (!$logger instanceof CakeLogInterface) { if (!$logger instanceof CakeLogInterface) {
throw new CakeLogException(sprintf( throw new CakeLogException(
__d('cake_dev', 'logger class %s does not implement a write method.'), $loggerName __d('cake_dev', 'logger class %s does not implement a %s method.', $loggerName, 'write()')
)); );
} }
$this->_loaded[$name] = $logger; $this->_loaded[$name] = $logger;
if ($enable) { if ($enable) {
@ -63,7 +63,9 @@ class LogEngineCollection extends ObjectCollection {
*/ */
protected static function _getLogger($loggerName) { protected static function _getLogger($loggerName) {
list($plugin, $loggerName) = pluginSplit($loggerName, true); list($plugin, $loggerName) = pluginSplit($loggerName, true);
if (substr($loggerName, -3) !== 'Log') {
$loggerName .= 'Log';
}
App::uses($loggerName, $plugin . 'Log/Engine'); App::uses($loggerName, $plugin . 'Log/Engine');
if (!class_exists($loggerName)) { if (!class_exists($loggerName)) {
throw new CakeLogException(__d('cake_dev', 'Could not load class %s', $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); $model->{$type} = ClassRegistry::init($type);
} }
if (!method_exists($model, 'parentNode')) { 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))); $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) { $this->_recoverByParentId($Model);
$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]);
}
} else { } else {
$db = ConnectionManager::getDataSource($Model->useDbConfig); $db = ConnectionManager::getDataSource($Model->useDbConfig);
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) { 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; 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. * Reorder method.
* *
@ -729,10 +789,9 @@ class TreeBehavior extends ModelBehavior {
if ($node[$right] == $node[$left] + 1) { if ($node[$right] == $node[$left] + 1) {
if ($delete) { if ($delete) {
return $Model->delete($id); return $Model->delete($id);
} else {
$Model->id = $id;
return $Model->saveField($parent, null);
} }
$Model->id = $id;
return $Model->saveField($parent, null);
} elseif ($node[$parent]) { } elseif ($node[$parent]) {
list($parentNode) = array_values($Model->find('first', array( list($parentNode) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $node[$parent]), 'conditions' => array($scope, $Model->escapeField() => $node[$parent]),

View file

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

View file

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

View file

@ -132,7 +132,7 @@ class CakeSession {
self::$time = time(); self::$time = time();
$checkAgent = Configure::read('Session.checkAgent'); $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::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
} }
self::_setPath($base); self::_setPath($base);
@ -486,10 +486,7 @@ class CakeSession {
if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) { if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
foreach ($sessionConfig['ini'] as $setting => $value) { foreach ($sessionConfig['ini'] as $setting => $value) {
if (ini_set($setting, $value) === false) { if (ini_set($setting, $value) === false) {
throw new CakeSessionException(sprintf( throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
__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. * 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 * @return boolean True if the database could be connected, else false
* @throws MissingConnectionException * @throws MissingConnectionException
*/ */
@ -146,7 +155,13 @@ class Mysql extends DboSource {
if (!empty($config['encoding'])) { if (!empty($config['encoding'])) {
$flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $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'])) { if (empty($config['unix_socket'])) {
$dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}"; $dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
} else { } else {
@ -161,6 +176,11 @@ class Mysql extends DboSource {
$flags $flags
); );
$this->connected = true; $this->connected = true;
if (!empty($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->_execute("SET $key=$value");
}
}
} catch (PDOException $e) { } catch (PDOException $e) {
throw new MissingConnectionException(array( throw new MissingConnectionException(array(
'class' => get_class($this), 'class' => get_class($this),

View file

@ -131,6 +131,11 @@ class Postgres extends DboSource {
if (!empty($config['schema'])) { if (!empty($config['schema'])) {
$this->_execute('SET search_path TO ' . $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) { } catch (PDOException $e) {
throw new MissingConnectionException(array( throw new MissingConnectionException(array(
'class' => get_class($this), 'class' => get_class($this),

View file

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

View file

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

View file

@ -1497,6 +1497,17 @@ class Model extends Object implements CakeEventListener {
return $this->data; 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 * Returns a list of fields from the database, and sets the current model
* data (Model::$data) with the record found. * 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 mixed $value Value of the field
* @param boolean|array $validate Either a boolean, or an array. * @param boolean|array $validate Either a boolean, or an array.
* If a boolean, indicates whether or not to validate before saving. * 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() * @return boolean See Model::save()
* @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 * @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. * - fieldList: An array of fields you want to allow for saving.
* - callbacks: Set to false to disable callbacks. Using 'before' or 'after' * - callbacks: Set to false to disable callbacks. Using 'before' or 'after'
* will enable only those callbacks. * 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 * @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 * @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 * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html
*/ */
public function save($data = null, $validate = true, $fieldList = array()) { 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; $_whitelist = $this->whitelist;
$fields = array(); $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); $this->updateCounterCache($cache, $created);
} }
} }
@ -2030,7 +2046,9 @@ class Model extends Object implements CakeEventListener {
* 'AssociatedModel' => array('field', 'otherfield') * '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 * @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. * 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. * Should be set to false if database/table does not support transactions.
* - `fieldList`: Equivalent to the $fieldList parameter in Model::save() * - `fieldList`: Equivalent to the $fieldList parameter in Model::save()
* - `deep`: If set to true, all associated data will be saved as well. * - `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 $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. * @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. * - `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 $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. * @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 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); $results = $this->getDataSource()->read($this, $query);
$this->resetAssociations(); $this->resetAssociations();
@ -2706,10 +2756,6 @@ class Model extends Object implements CakeEventListener {
$this->findQueryType = null; $this->findQueryType = null;
if ($type === 'all') {
return $results;
}
if ($this->findMethods[$type] === true) { if ($this->findMethods[$type] === true) {
return $this->{'_find' . ucfirst($type)}('after', $query, $results); return $this->{'_find' . ucfirst($type)}('after', $query, $results);
} }
@ -2732,7 +2778,7 @@ class Model extends Object implements CakeEventListener {
(array)$query (array)$query
); );
if ($type !== 'all' && $this->findMethods[$type] === true) { if ($this->findMethods[$type] === true) {
$query = $this->{'_find' . ucfirst($type)}('before', $query); $query = $this->{'_find' . ucfirst($type)}('before', $query);
} }
@ -2760,6 +2806,23 @@ class Model extends Object implements CakeEventListener {
return $query; 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(). * 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 { class Permission extends AppModel {
/**
* Model name
*
* @var string
*/
public $name = 'Permission';
/** /**
* Explicitly disable in-memory query caching * Explicitly disable in-memory query caching
* *
@ -81,7 +74,7 @@ class Permission extends AppModel {
* @param string $action Action (defaults to *) * @param string $action Action (defaults to *)
* @return boolean Success (true if ARO has access to action in ACO, false otherwise) * @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) { if (!$aro || !$aco) {
return false; return false;
} }
@ -91,17 +84,29 @@ class Permission extends AppModel {
$acoPath = $this->Aco->node($aco); $acoPath = $this->Aco->node($aco);
if (!$aroPath || !$acoPath) { 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; return false;
} }
if (!$acoPath) { 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; return false;
} }
if ($action !== '*' && !in_array('_' . $action, $permKeys)) { 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; return false;
} }
@ -166,20 +171,20 @@ class Permission extends AppModel {
* @return boolean Success * @return boolean Success
* @throws AclException on Invalid permission key. * @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); $perms = $this->getAclLink($aro, $aco);
$permKeys = $this->getAcoKeys($this->schema()); $permKeys = $this->getAcoKeys($this->schema());
$save = array(); $save = array();
if (!$perms) { 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; return false;
} }
if (isset($perms[0])) { if (isset($perms[0])) {
$save = $perms[0][$this->alias]; $save = $perms[0][$this->alias];
} }
if ($actions === "*") { if ($actions === '*') {
$save = array_combine($permKeys, array_pad(array(), count($permKeys), $value)); $save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
} else { } else {
if (!is_array($actions)) { if (!is_array($actions)) {

View file

@ -238,7 +238,7 @@ class CakeRequest implements ArrayAccess {
if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) { if ($qPosition !== false && strpos($_SERVER['REQUEST_URI'], '://') > $qPosition) {
$uri = $_SERVER['REQUEST_URI']; $uri = $_SERVER['REQUEST_URI'];
} else { } 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'])) { } elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) {
$uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']); $uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']);
@ -424,10 +424,7 @@ class CakeRequest implements ArrayAccess {
$ref = $forwarded; $ref = $forwarded;
} }
$base = ''; $base = Configure::read('App.fullBaseUrl') . $this->webroot;
if (defined('FULL_BASE_URL')) {
$base = FULL_BASE_URL . $this->webroot;
}
if (!empty($ref) && !empty($base)) { if (!empty($ref) && !empty($base)) {
if ($local && strpos($ref, $base) === 0) { if ($local && strpos($ref, $base) === 0) {
$ref = substr($ref, strlen($base)); $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 * Check whether or not a Request is a certain type.
* as well as additional rules defined with CakeRequest::addDetector(). Any detector can be called *
* 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()`. * 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. * @return boolean Whether or not the request is the type you are checking.
*/ */
public function is($type) { public function is($type) {
if (is_array($type)) {
$result = array_map(array($this, 'is'), $type);
return count(array_filter($result)) > 0;
}
$type = strtolower($type); $type = strtolower($type);
if (!isset($this->_detectors[$type])) { if (!isset($this->_detectors[$type])) {
return false; return false;
@ -521,6 +525,22 @@ class CakeRequest implements ArrayAccess {
return false; 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. * 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. * 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); 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 * Read data from `php://input`. Useful when interacting with XML or JSON
* request body content. * request body content.

View file

@ -416,8 +416,10 @@ class CakeResponse {
$this->_setContent(); $this->_setContent();
$this->_setContentLength(); $this->_setContentLength();
$this->_setContentType(); $this->_setContentType();
foreach ($this->_headers as $header => $value) { foreach ($this->_headers as $header => $values) {
$this->_sendHeader($header, $value); foreach ((array)$values as $value) {
$this->_sendHeader($header, $value);
}
} }
if ($this->_file) { if ($this->_file) {
$this->_sendFile($this->_file, $this->_fileRange); $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 * @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 associative array of "header name" => "header value" is also accepted
* - an array of string headers 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 * @return array list of headers to be sent
*/ */
public function header($header = null, $value = null) { public function header($header = null, $value = null) {
if ($header === null) { if ($header === null) {
return $this->_headers; return $this->_headers;
} }
if (is_array($header)) { $headers = is_array($header) ? $header : array($header => $value);
foreach ($header as $h => $v) { foreach ($headers as $header => $value) {
if (is_numeric($h)) { if (is_numeric($header)) {
$this->header($v); list($header, $value) = array($value, null);
continue;
}
$this->_headers[$h] = trim($v);
} }
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; 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 * Buffers the response message to be sent
* if $content is null the current buffer is returned * if $content is null the current buffer is returned
@ -602,7 +612,7 @@ class CakeResponse {
* Sets the HTTP status code to be sent * Sets the HTTP status code to be sent
* if $code is null the current code is returned * if $code is null the current code is returned
* *
* @param integer $code * @param integer $code the HTTP status code
* @return integer current status code * @return integer current status code
* @throws CakeException When an unknown status code is reached. * @throws CakeException When an unknown status code is reached.
*/ */
@ -619,31 +629,47 @@ class CakeResponse {
/** /**
* Queries & sets valid HTTP response codes & messages. * Queries & sets valid HTTP response codes & messages.
* *
* @param integer|array $code If $code is an integer, then the corresponding code/message is * @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, * returned if it exists, null if it does not exist. If $code is an array, then the
* then the 'code' and 'message' keys of each nested array are added to the default * keys are used as codes and the values as messages to add to the default HTTP
* HTTP codes. Example: * 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(404); // returns array(404 => 'Not Found')
* *
* httpCodes(array( * httpCodes(array(
* 701 => 'Unicorn Moved', * 381 => 'Unicorn Moved',
* 800 => 'Unexpected Minotaur' * 555 => 'Unexpected Minotaur'
* )); // sets these new values, and returns true * )); // 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 * @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. * 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) { public function httpCodes($code = null) {
if (empty($code)) { if (empty($code)) {
return $this->_statusCodes; return $this->_statusCodes;
} }
if (is_array($code)) { 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; $this->_statusCodes = $code + $this->_statusCodes;
return true; return true;
} }
if (!isset($this->_statusCodes[$code])) { if (!isset($this->_statusCodes[$code])) {
return null; return null;
} }
@ -724,9 +750,7 @@ class CakeResponse {
} }
foreach ($this->_mimeTypes as $alias => $types) { foreach ($this->_mimeTypes as $alias => $types) {
if (is_array($types) && in_array($ctype, $types)) { if (in_array($ctype, (array)$types)) {
return $alias;
} elseif (is_string($types) && $types == $ctype) {
return $alias; return $alias;
} }
} }

View file

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

View file

@ -318,6 +318,14 @@ class CakeEmail {
'ISO-2022-JP-MS' => 'ISO-2022-JP' '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 * Constructor
* *
@ -521,6 +529,20 @@ class CakeEmail {
return $this->headerCharset = $charset; 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 * Set email
* *
@ -532,7 +554,7 @@ class CakeEmail {
*/ */
protected function _setEmail($varName, $email, $name) { protected function _setEmail($varName, $email, $name) {
if (!is_array($email)) { 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)); throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
} }
if ($name === null) { if ($name === null) {
@ -546,7 +568,7 @@ class CakeEmail {
if (is_int($key)) { if (is_int($key)) {
$key = $value; $key = $value;
} }
if (!Validation::email($key)) { if (!Validation::email($key, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key)); throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
} }
$list[$key] = $value; $list[$key] = $value;
@ -586,7 +608,7 @@ class CakeEmail {
*/ */
protected function _addEmail($varName, $email, $name) { protected function _addEmail($varName, $email, $name) {
if (!is_array($email)) { 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)); throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
} }
if ($name === null) { if ($name === null) {
@ -600,7 +622,7 @@ class CakeEmail {
if (is_int($key)) { if (is_int($key)) {
$key = $value; $key = $value;
} }
if (!Validation::email($key)) { if (!Validation::email($key, false, $this->_emailPattern)) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key)); throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
} }
$list[$key] = $value; $list[$key] = $value;
@ -884,7 +906,7 @@ class CakeEmail {
if (!class_exists($transportClassname)) { if (!class_exists($transportClassname)) {
throw new SocketException(__d('cake_dev', 'Class "%s" not found.', $transportClassname)); throw new SocketException(__d('cake_dev', 'Class "%s" not found.', $transportClassname));
} elseif (!method_exists($transportClassname, 'send')) { } 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(); return $this->_transportClass = new $transportClassname();
@ -953,6 +975,15 @@ class CakeEmail {
* 'contentDisposition' => false * '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 * 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. * can use `<img src="cid:abc123" />` to display the image inline.
@ -974,14 +1005,21 @@ class CakeEmail {
$fileInfo = array('file' => $fileInfo); $fileInfo = array('file' => $fileInfo);
} }
if (!isset($fileInfo['file'])) { if (!isset($fileInfo['file'])) {
throw new SocketException(__d('cake_dev', 'File not specified.')); if (!isset($fileInfo['data'])) {
} throw new SocketException(__d('cake_dev', 'No file or data specified.'));
$fileInfo['file'] = realpath($fileInfo['file']); }
if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) { if (is_int($name)) {
throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileInfo['file'])); throw new SocketException(__d('cake_dev', 'No filename specified.'));
} }
if (is_int($name)) { $fileInfo['data'] = chunk_split(base64_encode($fileInfo['data']), 76, "\r\n");
$name = basename($fileInfo['file']); } 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'])) { if (!isset($fileInfo['mimetype'])) {
$fileInfo['mimetype'] = 'application/octet-stream'; $fileInfo['mimetype'] = 'application/octet-stream';
@ -1074,11 +1112,21 @@ class CakeEmail {
$contents = $this->transportClass()->send($this); $contents = $this->transportClass()->send($this);
if (!empty($this->_config['log'])) { if (!empty($this->_config['log'])) {
$level = LOG_DEBUG; $config = array(
'level' => LOG_DEBUG,
'scope' => 'email'
);
if ($this->_config['log'] !== true) { 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; return $contents;
} }
@ -1149,7 +1197,7 @@ class CakeEmail {
$simpleMethods = array( $simpleMethods = array(
'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc', 'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments', 'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments',
'transport', 'emailFormat', 'theme', 'helpers' 'transport', 'emailFormat', 'theme', 'helpers', 'emailPattern'
); );
foreach ($simpleMethods as $method) { foreach ($simpleMethods as $method) {
if (isset($config[$method])) { if (isset($config[$method])) {
@ -1206,6 +1254,7 @@ class CakeEmail {
$this->headerCharset = null; $this->headerCharset = null;
$this->_attachments = array(); $this->_attachments = array();
$this->_config = array(); $this->_config = array();
$this->_emailPattern = null;
return $this; return $this;
} }
@ -1376,7 +1425,7 @@ class CakeEmail {
if (!empty($fileInfo['contentId'])) { if (!empty($fileInfo['contentId'])) {
continue; continue;
} }
$data = $this->_readFile($fileInfo['file']); $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
$msg[] = '--' . $boundary; $msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype']; $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
@ -1421,7 +1470,7 @@ class CakeEmail {
if (empty($fileInfo['contentId'])) { if (empty($fileInfo['contentId'])) {
continue; continue;
} }
$data = $this->_readFile($fileInfo['file']); $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
$msg[] = '--' . $boundary; $msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype']; $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];

View file

@ -96,6 +96,7 @@ class HttpSocket extends CakeSocket {
'port' => 80, 'port' => 80,
'timeout' => 30, 'timeout' => 30,
'ssl_verify_peer' => true, 'ssl_verify_peer' => true,
'ssl_allow_self_signed' => false,
'ssl_verify_depth' => 5, 'ssl_verify_depth' => 5,
'ssl_verify_host' => true, 'ssl_verify_host' => true,
'request' => array( 'request' => array(
@ -495,6 +496,19 @@ class HttpSocket extends CakeSocket {
return $this->request($request); 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. * 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.')); throw new SocketException(__d('cake_dev', 'Unknown authentication method.'));
} }
if (!method_exists($authClass, 'authentication')) { 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])); 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.')); throw new SocketException(__d('cake_dev', 'Unknown authentication method for proxy.'));
} }
if (!method_exists($authClass, 'proxyAuthentication')) { 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)); call_user_func_array("$authClass::proxyAuthentication", array($this, &$this->_proxy));
} }

View file

@ -56,6 +56,14 @@ class Router {
*/ */
public static $initialized = false; 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. * List of action prefixes used in connected routes.
* Includes admin prefix * Includes admin prefix
@ -551,7 +559,8 @@ class Router {
$url = '/' . $url; $url = '/' . $url;
} }
if (strpos($url, '?') !== false) { if (strpos($url, '?') !== false) {
$url = substr($url, 0, strpos($url, '?')); list($url, $queryParameters) = explode('?', $url, 2);
parse_str($queryParameters, $queryParameters);
} }
extract(self::_parseExtension($url)); extract(self::_parseExtension($url));
@ -572,6 +581,10 @@ class Router {
if (!empty($ext) && !isset($out['ext'])) { if (!empty($ext) && !isset($out['ext'])) {
$out['ext'] = $ext; $out['ext'] = $ext;
} }
if (!empty($queryParameters) && !isset($out['?'])) {
$out['?'] = $queryParameters;
}
return $out; return $out;
} }
@ -759,7 +772,7 @@ class Router {
* cake relative URLs are required when using requestAction. * cake relative URLs are required when using requestAction.
* - `?` - Takes an array of query string parameters * - `?` - Takes an array of query string parameters
* - `#` - Allows you to set URL hash fragments. * - `#` - 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" * @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', * or an array specifying any of the following: 'controller', 'action',
@ -796,8 +809,8 @@ class Router {
if (empty($url)) { if (empty($url)) {
$output = isset($path['here']) ? $path['here'] : '/'; $output = isset($path['here']) ? $path['here'] : '/';
if ($full && defined('FULL_BASE_URL')) { if ($full) {
$output = FULL_BASE_URL . $output; $output = self::fullBaseUrl() . $output;
} }
return $output; return $output;
} elseif (is_array($url)) { } elseif (is_array($url)) {
@ -860,7 +873,7 @@ class Router {
$output = self::_handleNoRoute($url); $output = self::_handleNoRoute($url);
} }
} else { } else {
if (preg_match('/:\/\/|^(javascript|mailto|tel|sms):|^\#/i', $url)) { if (preg_match('/^([a-z][a-z0-9.+\-]+:|:?\/\/|[#?])/i', $url)) {
return $url; return $url;
} }
if (substr($url, 0, 1) === '/') { if (substr($url, 0, 1) === '/') {
@ -878,12 +891,12 @@ class Router {
$output .= Inflector::underscore($params['controller']) . '/' . $url; $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) { if ($protocol === 0) {
$output = str_replace('//', '/', $base . '/' . $output); $output = str_replace('//', '/', $base . '/' . $output);
if ($full && defined('FULL_BASE_URL')) { if ($full) {
$output = FULL_BASE_URL . $output; $output = self::fullBaseUrl() . $output;
} }
if (!empty($extension)) { if (!empty($extension)) {
$output = rtrim($output, '/'); $output = rtrim($output, '/');
@ -892,6 +905,32 @@ class Router {
return $output . $extension . self::queryString($q, array(), $escape) . $frag; 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 * A special fallback method that handles URL arrays that cannot match
* any defined routes. * any defined routes.

View file

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

View file

@ -48,6 +48,9 @@ class CacheTest extends CakeTestCase {
*/ */
public function tearDown() { public function tearDown() {
parent::tearDown(); parent::tearDown();
Cache::drop('latest');
Cache::drop('page');
Cache::drop('archive');
Configure::write('Cache.disable', $this->_cacheDisable); Configure::write('Cache.disable', $this->_cacheDisable);
Cache::config('default', $this->_defaultCacheConfig['settings']); Cache::config('default', $this->_defaultCacheConfig['settings']);
} }
@ -129,6 +132,10 @@ class CacheTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testInvalidConfig() { public function testInvalidConfig() {
// In debug mode it would auto create the folder.
$debug = Configure::read('debug');
Configure::write('debug', 0);
Cache::config('invalid', array( Cache::config('invalid', array(
'engine' => 'File', 'engine' => 'File',
'duration' => '+1 year', 'duration' => '+1 year',
@ -138,6 +145,8 @@ class CacheTest extends CakeTestCase {
'random' => 'wii' 'random' => 'wii'
)); ));
Cache::read('Test', 'invalid'); Cache::read('Test', 'invalid');
Configure::write('debug', $debug);
} }
/** /**
@ -237,6 +246,67 @@ class CacheTest extends CakeTestCase {
Cache::config('sessions', $_cacheConfigSessions['settings']); 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 * test that configured returns an array of the currently configured cache
* settings * 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 * @return void
*/ */
public function testErrorWhenPathDoesNotExist() { public function testPathDoesNotExist() {
$this->skipIf(is_dir(TMP . 'tests' . DS . 'file_failure'), 'Cannot run test directory exists.'); $this->skipIf(is_dir(TMP . 'tests' . DS . 'autocreate'), 'Cannot run if test directory exists.');
Cache::config('failure', array( Cache::config('autocreate', array(
'engine' => 'File', '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