diff --git a/.stickler.yml b/.stickler.yml new file mode 100644 index 000000000..61243f090 --- /dev/null +++ b/.stickler.yml @@ -0,0 +1,2 @@ +branches: + ignore: ['2.x', '2.next'] diff --git a/.travis.yml b/.travis.yml index c3492d555..b43db1d25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ before_script: - sh -c "composer global require 'phpunit/phpunit=3.7.33'" - sh -c "ln -s ~/.composer/vendor/phpunit/phpunit/PHPUnit ./vendors/PHPUnit" - sudo locale-gen de_DE + - sudo locale-gen es_ES - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test2;'; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test3;'; fi" diff --git a/app/webroot/.htaccess b/app/webroot/.htaccess index 1d499ba73..e3543be40 100644 --- a/app/webroot/.htaccess +++ b/app/webroot/.htaccess @@ -1,3 +1,9 @@ +# Uncomment the following to prevent the httpoxy vulnerability +# See: https://httpoxy.org/ +# +# RequestHeader unset Proxy +# + RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d diff --git a/lib/Cake/Console/Command/TestShell.php b/lib/Cake/Console/Command/TestShell.php index 4dfacfdfc..29eca2721 100644 --- a/lib/Cake/Console/Command/TestShell.php +++ b/lib/Cake/Console/Command/TestShell.php @@ -71,6 +71,9 @@ class TestShell extends Shell { ))->addOption('coverage-clover', array( 'help' => __d('cake_console', ' Write code coverage data in Clover XML format.'), 'default' => false + ))->addOption('coverage-text', array( + 'help' => __d('cake_console', 'Output code coverage report in Text format.'), + 'boolean' => true ))->addOption('testdox-html', array( 'help' => __d('cake_console', ' Write agile documentation in HTML format to file.'), 'default' => false @@ -152,6 +155,7 @@ class TestShell extends Shell { 'default' => false ))->addOption('directive', array( 'help' => __d('cake_console', 'key[=value] Sets a php.ini value.'), + 'short' => 'd', 'default' => false ))->addOption('fixture', array( 'help' => __d('cake_console', 'Choose a custom fixture manager.') @@ -234,7 +238,11 @@ class TestShell extends Shell { if ($value === false) { continue; } - $options[] = '--' . $param; + if ($param === 'directive') { + $options[] = '-d'; + } else { + $options[] = '--' . $param; + } if (is_string($value)) { $options[] = $value; } diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index 3381d68f1..3e62e2cc9 100644 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -359,7 +359,7 @@ class AuthComponent extends Component { return true; } - if (!$controller->request->is('ajax')) { + if (!$controller->request->is('ajax') && !$controller->request->is('json')) { $this->flash($this->authError); $this->Session->write('Auth.redirect', $controller->request->here(false)); $controller->redirect($this->loginAction); @@ -611,8 +611,12 @@ class AuthComponent extends Component { $user = $this->identify($this->request, $this->response); } if ($user) { - $this->Session->renew(); - $this->Session->write(static::$sessionKey, $user); + if (static::$sessionKey) { + $this->Session->renew(); + $this->Session->write(static::$sessionKey, $user); + } else { + static::$_user = $user; + } $event = new CakeEvent('Auth.afterIdentify', $this, array('user' => $user)); $this->_Collection->getController()->getEventManager()->dispatch($event); } diff --git a/lib/Cake/Error/ExceptionRenderer.php b/lib/Cake/Error/ExceptionRenderer.php index a245b64ee..d5d32ff95 100644 --- a/lib/Cake/Error/ExceptionRenderer.php +++ b/lib/Cake/Error/ExceptionRenderer.php @@ -20,9 +20,12 @@ */ App::uses('Sanitize', 'Utility'); +App::uses('Dispatcher', 'Routing'); App::uses('Router', 'Routing'); -App::uses('CakeResponse', 'Network'); App::uses('Controller', 'Controller'); +App::uses('CakeRequest', 'Network'); +App::uses('CakeResponse', 'Network'); +App::uses('CakeEvent', 'Event'); /** * Exception Renderer. @@ -287,7 +290,7 @@ class ExceptionRenderer { protected function _outputMessage($template) { try { $this->controller->render($template); - $this->controller->afterFilter(); + $this->_shutdown(); $this->controller->response->send(); } catch (MissingViewException $e) { $attributes = $e->getAttributes(); @@ -327,4 +330,23 @@ class ExceptionRenderer { $this->controller->response->send(); } +/** + * Run the shutdown events. + * + * Triggers the afterFilter and afterDispatch events. + * + * @return void + */ + protected function _shutdown() { + $afterFilterEvent = new CakeEvent('Controller.shutdown', $this->controller); + $this->controller->getEventManager()->dispatch($afterFilterEvent); + + $Dispatcher = new Dispatcher(); + $afterDispatchEvent = new CakeEvent('Dispatcher.afterDispatch', $Dispatcher, array( + 'request' => $this->controller->request, + 'response' => $this->controller->response + )); + $Dispatcher->getEventManager()->dispatch($afterDispatchEvent); + } + } diff --git a/lib/Cake/I18n/L10n.php b/lib/Cake/I18n/L10n.php index 7e57a36fb..a87be16c0 100644 --- a/lib/Cake/I18n/L10n.php +++ b/lib/Cake/I18n/L10n.php @@ -272,6 +272,7 @@ class L10n { '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'), 'is' => array('language' => 'Icelandic', 'locale' => 'isl', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'is-is' => array('language' => 'Icelandic (Iceland)', 'locale' => 'is_is', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'), 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'), diff --git a/lib/Cake/Model/CakeSchema.php b/lib/Cake/Model/CakeSchema.php index d1a9be806..74c9e4ac4 100644 --- a/lib/Cake/Model/CakeSchema.php +++ b/lib/Cake/Model/CakeSchema.php @@ -487,6 +487,9 @@ class CakeSchema extends CakeObject { foreach ($fields as $field => $value) { if (!empty($old[$table][$field])) { $diff = $this->_arrayDiffAssoc($value, $old[$table][$field]); + if (empty($diff)) { + $diff = $this->_arrayDiffAssoc($old[$table][$field], $value); + } if (!empty($diff) && $field !== 'indexes' && $field !== 'tableParameters') { $tables[$table]['change'][$field] = $value; } diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php index 24358c107..21f763dc4 100644 --- a/lib/Cake/Model/Datasource/Database/Mysql.php +++ b/lib/Cake/Model/Datasource/Database/Mysql.php @@ -437,13 +437,7 @@ class Mysql extends DboSource { if (empty($conditions)) { $alias = $joins = false; } - $complexConditions = false; - foreach ((array)$conditions as $key => $value) { - if (strpos($key, $model->alias) === false) { - $complexConditions = true; - break; - } - } + $complexConditions = $this->_deleteNeedsComplexConditions($model, $conditions); if (!$complexConditions) { $joins = false; } @@ -459,6 +453,27 @@ class Mysql extends DboSource { return true; } +/** + * Checks whether complex conditions are needed for a delete with the given conditions. + * + * @param Model $model The model to delete from. + * @param mixed $conditions The conditions to use. + * @return bool Whether or not complex conditions are needed + */ + protected function _deleteNeedsComplexConditions(Model $model, $conditions) { + $fields = array_keys($this->describe($model)); + foreach ((array)$conditions as $key => $value) { + if (in_array(strtolower(trim($key)), $this->_sqlBoolOps, true)) { + if ($this->_deleteNeedsComplexConditions($model, $value)) { + return true; + } + } elseif (strpos($key, $model->alias) === false && !in_array($key, $fields, true)) { + return true; + } + } + return false; + } + /** * Sets the database encoding * diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index dfb59157c..f37d6310e 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -183,6 +183,13 @@ class DboSource extends DataSource { */ protected $_sqlOps = array('like', 'ilike', 'rlike', 'or', 'not', 'in', 'between', 'regexp', 'similar to'); +/** + * The set of valid SQL boolean operations usable in a WHERE statement + * + * @var array + */ + protected $_sqlBoolOps = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); + /** * Indicates the level of nested transactions * @@ -2678,7 +2685,6 @@ class DboSource extends DataSource { public function conditionKeysToString($conditions, $quoteValues = true, Model $Model = null) { $out = array(); $data = $columnType = null; - $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); foreach ($conditions as $key => $value) { $join = ' AND '; @@ -2695,8 +2701,8 @@ class DboSource extends DataSource { continue; } elseif (is_numeric($key) && is_string($value)) { $out[] = $this->_quoteFields($value); - } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) { - if (in_array(strtolower(trim($key)), $bool)) { + } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $this->_sqlBoolOps)) { + if (in_array(strtolower(trim($key)), $this->_sqlBoolOps)) { $join = ' ' . strtoupper($key) . ' '; } else { $key = $join; diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 02aa1aca2..87336976f 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1035,13 +1035,13 @@ class Model extends CakeObject implements CakeEventListener { unset($association[$assoc]); $assoc = $value; $value = array(); + $association[$assoc] = $value; + } - if (strpos($assoc, '.') !== false) { - list($plugin, $assoc) = pluginSplit($assoc, true); - $association[$assoc] = array('className' => $plugin . $assoc); - } else { - $association[$assoc] = $value; - } + if (!isset($value['className']) && strpos($assoc, '.') !== false) { + unset($association[$assoc]); + list($plugin, $assoc) = pluginSplit($assoc, true); + $association[$assoc] = array('className' => $plugin . $assoc) + $value; } $this->_generateAssociation($type, $assoc); diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index db97a04ea..fda635e62 100644 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -397,7 +397,7 @@ class CakeRequest implements ArrayAccess { /** * Get the content type used in this request. - * + * * @return string */ public function contentType() { @@ -748,7 +748,13 @@ class CakeRequest implements ArrayAccess { * @return mixed Either false on no header being set or the value of the header. */ public static function header($name) { - $name = 'HTTP_' . strtoupper(str_replace('-', '_', $name)); + $name = strtoupper(str_replace('-', '_', $name)); + $httpName = 'HTTP_' . $name; + if (isset($_SERVER[$httpName])) { + return $_SERVER[$httpName]; + } + // Work around Apache issues where 'Authorization' is not + // passed to PHP. if (isset($_SERVER[$name])) { return $_SERVER[$name]; } diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index bde506011..c0141a53f 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -587,7 +587,7 @@ class CakeResponse { if (is_numeric($header)) { list($header, $value) = array($value, null); } - if ($value === null) { + if ($value === null && strpos($header, ':') !== false) { list($header, $value) = explode(':', $header, 2); } $this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value); diff --git a/lib/Cake/Network/Email/SmtpTransport.php b/lib/Cake/Network/Email/SmtpTransport.php index c581c9a2f..b0761d496 100644 --- a/lib/Cake/Network/Email/SmtpTransport.php +++ b/lib/Cake/Network/Email/SmtpTransport.php @@ -32,13 +32,6 @@ class SmtpTransport extends AbstractTransport { */ protected $_socket; -/** - * CakeEmail - * - * @var CakeEmail - */ - protected $_cakeEmail; - /** * Content of email to return * @@ -90,12 +83,10 @@ class SmtpTransport extends AbstractTransport { * @throws SocketException */ public function send(CakeEmail $email) { - $this->_cakeEmail = $email; - $this->_connect(); $this->_auth(); - $this->_sendRcpt(); - $this->_sendData(); + $this->_sendRcpt($email); + $this->_sendData($email); $this->_disconnect(); return $this->_content; @@ -235,12 +226,13 @@ class SmtpTransport extends AbstractTransport { /** * Prepares the `from` email address. * + * @param CakeEmail $email CakeEmail * @return array */ - protected function _prepareFromAddress() { - $from = $this->_cakeEmail->returnPath(); + protected function _prepareFromAddress(CakeEmail $email) { + $from = $email->returnPath(); if (empty($from)) { - $from = $this->_cakeEmail->from(); + $from = $email->from(); } return $from; } @@ -248,31 +240,34 @@ class SmtpTransport extends AbstractTransport { /** * Prepares the recipient email addresses. * + * @param CakeEmail $email CakeEmail * @return array */ - protected function _prepareRecipientAddresses() { - $to = $this->_cakeEmail->to(); - $cc = $this->_cakeEmail->cc(); - $bcc = $this->_cakeEmail->bcc(); + protected function _prepareRecipientAddresses(CakeEmail $email) { + $to = $email->to(); + $cc = $email->cc(); + $bcc = $email->bcc(); return array_merge(array_keys($to), array_keys($cc), array_keys($bcc)); } /** * Prepares the message headers. * + * @param CakeEmail $email CakeEmail * @return array */ - protected function _prepareMessageHeaders() { - return $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject')); + protected function _prepareMessageHeaders(CakeEmail $email) { + return $email->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject')); } /** * Prepares the message body. * + * @param CakeEmail $email CakeEmail * @return string */ - protected function _prepareMessage() { - $lines = $this->_cakeEmail->message(); + protected function _prepareMessage(CakeEmail $email) { + $lines = $email->message(); $messages = array(); foreach ($lines as $line) { if ((!empty($line)) && ($line[0] === '.')) { @@ -287,14 +282,15 @@ class SmtpTransport extends AbstractTransport { /** * Send emails * + * @param CakeEmail $email CakeEmail * @return void * @throws SocketException */ - protected function _sendRcpt() { - $from = $this->_prepareFromAddress(); + protected function _sendRcpt(CakeEmail $email) { + $from = $this->_prepareFromAddress($email); $this->_smtpSend($this->_prepareFromCmd(key($from))); - $emails = $this->_prepareRecipientAddresses(); + $emails = $this->_prepareRecipientAddresses($email); foreach ($emails as $email) { $this->_smtpSend($this->_prepareRcptCmd($email)); } @@ -303,14 +299,15 @@ class SmtpTransport extends AbstractTransport { /** * Send Data * + * @param CakeEmail $email CakeEmail * @return void * @throws SocketException */ - protected function _sendData() { + protected function _sendData(CakeEmail $email) { $this->_smtpSend('DATA', '354'); - $headers = $this->_headersToString($this->_prepareMessageHeaders()); - $message = $this->_prepareMessage(); + $headers = $this->_headersToString($this->_prepareMessageHeaders($email)); + $message = $this->_prepareMessage($email); $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n."); $this->_content = array('headers' => $headers, 'message' => $message); diff --git a/lib/Cake/Test/Case/Console/Command/TestShellTest.php b/lib/Cake/Test/Case/Console/Command/TestShellTest.php index d3d379a1d..bcc100628 100644 --- a/lib/Cake/Test/Case/Console/Command/TestShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/TestShellTest.php @@ -359,4 +359,22 @@ class TestShellTest extends CakeTestCase { ); $this->Shell->main(); } + +/** + * Tests that the '--directive' parameter change to '-d' before calling PHPUnit + * + * @return void + */ + public function testRunnerOptionsDirective() { + $this->Shell->startup(); + $this->Shell->args = array('core', 'Basics'); + $this->Shell->params = array('directive' => 'memory_limit=128M'); + + $this->Shell->expects($this->once())->method('_run') + ->with( + array('app' => false, 'plugin' => null, 'core' => true, 'output' => 'text', 'case' => 'Basics'), + array('-d', 'memory_limit=128M', '--colors') + ); + $this->Shell->main(); + } } diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 23e6aead3..4514e5ec8 100644 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -1722,6 +1722,27 @@ class AuthComponentTest extends CakeTestCase { $this->Auth->startup($this->Controller); } +/** + * testStatelessLoginSetUserNoSessionStart method + * + * @return void + */ + public function testStatelessLoginSetUserNoSessionStart() { + $user = array( + 'id' => 1, + 'username' => 'mark' + ); + + AuthComponent::$sessionKey = false; + $result = $this->Auth->login($user); + $this->assertTrue($result); + + $this->assertTrue($this->Auth->loggedIn()); + $this->assertEquals($user, $this->Auth->user()); + + $this->assertFalse($this->Auth->Session->started()); + } + /** * testStatelessAuthNoSessionStart method * diff --git a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php index 51cc4ef70..cebc7dd2c 100644 --- a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php +++ b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php @@ -20,6 +20,7 @@ App::uses('ExceptionRenderer', 'Error'); App::uses('Controller', 'Controller'); App::uses('Component', 'Controller'); App::uses('Router', 'Routing'); +App::uses('CakeEventManager', 'Event'); /** * Short description for class. @@ -877,4 +878,30 @@ class ExceptionRendererTest extends CakeTestCase { $this->assertContains(h('SELECT * from poo_query < 5 and :seven'), $result); $this->assertContains("'seven' => (int) 7", $result); } + +/** + * Test that rendering exceptions triggers shutdown events. + * + * @return void + */ + public function testRenderShutdownEvents() { + $fired = array(); + $listener = function ($event) use (&$fired) { + $fired[] = $event->name(); + }; + + $EventManager = CakeEventManager::instance(); + $EventManager->attach($listener, 'Controller.shutdown'); + $EventManager->attach($listener, 'Dispatcher.afterDispatch'); + + $exception = new Exception('Terrible'); + $ExceptionRenderer = new ExceptionRenderer($exception); + + ob_start(); + $ExceptionRenderer->render(); + ob_get_clean(); + + $expected = array('Controller.shutdown', 'Dispatcher.afterDispatch'); + $this->assertEquals($expected, $fired); + } } diff --git a/lib/Cake/Test/Case/Model/CakeSchemaTest.php b/lib/Cake/Test/Case/Model/CakeSchemaTest.php index 67c4d8bd5..eb2a64a82 100644 --- a/lib/Cake/Test/Case/Model/CakeSchemaTest.php +++ b/lib/Cake/Test/Case/Model/CakeSchemaTest.php @@ -953,6 +953,63 @@ class CakeSchemaTest extends CakeTestCase { $this->assertEquals($expected, $compare, 'Invalid SQL, datetime does not have length'); } +/** + * Test comparing with field length/limit changed from some non-default value to the default + * + * @return void + */ + public function testCompareLimitToDefault() { + $old = array( + 'posts' => array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => 1, 'key' => 'primary'), + 'author_id' => array('type' => 'integer', 'null' => false, 'limit' => 5), + 'title' => array('type' => 'string', 'null' => true, 'length' => 45), + 'indexes' => array( + 'PRIMARY' => array('column' => 'id', 'unique' => true) + ), + 'tableParameters' => array( + 'charset' => 'latin1', + 'collate' => 'latin1_general_ci' + ) + ), + ); + $new = array( + 'posts' => array( + 'id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'), + 'author_id' => array('type' => 'integer', 'null' => false), + 'title' => array('type' => 'varchar', 'null' => true), + 'indexes' => array( + 'PRIMARY' => array('column' => 'id', 'unique' => true) + ), + 'tableParameters' => array( + 'charset' => 'latin1', + 'collate' => 'latin1_general_ci' + ) + ), + ); + $compare = $this->Schema->compare($old, $new); + $expected = array( + 'posts' => array( + 'change' => array( + 'id' => array( + 'type' => 'integer', + 'null' => false, + 'key' => 'primary' + ), + 'author_id' => array( + 'type' => 'integer', + 'null' => false, + ), + 'title' => array( + 'type' => 'varchar', + 'null' => true, + ) + ) + ), + ); + $this->assertEquals($expected, $compare, 'Invalid SQL, field length change not detected'); + } + /** * testSchemaLoading method * diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index aa1353ecf..dcae76f65 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -4045,6 +4045,32 @@ SQL; $this->Dbo->delete($Article, '2=2'); } +/** + * Test deletes without complex conditions. + * + * @return void + */ + public function testDeleteNoComplexCondition() { + $this->loadFixtures('Article', 'User'); + $test = ConnectionManager::getDatasource('test'); + $db = $test->config['database']; + + $this->Dbo = $this->getMock('Mysql', array('execute'), array($test->config)); + + $this->Dbo->expects($this->at(0))->method('execute') + ->with("DELETE `Article` FROM `$db`.`articles` AS `Article` WHERE `id` = 1"); + + $this->Dbo->expects($this->at(1))->method('execute') + ->with("DELETE `Article` FROM `$db`.`articles` AS `Article` WHERE NOT (`id` = 1)"); + + $Article = new Article(); + + $conditions = array('id' => 1); + $this->Dbo->delete($Article, $conditions); + $conditions = array('NOT' => array('id' => 1)); + $this->Dbo->delete($Article, $conditions); + } + /** * Test truncate with a mock. * diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index 65ab2ad3a..ffab35fa7 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -1617,10 +1617,12 @@ class ModelIntegrationTest extends BaseModelTest { public function testAutoConstructPluginAssociations() { $Comment = ClassRegistry::init('TestPluginComment'); - $this->assertEquals(2, count($Comment->belongsTo), 'Too many associations'); + $this->assertEquals(3, count($Comment->belongsTo), 'Too many associations'); $this->assertFalse(isset($Comment->belongsTo['TestPlugin.User'])); + $this->assertFalse(isset($Comment->belongsTo['TestPlugin.Source'])); $this->assertTrue(isset($Comment->belongsTo['User']), 'Missing association'); $this->assertTrue(isset($Comment->belongsTo['TestPluginArticle']), 'Missing association'); + $this->assertTrue(isset($Comment->belongsTo['Source']), 'Missing association'); } /** diff --git a/lib/Cake/Test/Case/Model/models.php b/lib/Cake/Test/Case/Model/models.php index f49299444..f42bcae35 100644 --- a/lib/Cake/Test/Case/Model/models.php +++ b/lib/Cake/Test/Case/Model/models.php @@ -2991,7 +2991,10 @@ class TestPluginComment extends CakeTestModel { 'className' => 'TestPlugin.TestPluginArticle', 'foreignKey' => 'article_id', ), - 'TestPlugin.User' + 'TestPlugin.User', + 'TestPlugin.Source' => array( + 'foreignKey' => 'source_id' + ) ); } diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index 418ef6355..66bd69fc5 100644 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -148,7 +148,7 @@ class CakeRequestTest extends CakeTestCase { /** * Test the content type method. - * + * * @return void */ public function testContentType() { @@ -1147,11 +1147,13 @@ class CakeRequestTest extends CakeTestCase { $_SERVER['HTTP_X_THING'] = ''; $_SERVER['HTTP_HOST'] = 'localhost'; $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-ca) AppleWebKit/534.8+ (KHTML, like Gecko) Version/5.0 Safari/533.16'; + $_SERVER['AUTHORIZATION'] = 'foobar'; $request = new CakeRequest('/', false); $this->assertEquals($_SERVER['HTTP_HOST'], $request->header('host')); $this->assertEquals($_SERVER['HTTP_USER_AGENT'], $request->header('User-Agent')); $this->assertSame('', $request->header('X-thing')); + $this->assertEquals($_SERVER['AUTHORIZATION'], $request->header('Authorization')); } /** diff --git a/lib/Cake/Test/Case/Network/CakeResponseTest.php b/lib/Cake/Test/Case/Network/CakeResponseTest.php index effb57daa..cff657d62 100644 --- a/lib/Cake/Test/Case/Network/CakeResponseTest.php +++ b/lib/Cake/Test/Case/Network/CakeResponseTest.php @@ -164,11 +164,15 @@ class CakeResponseTest extends CakeTestCase { $headers += array('Location' => 'http://example.com'); $this->assertEquals($headers, $response->header()); - //Headers with the same name are overwritten + // Headers with the same name are overwritten $response->header('Location', 'http://example2.com'); $headers = array('Location' => 'http://example2.com'); $this->assertEquals($headers, $response->header()); + $response->header('Date', null); + $headers += array('Date' => null); + $this->assertEquals($headers, $response->header()); + $response->header(array('WWW-Authenticate' => 'Negotiate')); $headers += array('WWW-Authenticate' => 'Negotiate'); $this->assertEquals($headers, $response->header()); diff --git a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php index 3b334badd..712ea54ae 100644 --- a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php @@ -35,16 +35,6 @@ class SmtpTestTransport extends SmtpTransport { $this->_socket = $socket; } -/** - * Helper to change the CakeEmail - * - * @param CakeEmail $cakeEmail An email object. - * @return void - */ - public function setCakeEmail($cakeEmail) { - $this->_cakeEmail = $cakeEmail; - } - /** * Disabled the socket change * @@ -348,8 +338,7 @@ class SmtpTransportTest extends CakeTestCase { $this->socket->expects($this->at(13))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(14))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->SmtpTransport->setCakeEmail($email); - $this->SmtpTransport->sendRcpt(); + $this->SmtpTransport->sendRcpt($email); } /** @@ -370,8 +359,7 @@ class SmtpTransportTest extends CakeTestCase { $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->SmtpTransport->setCakeEmail($email); - $this->SmtpTransport->sendRcpt(); + $this->SmtpTransport->sendRcpt($email); } /** @@ -416,8 +404,7 @@ class SmtpTransportTest extends CakeTestCase { $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->SmtpTransport->setCakeEmail($email); - $this->SmtpTransport->sendData(); + $this->SmtpTransport->sendData($email); } /** @@ -498,8 +485,7 @@ class SmtpTransportTest extends CakeTestCase { $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->SmtpTransport->setCakeEmail($email); - $this->SmtpTransport->sendRcpt(); + $this->SmtpTransport->sendRcpt($email); $expected = array( array('code' => '250', 'message' => 'OK'), diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 27cc95f27..f0c49ce88 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -454,6 +454,19 @@ class CakeTimeTest extends CakeTestCase { $this->_restoreSystemTimezone(); } +/** + * testNiceShort translations + * + * @return void + */ + public function testNiceShortI18n() { + $restore = setlocale(LC_ALL, 0); + setlocale(LC_ALL, 'es_ES'); + $time = strtotime('2015-01-07 03:05:00'); + $this->assertEquals('ene 7th 2015, 03:05', $this->Time->niceShort($time)); + setlocale(LC_ALL, $restore); + } + /** * testDaysAsSql method * diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index bf20c7fdd..1bbaf435b 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -1621,6 +1621,29 @@ class FormHelperTest extends CakeTestCase { $this->assertEquals(1, $this->Form->fields['Contact.id'], 'Hidden input should be secured.'); } +/** + * test unlockField removing from fields array. multiple field version. + * + * @return void + */ + public function testUnlockMultipleFieldRemovingFromFields() { + $this->Form->request['_Token'] = array( + 'key' => 'testKey', + 'unlockedFields' => array() + ); + $this->Form->create('Order'); + $this->Form->hidden('Order.id', array('value' => 1)); + $this->Form->checkbox('Ticked.id.'); + $this->Form->checkbox('Ticked.id.'); + + $this->assertEquals(1, $this->Form->fields['Order.id'], 'Hidden input should be secured.'); + $this->assertTrue(in_array('Ticked.id', $this->Form->fields), 'Field should be secured.'); + + $this->Form->unlockField('Order.id'); + $this->Form->unlockField('Ticked.id'); + $this->assertEquals(array(), $this->Form->fields); + } + /** * testTagIsInvalid method * diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index fd8eab62c..fdb6195ba 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -663,7 +663,10 @@ class FormHelper extends AppHelper { if (!$field) { $field = $this->entity(); } elseif (is_string($field)) { - $field = Hash::filter(explode('.', $field)); + $field = explode('.', $field); + } + if (is_array($field)) { + $field = Hash::filter($field); } foreach ($this->_unlockedFields as $unlockField) { diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php index 103511660..e18967e4a 100644 --- a/lib/Cake/View/Helper/HtmlHelper.php +++ b/lib/Cake/View/Helper/HtmlHelper.php @@ -324,7 +324,7 @@ class HtmlHelper extends AppHelper { * - `escapeTitle` Set to false to disable escaping of title. (Takes precedence over value of `escape`) * - `confirm` JavaScript confirmation message. * - * @param string $title The content to be wrapped by tags. + * @param string $title The content to be wrapped by `` tags. * @param string|array $url Cake-relative URL or array of URL parameters, or external URL (starts with http://) * @param array $options Array of options and HTML attributes. * @param string $confirmMessage JavaScript confirmation message. This @@ -412,7 +412,7 @@ class HtmlHelper extends AppHelper { * CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot * of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css. * @param array $options Array of options and HTML arguments. - * @return string CSS or