Merge branch '2.x' into 2.next

This commit is contained in:
mark_story 2016-08-09 22:12:26 -04:00
commit 61b3fbd605
28 changed files with 335 additions and 79 deletions

2
.stickler.yml Normal file
View file

@ -0,0 +1,2 @@
branches:
ignore: ['2.x', '2.next']

View file

@ -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"

View file

@ -1,3 +1,9 @@
# Uncomment the following to prevent the httpoxy vulnerability
# See: https://httpoxy.org/
#<IfModule mod_headers.c>
# RequestHeader unset Proxy
#</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d

View file

@ -71,6 +71,9 @@ class TestShell extends Shell {
))->addOption('coverage-clover', array(
'help' => __d('cake_console', '<file> 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', '<file> 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;
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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'),

View file

@ -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;
}

View file

@ -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
*

View file

@ -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;

View file

@ -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);

View file

@ -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];
}

View file

@ -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);

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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
*

View file

@ -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);
}
}

View file

@ -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
*

View file

@ -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.
*

View file

@ -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');
}
/**

View file

@ -2991,7 +2991,10 @@ class TestPluginComment extends CakeTestModel {
'className' => 'TestPlugin.TestPluginArticle',
'foreignKey' => 'article_id',
),
'TestPlugin.User'
'TestPlugin.User',
'TestPlugin.Source' => array(
'foreignKey' => 'source_id'
)
);
}

View file

@ -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'));
}
/**

View file

@ -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());

View file

@ -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'),

View file

@ -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
*

View file

@ -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
*

View file

@ -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) {

View file

@ -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 <a> tags.
* @param string $title The content to be wrapped by `<a>` 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 <link /> or <style /> tag, depending on the type of link.
* @return string CSS `<link />` or `<style />` 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 <![CDATA[ ]]>
* - `safe` (boolean) Whether or not the $script should be wrapped in `<![CDATA[ ]]>`
* - `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.