From ae7855692d131241f06ba78a837de536508f73f1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 20 Nov 2010 23:42:54 -0500 Subject: [PATCH 01/23] Removing unserialize() as its dangerous. Instead using | delimited fields for locked fields. This totally avoids issues with serialize(). Removing str_rot13, as its only child proof. Tests updated. --- cake/libs/controller/components/security.php | 6 +-- .../controller/components/security.test.php | 50 ++++++++----------- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php index 7e5b3b28f..0de6c6ae4 100644 --- a/cake/libs/controller/components/security.php +++ b/cake/libs/controller/components/security.php @@ -618,15 +618,11 @@ class SecurityComponent extends Object { } unset($check['_Token']); - $locked = str_rot13($locked); - if (preg_match('/(\A|;|{|})O\:[0-9]+/', $locked)) { - return false; - } + $locked = explode('|', $locked); $lockedFields = array(); $fields = Set::flatten($check); $fieldList = array_keys($fields); - $locked = unserialize($locked); $multi = array(); foreach ($fieldList as $i => $key) { diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php index bbd514100..66cff58ba 100644 --- a/cake/tests/cases/libs/controller/components/security.test.php +++ b/cake/tests/cases/libs/controller/components/security.test.php @@ -573,8 +573,7 @@ DIGEST; function testValidatePost() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%3B'; - $fields .= 'f%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; $this->Controller->data = array( 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), @@ -591,8 +590,7 @@ DIGEST; function testValidatePostFormHacking() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%3B'; - $fields .= 'f%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; $this->Controller->data = array( 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), @@ -641,7 +639,7 @@ DIGEST; function testValidatePostArray() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3An%3A0%3A%7B%7D'; + $fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A'; $this->Controller->data = array( 'Model' => array('multi_field' => array('1', '3')), @@ -659,7 +657,7 @@ DIGEST; function testValidatePostNoModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3An%3A0%3A%7B%7D'; + $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3A'; $this->Controller->data = array( 'anything' => 'some_data', @@ -679,7 +677,7 @@ DIGEST; function testValidatePostSimple() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3An%3A0%3A%7B%7D'; + $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3A'; $this->Controller->data = $data = array( 'Model' => array('username' => '', 'password' => ''), @@ -699,8 +697,7 @@ DIGEST; function testValidatePostComplex() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3An%3A2%3A%7Bv%3A0%3Bf%3A14%3A%'; - $fields .= '22Nqqerffrf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id'; $this->Controller->data = array( 'Addresses' => array( @@ -727,7 +724,7 @@ DIGEST; function testValidatePostMultipleSelect() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '422cde416475abc171568be690a98cad20e66079%3An%3A0%3A%7B%7D'; + $fields = '422cde416475abc171568be690a98cad20e66079%3A'; $this->Controller->data = array( 'Tag' => array('Tag' => array(1, 2)), @@ -750,7 +747,7 @@ DIGEST; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - $fields = '19464422eafe977ee729c59222af07f983010c5f%3An%3A0%3A%7B%7D'; + $fields = '19464422eafe977ee729c59222af07f983010c5f%3A'; $this->Controller->data = array( 'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1', 'Tag' => array('Tag' => array(1)), '_Token' => compact('key', 'fields'), @@ -771,8 +768,7 @@ DIGEST; function testValidatePostCheckbox() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%'; - $fields .= '3Bf%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; $this->Controller->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), @@ -782,7 +778,7 @@ DIGEST; $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); - $fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3An%3A0%3A%7B%7D'; + $fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3A'; $this->Controller->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), @@ -815,8 +811,8 @@ DIGEST; function testValidatePostHidden() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3An%3A2%3A%7Bv%3A0%3Bf%3A12%3A'; - $fields .= '%22Zbqry.uvqqra%22%3Bv%3A1%3Bf%3A18%3A%22Zbqry.bgure_uvqqra%22%3B%7D'; + $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3AModel.hidden%7CModel.other_hidden'; + $fields .= ''; $this->Controller->data = array( 'Model' => array( @@ -839,8 +835,7 @@ DIGEST; $this->Controller->Security->disabledFields = array('Model.username', 'Model.password'); $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'ef1082968c449397bcd849f963636864383278b1%3An%3A1%3A%7Bv%'; - $fields .= '3A0%3Bf%3A12%3A%22Zbqry.uvqqra%22%3B%7D'; + $fields = 'ef1082968c449397bcd849f963636864383278b1%3AModel.hidden'; $this->Controller->data = array( 'Model' => array( @@ -862,9 +857,7 @@ DIGEST; function testValidateHiddenMultipleModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3An%3A3%3A%7Bv%3A0%3Bf%3A11'; - $fields .= '%3A%22Zbqry.inyvq%22%3Bv%3A1%3Bf%3A12%3A%22Zbqry2.inyvq%22%3Bv%3A2%'; - $fields .= '3Bf%3A12%3A%22Zbqry3.inyvq%22%3B%7D'; + $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3AModel.valid%7CModel2.valid%7CModel3.valid'; $this->Controller->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), @@ -895,9 +888,8 @@ DIGEST; function testValidateHasManyModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3An%3A4%3A%7Bv%3A0%3Bf%3A14%3A%2'; - $fields .= '2Zbqry.0.uvqqra%22%3Bv%3A1%3Bf%3A13%3A%22Zbqry.0.inyvq%22%3Bv%3A2%3Bf%3'; - $fields .= 'A14%3A%22Zbqry.1.uvqqra%22%3Bv%3A3%3Bf%3A13%3A%22Zbqry.1.inyvq%22%3B%7D'; + $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid'; + $fields .= '%7CModel.1.hidden%7CModel.1.valid'; $this->Controller->data = array( 'Model' => array( @@ -926,9 +918,8 @@ DIGEST; function testValidateHasManyRecordsPass() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; - $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; - $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C'; + $fields .= 'Address.1.id%7CAddress.1.primary'; $this->Controller->data = array( 'Address' => array( @@ -971,9 +962,8 @@ DIGEST; function testValidateHasManyRecordsFail() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; - $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; - $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; - $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C'; + $fields .= 'Address.1.id%7CAddress.1.primary'; $this->Controller->data = array( 'Address' => array( From 79aafda6986fba23b2308050fee9bb3c955741db Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 21 Nov 2010 00:09:45 -0500 Subject: [PATCH 02/23] Removing use of serialize() for locked fields. This removes any possible exploit related to serialize()/unserialize(). Instead values are passed as | delimited. --- cake/libs/view/helpers/form.php | 2 +- .../cases/libs/view/helpers/form.test.php | 21 +++++++------------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/cake/libs/view/helpers/form.php b/cake/libs/view/helpers/form.php index 538d844e5..5ee8a6c2c 100644 --- a/cake/libs/view/helpers/form.php +++ b/cake/libs/view/helpers/form.php @@ -406,7 +406,7 @@ class FormHelper extends AppHelper { $fields += $locked; $fields = Security::hash(serialize($fields) . Configure::read('Security.salt')); - $locked = str_rot13(serialize(array_keys($locked))); + $locked = implode(array_keys($locked), '|'); $out = $this->hidden('_Token.fields', array( 'value' => urlencode($fields . ':' . $locked), diff --git a/cake/tests/cases/libs/view/helpers/form.test.php b/cake/tests/cases/libs/view/helpers/form.test.php index 0dc83aed7..3a9868c77 100644 --- a/cake/tests/cases/libs/view/helpers/form.test.php +++ b/cake/tests/cases/libs/view/helpers/form.test.php @@ -832,7 +832,7 @@ class FormHelperTest extends CakeTestCase { $result = $this->Form->secure($fields); $expected = Security::hash(serialize($fields) . Configure::read('Security.salt')); - $expected .= ':' . str_rot13(serialize(array('Model.valid'))); + $expected .= ':' . 'Model.valid'; $expected = array( 'div' => array('style' => 'display:none;'), @@ -894,9 +894,8 @@ class FormHelperTest extends CakeTestCase { $this->Form->params['_Token']['key'] = $key; $result = $this->Form->secure($fields); - $hash = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3An%3A4%3A%7Bv%3A0%3Bf%3A14%3A%22Zbqry.'; - $hash .= '0.uvqqra%22%3Bv%3A1%3Bf%3A13%3A%22Zbqry.0.inyvq%22%3Bv%3A2%3Bf%3A14%3A%22Zbqry.1'; - $hash .= '.uvqqra%22%3Bv%3A3%3Bf%3A13%3A%22Zbqry.1.inyvq%22%3B%7D'; + $hash = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid'; + $hash .= '%7CModel.1.hidden%7CModel.1.valid'; $expected = array( 'div' => array('style' => 'display:none;'), @@ -985,8 +984,7 @@ class FormHelperTest extends CakeTestCase { $result = $this->Form->secure($this->Form->fields); - $hash = 'c9118120e680a7201b543f562e5301006ccfcbe2%3An%3A2%3A%7Bv%3A0%3Bf%3A14%'; - $hash .= '3A%22Nqqerffrf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + $hash = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id'; $expected = array( 'div' => array('style' => 'display:none;'), @@ -1029,8 +1027,7 @@ class FormHelperTest extends CakeTestCase { $this->Form->input('Addresses.1.phone'); $result = $this->Form->secure($this->Form->fields); - $hash = '774df31936dc850b7d8a5277dc0b890123788b09%3An%3A2%3A%7Bv%3A0%3Bf%3A14%3A%22Nqqerf'; - $hash .= 'frf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + $hash = '774df31936dc850b7d8a5277dc0b890123788b09%3AAddresses.0.id%7CAddresses.1.id'; $expected = array( 'div' => array('style' => 'display:none;'), @@ -1074,8 +1071,7 @@ class FormHelperTest extends CakeTestCase { $result = $this->Form->secure($expected); - $hash = '449b7e889128e8e52c5e81d19df68f5346571492%3An%3A1%3A%'; - $hash .= '7Bv%3A0%3Bf%3A12%3A%22Nqqerffrf.vq%22%3B%7D'; + $hash = '449b7e889128e8e52c5e81d19df68f5346571492%3AAddresses.id'; $expected = array( 'div' => array('style' => 'display:none;'), 'input' => array( @@ -1179,8 +1175,7 @@ class FormHelperTest extends CakeTestCase { ); $this->assertEqual($result, $expected); - $hash = 'bd7c4a654e5361f9a433a43f488ff9a1065d0aaf%3An%3A2%3A%7Bv%3A0%3Bf%3A15%3'; - $hash .= 'A%22HfreSbez.uvqqra%22%3Bv%3A1%3Bf%3A14%3A%22HfreSbez.fghss%22%3B%7D'; + $hash = 'bd7c4a654e5361f9a433a43f488ff9a1065d0aaf%3AUserForm.hidden%7CUserForm.stuff'; $result = $this->Form->secure($this->Form->fields); $expected = array( @@ -3569,7 +3564,7 @@ class FormHelperTest extends CakeTestCase { $this->assertEqual($this->Form->fields, array('Model.multi_field')); $result = $this->Form->secure($this->Form->fields); - $key = 'f7d573650a295b94e0938d32b323fde775e5f32b%3An%3A0%3A%7B%7D'; + $key = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A'; $this->assertPattern('/"' . $key . '"/', $result); } From 244de1df8563dcd030245b251651d9f4a67b49eb Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 22 Nov 2010 12:58:51 -0500 Subject: [PATCH 03/23] Adding a comment about the messageId property and shells. Refs #1303 --- cake/libs/controller/components/email.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cake/libs/controller/components/email.php b/cake/libs/controller/components/email.php index 233945f53..52451fda1 100755 --- a/cake/libs/controller/components/email.php +++ b/cake/libs/controller/components/email.php @@ -261,6 +261,9 @@ class EmailComponent extends Object{ * it be handled by sendmail (or similar) or a string * to completely override the Message-ID. * + * If you are sending Email from a shell, be sure to set this value. As you + * could encounter delivery issues if you do not. + * * @var mixed * @access public */ From f48811a2ff471094462bd043dfc69792c30923f5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 22 Nov 2010 21:15:02 -0500 Subject: [PATCH 04/23] Moving include up so its not buried deep inside the class. --- cake/libs/cake_session.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cake/libs/cake_session.php b/cake/libs/cake_session.php index bcb6459f9..0ad09de2c 100644 --- a/cake/libs/cake_session.php +++ b/cake/libs/cake_session.php @@ -22,6 +22,9 @@ * @since CakePHP(tm) v .0.10.0.1222 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +if (!class_exists('Security')) { + App::import('Core', 'Security'); +} /** * Session class for Cake. @@ -193,9 +196,6 @@ class CakeSession extends Object { } } if (isset($_SESSION) || $start === true) { - if (!class_exists('Security')) { - App::import('Core', 'Security'); - } $this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout')); $this->security = Configure::read('Security.level'); } From bf10723f897466a1b02149db4f45e9a279c37de8 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 22 Nov 2010 21:21:55 -0500 Subject: [PATCH 05/23] Applying patch from 'michealc' to fix duplicated comments. Fixes #1306 --- cake/libs/model/datasources/datasource.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cake/libs/model/datasources/datasource.php b/cake/libs/model/datasources/datasource.php index 2c5a0ab12..94b5d5cef 100644 --- a/cake/libs/model/datasources/datasource.php +++ b/cake/libs/model/datasources/datasource.php @@ -396,7 +396,7 @@ class DataSource extends Object { } /** - * Returns the ID generated from the previous INSERT operation. + * Returns the number of rows returned by last operation. * * @param unknown_type $source * @return integer Number of rows returned by last operation @@ -407,7 +407,7 @@ class DataSource extends Object { } /** - * Returns the ID generated from the previous INSERT operation. + * Returns the number of rows affected by last query. * * @param unknown_type $source * @return integer Number of rows affected by last query. @@ -428,6 +428,7 @@ class DataSource extends Object { function enabled() { return true; } + /** * Returns true if the DataSource supports the given interface (method) * From d0d16a7edaa9a3b26f6a64c7bbaf9f50f0f7d1f2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 22 Nov 2010 21:29:11 -0500 Subject: [PATCH 06/23] Fixing i18n extraction errors in Scaffold. Fixes #1305 --- cake/libs/controller/scaffold.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cake/libs/controller/scaffold.php b/cake/libs/controller/scaffold.php index 0891616fd..ea5e9031f 100644 --- a/cake/libs/controller/scaffold.php +++ b/cake/libs/controller/scaffold.php @@ -303,7 +303,7 @@ class Scaffold extends Object { } if (!$this->ScaffoldModel->exists()) { - $message = __(sprintf("Invalid id for %s::edit()", Inflector::humanize($this->modelKey), true)); + $message = sprintf(__("Invalid id for %s::edit()", true), Inflector::humanize($this->modelKey)); if ($this->_validSession) { $this->controller->Session->setFlash($message); $this->controller->redirect($this->redirect); @@ -321,9 +321,10 @@ class Scaffold extends Object { if ($this->ScaffoldModel->save($this->controller->data)) { if ($this->controller->_afterScaffoldSave($action)) { - $message = __( - sprintf('The %1$s has been %2$s', Inflector::humanize($this->modelKey), $success), - true + $message = sprintf( + __('The %1$s has been %2$s', true), + Inflector::humanize($this->modelKey), + $success ); if ($this->_validSession) { $this->controller->Session->setFlash($message); @@ -376,8 +377,9 @@ class Scaffold extends Object { */ function __scaffoldDelete($params = array()) { if ($this->controller->_beforeScaffold('delete')) { - $message = __( - sprintf("No id set for %s::delete()", Inflector::humanize($this->modelKey)), true + $message = sprintf( + __("No id set for %s::delete()", true), + Inflector::humanize($this->modelKey) ); if (isset($params['pass'][0])) { $id = $params['pass'][0]; @@ -390,9 +392,9 @@ class Scaffold extends Object { } if ($this->ScaffoldModel->delete($id)) { - $message = __( - sprintf('The %1$s with id: %2$d has been deleted.', Inflector::humanize($this->modelClass), $id), - true + $message = sprintf( + __('The %1$s with id: %2$d has been deleted.', true), + Inflector::humanize($this->modelClass), $id ); if ($this->_validSession) { $this->controller->Session->setFlash($message); @@ -402,10 +404,10 @@ class Scaffold extends Object { return $this->_output(); } } else { - $message = __(sprintf( - 'There was an error deleting the %1$s with id: %2$d', + $message = sprintf( + __('There was an error deleting the %1$s with id: %2$d', true), Inflector::humanize($this->modelClass), $id - ), true); + ); if ($this->_validSession) { $this->controller->Session->setFlash($message); $this->controller->redirect($this->redirect); From d5fb0b25cb521d3b65e10d5265571ccd941b29ac Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 22 Nov 2010 22:08:46 -0500 Subject: [PATCH 07/23] Fixing issue where Date header would be missing from Emails sent by EmailComponent. Adding user configurable field for date. Test cases added. Fixes #1304 --- cake/libs/controller/components/email.php | 16 ++++++++++++ .../libs/controller/components/email.test.php | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/cake/libs/controller/components/email.php b/cake/libs/controller/components/email.php index 52451fda1..d27fd5c13 100755 --- a/cake/libs/controller/components/email.php +++ b/cake/libs/controller/components/email.php @@ -97,6 +97,15 @@ class EmailComponent extends Object{ */ var $bcc = array(); +/** + * The date to put in the Date: header. This should be a date + * conformant with the RFC2822 standard. Leave null, to have + * today's date generated. + * + * @var string + */ + var $date = null; + /** * The subject of the email * @@ -410,6 +419,7 @@ class EmailComponent extends Object{ $this->bcc = array(); $this->subject = null; $this->additionalParams = null; + $this->date = null; $this->smtpError = null; $this->attachments = array(); $this->htmlMessage = null; @@ -574,6 +584,12 @@ class EmailComponent extends Object{ } } + $date = $this->date; + if ($date == false) { + $date = date(DATE_RFC2822); + } + $headers['Date'] = $date; + $headers['X-Mailer'] = $this->xMailer; if (!empty($this->headers)) { diff --git a/cake/tests/cases/libs/controller/components/email.test.php b/cake/tests/cases/libs/controller/components/email.test.php index 8d7f588e6..8379e3e7b 100755 --- a/cake/tests/cases/libs/controller/components/email.test.php +++ b/cake/tests/cases/libs/controller/components/email.test.php @@ -493,6 +493,7 @@ TEMPDOC; $this->Controller->EmailTest->delivery = 'debug'; $this->Controller->EmailTest->messageId = false; + $date = date(DATE_RFC2822); $message = <<To: postmaster@localhost From: noreply@example.com @@ -501,6 +502,7 @@ Header: From: noreply@example.com Reply-To: noreply@example.com +Date: $date X-Mailer: CakePHP Email Component Content-Type: {CONTENTTYPE} Content-Transfer-Encoding: 7bitParameters: @@ -544,6 +546,7 @@ MSGBLOC; $this->Controller->EmailTest->delivery = 'debug'; $this->Controller->EmailTest->messageId = false; + $date = date(DATE_RFC2822); $header = <<assertPattern('/Subject: Cake Debug Test\n/', $result); $this->assertPattern('/Reply-To: noreply@example.com\n/', $result); $this->assertPattern('/From: noreply@example.com\n/', $result); + $this->assertPattern('/Date: ' . date(DATE_RFC2822) . '\n/', $result); $this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result); $this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result); $this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result); @@ -716,6 +721,7 @@ TEXTBLOC; $this->assertPattern('/Subject: Cake Debug Test\n/', $result); $this->assertPattern('/Reply-To: noreply@example.com\n/', $result); $this->assertPattern('/From: noreply@example.com\n/', $result); + $this->assertPattern('/Date: ' . date(DATE_RFC2822) . '\n/', $result); $this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result); $this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result); $this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result); @@ -849,7 +855,24 @@ HTMLBLOC; $this->assertPattern('/First line\n/', $result); $this->assertPattern('/Second line\n/', $result); $this->assertPattern('/Third line\n/', $result); + } +/** + * test setting a custom date. + * + * @return void + */ + function testDateProperty() { + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake Debug Test'; + $this->Controller->EmailTest->date = 'Today!'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->delivery = 'debug'; + + $this->assertTrue($this->Controller->EmailTest->send('test message')); + $result = $this->Controller->Session->read('Message.email.message'); + $this->assertPattern('/Date: Today!\n/', $result); } /** @@ -1043,6 +1066,7 @@ HTMLBLOC; $this->Controller->EmailTest->return = 'test.return@example.com'; $this->Controller->EmailTest->cc = array('cc1@example.com', 'cc2@example.com'); $this->Controller->EmailTest->bcc = array('bcc1@example.com', 'bcc2@example.com'); + $this->Controller->EmailTest->date = 'Today!'; $this->Controller->EmailTest->subject = 'Test subject'; $this->Controller->EmailTest->additionalParams = 'X-additional-header'; $this->Controller->EmailTest->delivery = 'smtp'; @@ -1064,6 +1088,7 @@ HTMLBLOC; $this->assertNull($this->Controller->EmailTest->return); $this->assertIdentical($this->Controller->EmailTest->cc, array()); $this->assertIdentical($this->Controller->EmailTest->bcc, array()); + $this->assertNull($this->Controller->EmailTest->date); $this->assertNull($this->Controller->EmailTest->subject); $this->assertNull($this->Controller->EmailTest->additionalParams); $this->assertIdentical($this->Controller->EmailTest->getHeaders(), array()); From b567de977e6d1e570095f26ae1eb1eaccbb6cb78 Mon Sep 17 00:00:00 2001 From: ADmad Date: Wed, 24 Nov 2010 01:43:10 +0530 Subject: [PATCH 08/23] Fixed bug where Dispatcher::getUrl() returned incorrect URL if $base appeared in a $uri besides at start of $uri. --- cake/dispatcher.php | 8 ++++---- cake/tests/cases/dispatcher.test.php | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cake/dispatcher.php b/cake/dispatcher.php index 1597c666f..e95771b2c 100644 --- a/cake/dispatcher.php +++ b/cake/dispatcher.php @@ -82,7 +82,7 @@ class Dispatcher extends Object { } /** - * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the + * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the * results (if autoRender is set). * * If no controller of given name can be found, invoke() shows error messages in @@ -483,8 +483,8 @@ class Dispatcher extends Object { if ($tmpUri === '/' || $tmpUri == $baseDir || $tmpUri == $base) { $url = $_GET['url'] = '/'; } else { - if ($base && strpos($uri, $base) !== false) { - $elements = explode($base, $uri); + if ($base && strpos($uri, $base) === 0) { + $elements = explode($base, $uri, 2); } elseif (preg_match('/^[\/\?\/|\/\?|\?\/]/', $uri)) { $elements = array(1 => preg_replace('/^[\/\?\/|\/\?|\?\/]/', '', $uri)); } else { @@ -560,7 +560,7 @@ class Dispatcher extends Object { } $filters = Configure::read('Asset.filter'); $isCss = ( - strpos($url, 'ccss/') === 0 || + strpos($url, 'ccss/') === 0 || preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?assertEqual($expected, $result); + $_GET['url'] = array(); + $Dispatcher =& new Dispatcher(); + $Dispatcher->base = '/shop'; + $uri = '/shop/fr/pages/shop'; + $result = $Dispatcher->getUrl($uri); + $expected = 'fr/pages/shop'; + $this->assertEqual($expected, $result); } /** @@ -1367,11 +1374,11 @@ class DispatcherTest extends CakeTestCase { $url = 'test_dispatch_pages/camelCased'; $controller = $Dispatcher->dispatch($url, array('return' => 1)); $this->assertEqual('TestDispatchPages', $controller->name); - + $url = 'test_dispatch_pages/camelCased/something. .'; $controller = $Dispatcher->dispatch($url, array('return' => 1)); $this->assertEqual($controller->params['pass'][0], 'something. .', 'Period was chopped off. %s'); - + } /** @@ -1395,7 +1402,7 @@ class DispatcherTest extends CakeTestCase { $expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2'); $this->assertIdentical($expected, $controller->passedArgs); - + $this->assertEqual($Dispatcher->base . '/pages/display/home/param:value/param2:value2', $Dispatcher->here); } @@ -1441,7 +1448,7 @@ class DispatcherTest extends CakeTestCase { Router::reload(); $Dispatcher =& new TestDispatcher(); Router::connect( - '/my_plugin/:controller/*', + '/my_plugin/:controller/*', array('plugin' => 'my_plugin', 'controller' => 'pages', 'action' => 'display') ); @@ -1598,7 +1605,7 @@ class DispatcherTest extends CakeTestCase { 'action' => 'admin_index', 'prefix' => 'admin', 'admin' => true, - 'form' => array(), + 'form' => array(), 'url' => array('url' => 'admin/articles_test'), 'return' => 1 ); @@ -1980,7 +1987,7 @@ class DispatcherTest extends CakeTestCase { } /** - * test that asset filters work for theme and plugin assets + * test that asset filters work for theme and plugin assets * * @return void */ @@ -2008,7 +2015,7 @@ class DispatcherTest extends CakeTestCase { $Dispatcher->stopped = false; $Dispatcher->asset('css/ccss/debug_kit.css'); $this->assertFalse($Dispatcher->stopped); - + $Dispatcher->stopped = false; $Dispatcher->asset('js/cjs/debug_kit.js'); $this->assertFalse($Dispatcher->stopped); From 0e29567f8d8110948140b6445be4812750181ba8 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 24 Nov 2010 22:09:08 -0500 Subject: [PATCH 09/23] Adding an array cast to fix issues where users could modify cookie values causing iteration errors. Fixes #1309 --- cake/libs/controller/components/cookie.php | 2 +- .../libs/controller/components/cookie.test.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/cookie.php b/cake/libs/controller/components/cookie.php index 67c3fe4db..38c5f00ae 100644 --- a/cake/libs/controller/components/cookie.php +++ b/cake/libs/controller/components/cookie.php @@ -411,7 +411,7 @@ class CookieComponent extends Object { $decrypted = array(); $type = $this->__type; - foreach ($values as $name => $value) { + foreach ((array)$values as $name => $value) { if (is_array($value)) { foreach ($value as $key => $val) { $pos = strpos($val, 'Q2FrZQ==.'); diff --git a/cake/tests/cases/libs/controller/components/cookie.test.php b/cake/tests/cases/libs/controller/components/cookie.test.php index 86e7789b5..5e3751f8b 100644 --- a/cake/tests/cases/libs/controller/components/cookie.test.php +++ b/cake/tests/cases/libs/controller/components/cookie.test.php @@ -437,6 +437,19 @@ class CookieComponentTest extends CakeTestCase { unset($_COOKIE['CakeTestCookie']); } + +/** + * test that no error is issued for non array data. + * + * @return void + */ + function testNoErrorOnNonArrayData() { + $this->Controller->Cookie->destroy(); + $_COOKIE['CakeTestCookie'] = 'kaboom'; + + $this->assertNull($this->Controller->Cookie->read('value')); + } + /** * encrypt method * From 4214242efd05645db042524aa85a953acda152f8 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 24 Nov 2010 22:44:12 -0500 Subject: [PATCH 10/23] Adding test for correct merge order for $uses. Fixing incorrect merge ordering for $uses, so it matches historical behaviour. Fixes #1309 --- cake/libs/controller/controller.php | 8 ++------ cake/tests/cases/libs/controller/controller.test.php | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cake/libs/controller/controller.php b/cake/libs/controller/controller.php index 4a9827975..799d93f62 100644 --- a/cake/libs/controller/controller.php +++ b/cake/libs/controller/controller.php @@ -437,9 +437,7 @@ class Controller extends Object { $this->{$var} = Set::merge($app, $normal); } } else { - $this->{$var} = Set::merge( - array_diff($appVars[$var], $this->{$var}), $this->{$var} - ); + $this->{$var} = array_merge($this->{$var}, array_diff($appVars[$var], $this->{$var})); } } } @@ -463,9 +461,7 @@ class Controller extends Object { $this->{$var} = Set::merge($app, $normal); } } else { - $this->{$var} = Set::merge( - array_diff($appVars[$var], $this->{$var}), $this->{$var} - ); + $this->{$var} = array_merge($this->{$var}, array_diff($appVars[$var], $this->{$var})); } } } diff --git a/cake/tests/cases/libs/controller/controller.test.php b/cake/tests/cases/libs/controller/controller.test.php index d73672814..55a9e53cd 100644 --- a/cake/tests/cases/libs/controller/controller.test.php +++ b/cake/tests/cases/libs/controller/controller.test.php @@ -1153,6 +1153,10 @@ class ControllerTest extends CakeTestCase { $this->assertEqual(count(array_diff($TestController->uses, $uses)), 0); $this->assertEqual(count(array_diff_assoc(Set::normalize($TestController->components), Set::normalize($components))), 0); + $expected = array('ControllerComment', 'ControllerAlias', 'ControllerPost'); + $this->assertEqual($expected, $TestController->uses, '$uses was merged incorrectly, AppController models should be last.'); + + $TestController =& new AnotherTestController(); $TestController->constructClasses(); From 7d158e8d1f8ea6a156e169c021b831740d8ca838 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 25 Nov 2010 06:39:08 -0500 Subject: [PATCH 11/23] Fixing Set::sort() for arrays with out of sequence, or missing keys. Tests added. Fixes #1312 --- cake/libs/set.php | 5 ++++- cake/tests/cases/libs/set.test.php | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cake/libs/set.php b/cake/libs/set.php index 69706e8c9..3d4e89f02 100644 --- a/cake/libs/set.php +++ b/cake/libs/set.php @@ -1092,6 +1092,10 @@ class Set { * @static */ function sort($data, $path, $dir) { + $originalKeys = array_keys($data); + if (is_numeric(implode('', $originalKeys))) { + $data = array_values($data); + } $result = Set::__flatten(Set::extract($data, $path)); list($keys, $values) = array(Set::extract($result, '{n}.id'), Set::extract($result, '{n}.value')); @@ -1103,7 +1107,6 @@ class Set { } array_multisort($values, $dir, $keys, $dir); $sorted = array(); - $keys = array_unique($keys); foreach ($keys as $k) { diff --git a/cake/tests/cases/libs/set.test.php b/cake/tests/cases/libs/set.test.php index ee75ba1a9..3018c351a 100644 --- a/cake/tests/cases/libs/set.test.php +++ b/cake/tests/cases/libs/set.test.php @@ -354,6 +354,33 @@ class SetTest extends CakeTestCase { $this->assertEqual($result, $expected); } +/** + * test sorting with out of order keys. + * + * @return void + */ + function testSortWithOutOfOrderKeys() { + $data = array( + 9 => array('class' => 510, 'test2' => 2), + 1 => array('class' => 500, 'test2' => 1), + 2 => array('class' => 600, 'test2' => 2), + 5 => array('class' => 625, 'test2' => 4), + 0 => array('class' => 605, 'test2' => 3), + ); + $expected = array( + array('class' => 500, 'test2' => 1), + array('class' => 510, 'test2' => 2), + array('class' => 600, 'test2' => 2), + array('class' => 605, 'test2' => 3), + array('class' => 625, 'test2' => 4), + ); + $result = Set::sort($data, '{n}.class', 'asc'); + $this->assertEqual($expected, $result); + + $result = Set::sort($data, '{n}.test2', 'asc'); + $this->assertEqual($expected, $result); + } + /** * testExtract method * From 15ca2400bc546a42cdea462f6609cc65e0db6a8f Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 25 Nov 2010 06:52:23 -0500 Subject: [PATCH 12/23] Fixing issue in DboSource::name() where special characters in column names would not be correctly quoted. Tests added. Fixes #1264 --- cake/libs/model/datasources/dbo_source.php | 2 +- cake/tests/cases/libs/model/datasources/dbo_source.test.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php index 3f82a09d9..42d7db354 100755 --- a/cake/libs/model/datasources/dbo_source.php +++ b/cake/libs/model/datasources/dbo_source.php @@ -542,7 +542,7 @@ class DboSource extends DataSource { return $return; } $data = trim($data); - if (preg_match('/^[\w-]+(\.[\w-]+)*$/', $data)) { // string, string.string + if (preg_match('/^[\w-]+(?:\.[^ \*]*)*$/', $data)) { // string, string.string if (strpos($data, '.') === false) { // string return $this->cacheMethod(__FUNCTION__, $cacheKey, $this->startQuote . $data . $this->endQuote); } diff --git a/cake/tests/cases/libs/model/datasources/dbo_source.test.php b/cake/tests/cases/libs/model/datasources/dbo_source.test.php index bcb11045d..d60898dab 100644 --- a/cake/tests/cases/libs/model/datasources/dbo_source.test.php +++ b/cake/tests/cases/libs/model/datasources/dbo_source.test.php @@ -4077,6 +4077,10 @@ class DboSourceTest extends CakeTestCase { $result = $this->testDb->name(array('my-name', 'Foo-Model.*')); $expected = array('`my-name`', '`Foo-Model`.*'); $this->assertEqual($result, $expected); + + $result = $this->testDb->name(array('Team.P%', 'Team.G/G')); + $expected = array('`Team`.`P%`', '`Team`.`G/G`'); + $this->assertEqual($result, $expected); } /** From 70ae66edf7a0dccae40b1e004d190cf0a1a73170 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sat, 27 Nov 2010 01:21:19 +0530 Subject: [PATCH 13/23] Fixing test cases for EmailComponent --- .../tests/cases/libs/controller/components/email.test.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/email.test.php b/cake/tests/cases/libs/controller/components/email.test.php index 8379e3e7b..3a0472877 100755 --- a/cake/tests/cases/libs/controller/components/email.test.php +++ b/cake/tests/cases/libs/controller/components/email.test.php @@ -693,7 +693,7 @@ TEXTBLOC; $this->assertPattern('/Subject: Cake Debug Test\n/', $result); $this->assertPattern('/Reply-To: noreply@example.com\n/', $result); $this->assertPattern('/From: noreply@example.com\n/', $result); - $this->assertPattern('/Date: ' . date(DATE_RFC2822) . '\n/', $result); + $this->assertPattern('/Date: ' . preg_quote(date(DATE_RFC2822)) . '\n/', $result); $this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result); $this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result); $this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result); @@ -721,7 +721,7 @@ TEXTBLOC; $this->assertPattern('/Subject: Cake Debug Test\n/', $result); $this->assertPattern('/Reply-To: noreply@example.com\n/', $result); $this->assertPattern('/From: noreply@example.com\n/', $result); - $this->assertPattern('/Date: ' . date(DATE_RFC2822) . '\n/', $result); + $this->assertPattern('/Date: ' . preg_quote(date(DATE_RFC2822)) . '\n/', $result); $this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result); $this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result); $this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result); @@ -1217,10 +1217,10 @@ HTMLBLOC; $result = $this->Controller->EmailTest->formatAddress('email@example.com', true); $this->assertEqual($result, ''); - + $result = $this->Controller->EmailTest->formatAddress('', true); $this->assertEqual($result, ''); - + $result = $this->Controller->EmailTest->formatAddress('alias name ', true); $this->assertEqual($result, ''); } From 1186bc56f194defe827dd0802ea17cc243b5b836 Mon Sep 17 00:00:00 2001 From: ADmad Date: Sat, 27 Nov 2010 02:11:43 +0530 Subject: [PATCH 14/23] Adding EmailCompnent::lineFeed property toallow overriding the default line feed string when using mail() function to send mail. Closes #1320 --- cake/libs/controller/components/email.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/cake/libs/controller/components/email.php b/cake/libs/controller/components/email.php index d27fd5c13..2aa96c3e0 100755 --- a/cake/libs/controller/components/email.php +++ b/cake/libs/controller/components/email.php @@ -98,7 +98,7 @@ class EmailComponent extends Object{ var $bcc = array(); /** - * The date to put in the Date: header. This should be a date + * The date to put in the Date: header. This should be a date * conformant with the RFC2822 standard. Leave null, to have * today's date generated. * @@ -157,6 +157,18 @@ class EmailComponent extends Object{ */ var $lineLength = 70; +/** + * Line feed character(s) to be used when sending using mail() function + * If null PHP_EOL is used. + * RFC2822 requires it to be CRLF but some Unix + * mail transfer agents replace LF by CRLF automatically + * (which leads to doubling CR if CRLF is used). + * + * @var string + * @access public + */ + var $lineFeed = null; + /** * @deprecated see lineLength */ @@ -804,8 +816,13 @@ class EmailComponent extends Object{ * @access private */ function _mail() { - $header = implode("\r\n", $this->__header); - $message = implode("\r\n", $this->__message); + if ($this->lineFeed === null) { + $lineFeed = PHP_EOL; + } else { + $lineFeed = $this->lineFeed; + } + $header = implode($lineFeed, $this->__header); + $message = implode($lineFeed, $this->__message); if (is_array($this->to)) { $to = implode(', ', array_map(array($this, '_formatAddress'), $this->to)); } else { From c55a57927c0ec3b20cae7515de1ebb972bfe3c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Markovi=C4=87?= Date: Tue, 30 Nov 2010 18:35:43 +0100 Subject: [PATCH 15/23] Typos in documentation: reguired -> required --- cake/libs/controller/components/cookie.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cake/libs/controller/components/cookie.php b/cake/libs/controller/components/cookie.php index 38c5f00ae..002395b7b 100644 --- a/cake/libs/controller/components/cookie.php +++ b/cake/libs/controller/components/cookie.php @@ -184,7 +184,7 @@ class CookieComponent extends Object { /** * Write a value to the $_COOKIE[$key]; * - * Optional [Name.], reguired key, optional $value, optional $encrypt, optional $expires + * Optional [Name.], required key, optional $value, optional $encrypt, optional $expires * $this->Cookie->write('[Name.]key, $value); * * By default all values are encrypted. @@ -205,7 +205,7 @@ class CookieComponent extends Object { } $this->__encrypted = $encrypt; $this->__expire($expires); - + if (!is_array($key)) { $key = array($key => $value); } @@ -214,7 +214,7 @@ class CookieComponent extends Object { if (strpos($name, '.') === false) { $this->__values[$name] = $value; $this->__write("[$name]", $value); - + } else { $names = explode('.', $name, 2); if (!isset($this->__values[$names[0]])) { @@ -230,7 +230,7 @@ class CookieComponent extends Object { /** * Read the value of the $_COOKIE[$key]; * - * Optional [Name.], reguired key + * Optional [Name.], required key * $this->Cookie->read(Name.key); * * @param mixed $key Key of the value to be obtained. If none specified, obtain map key => values @@ -245,7 +245,7 @@ class CookieComponent extends Object { if (is_null($key)) { return $this->__values; } - + if (strpos($key, '.') !== false) { $names = explode('.', $key, 2); $key = $names[0]; @@ -263,7 +263,7 @@ class CookieComponent extends Object { /** * Delete a cookie value * - * Optional [Name.], reguired key + * Optional [Name.], required key * $this->Cookie->read('Name.key); * * You must use this method before any output is sent to the browser. @@ -344,11 +344,11 @@ class CookieComponent extends Object { return $this->__expires; } $this->__reset = $this->__expires; - + if ($expires == 0) { return $this->__expires = 0; } - + if (is_integer($expires) || is_numeric($expires)) { return $this->__expires = $now + intval($expires); } From 96b30f0547dd9e85d768e05da83a2dd702b5812b Mon Sep 17 00:00:00 2001 From: dogmatic69 Date: Wed, 1 Dec 2010 17:07:45 +0000 Subject: [PATCH 16/23] adding tests for places that will leave a trailing 0 because of the way phps number_format() method works Signed-off-by: mark_story --- cake/tests/cases/libs/view/helpers/number.test.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cake/tests/cases/libs/view/helpers/number.test.php b/cake/tests/cases/libs/view/helpers/number.test.php index d5f88aa61..0e2a53f5b 100644 --- a/cake/tests/cases/libs/view/helpers/number.test.php +++ b/cake/tests/cases/libs/view/helpers/number.test.php @@ -321,6 +321,14 @@ class NumberHelperTest extends CakeTestCase { $expected = '£1,234,568'; $this->assertEqual($expected, $result); + $result = $this->Number->currency('1234567.8912345', null, array('before' => 'GBP', 'places' => 3)); + $expected = 'GBP1,234,567.891'; + $this->assertEqual($expected, $result); + + $result = $this->Number->currency('650.120001', null, array('before' => 'GBP', 'places' => 4)); + $expected = 'GBP650.1200'; + $this->assertEqual($expected, $result); + $result = $this->Number->currency($value, 'GBP', array('escape' => true)); $expected = '&#163;1,234,567.89'; $this->assertEqual($expected, $result); From 44b09171ef9d9910778c8b8adf7891b05a2c60b0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 1 Dec 2010 23:47:30 -0500 Subject: [PATCH 17/23] Adding unicode letters and numbers to url path segment regex. Test case added. Fixes #1284 --- cake/libs/validation.php | 4 ++-- cake/tests/cases/libs/validation.test.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cake/libs/validation.php b/cake/libs/validation.php index 51572e75e..71d178933 100644 --- a/cake/libs/validation.php +++ b/cake/libs/validation.php @@ -874,13 +874,13 @@ class Validation extends Object { $_this =& Validation::getInstance(); $_this->__populateIp(); $_this->check = $check; - $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z]|(%[0-9a-f]{2}))'; + $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~') . '\/0-9a-z\p{L}\p{N}]|(%[0-9a-f]{2}))'; $_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') . '(?:' . $_this->__pattern['IPv4'] . '|\[' . $_this->__pattern['IPv6'] . '\]|' . $_this->__pattern['hostname'] . ')' . '(?::[1-9][0-9]{0,4})?' . '(?:\/?|\/' . $validChars . '*)?' . '(?:\?' . $validChars . '*)?' . - '(?:#' . $validChars . '*)?$/i'; + '(?:#' . $validChars . '*)?$/iu'; return $_this->_check(); } diff --git a/cake/tests/cases/libs/validation.test.php b/cake/tests/cases/libs/validation.test.php index 5c51b1bad..3ac9d97a2 100644 --- a/cake/tests/cases/libs/validation.test.php +++ b/cake/tests/cases/libs/validation.test.php @@ -1879,6 +1879,7 @@ class ValidationTest extends CakeTestCase { $this->assertTrue(Validation::url('http://example.com/~userdir/subdir/index.html')); $this->assertTrue(Validation::url('http://www.zwischenraume.de')); $this->assertTrue(Validation::url('http://www.zwischenraume.cz')); + $this->assertTrue(Validation::url('http://www.last.fm/music/浜崎あゆみ'), 'utf8 path failed'); $this->assertTrue(Validation::url('http://cakephp.org:80')); $this->assertTrue(Validation::url('http://cakephp.org:443')); From 1dbed85979a46a5508b3386d595b8ffd827e0a9a Mon Sep 17 00:00:00 2001 From: Graham Weldon Date: Sat, 4 Dec 2010 22:14:33 +1100 Subject: [PATCH 18/23] Append Controller to error class name for isAuthorized() not implemented. --- cake/libs/controller/controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/controller/controller.php b/cake/libs/controller/controller.php index 799d93f62..0bfe924f0 100644 --- a/cake/libs/controller/controller.php +++ b/cake/libs/controller/controller.php @@ -800,7 +800,7 @@ class Controller extends Object { */ function isAuthorized() { trigger_error(sprintf( - __('%s::isAuthorized() is not defined.', true), $this->name + __('%sController::isAuthorized() is not defined.', true), $this->name ), E_USER_WARNING); return false; } From 7024d14c748a312af19ca2f8546db3248b844ec2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 5 Dec 2010 18:41:26 -0500 Subject: [PATCH 19/23] Removing private annotations for File::__construct() and File::__destruct() as its just not true. This also prevents them from being included in the API docs. --- cake/libs/file.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/cake/libs/file.php b/cake/libs/file.php index cc69c2122..86755a513 100644 --- a/cake/libs/file.php +++ b/cake/libs/file.php @@ -93,7 +93,6 @@ class File extends Object { * @param string $path Path to file * @param boolean $create Create file if it does not exist (if true) * @param integer $mode Mode to apply to the folder holding the file - * @access private */ function __construct($path, $create = false, $mode = 0755) { parent::__construct(); @@ -108,7 +107,6 @@ class File extends Object { /** * Closes the current file if it is opened * - * @access private */ function __destruct() { $this->close(); From 8681399fc29443281107a0788e65056f44e72598 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 6 Dec 2010 21:29:11 -0500 Subject: [PATCH 20/23] Forcing the plugin list to use a fresh directory listing in the web runner. Fixes #1338 --- cake/tests/lib/reporter/cake_html_reporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/tests/lib/reporter/cake_html_reporter.php b/cake/tests/lib/reporter/cake_html_reporter.php index 7074b844a..b1ef7ac9f 100755 --- a/cake/tests/lib/reporter/cake_html_reporter.php +++ b/cake/tests/lib/reporter/cake_html_reporter.php @@ -74,7 +74,7 @@ class CakeHtmlReporter extends CakeBaseReporter { function paintTestMenu() { $groups = $this->baseUrl() . '?show=groups'; $cases = $this->baseUrl() . '?show=cases'; - $plugins = App::objects('plugin'); + $plugins = App::objects('plugin', null, false); sort($plugins); include CAKE_TESTS_LIB . 'templates' . DS . 'menu.php'; } From 48f32a11e03b7af923aea601e17fb93f77df5cee Mon Sep 17 00:00:00 2001 From: jblotus Date: Wed, 8 Dec 2010 09:41:28 -0800 Subject: [PATCH 21/23] Fixed incorrect docblock. Fixes #1350 Signed-off-by: mark_story --- cake/libs/view/helpers/html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/view/helpers/html.php b/cake/libs/view/helpers/html.php index 3dedaccbc..dee7cc309 100644 --- a/cake/libs/view/helpers/html.php +++ b/cake/libs/view/helpers/html.php @@ -806,7 +806,7 @@ class HtmlHelper extends AppHelper { /** * Internal function to build a nested list (UL/OL) out of an associative array. * - * @param array $list Set of elements to list + * @param array $items Set of elements to list * @param array $options Additional HTML attributes of the list (ol/ul) tag * @param array $itemOptions Additional HTML attributes of the list item (LI) tag * @param string $tag Type of list tag to use (ol/ul) From a8306320717feb75f870b0c68eeeb5e708dae64f Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 9 Dec 2010 22:06:23 -0500 Subject: [PATCH 22/23] Changing View::element() to not overwrite viewVars with helpers that have the same name. Test added. Fixes #1354 --- cake/libs/view/view.php | 9 +++++++-- cake/tests/cases/libs/view/view.test.php | 18 ++++++++++++++++++ .../test_app/views/elements/type_check.ctp | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 cake/tests/test_app/views/elements/type_check.ctp diff --git a/cake/libs/view/view.php b/cake/libs/view/view.php index ad7600b1f..fd1999e9a 100644 --- a/cake/libs/view/view.php +++ b/cake/libs/view/view.php @@ -381,8 +381,13 @@ class View extends Object { } if (is_file($file)) { - $params = array_merge_recursive($params, $this->loaded); - $element = $this->_render($file, array_merge($this->viewVars, $params), $loadHelpers); + $vars = array_merge($this->viewVars, $params); + foreach ($this->loaded as $name => $helper) { + if (!isset($vars[$name])) { + $vars[$name] =& $this->loaded[$name]; + } + } + $element = $this->_render($file, $vars, $loadHelpers); if (isset($params['cache']) && isset($cacheFile) && isset($expires)) { cache('views' . DS . $cacheFile, $element, $expires); } diff --git a/cake/tests/cases/libs/view/view.test.php b/cake/tests/cases/libs/view/view.test.php index 4905cad20..8ffb0837a 100644 --- a/cake/tests/cases/libs/view/view.test.php +++ b/cake/tests/cases/libs/view/view.test.php @@ -487,6 +487,24 @@ class ViewTest extends CakeTestCase { $this->assertPattern('/non_existant_element/', $result); } +/** + * test that additional element viewVars don't get overwritten with helpers. + * + * @return void + */ + function testElementParamsDontOverwriteHelpers() { + $Controller = new ViewPostsController(); + $Controller->helpers = array('Form'); + + $View = new View($Controller); + $result = $View->element('type_check', array('form' => 'string'), true); + $this->assertEqual('string', $result); + + $View->set('form', 'string'); + $result = $View->element('type_check', array(), true); + $this->assertEqual('string', $result); + } + /** * testElementCacheHelperNoCache method * diff --git a/cake/tests/test_app/views/elements/type_check.ctp b/cake/tests/test_app/views/elements/type_check.ctp new file mode 100644 index 000000000..a863c2b48 --- /dev/null +++ b/cake/tests/test_app/views/elements/type_check.ctp @@ -0,0 +1 @@ + \ No newline at end of file From d7e62b88bc889921040dc256987e51a2241fd373 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 9 Dec 2010 22:34:20 -0500 Subject: [PATCH 23/23] Adding test cases for using helpers in nested elements from email templates. Closes #1355 --- .../libs/controller/components/email.test.php | 34 ++++++++++++------- .../elements/email/html/nested_element.ctp | 3 ++ .../test_app/views/elements/html_call.ctp | 3 ++ 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 cake/tests/test_app/views/elements/email/html/nested_element.ctp create mode 100644 cake/tests/test_app/views/elements/html_call.ctp diff --git a/cake/tests/cases/libs/controller/components/email.test.php b/cake/tests/cases/libs/controller/components/email.test.php index 3a0472877..ed80c1b2b 100755 --- a/cake/tests/cases/libs/controller/components/email.test.php +++ b/cake/tests/cases/libs/controller/components/email.test.php @@ -629,22 +629,30 @@ HTMLBLOC; $expect = '
' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . '
'; $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin')); $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + } - return; +/** + * test that elements used in email templates get helpers. + * + * @return void + */ + function testTemplateNestedElements() { + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; - $text = <<Controller->EmailTest->delivery = 'debug'; + $this->Controller->EmailTest->messageId = false; + $this->Controller->EmailTest->layout = 'default'; + $this->Controller->EmailTest->template = 'nested_element'; + $this->Controller->EmailTest->sendAs = 'html'; + $this->Controller->helpers = array('Html'); -This element has some text that is just too wide to comply with email -standards. -This is the body of the message - -This email was sent using the CakePHP Framework, http://cakephp.org. -TEXTBLOC; - - $this->Controller->EmailTest->sendAs = 'text'; - $expect = '
' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '
'; - $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'wide', 'default')); - $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + $this->Controller->EmailTest->send(); + $result = $this->Controller->Session->read('Message.email.message'); + $this->assertPattern('/Test/', $result); + $this->assertPattern('/http\:\/\/example\.com/', $result); } /** diff --git a/cake/tests/test_app/views/elements/email/html/nested_element.ctp b/cake/tests/test_app/views/elements/email/html/nested_element.ctp new file mode 100644 index 000000000..49858200e --- /dev/null +++ b/cake/tests/test_app/views/elements/email/html/nested_element.ctp @@ -0,0 +1,3 @@ +Before the element. +element('html_call'); ?> +After the element. \ No newline at end of file diff --git a/cake/tests/test_app/views/elements/html_call.ctp b/cake/tests/test_app/views/elements/html_call.ctp new file mode 100644 index 000000000..8c2c08978 --- /dev/null +++ b/cake/tests/test_app/views/elements/html_call.ctp @@ -0,0 +1,3 @@ +Html->link('Test', 'http://example.com'); +?> \ No newline at end of file