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 tag, depending on the type of link.
+ * @return string CSS `` or `` tag, depending on the type of link.
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::css
*/
public function css($path, $options = array()) {
@@ -580,7 +580,7 @@ class HtmlHelper extends AppHelper {
*
* ### Options
*
- * - `safe` (boolean) Whether or not the $script should be wrapped in
+ * - `safe` (boolean) Whether or not the $script should be wrapped in ``
* - `inline` (boolean) Whether or not the $script should be added to
* `$scripts_for_layout` / `script` block, or output inline. (Deprecated, use `block` instead)
* - `block` Which block you want this script block appended to.