Merge branch '2.0' into 2.1

Conflicts:
	lib/Cake/Model/Model.php
	lib/Cake/Test/Case/Routing/RouterTest.php
This commit is contained in:
mark_story 2012-01-20 20:28:15 -05:00
commit df5d9ac3d1
24 changed files with 297 additions and 122 deletions

View file

@ -576,12 +576,9 @@ class AclShell extends AppShell {
if (is_string($aco)) {
$aco = $this->parseIdentifier($aco);
}
$action = null;
if (isset($this->args[2])) {
$action = $this->args[2];
if ($action == '' || $action == 'all') {
$action = '*';
}
if (isset($this->args[2]) && !in_array($this->args[2], array('', 'all'))) {
$action = $this->args[2];
}
return compact('aro', 'aco', 'action', 'aroName', 'acoName');
}

View file

@ -87,7 +87,7 @@ class DbConfigTask extends AppShell {
*/
protected function _interactive() {
$this->hr();
$this->out('Database Configuration:');
$this->out(__d('cake_console', 'Database Configuration:'));
$this->hr();
$done = false;
$dbConfigs = array();

View file

@ -169,7 +169,10 @@ class UpgradeShell extends AppShell {
}
}
}
$this->_moveViewFiles();
$this->_moveAppClasses();
$sourceDirs = array(
'.' => array('recursive' => false),
'Console',
@ -593,6 +596,35 @@ class UpgradeShell extends AppShell {
}
}
/**
* Move the AppController, and AppModel classes.
*
* @return void
*/
protected function _moveAppClasses() {
$files = array(
APP . 'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
APP . 'controllers' . DS .'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
APP . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
APP . 'models' . DS . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
);
foreach ($files as $old => $new) {
if (file_exists($old)) {
$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
if ($this->params['dry-run']) {
continue;
}
if ($this->params['git']) {
exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
} else {
rename($old, $new);
}
}
}
}
/**
* Move application php files to where they now should be
*

View file

@ -467,7 +467,7 @@ class ConsoleOptionParser {
}
$params = $args = array();
$this->_tokens = $argv;
while ($token = array_shift($this->_tokens)) {
while (($token = array_shift($this->_tokens)) !== null) {
if (substr($token, 0, 2) == '--') {
$params = $this->_parseLongOption($token, $params);
} elseif (substr($token, 0, 1) == '-') {

View file

@ -465,7 +465,12 @@ class Shell extends Object {
}
}
if (is_array($options)) {
while ($in === '' || ($in !== '' && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) {
$options = array_merge(
array_map('strtolower', $options),
array_map('strtoupper', $options),
$options
);
while ($in === '' || !in_array($in, $options)) {
$in = $this->_getInput($prompt, $options, $default);
}
}

View file

@ -29,13 +29,6 @@
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/**
* Default helper
*

View file

@ -393,7 +393,12 @@ class SecurityComponent extends Component {
if ($this->Session->check('_Token')) {
$tData = $this->Session->read('_Token');
if (!empty($tData['allowedControllers']) && !in_array($this->request->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($this->request->params['action'], $tData['allowedActions'])) {
if (
!empty($tData['allowedControllers']) &&
!in_array($this->request->params['controller'], $tData['allowedControllers']) ||
!empty($tData['allowedActions']) &&
!in_array($this->request->params['action'], $tData['allowedActions'])
) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
@ -443,8 +448,8 @@ class SecurityComponent extends Component {
$multi = array();
foreach ($fieldList as $i => $key) {
if (preg_match('/\.\d+$/', $key)) {
$multi[$i] = preg_replace('/\.\d+$/', '', $key);
if (preg_match('/(\.\d+)+$/', $key)) {
$multi[$i] = preg_replace('/(\.\d+)+$/', '', $key);
unset($fieldList[$i]);
}
}

View file

@ -838,7 +838,7 @@ class Controller extends Object implements CakeEventListener {
* @return mixed Returns the return value of the called action
*/
public function setAction($action) {
$this->request->action = $action;
$this->request->params['action'] = $action;
$this->view = $action;
$args = func_get_args();
unset($args[0]);

View file

@ -2105,11 +2105,11 @@ class Model extends Object implements CakeEventListener {
*
* #### Options
*
* - validate: Set to false to disable validation, true to validate each record before saving,
* - `validate` Set to `false` to disable validation, `true` to validate each record before saving,
* 'first' to validate *all* records before any are saved(default),
* - atomic: If true (default), will attempt to save all records in a single transaction.
* - `atomic` If true (default), will attempt to save all records in a single transaction.
* 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()
*
* @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.
@ -2123,7 +2123,7 @@ class Model extends Object implements CakeEventListener {
$data = $this->data;
}
$options = array_merge(array('validate' => true, 'atomic' => true), $options);
$options = array_merge(array('validate' => 'first', 'atomic' => true), $options);
$this->validationErrors = $validationErrors = array();
if (empty($data) && $options['validate'] !== false) {
@ -2970,7 +2970,7 @@ class Model extends Object implements CakeEventListener {
$data = array();
}
$exists = $this->exists();
$exists = null;
$_validate = $this->validate;
$whitelist = $this->whitelist;
@ -3016,10 +3016,15 @@ class Model extends Object implements CakeEventListener {
}
$validator = array_merge($default, $validator);
if (
empty($validator['on']) || ($validator['on'] == 'create' &&
!$exists) || ($validator['on'] == 'update' && $exists
)) {
if (!empty($validator['on'])) {
if ($exists === null) {
$exists = $this->exists();
}
if (($validator['on'] == 'create' && $exists) || ($validator['on'] == 'update' && !$exists)) {
continue;
}
}
$valid = true;
$requiredFail = (
(!isset($data[$fieldName]) && $validator['required'] === true) ||
@ -3050,7 +3055,7 @@ class Model extends Object implements CakeEventListener {
$ruleParams[] = $validator;
$ruleParams[0] = array($fieldName => $ruleParams[0]);
$valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams);
} elseif (class_exists('Validation') && method_exists('Validation', $rule)) {
} elseif (method_exists('Validation', $rule)) {
$valid = call_user_func_array(array('Validation', $rule), $ruleParams);
} elseif (!is_array($validator['rule'])) {
$valid = preg_match($rule, $data[$fieldName]);
@ -3094,7 +3099,6 @@ class Model extends Object implements CakeEventListener {
}
}
}
}
$this->validate = $_validate;
return $this->validationErrors;
}

View file

@ -1240,9 +1240,14 @@ class CakeEmail {
/**
* Attach non-embedded files by adding file contents inside boundaries.
*
* @param string $boundary Boundary to use. If null, will default to $this->_boundary
* @return array An array of lines to add to the message
*/
protected function _attachFiles() {
protected function _attachFiles($boundary = null) {
if ($boundary === null) {
$boundary = $this->_boundary;
}
$msg = array();
foreach ($this->_attachments as $filename => $fileInfo) {
if (!empty($fileInfo['contentId'])) {
@ -1250,7 +1255,7 @@ class CakeEmail {
}
$data = $this->_readFile($fileInfo['file']);
$msg[] = '--' . $this->_boundary;
$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
$msg[] = 'Content-Transfer-Encoding: base64';
$msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
@ -1278,9 +1283,14 @@ class CakeEmail {
/**
* Attach inline/embedded files to the message.
*
* @param string $boundary Boundary to use. If null, will default to $this->_boundary
* @return array An array of lines to add to the message
*/
protected function _attachInlineFiles() {
protected function _attachInlineFiles($boundary = null) {
if ($boundary === null) {
$boundary = $this->_boundary;
}
$msg = array();
foreach ($this->_attachments as $filename => $fileInfo) {
if (empty($fileInfo['contentId'])) {
@ -1288,7 +1298,7 @@ class CakeEmail {
}
$data = $this->_readFile($fileInfo['file']);
$msg[] = '--rel-' . $this->_boundary;
$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
$msg[] = 'Content-Transfer-Encoding: base64';
$msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
@ -1365,7 +1375,7 @@ class CakeEmail {
}
if ($hasInlineAttachments) {
$attachments = $this->_attachInlineFiles();
$attachments = $this->_attachInlineFiles($relBoundary);
$msg = array_merge($msg, $attachments);
$msg[] = '';
$msg[] = '--' . $relBoundary . '--';
@ -1373,7 +1383,7 @@ class CakeEmail {
}
if ($hasAttachments) {
$attachments = $this->_attachFiles();
$attachments = $this->_attachFiles($boundary);
$msg = array_merge($msg, $attachments);
}
if ($hasAttachments || $hasMultipleTypes) {

View file

@ -283,7 +283,11 @@ class Router {
public static function connect($route, $defaults = array(), $options = array()) {
foreach (self::$_prefixes as $prefix) {
if (isset($defaults[$prefix])) {
if ($defaults[$prefix]) {
$defaults['prefix'] = $prefix;
} else {
unset($defaults[$prefix]);
}
break;
}
}

View file

@ -350,6 +350,19 @@ class ConsoleOptionParserTest extends CakeTestCase {
$result = $parser->parse(array('one', 'two', 'three'));
}
/**
* test parsing arguments with 0 value.
*
* @return void
*/
public function testParseArgumentZero() {
$parser = new ConsoleOptionParser('test', false);
$expected = array('one', 'two', 0, 'after', 'zero');
$result = $parser->parse($expected);
$this->assertEquals($expected, $result[1], 'Arguments are not as expected');
}
/**
* test that when there are not enough arguments an exception is raised
*

View file

@ -909,6 +909,30 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertTrue($result);
}
/**
* Test that values like Foo.0.1
*
* @return void
*/
public function testValidateNestedNumericSets() {
$this->Controller->Security->startup($this->Controller);
$key = $this->Controller->request->params['_Token']['key'];
$unlocked = '';
$hashFields = array('TaxonomyData');
$fields = urlencode(Security::hash(serialize($hashFields) . $unlocked . Configure::read('Security.salt')));
$this->Controller->request->data = array(
'TaxonomyData' => array(
1 => array(array(2)),
2 => array(array(3))
),
'_Token' => compact('key', 'fields', 'unlocked')
);
$result = $this->Controller->Security->validatePost($this->Controller);
$this->assertTrue($result);
}
/**
* testValidateHasManyRecords method
*

View file

@ -273,6 +273,21 @@ class TestController extends ControllerTestAppController {
);
}
/**
* view method
*
* @param mixed $testId
* @param mixed $test2Id
* @return void
*/
public function view($testId, $test2Id) {
$this->data = array(
'testId' => $testId,
'test2Id' => $test2Id
);
}
public function returner() {
return 'I am from the controller.';
}
@ -979,10 +994,11 @@ class ControllerTest extends CakeTestCase {
$request = new CakeRequest('controller_posts/index');
$TestController = new TestController($request);
$TestController->setAction('index', 1, 2);
$TestController->setAction('view', 1, 2);
$expected = array('testId' => 1, 'test2Id' => 2);
$this->assertSame($expected, $TestController->request->data);
$this->assertSame('index', $TestController->view);
$this->assertSame('view', $TestController->request->params['action']);
$this->assertSame('view', $TestController->view);
}
/**

View file

@ -761,4 +761,57 @@ class ModelValidationTest extends BaseModelTest {
$this->assertEquals($TestModel->validationErrors, $expected);
}
/**
* Test for 'on' => [create|update] in validation rules.
*
* @return void
*/
function testStateValidation() {
$this->loadFixtures('Article');
$Article = new Article();
$data = array(
'Article' => array(
'title' => '',
'body' => 'Extra Fields Body',
'published' => '1'
)
);
$Article->validate = array(
'title' => array(
'notempty' => array(
'rule' => 'notEmpty',
'on' => 'create'
)
)
);
$Article->create($data);
$this->assertFalse($Article->validates());
$Article->save(null, array('validate' => false));
$data['Article']['id'] = $Article->id;
$Article->set($data);
$this->assertTrue($Article->validates());
unset($data['Article']['id']);
$Article->validate = array(
'title' => array(
'notempty' => array(
'rule' => 'notEmpty',
'on' => 'update'
)
)
);
$Article->create($data);
$this->assertTrue($Article->validates());
$Article->save(null, array('validate' => false));
$data['Article']['id'] = $Article->id;
$Article->set($data);
$this->assertFalse($Article->validates());
}
}

View file

@ -4260,8 +4260,7 @@ class ModelWriteTest extends BaseModelTest {
'user_id' => 1
),
'Attachment' => array('attachment' => '')
),
array('validate' => 'first')
)
), false);
$expected = array(
'Comment' => array('comment' => array('This field cannot be left blank')),

View file

@ -1698,6 +1698,27 @@ class RouterTest extends CakeTestCase {
$this->assertEquals($expected, $result);
}
/**
* Test that setting a prefix to false is ignored, as its generally user error.
*
* @return void
*/
public function testPrefixFalseIgnored() {
Configure::write('Routing.prefixes', array('admin'));
Router::connect('/cache_css/*', array('admin' => false, 'controller' => 'asset_compress', 'action' => 'get'));
$url = Router::url(array('controller' => 'asset_compress', 'action' => 'get', 'test'));
$expected = '/cache_css/test';
$this->assertEquals($expected, $url);
$url = Router::url(array('admin' => false, 'controller' => 'asset_compress', 'action' => 'get', 'test'));
$expected = '/cache_css/test';
$this->assertEquals($expected, $url);
$url = Router::url(array('admin' => true, 'controller' => 'asset_compress', 'action' => 'get', 'test'));
$this->assertEquals('/admin/asset_compress/get/test', $url);
}
/**
* testRemoveBase method
*

View file

@ -1738,6 +1738,7 @@ class ValidationTest extends CakeTestCase {
$this->assertTrue(Validation::url('ftp://www.cakephp.org/pub/cake'));
$this->assertTrue(Validation::url('ftp://cakephp.org/pub/cake'));
$this->assertTrue(Validation::url('ftp://192.168.0.1/pub/cake'));
$this->assertTrue(Validation::url('sftp://192.168.0.1/pub/cake'));
$this->assertFalse(Validation::url('ftps://256.168.0.1/pub/cake'));
$this->assertFalse(Validation::url('ftp://256.168.0.1/pub/cake'));
$this->assertTrue(Validation::url('https://my.domain.com/gizmo/app?class=MySip;proc=start'));

View file

@ -136,11 +136,11 @@ class NumberHelperTest extends CakeTestCase {
$expected = '1.00 $';
$this->assertEquals($expected, $result);
$result = $this->Number->currency(.2, null, array('wholeSymbol' => ' $', 'wholePosition' => 'after', 'fractionSymbol' => 'cents'));
$result = $this->Number->currency(0.2, null, array('wholeSymbol' => ' $', 'wholePosition' => 'after', 'fractionSymbol' => 'cents'));
$expected = '20cents';
$this->assertEquals($expected, $result);
$result = $this->Number->currency(.2, null, array('wholeSymbol' => ' $', 'wholePosition' => 'after', 'fractionSymbol' => 'cents', 'fractionPosition' => 'before'));
$result = $this->Number->currency(0.2, null, array('wholeSymbol' => ' $', 'wholePosition' => 'after', 'fractionSymbol' => 'cents', 'fractionPosition' => 'before'));
$expected = 'cents20';
$this->assertEquals($expected, $result);
@ -148,7 +148,7 @@ class NumberHelperTest extends CakeTestCase {
$expected = '311.00$';
$this->assertEquals($expected, $result);
$result = $this->Number->currency(.2, 'EUR');
$result = $this->Number->currency(0.2, 'EUR');
$expected = '&#8364;0,20';
$this->assertEquals($expected, $result);
@ -156,11 +156,11 @@ class NumberHelperTest extends CakeTestCase {
$expected = '12.00 dollars';
$this->assertEquals($expected, $result);
$result = $this->Number->currency(.12, null, array('wholeSymbol' => ' dollars', 'wholePosition' => 'after', 'fractionSymbol' => ' cents', 'fractionPosition' => 'after'));
$result = $this->Number->currency(0.12, null, array('wholeSymbol' => ' dollars', 'wholePosition' => 'after', 'fractionSymbol' => ' cents', 'fractionPosition' => 'after'));
$expected = '12 cents';
$this->assertEquals($expected, $result);
$result = $this->Number->currency(.5, null, array('fractionSymbol' => false, 'fractionPosition' => 'before', 'wholeSymbol' => '$'));
$result = $this->Number->currency(0.5, null, array('fractionSymbol' => false, 'fractionPosition' => 'before', 'wholeSymbol' => '$'));
$expected = '$0.50';
$this->assertEquals($expected, $result);
}
@ -184,6 +184,11 @@ class NumberHelperTest extends CakeTestCase {
$result = $this->Number->currency(-10, 'Other');
$expected = '($$ 10.00)';
$this->assertEquals($expected, $result);
$this->Number->addFormat('Other2', array('before' => '$ ', 'after' => false));
$result = $this->Number->currency(0.22, 'Other2');
$expected = '$ 0.22';
$this->assertEquals($expected,$result);
}
/**

View file

@ -281,7 +281,7 @@ class ScaffoldViewTest extends CakeTestCase {
//TODO: add specific tests for fields.
$this->assertRegExp('/<a href="\/scaffold_users\/view\/1">1<\/a>/', $result); //belongsTo links
$this->assertRegExp('/<li><a href="\/scaffold_mock\/edit\/1">Edit Scaffold Mock<\/a>\s<\/li>/', $result);
$this->assertRegExp('/<li><a href="\/scaffold_mock\/delete\/1"[^>]*>Delete Scaffold Mock<\/a>\s*<\/li>/', $result);
$this->assertRegExp('/<a href="\#" onclick="if[^>]*>Delete Scaffold Mock<\/a>\s<\/li>/', $result);
//check related table
$this->assertRegExp('/<div class="related">\s*<h3>Related Scaffold Comments<\/h3>\s*<table cellpadding="0" cellspacing="0">/', $result);
$this->assertRegExp('/<li><a href="\/scaffold_comments\/add">New Comment<\/a><\/li>/', $result);

View file

@ -31,13 +31,6 @@ App::uses('AppController', 'Controller');
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
*/
public $name = 'Pages';
/**
* Default helper
*

View file

@ -702,7 +702,7 @@ class Validation {
public static function url($check, $strict = false) {
self::_populateIp();
$validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~[]') . '\/0-9a-z\p{L}\p{N}]|(%[0-9a-f]{2}))';
$regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
$regex = '/^(?:(?:https?|ftps?|sftp|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
'(?:' . self::$_pattern['IPv4'] . '|\[' . self::$_pattern['IPv6'] . '\]|' . self::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' .
'(?:\/?|\/' . $validChars . '*)?' .
'(?:\?' . $validChars . '*)?' .

View file

@ -59,7 +59,7 @@ class NumberHelper extends AppHelper {
*/
protected $_currencyDefaults = array(
'wholeSymbol' => '', 'wholePosition' => 'before', 'fractionSymbol' => '', 'fractionPosition' => 'after',
'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.','negative' => '()', 'escape' => true
'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.','negative' => '()', 'escape' => true,
);
/**

View file

@ -46,7 +46,7 @@ foreach ($scaffoldFields as $_field) {
<ul>
<?php
echo "\t\t<li>" . $this->Html->link(__d('cake', 'Edit %s', $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])) . " </li>\n";
echo "\t\t<li>" . $this->Html->link(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?') . " </li>\n";
echo "\t\t<li>" . $this->Form->postLink(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?') . " </li>\n";
echo "\t\t<li>" . $this->Html->link(__d('cake', 'List %s', $pluralHumanName), array('action' => 'index')) . " </li>\n";
echo "\t\t<li>" . $this->Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')) . " </li>\n";
@ -130,7 +130,7 @@ $otherSingularVar = Inflector::variable($_alias);
echo "\t\t\t<td class=\"actions\">\n";
echo "\t\t\t\t" . $this->Html->link(__d('cake', 'View'), array('controller' => $_details['controller'], 'action' => 'view', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
echo "\t\t\t\t" . $this->Html->link(__d('cake', 'Edit'), array('controller' => $_details['controller'], 'action' => 'edit', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
echo "\t\t\t\t" . $this->Html->link(__d('cake', 'Delete'), array('controller' => $_details['controller'], 'action' => 'delete', ${$otherSingularVar}[$_details['primaryKey']]), null, __d('cake', 'Are you sure you want to delete', true).' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?'). "\n";
echo "\t\t\t\t" . $this->Form->postLink(__d('cake', 'Delete'), array('controller' => $_details['controller'], 'action' => 'delete', ${$otherSingularVar}[$_details['primaryKey']]), null, __d('cake', 'Are you sure you want to delete', true).' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?'). "\n";
echo "\t\t\t</td>\n";
echo "\t\t</tr>\n";
endforeach;