diff --git a/lib/Cake/Console/Command/Task/ViewTask.php b/lib/Cake/Console/Command/Task/ViewTask.php index 6494b90aa..68eef867d 100644 --- a/lib/Cake/Console/Command/Task/ViewTask.php +++ b/lib/Cake/Console/Command/Task/ViewTask.php @@ -327,7 +327,7 @@ class ViewTask extends BakeTask { $this->hr(); $this->out(__d('cake_console', 'Controller Name: %s', $this->controllerName)); $this->out(__d('cake_console', 'Action Name: %s', $action)); - $this->out(__d('cake_console', 'Path: %s', $this->params['app'] . DS . $this->controllerName . DS . Inflector::underscore($action) . ".ctp")); + $this->out(__d('cake_console', 'Path: %s', $this->params['app'] . DS . 'View' . DS . $this->controllerName . DS . Inflector::underscore($action) . ".ctp")); $this->hr(); $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y','n'), 'y'); if (strtolower($looksGood) == 'y') { diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 6ca24960f..e8912d886 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -1488,7 +1488,7 @@ class DboSource extends DataSource { $query += array('order' => $assocData['order'], 'limit' => $assocData['limit']); } else { $join = array( - 'table' => $this->fullTableName($linkModel), + 'table' => $linkModel, 'alias' => $association, 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', 'conditions' => trim($this->conditions($conditions, true, false, $model)) @@ -1527,7 +1527,7 @@ class DboSource extends DataSource { $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']); list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys); - $joinTbl = $this->fullTableName($model->{$with}); + $joinTbl = $model->{$with}; $joinAlias = $joinTbl; if (is_array($joinFields) && !empty($joinFields)) { @@ -1537,8 +1537,8 @@ class DboSource extends DataSource { $joinFields = array(); } } else { - $joinTbl = $this->fullTableName($assocData['joinTable']); - $joinAlias = $joinTbl; + $joinTbl = $assocData['joinTable']; + $joinAlias = $this->fullTableName($assocData['joinTable']); } $query = array( 'conditions' => $assocData['conditions'], @@ -1622,6 +1622,9 @@ class DboSource extends DataSource { if (!empty($data['conditions'])) { $data['conditions'] = trim($this->conditions($data['conditions'], true, false)); } + if (!empty($data['table'])) { + $data['table'] = $this->fullTableName($data['table']); + } return $this->renderJoinStatement($data); } @@ -1904,7 +1907,7 @@ class DboSource extends DataSource { if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig && $model->{$assoc}->getDataSource()) { $assocData = $model->getAssociated($assoc); $join[] = $this->buildJoinStatement(array( - 'table' => $this->fullTableName($model->{$assoc}), + 'table' => $model->{$assoc}, 'alias' => $assoc, 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', 'conditions' => trim($this->conditions( diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index 679d300c4..31e9f68be 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -626,7 +626,7 @@ class CakeEmail { } if ($this->_messageId !== false) { if ($this->_messageId === true) { - $headers['Message-ID'] = '<' . String::UUID() . '@' . env('HTTP_HOST') . '>'; + $headers['Message-ID'] = '<' . str_replace('-', '', String::UUID()) . '@' . env('HTTP_HOST') . '>'; } else { $headers['Message-ID'] = $this->_messageId; } @@ -639,9 +639,6 @@ class CakeEmail { $headers['MIME-Version'] = '1.0'; if (!empty($this->_attachments)) { $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"'; - $headers[] = 'This part of the E-mail should never be seen. If'; - $headers[] = 'you are reading this, consider upgrading your e-mail'; - $headers[] = 'client to a MIME-compatible client.'; } elseif ($this->_emailFormat === 'text') { $headers['Content-Type'] = 'text/plain; charset=' . $this->charset; } elseif ($this->_emailFormat === 'html') { diff --git a/lib/Cake/Test/Case/Controller/Component/EmailComponentTest.php b/lib/Cake/Test/Case/Controller/Component/EmailComponentTest.php index 7862f7416..58e409d99 100644 --- a/lib/Cake/Test/Case/Controller/Component/EmailComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/EmailComponentTest.php @@ -852,7 +852,7 @@ HTMLBLOC; $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $result = DebugCompTransport::$lastEmail; - $this->assertPattern('/Message-ID: \<[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}@' . env('HTTP_HOST') . '\>\n/', $result); + $this->assertPattern('/Message-ID: \<[a-f0-9]{8}[a-f0-9]{4}[a-f0-9]{4}[a-f0-9]{4}[a-f0-9]{12}@' . env('HTTP_HOST') . '\>\n/', $result); $this->Controller->EmailTest->messageId = '<22091985.998877@example.com>'; diff --git a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php index 455c1c52c..8d64cae34 100644 --- a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php @@ -3503,6 +3503,41 @@ class ContainableBehaviorTest extends CakeTestCase { $this->assertEqual($expected, $this->Article->hasAndBelongsToMany); } +/** + * test that bindModel and unbindModel work with find() calls in between. + */ + function testBindMultipleTimesWithFind() { + $binding = array( + 'hasOne' => array( + 'ArticlesTag' => array( + 'foreignKey' => false, + 'type' => 'INNER', + 'conditions' => array( + 'ArticlesTag.article_id = Article.id' + ) + ), + 'Tag' => array( + 'type' => 'INNER', + 'foreignKey' => false, + 'conditions' => array( + 'ArticlesTag.tag_id = Tag.id' + ) + ) + ) + ); + $this->Article->unbindModel(array('hasAndBelongsToMany' => array('Tag'))); + $this->Article->bindModel($binding); + $result = $this->Article->find('all', array('limit' => 1, 'contain' => array('ArticlesTag', 'Tag'))); + + $this->Article->unbindModel(array('hasAndBelongsToMany' => array('Tag'))); + $this->Article->bindModel($binding); + $result = $this->Article->find('all', array('limit' => 1, 'contain' => array('ArticlesTag', 'Tag'))); + + $associated = $this->Article->getAssociated(); + $this->assertEqual('hasAndBelongsToMany', $associated['Tag']); + $this->assertFalse(isset($associated['ArticleTag'])); + } + /** * test that autoFields doesn't splice in fields from other databases. * diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index 67f9665e1..c107a078b 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -978,6 +978,7 @@ class MysqlTest extends CakeTestCase { 'offset' => array(), 'group' => array() ); + $queryData['joins'][0]['table'] = $this->Dbo->fullTableName($queryData['joins'][0]['table']); $this->assertEqual($queryData, $expected); $result = $this->Dbo->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null); diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index 2140ae195..00e2194c0 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -237,6 +237,44 @@ class ModelIntegrationTest extends BaseModelTest { $this->assertFalse(isset($TestModel->Behaviors->Tree)); } +/** + * testFindWithJoinsOption method + * + * @access public + * @return void + */ + function testFindWithJoinsOption() { + $this->loadFixtures('Article', 'User'); + $TestUser =& new User(); + + $options = array ( + 'fields' => array( + 'user', + 'Article.published', + ), + 'joins' => array ( + array ( + 'table' => 'articles', + 'alias' => 'Article', + 'type' => 'LEFT', + 'conditions' => array( + 'User.id = Article.user_id', + ), + ), + ), + 'group' => array('User.user'), + 'recursive' => -1, + ); + $result = $TestUser->find('all', $options); + $expected = array( + array('User' => array('user' => 'garrett'), 'Article' => array('published' => '')), + array('User' => array('user' => 'larry'), 'Article' => array('published' => 'Y')), + array('User' => array('user' => 'mariano'), 'Article' => array('published' => 'Y')), + array('User' => array('user' => 'nate'), 'Article' => array('published' => '')) + ); + $this->assertEqual($result, $expected); + } + /** * Tests cross database joins. Requires $test and $test2 to both be set in DATABASE_CONFIG * NOTE: When testing on MySQL, you must set 'persistent' => false on *both* database connections, diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index ac7949102..45c078bf2 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -4750,7 +4750,7 @@ class ModelReadTest extends BaseModelTest { * * @return void */ - public function bindWithCustomPrimaryKey() { + public function testBindWithCustomPrimaryKey() { $this->loadFixtures('Story', 'StoriesTag', 'Tag'); $Model = ClassRegistry::init('StoriesTag'); $Model->bindModel(array( @@ -5234,7 +5234,7 @@ class ModelReadTest extends BaseModelTest { 'group' => null, 'joins' => array(array( 'alias' => 'ArticlesTag', - 'table' => $this->db->fullTableName('articles_tags'), + 'table' => 'articles_tags', 'conditions' => array( array("ArticlesTag.article_id" => '{$__cakeID__$}'), array("ArticlesTag.tag_id" => $this->db->identifier('Tag.id')) diff --git a/lib/Cake/Test/Case/Utility/InflectorTest.php b/lib/Cake/Test/Case/Utility/InflectorTest.php index db14a0c27..14891f452 100644 --- a/lib/Cake/Test/Case/Utility/InflectorTest.php +++ b/lib/Cake/Test/Case/Utility/InflectorTest.php @@ -399,9 +399,11 @@ class InflectorTest extends CakeTestCase { $this->assertEquals('Banazzz', Inflector::singularize('Bananas'), 'Was inflected with old rules.'); Inflector::rules('plural', array( - 'rules' => array('/(.*)na$/i' => '\1zzz') + 'rules' => array('/(.*)na$/i' => '\1zzz'), + 'irregular' => array('corpus' => 'corpora') )); - $this->assertEqual('Banazzz', Inflector::pluralize('Banana'), 'Was inflected with old rules.'); + $this->assertEqual(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules.'); + $this->assertEqual(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form.'); } /** diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index bd2d5dbbd..ae718647b 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -1424,6 +1424,8 @@ class ValidationTest extends CakeTestCase { $this->assertTrue(Validation::time('12:01am')); $this->assertTrue(Validation::time('12:01pm')); $this->assertTrue(Validation::time('1pm')); + $this->assertTrue(Validation::time('1 pm')); + $this->assertTrue(Validation::time('1 PM')); $this->assertTrue(Validation::time('01:00')); $this->assertFalse(Validation::time('1:00')); $this->assertTrue(Validation::time('1:00pm')); diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index c46d38840..42618aafc 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -1048,6 +1048,20 @@ class FormHelperTest extends CakeTestCase { $this->assertTags($result, $expected); } +/** + * Test form security with Model.field.0 style inputs + * + * @return void + */ + function testFormSecurityArrayFields() { + $key = 'testKey'; + + $this->Form->request->params['_Token']['key'] = $key; + $this->Form->create('Address'); + $this->Form->input('Address.primary.1'); + $this->assertEqual('Address.primary', $this->Form->fields[0]); + } + /** * testFormSecurityMultipleInputDisabledFields method * @@ -1416,6 +1430,62 @@ class FormHelperTest extends CakeTestCase { $this->assertTags($result, $expected); } +/** + * testEmptyErrorValidation method + * + * test validation error div when validation message is an empty string + * + * @access public + * @return void + */ + function testEmptyErrorValidation() { + $this->Form->validationErrors['Contact']['password'] = ''; + $result = $this->Form->input('Contact.password'); + $expected = array( + 'div' => array('class' => 'input password error'), + 'label' => array('for' => 'ContactPassword'), + 'Password', + '/label', + 'input' => array( + 'type' => 'password', 'name' => 'data[Contact][password]', + 'id' => 'ContactPassword', 'class' => 'form-error' + ), + array('div' => array('class' => 'error-message')), + array(), + '/div', + '/div' + ); + $this->assertTags($result, $expected); + } + +/** + * testEmptyInputErrorValidation method + * + * test validation error div when validation message is overriden by an empty string when calling input() + * + * @access public + * @return void + */ + function testEmptyInputErrorValidation() { + $this->Form->validationErrors['Contact']['password'] = 'Please provide a password'; + $result = $this->Form->input('Contact.password', array('error' => '')); + $expected = array( + 'div' => array('class' => 'input password error'), + 'label' => array('for' => 'ContactPassword'), + 'Password', + '/label', + 'input' => array( + 'type' => 'password', 'name' => 'data[Contact][password]', + 'id' => 'ContactPassword', 'class' => 'form-error' + ), + array('div' => array('class' => 'error-message')), + array(), + '/div', + '/div' + ); + $this->assertTags($result, $expected); + } + /** * testFormValidationAssociated method * @@ -6147,7 +6217,7 @@ class FormHelperTest extends CakeTestCase { $expected = array( 'form' => array( - 'id' => 'ContactAddForm', 'method' => 'post', + 'id' => 'ContactAddForm', 'method' => 'post', 'onsubmit' => 'someFunction();event.returnValue = false; return false;', 'action' => '/contacts/index/param', 'accept-charset' => 'utf-8' diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index 736ac9dd1..a27d4752f 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -319,7 +319,11 @@ class Inflector { if ($reset) { self::${$var}[$rule] = $pattern; } else { - self::${$var}[$rule] = array_merge($pattern, self::${$var}[$rule]); + if ($rule === 'uninflected') { + self::${$var}[$rule] = array_merge($pattern, self::${$var}[$rule]); + } else { + self::${$var}[$rule] = $pattern + self::${$var}[$rule]; + } } unset($rules[$rule], self::${$var}['cache' . ucfirst($rule)]); if (isset(self::${$var}['merged'][$rule])) { @@ -332,7 +336,7 @@ class Inflector { } } } - self::${$var}['rules'] = array_merge($rules, self::${$var}['rules']); + self::${$var}['rules'] = $rules + self::${$var}['rules']; break; } } diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 13fe05ca9..11a13e7b5 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -356,7 +356,7 @@ class Validation { * @return boolean Success */ public static function time($check) { - return self::_check($check, '%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%'); + return self::_check($check, '%^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%'); } /** diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index 0c5f1a7b9..6e84590c6 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -18,4 +18,3 @@ // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// 2.0.0-RC2 - diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index 6f93005ab..5e4944915 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -621,7 +621,7 @@ class Helper extends Object { $entity = $this->entity(); if (!empty($data) && !empty($entity)) { - $result = Set::extract($data, implode('.', $entity)); + $result = Set::extract(implode('.', $entity), $data); } $habtmKey = $this->field(); @@ -666,7 +666,7 @@ class Helper extends Object { $options = $this->_name($options); $options = $this->value($options); $options = $this->domId($options); - if ($this->tagIsInvalid()) { + if ($this->tagIsInvalid() !== false) { $options = $this->addClass($options, 'form-error'); } return $options; diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 360cec701..13b84d6c3 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -271,18 +271,24 @@ class FormHelper extends AppHelper { * Returns false if given form field described by the current entity has no errors. * Otherwise it returns the validation message * - * @return boolean True on errors. + * @return mixed Either false when there or no errors, or the error + * string. The error string could be ''. */ public function tagIsInvalid() { $entity = $this->entity(); $model = array_shift($entity); + $errors = array(); if (!empty($entity) && isset($this->validationErrors[$model])) { - return Set::classicExtract($this->validationErrors[$model], join('.', $entity)); + $errors = $this->validationErrors[$model]; } - if (!empty($entity)) { + if (!empty($entity) && empty($errors)) { $errors = $this->_introspectModel($model, 'errors'); - return ($errors) ? Set::classicExtract($errors, join('.', $entity)) : false; } + if (empty($errors)) { + return false; + } + $error = Set::classicExtract($errors, join('.', $entity)); + return $error === null ? false : $error; } /** @@ -598,6 +604,11 @@ class FormHelper extends AppHelper { } } + $last = end($field); + if (is_numeric($last) || empty($last)) { + array_pop($field); + } + $field = implode('.', $field); if ($lock) { @@ -645,7 +656,9 @@ class FormHelper extends AppHelper { $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true); $options = array_merge($defaults, $options); $this->setEntity($field); - if (!$error = $this->tagIsInvalid()) { + + $error = $this->tagIsInvalid(); + if ($error === false) { return null; } if (is_array($text)) { @@ -664,7 +677,7 @@ class FormHelper extends AppHelper { $text = $tmp; } - if ($text != null) { + if ($text !== null) { $error = $text; } if (is_array($error)) {