From cd562f52bad9aca436335dfa0b5b26dab954c7f4 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 7 Jan 2012 00:37:50 -0430 Subject: [PATCH 01/22] Fixing a couple failing tests on windows using sqlserver --- lib/Cake/Model/Datasource/Database/Sqlserver.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index 9e60c1622..df5cd8884 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -645,14 +645,7 @@ class Sqlserver extends DboSource { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON'); } - $table = $this->fullTableName($table); - $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); - $this->begin(); - foreach ($values as $value) { - $holder = implode(', ', array_map(array(&$this, 'value'), $value)); - $this->_execute("INSERT INTO {$table} ({$fields}) VALUES ({$holder})"); - } - $this->commit(); + parent::insertMulti($table, $fields, $values); if ($hasPrimaryKey) { $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); @@ -717,9 +710,6 @@ class Sqlserver extends DboSource { * @return string */ protected function _getPrimaryKey($model) { - if (!is_object($model)) { - $model = new Model(false, $model); - } $schema = $this->describe($model); foreach ($schema as $field => $props) { if (isset($props['key']) && $props['key'] == 'primary') { From ba097d404fecc1336526fbd9485ef087eabe8886 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 7 Jan 2012 01:04:02 -0430 Subject: [PATCH 02/22] Explicitly using a fetch mode in SQLServer, not doing it causes serious memory leaks --- lib/Cake/Model/Datasource/Database/Sqlserver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index df5cd8884..edb29603d 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -182,7 +182,7 @@ class Sqlserver extends DboSource { } else { $tables = array(); - while ($line = $result->fetch()) { + while ($line = $result->fetch(PDO::FETCH_NUM)) { $tables[] = $line[0]; } @@ -222,7 +222,7 @@ class Sqlserver extends DboSource { throw new CakeException(__d('cake_dev', 'Could not describe table for %s', $table)); } - foreach ($cols as $column) { + while ($column = $cols->fetch(PDO::FETCH_OBJ)) { $field = $column->Field; $fields[$field] = array( 'type' => $this->column($column), From abb370fe3f3a03f4552cbef225623b7b68f773c7 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 7 Jan 2012 01:39:40 -0430 Subject: [PATCH 03/22] Fixing windows failing test case on the debug function --- lib/Cake/basics.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/basics.php b/lib/Cake/basics.php index 9f11f3397..623a95d96 100644 --- a/lib/Cake/basics.php +++ b/lib/Cake/basics.php @@ -76,7 +76,7 @@ function debug($var = false, $showHtml = null, $showFrom = true) { $lineInfo = ''; if ($showFrom) { $calledFrom = debug_backtrace(); - $file = substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1); + $file = substr(str_ireplace(ROOT, '', $calledFrom[0]['file']), 1); $line = $calledFrom[0]['line']; } $html = << Date: Sat, 7 Jan 2012 02:09:47 -0430 Subject: [PATCH 04/22] Fixing a few SqlServer failing tests --- .../Datasource/Database/SqlserverTest.php | 20 ++++++++++++++++--- .../Case/Model/Datasource/DboSourceTest.php | 3 +++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 05890dac1..7838b46ff 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -111,7 +111,7 @@ class SqlserverTestDb extends Sqlserver { * * @package Cake.Test.Case.Model.Datasource.Database */ -class SqlserverTestModel extends Model { +class SqlserverTestModel extends CakeTestModel { /** * name property @@ -183,7 +183,7 @@ class SqlserverTestModel extends Model { * * @package Cake.Test.Case.Model.Datasource.Database */ -class SqlserverClientTestModel extends Model { +class SqlserverClientTestModel extends CakeTestModel { /** * name property * @@ -224,6 +224,20 @@ class SqlserverTestResultIterator extends ArrayIterator { * @return void */ public function closeCursor() {} + +/** + * fetch method + * + * @return void + */ + public function fetch() { + if (!$this->valid()) { + return null; + } + $current = $this->current(); + $this->next(); + return $current; + } } /** @@ -283,7 +297,7 @@ class SqlserverTest extends CakeTestCase { * @return void */ public function testQuoting() { - $expected = "1.200000"; + $expected = "1.2"; $result = $this->db->value(1.2, 'float'); $this->assertSame($expected, $result); diff --git a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php index 17dbad4f4..1e93a9d9e 100644 --- a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php @@ -493,6 +493,9 @@ class DboSourceTest extends CakeTestCase { * @return void */ public function testValue() { + if ($this->db instanceof Sqlserver) { + $this->markTestSkipped('Cannot run this test with SqlServer'); + } $result = $this->db->value('{$__cakeForeignKey__$}'); $this->assertEquals($result, '{$__cakeForeignKey__$}'); From 1991430584fc7f41eb15833926f37864162075a6 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 7 Jan 2012 02:17:27 -0430 Subject: [PATCH 05/22] Removing test that does not relate anymore to SqlServer datasource --- .../Datasource/Database/SqlserverTest.php | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 7838b46ff..95394bf62 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -600,43 +600,6 @@ class SqlserverTest extends CakeTestCase { $this->assertNull($result); } -/** - * testInsertMulti - * - * @return void - */ - public function testInsertMulti() { - $this->db->describe = $this->model->schema(); - - $fields = array('id', 'name', 'login'); - $values = array( - array(1, 'Larry', 'PhpNut'), - array(2, 'Renan', 'renan.saddam')); - $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values); - $result = $this->db->simulated; - $expected = array( - 'SET IDENTITY_INSERT [sqlserver_test_models] ON', - "INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (1, N'Larry', N'PhpNut')", - "INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (2, N'Renan', N'renan.saddam')", - 'SET IDENTITY_INSERT [sqlserver_test_models] OFF' - ); - $this->assertEquals($expected, $result); - - $fields = array('name', 'login'); - $values = array( - array('Larry', 'PhpNut'), - array('Renan', 'renan.saddam')); - $this->db->simulated = array(); - $this->db->insertMulti($this->model, $fields, $values); - $result = $this->db->simulated; - $expected = array( - "INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES (N'Larry', N'PhpNut')", - "INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES (N'Renan', N'renan.saddam')", - ); - $this->assertEquals($expected, $result); - } - /** * SQL server < 11 doesn't have proper limit/offset support, test that our hack works. * From 87924414fce268f085069383e83e391799a9d6f3 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 7 Jan 2012 10:45:35 -0500 Subject: [PATCH 06/22] Update Configure::load() to use Set::merge() This fixes some of the awkward behavior that using array_merge_recursive() could create. Fixes #2441 --- lib/Cake/Core/Configure.php | 2 +- lib/Cake/Test/Case/Core/ConfigureTest.php | 2 ++ lib/Cake/Test/test_app/Config/var_test.php | 5 ++++- lib/Cake/Test/test_app/Config/var_test2.php | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Core/Configure.php b/lib/Cake/Core/Configure.php index 2772eb084..67cabfc50 100644 --- a/lib/Cake/Core/Configure.php +++ b/lib/Cake/Core/Configure.php @@ -312,7 +312,7 @@ class Configure { $keys = array_keys($values); foreach ($keys as $key) { if (($c = self::read($key)) && is_array($values[$key]) && is_array($c)) { - $values[$key] = array_merge_recursive($c, $values[$key]); + $values[$key] = Set::merge($c, $values[$key]); } } } diff --git a/lib/Cake/Test/Case/Core/ConfigureTest.php b/lib/Cake/Test/Case/Core/ConfigureTest.php index 746412fdf..5e94b5749 100644 --- a/lib/Cake/Test/Case/Core/ConfigureTest.php +++ b/lib/Cake/Test/Case/Core/ConfigureTest.php @@ -224,6 +224,8 @@ class ConfigureTest extends CakeTestCase { $this->assertEquals('value2', Configure::read('Read')); $this->assertEquals('buried2', Configure::read('Deep.Second.SecondDeepest')); $this->assertEquals('buried', Configure::read('Deep.Deeper.Deepest')); + $this->assertEquals('Overwrite', Configure::read('TestAcl.classname')); + $this->assertEquals('one', Configure::read('TestAcl.custom')); } /** diff --git a/lib/Cake/Test/test_app/Config/var_test.php b/lib/Cake/Test/test_app/Config/var_test.php index e0f3ae2a1..44c345563 100644 --- a/lib/Cake/Test/test_app/Config/var_test.php +++ b/lib/Cake/Test/test_app/Config/var_test.php @@ -5,5 +5,8 @@ $config = array( 'Deeper' => array( 'Deepest' => 'buried' ) + ), + 'TestAcl' => array( + 'classname' => 'Original' ) -); \ No newline at end of file +); diff --git a/lib/Cake/Test/test_app/Config/var_test2.php b/lib/Cake/Test/test_app/Config/var_test2.php index 33345b4ec..745b75dbc 100644 --- a/lib/Cake/Test/test_app/Config/var_test2.php +++ b/lib/Cake/Test/test_app/Config/var_test2.php @@ -5,5 +5,9 @@ $config = array( 'Second' => array( 'SecondDeepest' => 'buried2' ) + ), + 'TestAcl' => array( + 'classname' => 'Overwrite', + 'custom' => 'one' ) -); \ No newline at end of file +); From beced84d2d96b7580c78c1243255b683a670de29 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 7 Jan 2012 11:58:35 -0500 Subject: [PATCH 07/22] Fix illegal offset caused by TranslateBehavior. If you load TranslateBehavior at runtime in a disabled state, the enabled flag would be interpreted as an association and cause errors. Fixes #2443 --- lib/Cake/Model/BehaviorCollection.php | 4 +++- .../Test/Case/Model/BehaviorCollectionTest.php | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index 323cc63f6..1e154eaab 100644 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -106,6 +106,9 @@ class BehaviorCollection extends ObjectCollection { $alias = $behavior; $behavior = $config['className']; } + $configDisabled = isset($config['enabled']) && $config['enabled'] === false; + unset($config['enabled'], $config['className']); + list($plugin, $name) = pluginSplit($behavior, true); if (!isset($alias)) { $alias = $name; @@ -165,7 +168,6 @@ class BehaviorCollection extends ObjectCollection { } } - $configDisabled = isset($config['enabled']) && $config['enabled'] === false; if (!in_array($alias, $this->_enabled) && !$configDisabled) { $this->enable($alias); } elseif ($configDisabled) { diff --git a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php index f2cea74f8..b9022320b 100644 --- a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php +++ b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php @@ -419,9 +419,22 @@ class BehaviorCollectionTest extends CakeTestCase { */ public $fixtures = array( 'core.apple', 'core.sample', 'core.article', 'core.user', 'core.comment', - 'core.attachment', 'core.tag', 'core.articles_tag' + 'core.attachment', 'core.tag', 'core.articles_tag', 'core.translate' ); +/** + * Test load() with enabled => false + * + */ + public function testLoadDisabled() { + $Apple = new Apple(); + $this->assertSame($Apple->Behaviors->attached(), array()); + + $Apple->Behaviors->load('Translate', array('enabled' => false)); + $this->assertTrue($Apple->Behaviors->attached('Translate')); + $this->assertFalse($Apple->Behaviors->enabled('Translate')); + } + /** * Tests loading aliased behaviors */ From 9a67a7070383abe3d746c9f71e386a10e9548863 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 7 Jan 2012 20:24:33 -0500 Subject: [PATCH 08/22] Fix failing test. --- lib/Cake/Test/Case/Model/ModelIntegrationTest.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php index bd76cd878..5f94c88d5 100644 --- a/lib/Cake/Test/Case/Model/ModelIntegrationTest.php +++ b/lib/Cake/Test/Case/Model/ModelIntegrationTest.php @@ -208,11 +208,11 @@ class ModelIntegrationTest extends BaseModelTest { public function testDynamicBehaviorAttachment() { $this->loadFixtures('Apple', 'Sample', 'Author'); $TestModel = new Apple(); - $this->assertEquals($TestModel->Behaviors->attached(), array()); + $this->assertEquals(array(), $TestModel->Behaviors->attached()); $TestModel->Behaviors->attach('Tree', array('left' => 'left_field', 'right' => 'right_field')); $this->assertTrue(is_object($TestModel->Behaviors->Tree)); - $this->assertEquals($TestModel->Behaviors->attached(), array('Tree')); + $this->assertEquals(array('Tree'), $TestModel->Behaviors->attached()); $expected = array( 'parent' => 'parent_id', @@ -223,16 +223,14 @@ class ModelIntegrationTest extends BaseModelTest { '__parentChange' => false, 'recursive' => -1 ); + $this->assertEquals($expected, $TestModel->Behaviors->Tree->settings['Apple']); - $this->assertEquals($TestModel->Behaviors->Tree->settings['Apple'], $expected); - - $expected['enabled'] = false; $TestModel->Behaviors->attach('Tree', array('enabled' => false)); - $this->assertEquals($TestModel->Behaviors->Tree->settings['Apple'], $expected); - $this->assertEquals($TestModel->Behaviors->attached(), array('Tree')); + $this->assertEquals($expected, $TestModel->Behaviors->Tree->settings['Apple']); + $this->assertEquals(array('Tree'), $TestModel->Behaviors->attached()); $TestModel->Behaviors->detach('Tree'); - $this->assertEquals($TestModel->Behaviors->attached(), array()); + $this->assertEquals(array(), $TestModel->Behaviors->attached()); $this->assertFalse(isset($TestModel->Behaviors->Tree)); } From c548b6b88ac2851578088feb34effb7374efee17 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 8 Jan 2012 12:28:22 -0500 Subject: [PATCH 09/22] Fix issue with find(count) and translated conditions. Because count queries did not have joins created for the translated fields pagination would generate invalid queries. Checking the conditions for translated fields and adding in the correct joins solves that. Extract what would have been duplicated code into methods. Add a few protected properties to keep method signatures sane. The code could be even simpler if the existing find(count) join was removed. Fixes #2349 --- lib/Cake/Model/Behavior/TranslateBehavior.php | 144 +++++++++++++----- .../Model/Behavior/TranslateBehaviorTest.php | 18 +++ 2 files changed, 125 insertions(+), 37 deletions(-) diff --git a/lib/Cake/Model/Behavior/TranslateBehavior.php b/lib/Cake/Model/Behavior/TranslateBehavior.php index 5c2460d06..84a48a627 100644 --- a/lib/Cake/Model/Behavior/TranslateBehavior.php +++ b/lib/Cake/Model/Behavior/TranslateBehavior.php @@ -34,6 +34,20 @@ class TranslateBehavior extends ModelBehavior { */ public $runtime = array(); +/** + * Stores the joinTable object for generating joins. + * + * @var object + */ + var $_joinTable; + +/** + * Stores the runtime model for generating joins. + * + * @var Model + */ + var $_runtimeModel; + /** * Callback * @@ -94,6 +108,7 @@ class TranslateBehavior extends ModelBehavior { } $db = $model->getDataSource(); $RuntimeModel = $this->translateModel($model); + if (!empty($RuntimeModel->tablePrefix)) { $tablePrefix = $RuntimeModel->tablePrefix; } else { @@ -103,8 +118,11 @@ class TranslateBehavior extends ModelBehavior { $joinTable->tablePrefix = $tablePrefix; $joinTable->table = $RuntimeModel->table; + $this->_joinTable = $joinTable; + $this->_runtimeModel = $RuntimeModel; + if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) { - $query['fields'] = 'COUNT(DISTINCT(' . $db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count'; + $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count'; $query['joins'][] = array( 'type' => 'INNER', 'alias' => $RuntimeModel->alias, @@ -115,6 +133,11 @@ class TranslateBehavior extends ModelBehavior { $RuntimeModel->alias.'.locale' => $locale ) ); + $conditionFields = $this->_checkConditions($model, $query); + foreach ($conditionFields as $field) { + $query = $this->_addJoin($model, $query, $field, $locale); + } + unset($this->_joinTable, $this->_runtimeModel); return $query; } @@ -144,45 +167,92 @@ class TranslateBehavior extends ModelBehavior { unset($query['fields'][$key]); } } - - if (is_array($locale)) { - foreach ($locale as $_locale) { - $model->virtualFields['i18n_' . $field . '_' . $_locale] = 'I18n__' . $field . '__' . $_locale . '.content'; - if (!empty($query['fields'])) { - $query['fields'][] = 'i18n_' . $field . '_' . $_locale; - } - $query['joins'][] = array( - 'type' => 'LEFT', - 'alias' => 'I18n__' . $field . '__' . $_locale, - 'table' => $joinTable, - 'conditions' => array( - $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"), - 'I18n__' . $field . '__' . $_locale . '.model' => $model->name, - 'I18n__' . $field . '__' . $_locale . '.' . $RuntimeModel->displayField => $aliasField, - 'I18n__' . $field . '__' . $_locale . '.locale' => $_locale - ) - ); - } - } else { - $model->virtualFields['i18n_' . $field] = 'I18n__' . $field . '.content'; - if (!empty($query['fields'])) { - $query['fields'][] = 'i18n_' . $field; - } - $query['joins'][] = array( - 'type' => 'INNER', - 'alias' => 'I18n__' . $field, - 'table' => $joinTable, - 'conditions' => array( - $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"), - 'I18n__' . $field . '.model' => $model->name, - 'I18n__' . $field . '.' . $RuntimeModel->displayField => $aliasField, - 'I18n__' . $field . '.locale' => $locale - ) - ); - } + $query = $this->_addJoin($model, $query, $field, $locale); } } $this->runtime[$model->alias]['beforeFind'] = $addFields; + unset($this->_joinTable, $this->_runtimeModel); + return $query; + } + +/** + * Check a query's conditions for translated fields. + * Return an array of translated fields found in the conditions. + * + * @param Model $model The model being read. + * @param array $query The query array. + * @return array The list of translated fields that are in the conditions. + */ + protected function _checkConditions(Model $model, $query) { + $conditionFields = array(); + if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions'])) ) { + return $conditionFields; + } + foreach ($query['conditions'] as $col => $val) { + foreach ($this->settings[$model->alias] as $field => $assoc) { + if (is_numeric($field)) { + $field = $assoc; + } + if (strpos($col, $field) !== false) { + $conditionFields[] = $field; + } + } + } + return $conditionFields; + } + +/** + * Appends a join for translated fields and possibly a field. + * + * @param Model $model The model being worked on. + * @param object $joinTable The jointable object. + * @param array $query The query array to append a join to. + * @param string $field The field name being joined. + * @param mixed $locale The locale(s) having joins added. + * @param boolean $addField Whether or not to add a field. + * @return array The modfied query + */ + protected function _addJoin(Model $model, $query, $field, $locale, $addField = false) { + $db = ConnectionManager::getDataSource($model->useDbConfig); + + $RuntimeModel = $this->_runtimeModel; + $joinTable = $this->_joinTable; + + if (is_array($locale)) { + foreach ($locale as $_locale) { + $model->virtualFields['i18n_' . $field . '_' . $_locale] = 'I18n__' . $field . '__' . $_locale . '.content'; + if (!empty($query['fields']) && is_array($query['fields'])) { + $query['fields'][] = 'i18n_'.$field.'_'.$_locale; + } + $query['joins'][] = array( + 'type' => 'LEFT', + 'alias' => 'I18n__'.$field.'__'.$_locale, + 'table' => $joinTable, + 'conditions' => array( + $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"), + 'I18n__'.$field.'__'.$_locale.'.model' => $model->name, + 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field, + 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale + ) + ); + } + } else { + $model->virtualFields['i18n_' . $field] = 'I18n__' . $field . '.content'; + if (!empty($query['fields']) && is_array($query['fields'])) { + $query['fields'][] = 'i18n_'.$field; + } + $query['joins'][] = array( + 'type' => 'INNER', + 'alias' => 'I18n__'.$field, + 'table' => $joinTable, + 'conditions' => array( + $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"), + 'I18n__'.$field.'.model' => $model->name, + 'I18n__'.$field.'.'.$RuntimeModel->displayField => $field, + 'I18n__'.$field.'.locale' => $locale + ) + ); + } return $query; } diff --git a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php index 89fa6a864..538eb7623 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php @@ -58,6 +58,24 @@ class TranslateBehaviorTest extends CakeTestCase { ClassRegistry::flush(); } +/** + * Test that count queries with conditions get the correct joins + * + * @return void + */ + function testCountWithConditions() { + $this->loadFixtures('Translate', 'TranslatedItem'); + + $Model =& new TranslatedItem(); + $Model->locale = 'eng'; + $result = $Model->find('count', array( + 'conditions' => array( + 'I18n__content.locale' => 'eng' + ) + )); + $this->assertEqual(3, $result); + } + /** * testTranslateModel method * From 3c48552f81f486b8fd7bd9d3dabf9788f9b26018 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 8 Jan 2012 21:40:12 -0500 Subject: [PATCH 10/22] Fix error with more case-sensitive databases. --- lib/Cake/Model/Behavior/TranslateBehavior.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Cake/Model/Behavior/TranslateBehavior.php b/lib/Cake/Model/Behavior/TranslateBehavior.php index 84a48a627..8d8067228 100644 --- a/lib/Cake/Model/Behavior/TranslateBehavior.php +++ b/lib/Cake/Model/Behavior/TranslateBehavior.php @@ -135,7 +135,7 @@ class TranslateBehavior extends ModelBehavior { ); $conditionFields = $this->_checkConditions($model, $query); foreach ($conditionFields as $field) { - $query = $this->_addJoin($model, $query, $field, $locale); + $query = $this->_addJoin($model, $query, $field, $field, $locale); } unset($this->_joinTable, $this->_runtimeModel); return $query; @@ -167,7 +167,7 @@ class TranslateBehavior extends ModelBehavior { unset($query['fields'][$key]); } } - $query = $this->_addJoin($model, $query, $field, $locale); + $query = $this->_addJoin($model, $query, $field, $aliasField, $locale); } } $this->runtime[$model->alias]['beforeFind'] = $addFields; @@ -208,11 +208,12 @@ class TranslateBehavior extends ModelBehavior { * @param object $joinTable The jointable object. * @param array $query The query array to append a join to. * @param string $field The field name being joined. + * @param string $aliasField The aliased field name being joined. * @param mixed $locale The locale(s) having joins added. * @param boolean $addField Whether or not to add a field. * @return array The modfied query */ - protected function _addJoin(Model $model, $query, $field, $locale, $addField = false) { + protected function _addJoin(Model $model, $query, $field, $aliasField, $locale, $addField = false) { $db = ConnectionManager::getDataSource($model->useDbConfig); $RuntimeModel = $this->_runtimeModel; @@ -231,7 +232,7 @@ class TranslateBehavior extends ModelBehavior { 'conditions' => array( $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"), 'I18n__'.$field.'__'.$_locale.'.model' => $model->name, - 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field, + 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $aliasField, 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale ) ); @@ -248,7 +249,7 @@ class TranslateBehavior extends ModelBehavior { 'conditions' => array( $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"), 'I18n__'.$field.'.model' => $model->name, - 'I18n__'.$field.'.'.$RuntimeModel->displayField => $field, + 'I18n__'.$field.'.'.$RuntimeModel->displayField => $aliasField, 'I18n__'.$field.'.locale' => $locale ) ); From f4c27e04bcad304bf9a3127e778dfce1c82f9ed6 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 8 Jan 2012 21:55:51 -0500 Subject: [PATCH 11/22] Fix errors generated when option['order'] is undefined. Fixes #2447 --- .../Component/PaginatorComponent.php | 2 +- .../Component/PaginatorComponentTest.php | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Controller/Component/PaginatorComponent.php b/lib/Cake/Controller/Component/PaginatorComponent.php index ecc1f2ec4..cc82ab259 100644 --- a/lib/Cake/Controller/Component/PaginatorComponent.php +++ b/lib/Cake/Controller/Component/PaginatorComponent.php @@ -333,7 +333,7 @@ class PaginatorComponent extends Component { $options['order'] = array($options['sort'] => $direction); } - if (!empty($whitelist)) { + if (!empty($whitelist) && isset($options['order']) && is_array($options['order'])) { $field = key($options['order']); if (!in_array($field, $whitelist)) { $options['order'] = null; diff --git a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php index fe6c961a3..814fa6b7d 100644 --- a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php @@ -776,6 +776,26 @@ class PaginatorComponentTest extends CakeTestCase { $this->assertEquals($expected, $result['order']); } +/** + * Test that no sort doesn't trigger an error. + * + * @return void + */ + public function testValidateSortNoSort() { + $model = $this->getMock('Model'); + $model->alias = 'model'; + $model->expects($this->any())->method('hasField')->will($this->returnValue(true)); + + $options = array('direction' => 'asc'); + $result = $this->Paginator->validateSort($model, $options, array('title', 'id')); + $this->assertFalse(isset($result['order'])); + + $options = array('order' => 'invalid desc'); + $result = $this->Paginator->validateSort($model, $options, array('title', 'id')); + + $this->assertEquals($options['order'], $result['order']); + } + /** * test that maxLimit is respected * From 1bc4562f3a52c8241c0921c9af3cedb530c159ec Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 8 Jan 2012 22:14:32 -0500 Subject: [PATCH 12/22] Fix failing test in App. --- lib/Cake/Config/config.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Config/config.php b/lib/Cake/Config/config.php index d90976331..a5c8761de 100644 --- a/lib/Cake/Config/config.php +++ b/lib/Cake/Config/config.php @@ -17,4 +17,5 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ $versionFile = file(CAKE . 'VERSION.txt'); -return $config['Cake.version'] = trim(array_pop($versionFile)); +$config['Cake.version'] = trim(array_pop($versionFile)); +return $config; From 1ffde95239294add5168342ea78542696145467a Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 8 Jan 2012 22:23:56 -0500 Subject: [PATCH 13/22] Update version number to 2.1.0-alpha --- lib/Cake/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index fd4d5ac86..e3c802c09 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license MIT License (http://www.opensource.org/licenses/mit-license.php) // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.1.0-dev +2.1.0-alpha From 1f246d61ca7781a9efeca4c3f03fa9f0e9b39675 Mon Sep 17 00:00:00 2001 From: ADmad Date: Thu, 29 Sep 2011 20:54:08 +0530 Subject: [PATCH 14/22] Refactoring I18n class to expose public methods to read .po, .mo and locale definition files. --- lib/Cake/I18n/I18n.php | 110 +++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index 8bab6fa1f..dc583abca 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -304,41 +304,51 @@ class I18n { } } - foreach ($searchPaths as $directory) { - foreach ($this->l10n->languagePath as $lang) { - $file = $directory . $lang . DS . $this->category . DS . $domain; $localeDef = $directory . $lang . DS . $this->category; + if (is_file($localeDef)) { + $definitions = self::loadLocaleDefinition($localeDef); + if ($definitions !== false) { + $this->_domains[$domain][$this->_lang][$this->category] = self::loadLocaleDefinition($localeDef); + $this->_noLocale = false; + return $domain; + } + } if ($core) { $app = $directory . $lang . DS . $this->category . DS . 'core'; + $translations = false; - if (file_exists($fn = "$app.mo")) { - $this->_loadMo($fn, $domain); - $this->_noLocale = false; + if (is_file($app . '.mo')) { + $translations = self::loadMo($app . '.mo'); + } + if ($translations === false && is_file($app . '.po')) { + $translations = self::loadPo($app . '.po'); + } + + if ($translations !== false) { + $this->_domains[$domain][$this->_lang][$this->category] = $translations; $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category]; - $core = null; - } elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) { - $this->_loadPo($f, $domain); $this->_noLocale = false; - $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category]; $core = null; } } - if (file_exists($fn = "$file.mo")) { - $this->_loadMo($fn, $domain); + $file = $directory . $lang . DS . $this->category . DS . $domain; + $translations = false; + + if (is_file($file . '.mo')) { + $translations = self::loadMo($file . '.mo'); + } + if ($translations === false && is_file($file . '.po')) { + $translations = self::loadPo($file . '.po'); + } + + if ($translations !== false) { + $this->_domains[$domain][$this->_lang][$this->category] = $translations; $this->_noLocale = false; break 2; - } elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) { - $this->_loadPo($f, $domain); - $this->_noLocale = false; - break 2; - } elseif (is_file($localeDef) && ($f = fopen($localeDef, "r"))) { - $this->_loadLocaleDefinition($f, $domain); - $this->_noLocale = false; - return $domain; } } } @@ -368,20 +378,21 @@ class I18n { unset($this->_domains[$domain][$this->_lang][$this->category][null]); } } + return $domain; } /** - * Loads the binary .mo file for translation and sets the values for this translation in the var I18n::_domains + * Loads the binary .mo file and returns array of translations * - * @param string $file Binary .mo file to load - * @param string $domain Domain where to load file in - * @return void + * @param string $filename Binary .mo file to load + * @return mixed Array of translations on success or false on failure */ - protected function _loadMo($file, $domain) { - $data = file_get_contents($file); + public static function loadMo($filename) { + $translations = false; - if ($data) { + if ($data = file_get_contents($filename)) { + $translations = array(); $header = substr($data, 0, 20); $header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header); extract($header); @@ -401,24 +412,29 @@ class I18n { if (strpos($msgstr, "\000")) { $msgstr = explode("\000", $msgstr); } - $this->_domains[$domain][$this->_lang][$this->category][$msgid] = $msgstr; + $translations[$msgid] = $msgstr; if (isset($msgid_plural)) { - $this->_domains[$domain][$this->_lang][$this->category][$msgid_plural] =& $this->_domains[$domain][$this->_lang][$this->category][$msgid]; + $translations[$msgid_plural] =& $translations[$msgid]; } } } } + + return $translations; } /** - * Loads the text .po file for translation and sets the values for this translation in the var I18n::_domains + * Loads the text .po file and returns array of translations * - * @param resource $file Text .po file to load - * @param string $domain Domain to load file in - * @return array Binded domain elements + * @param string $filename Text .po file to load + * @return mixed Array of translations on success or false on failure */ - protected function _loadPo($file, $domain) { + public static function loadPo($filename) { + if (!$file = fopen($filename, "r")) { + return false; + } + $type = 0; $translations = array(); $translationKey = ""; @@ -477,22 +493,28 @@ class I18n { } } while (!feof($file)); fclose($file); + $merge[""] = $header; - return $this->_domains[$domain][$this->_lang][$this->category] = array_merge($merge, $translations); + return array_merge($merge, $translations); } /** * Parses a locale definition file following the POSIX standard * - * @param resource $file file handler - * @param string $domain Domain where locale definitions will be stored - * @return void + * @param string $filename Locale definition filename + * @return mixed Array of definitions on success or false on failure */ - protected function _loadLocaleDefinition($file, $domain = null) { + public static function loadLocaleDefinition($filename) { + if (!$file = fopen($filename, "r")) { + return false; + } + + $definitions = array(); $comment = '#'; $escape = '\\'; $currentToken = false; $value = ''; + $_this = I18n::getInstance(); while ($line = fgets($file)) { $line = trim($line); if (empty($line) || $line[0] === $comment) { @@ -527,19 +549,21 @@ class I18n { $replacements = array_map('crc32', $mustEscape); $value = str_replace($mustEscape, $replacements, $value); $value = explode(';', $value); - $this->__escape = $escape; + $_this->__escape = $escape; foreach ($value as $i => $val) { $val = trim($val, '"'); - $val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$this, '_parseLiteralValue'), $val); + $val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$_this, '_parseLiteralValue'), $val); $val = str_replace($replacements, $mustEscape, $val); $value[$i] = $val; } if (count($value) == 1) { - $this->_domains[$domain][$this->_lang][$this->category][$currentToken] = array_pop($value); + $definitions[$currentToken] = array_pop($value); } else { - $this->_domains[$domain][$this->_lang][$this->category][$currentToken] = $value; + $definitions[$currentToken] = $value; } } + + return $definitions; } /** From 08825e54b7c3b58316c2451e5598d32fbfd0097a Mon Sep 17 00:00:00 2001 From: ADmad Date: Fri, 11 Nov 2011 01:46:21 +0530 Subject: [PATCH 15/22] Replacing hardcoded default domain with static property I18n::$defaultDomain --- lib/Cake/I18n/I18n.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index dc583abca..5672efa00 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -45,6 +45,13 @@ class I18n { */ public $l10n = null; +/** + * Default domain of translation + * + * @var string + */ + public static $defaultDomain = 'default'; + /** * Current domain of translation * @@ -149,7 +156,7 @@ class I18n { } if (is_null($domain)) { - $domain = 'default'; + $domain = self::$defaultDomain; } $_this->domain = $domain . '_' . $_this->l10n->lang; From 30522d0219cffeaa1bde068bd492e4d4d751c869 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sun, 20 Nov 2011 00:47:43 +0530 Subject: [PATCH 16/22] Adding $language parameter to I18n::translate() --- lib/Cake/I18n/I18n.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index 5672efa00..a736aab32 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -89,8 +89,7 @@ class I18n { protected $_noLocale = false; /** - * Set to true when I18N::_bindTextDomain() is called for the first time. - * If a translation file is found it is set to false again + * Translation categories * * @var array */ @@ -129,9 +128,11 @@ class I18n { * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations * @param string $category Category The integer value of the category to use. * @param integer $count Count Count is used with $plural to choose the correct plural form. + * @param string $language Language to translate string to. + * If null it checks for language in session followed by Config.language configuration variable. * @return string translated string. */ - public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null) { + public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null, $language = null) { $_this = I18n::getInstance(); if (strpos($singular, "\r\n") !== false) { @@ -144,10 +145,13 @@ class I18n { if (is_numeric($category)) { $_this->category = $_this->_categories[$category]; } - $language = Configure::read('Config.language'); - if (!empty($_SESSION['Config']['language'])) { - $language = $_SESSION['Config']['language']; + if (empty($language)) { + if (!empty($_SESSION['Config']['language'])) { + $language = $_SESSION['Config']['language']; + } else { + $language = Configure::read('Config.language'); + } } if (($_this->_lang && $_this->_lang !== $language) || !$_this->_lang) { From 0be71a08d6aeed2074d5e68f3e9cd029d944a540 Mon Sep 17 00:00:00 2001 From: ADmad Date: Tue, 10 Jan 2012 01:19:26 +0530 Subject: [PATCH 17/22] Adding / updating I18n class test cases. --- lib/Cake/Test/Case/I18n/I18nTest.php | 54 +++++++++++++++++++++------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/lib/Cake/Test/Case/I18n/I18nTest.php b/lib/Cake/Test/Case/I18n/I18nTest.php index 25226bb26..307cf0d7f 100644 --- a/lib/Cake/Test/Case/I18n/I18nTest.php +++ b/lib/Cake/Test/Case/I18n/I18nTest.php @@ -50,10 +50,14 @@ class I18nTest extends CakeTestCase { CakePlugin::unload(); } - +/** + * testTranslationCaching method + * + * @return void + */ public function testTranslationCaching() { Configure::write('Config.language', 'cache_test_po'); - $i18n = i18n::getInstance(); + $i18n = I18n::getInstance(); // reset internally stored entries I18n::clear(); @@ -93,7 +97,6 @@ class I18nTest extends CakeTestCase { $this->assertEquals('FOO', I18n::translate('dom1.foo', false, 'dom1')); } - /** * testDefaultStrings method * @@ -2588,6 +2591,11 @@ class I18nTest extends CakeTestCase { $this->assertEquals('Po (translated)', $singular); } +/** + * testTimeDefinition method + * + * @return void + */ public function testTimeDefinition() { Configure::write('Config.language', 'po'); $result = __c('d_fmt', 5); @@ -2603,6 +2611,11 @@ class I18nTest extends CakeTestCase { $this->assertEquals($expected, $result); } +/** + * testTimeDefinitionJapanese method + * + * @return void + */ public function testTimeDefinitionJapanese() { Configure::write('Config.language', 'ja_jp'); $result = __c('d_fmt', 5); @@ -2620,12 +2633,29 @@ class I18nTest extends CakeTestCase { $this->assertEquals($expected, $result); } +/** + * testTranslateLanguageParam method + * + * @return void + */ + public function testTranslateLanguageParam() { + Configure::write('Config.language', 'rule_0_po'); + + $result = I18n::translate('Plural Rule 1', null, null, 6); + $expected = 'Plural Rule 0 (translated)'; + $this->assertEquals($expected, $result); + + $result = I18n::translate('Plural Rule 1', null, null, 6, null, 'rule_1_po'); + $expected = 'Plural Rule 1 (translated)'; + $this->assertEquals($expected, $result); + } + /** * Singular method * * @return void */ - function __domainCategorySingular($domain = 'test_plugin', $category = 3) { + private function __domainCategorySingular($domain = 'test_plugin', $category = 3) { $singular = __dc($domain, 'Plural Rule 1', $category); return $singular; } @@ -2635,7 +2665,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __domainCategoryPlural($domain = 'test_plugin', $category = 3) { + private function __domainCategoryPlural($domain = 'test_plugin', $category = 3) { $plurals = array(); for ($number = 0; $number <= 25; $number++) { $plurals[] = sprintf(__dcn($domain, '%d = 1', '%d = 0 or > 1', (float)$number, $category), (float)$number); @@ -2648,7 +2678,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __domainSingular($domain = 'test_plugin') { + private function __domainSingular($domain = 'test_plugin') { $singular = __d($domain, 'Plural Rule 1'); return $singular; } @@ -2658,7 +2688,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __domainPlural($domain = 'test_plugin') { + private function __domainPlural($domain = 'test_plugin') { $plurals = array(); for ($number = 0; $number <= 25; $number++) { $plurals[] = sprintf(__dn($domain, '%d = 1', '%d = 0 or > 1', (float)$number), (float)$number ); @@ -2671,7 +2701,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __category($category = 3) { + private function __category($category = 3) { $singular = __c('Plural Rule 1', $category); return $singular; } @@ -2681,7 +2711,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __singular() { + private function __singular() { $singular = __('Plural Rule 1'); return $singular; } @@ -2691,7 +2721,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __plural() { + private function __plural() { $plurals = array(); for ($number = 0; $number <= 25; $number++) { $plurals[] = sprintf(__n('%d = 1', '%d = 0 or > 1', (float)$number), (float)$number); @@ -2704,7 +2734,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __singularFromCore() { + private function __singularFromCore() { $singular = __('Plural Rule 1 (from core)'); return $singular; } @@ -2714,7 +2744,7 @@ class I18nTest extends CakeTestCase { * * @return void */ - function __pluralFromCore() { + private function __pluralFromCore() { $plurals = array(); for ($number = 0; $number <= 25; $number++) { $plurals[] = sprintf(__n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', (float)$number), (float)$number ); From b8598c5ca03f6cadc2076d0dba543b37e3cfefc5 Mon Sep 17 00:00:00 2001 From: AD7six Date: Mon, 9 Jan 2012 23:38:32 +0100 Subject: [PATCH 18/22] Ensure the class has a constructor if there is no constructor (this means a model which does not inherit from Model) newInstance will throw an exception. Conflicts: lib/Cake/Utility/ClassRegistry.php --- lib/Cake/Utility/ClassRegistry.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Utility/ClassRegistry.php b/lib/Cake/Utility/ClassRegistry.php index 0dab3d442..77abb316e 100644 --- a/lib/Cake/Utility/ClassRegistry.php +++ b/lib/Cake/Utility/ClassRegistry.php @@ -158,7 +158,11 @@ class ClassRegistry { } } } - $instance = $reflection->newInstance($settings); + if ($reflection->getConstructor()) { + $instance = $reflection->newInstance($settings); + } else { + $instance = $reflection->newInstance(); + } if ($strict) { $instance = ($instance instanceof Model) ? $instance : null; } From d238d8c0bbf51580bdb5dbd86c912c5b85bdbe0d Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 9 Jan 2012 21:47:57 -0500 Subject: [PATCH 19/22] Fix incorrect `__isset()`. There was a missing $ before name. Fixes issues with dynamic properties not being handled correctly. Fixes #2450 --- lib/Cake/Test/Case/View/ViewTest.php | 12 ++++++++++++ lib/Cake/View/View.php | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Test/Case/View/ViewTest.php b/lib/Cake/Test/Case/View/ViewTest.php index 227c12454..67f68ca57 100644 --- a/lib/Cake/Test/Case/View/ViewTest.php +++ b/lib/Cake/Test/Case/View/ViewTest.php @@ -1236,4 +1236,16 @@ Element content. TEXT; $this->assertEquals($expected, $content); } + +/** + * Test that setting arbitrary properties still works. + * + * @return void + */ + public function testPropertySetting() { + $this->assertFalse(isset($this->View->pageTitle)); + $this->View->pageTitle = 'test'; + $this->assertTrue(isset($this->View->pageTitle)); + $this->assertEquals('test', $this->View->pageTitle); + } } diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index 0e8b04b31..61eca340b 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -796,7 +796,7 @@ class View extends Object { * @return boolean */ public function __isset($name) { - return isset($this->name); + return isset($this->{$name}); } /** From a8bc91610486f763c94498a798056ced3c773419 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 10 Jan 2012 20:15:59 -0500 Subject: [PATCH 20/22] Move check out of the loop, and allow nulls. Refs #2341 --- lib/Cake/Model/Model.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 8613634a9..d84a85b4a 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -2815,16 +2815,17 @@ class Model extends Object implements CakeEventListener { $return = $idMap = array(); $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey); + if (isset($results[0][$this->alias]) && !array_key_exists('parent_id', $results[0][$this->alias])) { + trigger_error( + __d('cake_dev', 'You cannot use find("threaded") on models without a "parent_id" field.'), + E_USER_WARNING + ); + return $return; + } + foreach ($results as $result) { $result['children'] = array(); $id = $result[$this->alias][$this->primaryKey]; - if (!isset($result[$this->alias]['parent_id'])) { - trigger_error( - __d('cake_dev', 'You cannot use find("threaded") on models without a "parent_id" field.'), - E_USER_WARNING - ); - return $return; - } $parentId = $result[$this->alias]['parent_id']; if (isset($idMap[$id]['children'])) { $idMap[$id] = array_merge($result, (array)$idMap[$id]); From 7877e7f997cf4592a5f3eb743069db605cb6561a Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 10 Jan 2012 20:32:12 -0500 Subject: [PATCH 21/22] Make allow(null) and deny(null) consistent with no args. No arguments and a single null should be handled the same. Fixes #2461 --- lib/Cake/Controller/Component/AuthComponent.php | 4 ++-- .../Case/Controller/Component/AuthComponentTest.php | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index 697d31815..0b5aea1d6 100644 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -432,7 +432,7 @@ class AuthComponent extends Component { */ public function allow($action = null) { $args = func_get_args(); - if (empty($args)) { + if (empty($args) || $action === null) { $this->allowedActions = $this->_methods; } else { if (isset($args[0]) && is_array($args[0])) { @@ -458,7 +458,7 @@ class AuthComponent extends Component { */ public function deny($action = null) { $args = func_get_args(); - if (empty($args)) { + if (empty($args) || $action === null) { $this->allowedActions = array(); } else { if (isset($args[0]) && is_array($args[0])) { diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 486bf88a9..f80e8ff81 100644 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -654,6 +654,18 @@ class AuthComponentTest extends CakeTestCase { $this->Controller->request['action'] = 'login'; $this->assertFalse($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->Auth->deny(); + $this->Controller->Auth->allow(null); + + $this->Controller->request['action'] = 'camelCase'; + $this->assertTrue($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->Auth->allow(); + $this->Controller->Auth->deny(null); + + $this->Controller->request['action'] = 'camelCase'; + $this->assertFalse($this->Controller->Auth->startup($this->Controller)); } /** From 0f8e1500deee08a5a923ec30f680a822650166f7 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 11 Jan 2012 20:21:56 -0500 Subject: [PATCH 22/22] Ensure helpers are loaded in data views. Both XmlView and JsonView should load helpers when they use view files. Fixes #2465 --- lib/Cake/View/JsonView.php | 3 +++ lib/Cake/View/XmlView.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/Cake/View/JsonView.php b/lib/Cake/View/JsonView.php index f057f08c9..66613a2c1 100644 --- a/lib/Cake/View/JsonView.php +++ b/lib/Cake/View/JsonView.php @@ -96,6 +96,9 @@ class JsonView extends View { return $content; } if ($view !== false && $viewFileName = $this->_getViewFileName($view)) { + if (!$this->_helpersLoaded) { + $this->loadHelpers(); + } $content = $this->_render($viewFileName); $this->Blocks->set('content', $content); return $content; diff --git a/lib/Cake/View/XmlView.php b/lib/Cake/View/XmlView.php index 9eea5e5ee..8d88a810f 100644 --- a/lib/Cake/View/XmlView.php +++ b/lib/Cake/View/XmlView.php @@ -99,6 +99,9 @@ class XmlView extends View { return $content; } if ($view !== false && $viewFileName = $this->_getViewFileName($view)) { + if (!$this->_helpersLoaded) { + $this->loadHelpers(); + } $content = $this->_render($viewFileName); $this->Blocks->set('content', (string)$content); return $content;