Browse Source

Merge remote-tracking branch 'origin/1.3' into merger

Conflicts:
	cake/console/cake.php
	cake/dispatcher.php
	cake/libs/controller/components/auth.php
	cake/libs/controller/controller.php
	cake/libs/view/helpers/xml.php
	cake/libs/view/pages/home.ctp
	cake/libs/xml.php
	cake/tests/cases/console/cake.test.php
	cake/tests/cases/libs/controller/components/cookie.test.php
	cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php
	cake/tests/cases/libs/model/datasources/dbo_source.test.php
	cake/tests/cases/libs/view/helpers/xml.test.php
	cake/tests/cases/libs/xml.test.php
	lib/Cake/Console/Command/SchemaShell.php
	lib/Cake/Controller/Component/CookieComponent.php
	lib/Cake/Network/Http/HttpSocket.php
	lib/Cake/TestSuite/Fixture/CakeTestFixture.php
	lib/Cake/VERSION.txt
	lib/Cake/View/Helper/FormHelper.php
	lib/Cake/View/Helper/TextHelper.php
	lib/Cake/View/Helper/TimeHelper.php
	lib/Cake/config/config.php
	lib/Cake/tests/Case/Cache/CacheTest.php
	lib/Cake/tests/Case/Model/CakeSchemaTest.php
	lib/Cake/tests/Case/TestSuite/CakeTestFixtureTest.php
Jose Lorenzo Rodriguez 15 years ago
parent
commit
d83555cc52

+ 3 - 9
README

@@ -18,17 +18,11 @@ http://bakery.cakephp.org
 The Show - live and archived podcasts about CakePHP and more
 http://live.cakephp.org
 
+CakePHP TV - screen casts from events and video tutorials
+http://tv.cakephp.org
+
 CakePHP Google Group - community mailing list and forum
 http://groups.google.com/group/cake-php
 
 #cakephp on irc.freenode.net - chat with CakePHP developers
 irc://irc.freenode.net/cakephp
-
-CakeForge - open development for CakePHP
-http://cakeforge.org
-
-CakePHP gear
-http://www.cafepress.com/cakefoundation
-
-Recommended Reading
-http://astore.amazon.com/cakesoftwaref-20/

+ 7 - 1
lib/Cake/Controller/Component/CookieComponent.php

@@ -282,8 +282,14 @@ class CookieComponent extends Component {
 			$this->read();
 		}
 		if (strpos($key, '.') === false) {
+			if (isset($this->_values[$key]) && is_array($this->_values[$key])) {
+				foreach ($this->_values[$key] as $idx => $val) {
+					$this->_delete("[$key][$idx]");
+				}
+			} else {
+				$this->_delete("[$key]");
+			}
 			unset($this->_values[$key]);
-			$this->_delete("[$key]");
 			return;
 		}
 		$names = explode('.', $key, 2);

+ 2 - 2
lib/Cake/Model/Behavior/TreeBehavior.php

@@ -426,7 +426,7 @@ class TreeBehavior extends ModelBehavior {
  *
  * @param AppModel $Model Model instance
  * @param mixed $id The ID of the record to move
- * @param mixed $number how many places to move the node or true to move to last position
+ * @param int|bool $number how many places to move the node or true to move to last position
  * @return boolean true on success, false on failure
  * @link http://book.cakephp.org/view/1352/moveDown
  */
@@ -484,7 +484,7 @@ class TreeBehavior extends ModelBehavior {
  *
  * @param AppModel $Model Model instance
  * @param mixed $id The ID of the record to move
- * @param mixed $number how many places to move the node, or true to move to first position
+ * @param int|bool $number how many places to move the node, or true to move to first position
  * @return boolean true on success, false on failure
  * @link http://book.cakephp.org/view/1353/moveUp
  */

+ 11 - 2
lib/Cake/Model/Datasource/Database/Mysql.php

@@ -390,12 +390,21 @@ class Mysql extends DboSource {
 		if (empty($conditions)) {
 			$alias = $joins = false;
 		}
-		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
+		$complexConditions = false;
+		foreach ((array)$conditions as $key => $value) {
+			if (strpos($key, $model->alias) === false) {
+				$complexConditions = true;
+				break;
+			}
+		}
+		if (!$complexConditions) {
+			$joins = false;
+		}
 
+		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
 		if ($conditions === false) {
 			return false;
 		}
-
 		if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
 			$model->onError();
 			return false;

+ 2 - 2
lib/Cake/Model/Datasource/DboSource.php

@@ -2650,7 +2650,7 @@ class DboSource extends DataSource {
 			}
 
 			if (strpos($key, '.')) {
-				$key = preg_replace_callback('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', array(&$this, '__quoteMatchedField'), $key);
+				$key = preg_replace_callback('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', array(&$this, '__quoteMatchedField'), $key);
 			}
 			if (!preg_match('/\s/', $key) && strpos($key, '.') === false) {
 				$key = $this->name($key);
@@ -3146,4 +3146,4 @@ class DboSource extends DataSource {
 		return false;
 	}
 
-}
+}

+ 8 - 2
lib/Cake/Network/CakeSocket.php

@@ -199,8 +199,14 @@ class CakeSocket {
 				return false;
 			}
 		}
-
-		return fwrite($this->connection, $data, strlen($data));
+		$totalBytes = strlen($data);
+		for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) {
+			$rv = fwrite($this->connection, substr($data, $written));
+			if ($rv === false || $rv === 0) {
+				return $written;
+			}
+		}
+		return $written;
 	}
 
 /**

+ 1 - 1
lib/Cake/Network/Http/HttpSocket.php

@@ -128,7 +128,7 @@ class HttpSocket extends CakeSocket {
  * You can use a url string to set the url and use default configurations for
  * all other options:
  *
- * `$http = new HttpSockect('http://cakephp.org/');`
+ * `$http = new HttpSocket('http://cakephp.org/');`
  *
  * Or use an array to configure multiple options:
  *

+ 9 - 2
lib/Cake/TestSuite/Fixture/CakeTestFixture.php

@@ -70,7 +70,7 @@ class CakeTestFixture {
 	public function init() {
 		if (isset($this->import) && (is_string($this->import) || is_array($this->import))) {
 			$import = array_merge(
-				array('connection' => 'default', 'records' => false), 
+				array('connection' => 'default', 'records' => false),
 				is_array($this->import) ? $this->import : array('model' => $this->import)
 			);
 
@@ -178,9 +178,15 @@ class CakeTestFixture {
 		if (!isset($this->_insert)) {
 			$values = array();
 			if (isset($this->records) && !empty($this->records)) {
+				$fields = array();
+				foreach($this->records as $record) {
+					$fields = array_merge($fields, array_keys(array_intersect_key($record, $this->fields)));
+				}
+				$fields = array_unique($fields);
+				$default = array_fill_keys($fields, null);
 				foreach ($this->records as $record) {
 					$fields = array_keys($record);
-					$values[] = array_values($record);
+					$values[] = array_values(array_merge($default, $record));
 				}
 				return $db->insertMulti($this->table, $fields, $values);
 			}
@@ -188,6 +194,7 @@ class CakeTestFixture {
 		}
 	}
 
+
 /**
  * Truncates the current fixture. Can be overwritten by classes extending CakeFixture to trigger other events before / after
  * truncate.

+ 7 - 6
lib/Cake/Utility/ClassRegistry.php

@@ -68,12 +68,13 @@ class ClassRegistry {
 	}
 
 /**
- * Loads a class, registers the object in the registry and returns instance of the object.
+ * Loads a class, registers the object in the registry and returns instance of the object. ClassRegistry::init()
+ * is used as a factory for models, and handle correct injecting of settings, that assist in testing.
  *
  * Examples
  * Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
  *
- * Exapanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass');```
+ * Exapanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');```
  *
  * Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);```
  *
@@ -81,14 +82,14 @@ class ClassRegistry {
  *  no instance of the object will be returned
  * {{{
  * array(
- *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
- *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
- *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass')
+ *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model'),
+ *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model'),
+ *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model')
  * );
  * }}}
  * @param mixed $class as a string or a single key => value array instance will be created,
  *  stored in the registry and returned.
- * @param string $type TypeOfClass
+ * @param string $type Only model is accepted as a valid value for $type.
  * @return object instance of ClassName
  */
 	public static function &init($class, $type = null) {

+ 7 - 7
lib/Cake/Utility/Set.php

@@ -421,6 +421,13 @@ class Set {
 						'key' => $key,
 						'item' => array_keys($context['item']),
 					);
+				} elseif (($key === $token || (ctype_digit($token) && $key == $token) || $token === '.')) {
+					$context['trace'][] = $key;
+					$matches[] = array(
+						'trace' => $context['trace'],
+						'key' => $key,
+						'item' => $context['item'],
+					);
 				} elseif (is_array($context['item']) && array_key_exists($token, $context['item'])) {
 					$items = $context['item'][$token];
 					if (!is_array($items)) {
@@ -460,13 +467,6 @@ class Set {
 							'item' => $item,
 						);
 					}
-				} elseif (($key === $token || (ctype_digit($token) && $key == $token) || $token === '.')) {
-					$context['trace'][] = $key;
-					$matches[] = array(
-						'trace' => $context['trace'],
-						'key' => $key,
-						'item' => $context['item'],
-					);
 				}
 			}
 			if ($conditions) {

+ 22 - 12
lib/Cake/View/Helper/FormHelper.php

@@ -695,7 +695,9 @@ class FormHelper extends AppHelper {
  * ### Options
  *
  * See each field type method for more information. Any options that are part of
- * $attributes or $options for the different **type** methods can be included in `$options` for input().
+ * $attributes or $options for the different **type** methods can be included in `$options` for input().i
+ * Additionally, any unknown keys that are not in the list below, or part of the selected type's options
+ * will be treated as a regular html attribute for the generated input.
  *
  * - `type` - Force the type of widget you want. e.g. `type => 'select'`
  * - `label` - Either a string label, or an array of options for the label. See FormHelper::label()
@@ -1498,14 +1500,16 @@ class FormHelper extends AppHelper {
 			'escape' => true,
 			'secure' => null,
 			'empty' => '',
-			'showParents' => false
+			'showParents' => false,
+			'hiddenField' => true
 		);
 
 		$escapeOptions = $this->_extractOption('escape', $attributes);
 		$secure = $this->_extractOption('secure', $attributes);
 		$showEmpty = $this->_extractOption('empty', $attributes);
 		$showParents = $this->_extractOption('showParents', $attributes);
-		unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents']);
+		$hiddenField = $this->_extractOption('hiddenField', $attributes);
+		unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
 
 		$attributes = $this->_initInputField($fieldName, array_merge(
 			(array)$attributes, array('secure' => false)
@@ -1520,19 +1524,25 @@ class FormHelper extends AppHelper {
 			unset($attributes['type']);
 		}
 
-		if (isset($attributes) && array_key_exists('multiple', $attributes)) {
+		if (!isset($selected)) {
+			$selected = $attributes['value'];
+		}
+
+		if (!empty($attributes['multiple'])) {
 			$style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
 			$template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
 			$tag = $template;
-			$hiddenAttributes = array(
-				'value' => '',
-				'id' => $attributes['id'] . ($style ? '' : '_'),
-				'secure' => false,
-				'name' => $attributes['name']
-			);
-			$select[] = $this->hidden(null, $hiddenAttributes);
+			if ($hiddenField) {
+				$hiddenAttributes = array(
+					'value' => '',
+					'id' => $attributes['id'] . ($style ? '' : '_'),
+					'secure' => false,
+					'name' => $attributes['name']
+				);
+				$select[] = $this->hidden(null, $hiddenAttributes);
+			}
 		} else {
-			$tag = 'selectstart';
+ 			$tag = 'selectstart';
 		}
 
 		if (!empty($tag) || isset($template)) {

+ 2 - 2
lib/Cake/View/Helper/TextHelper.php

@@ -179,10 +179,10 @@ class TextHelper extends AppHelper {
 		$this->_linkOptions = $options;
 		$atom = '[a-z0-9!#$%&\'*+\/=?^_`{|}~-]';
 		return preg_replace_callback(
-			'/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)*)/i',
+			'/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+)/i',
 			array(&$this, '_linkEmails'),
 			$text
-		);
+		);	
 	}
 
 /**

+ 16 - 16
lib/Cake/View/Helper/TimeHelper.php

@@ -618,33 +618,33 @@ class TimeHelper extends AppHelper {
 		} else {
 			if ($years > 0) {
 				// years and months and days
-				$relativeDate .= ($relativeDate ? ', ' : '') . $years . ' ' . __dn('cake', 'year', 'years', $years);
-				$relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . $months . ' ' . __dn('cake', 'month', 'months', $months) : '';
-				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __dn('cake', 'week', 'weeks', $weeks) : '';
-				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __dn('cake', 'day', 'days', $days) : '';
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d year', '%d years', $years, $years);
+				$relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months) : '';
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
 			} elseif (abs($months) > 0) {
 				// months, weeks and days
-				$relativeDate .= ($relativeDate ? ', ' : '') . $months . ' ' . __dn('cake', 'month', 'months', $months);
-				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __dn('cake', 'week', 'weeks', $weeks) : '';
-				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __dn('cake', 'day', 'days', $days) : '';
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months);
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
 			} elseif (abs($weeks) > 0) {
 				// weeks and days
-				$relativeDate .= ($relativeDate ? ', ' : '') . $weeks . ' ' . __dn('cake', 'week', 'weeks', $weeks);
-				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __dn('cake', 'day', 'days', $days) : '';
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks);
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
 			} elseif (abs($days) > 0) {
 				// days and hours
-				$relativeDate .= ($relativeDate ? ', ' : '') . $days . ' ' . __dn('cake', 'day', 'days', $days);
-				$relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . $hours . ' ' . __dn('cake', 'hour', 'hours', $hours) : '';
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days);
+				$relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours) : '';
 			} elseif (abs($hours) > 0) {
 				// hours and minutes
-				$relativeDate .= ($relativeDate ? ', ' : '') . $hours . ' ' . __dn('cake', 'hour', 'hours', $hours);
-				$relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . $minutes . ' ' . __dn('cake', 'minute', 'minutes', $minutes) : '';
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours);
+				$relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes) : '';
 			} elseif (abs($minutes) > 0) {
 				// minutes only
-				$relativeDate .= ($relativeDate ? ', ' : '') . $minutes . ' ' . __dn('cake', 'minute', 'minutes', $minutes);
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes);
 			} else {
 				// seconds only
-				$relativeDate .= ($relativeDate ? ', ' : '') . $seconds . ' ' . __dn('cake', 'second', 'seconds', $seconds);
+				$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d second', '%d seconds', $seconds, $seconds);
 			}
 
 			if (!$backwards) {
@@ -752,4 +752,4 @@ class TimeHelper extends AppHelper {
 		$format = $this->convertSpecifiers($format, $date);
 		return strftime($format, $date);
 	}
-}
+}

+ 2 - 1
lib/Cake/config/config.php

@@ -16,4 +16,5 @@
  * @since         CakePHP(tm) v 1.1.11.4062
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-return $config['Cake.version'] = '2.0.0-dev';
+
+return $config['Cake.version'] = '2.0.0-dev';

+ 1 - 1
lib/Cake/tests/Case/Cache/CacheTest.php

@@ -235,7 +235,7 @@ class CacheTest extends CakeTestCase {
 	function testInitSettings() {
 		$initial = Cache::settings();
 		$override = array('engine' => 'File', 'path' => TMP . 'tests');
-		Cache::config('default', $override);
+		Cache::config('for_test', $override);
 
 		$settings = Cache::settings();
 		$expecting = $override + $initial;

+ 17 - 1
lib/Cake/tests/Case/Controller/Component/CookieComponentTest.php

@@ -496,7 +496,23 @@ class CookieComponentTest extends CakeTestCase {
 
 		$this->assertNull($this->Cookie->read('value'));
 	}
-	
+
+/**
+ * test that deleting a top level keys kills the child elements too.
+ *
+ * @return void
+ */
+	function testDeleteRemovesChildren() {
+		$_COOKIE['CakeTestCookie'] = array(
+			'User' => array('email' => 'example@example.com', 'name' => 'mark'),
+			'other' => 'value'
+		);
+		$this->assertEqual('mark', $this->Cookie->read('User.name'));
+
+		$this->Cookie->delete('User');
+		$this->assertNull($this->Cookie->read('User.email'));
+		$this->Cookie->destroy();
+	}
 /**
  * Helper method for generating old style encoded cookie values.
  *

+ 1 - 1
lib/Cake/tests/Case/Model/CakeSchemaTest.php

@@ -209,7 +209,7 @@ class TestAppSchema extends CakeSchema {
  */
 	public $datatypes = array(
 		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
-		'float_field' => array('type' => 'float', 'null' => false, 'length' => '5,2', 'default' => '', 'collate' => null, 'comment' => null),
+		'float_field' => array('type' => 'float', 'null' => false, 'length' => '5,2', 'default' => ''),
 		'bool' => array('type' => 'boolean', 'null' => false, 'default' => false),
 		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
 		'tableParameters' => array()

+ 16 - 4
lib/Cake/tests/Case/Model/ModelWriteTest.php

@@ -3824,12 +3824,24 @@ class ModelWriteTest extends BaseModelTest {
 	}
 
 /**
- * testProductUpdateAllWithForeignKey
+ * test updateAll with empty values.
+ *
+ * @return void
+ */
+	function testUpdateAllEmptyValues() {
+		$this->loadFixtures('Author', 'Post');
+		$model = new Author();
+		$result = $model->updateAll(array('user' => '""'));
+		$this->assertTrue($result);
+	}
+
+/**
+ * testUpdateAllWithJoins
  *
  * @access public
  * @return void
  */
-	function testProductUpdateAll() {
+	function testUpdateAllWithJoins() {
 		$this->skipIf(
 			!$this->db instanceof Mysql,
 			'%s Currently, there is no way of doing joins in an update statement in postgresql or sqlite'
@@ -3874,12 +3886,12 @@ class ModelWriteTest extends BaseModelTest {
 	}
 
 /**
- * testProductUpdateAllWithoutForeignKey
+ * testUpdateAllWithoutForeignKey
  *
  * @access public
  * @return void
  */
-    function testProductUpdateAllWithoutForeignKey() {
+    function testUpdateAllWithoutForeignKey() {
 		$this->skipIf(
 			!$this->db instanceof Mysql,
 			'%s Currently, there is no way of doing joins in an update statement in postgresql'

+ 103 - 4
lib/Cake/tests/Case/TestSuite/CakeTestFixtureTest.php

@@ -65,6 +65,53 @@ class CakeTestFixtureTestFixture extends CakeTestFixture {
 }
 
 /**
+ * StringFieldsTestFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class StringsTestFixture extends CakeTestFixture {
+
+/**
+ * name Property
+ *
+ * @var string
+ */
+	var $name = 'Strings';
+
+/**
+ * table property
+ *
+ * @var string
+ */
+	var $table = 'strings';
+
+/**
+ * Fields array
+ *
+ * @var array
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer',  'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => '255'),
+		'email' => array('type' => 'string', 'length' => '255'),
+        'age' => array('type' => 'integer', 'default' => 10)
+	);
+
+/**
+ * Records property
+ *
+ * @var array
+ */
+	var $records = array(
+		array('name' => 'Mark Doe', 'email' => 'mark.doe@email.com'),
+        array('name' => 'John Doe', 'email' => 'john.doe@email.com', 'age' => 20),
+        array('email' => 'jane.doe@email.com', 'name' => 'Jane Doe', 'age' => 30)
+	);
+}
+
+
+/**
  * CakeTestFixtureImportFixture class
  *
  * @package       cake.tests.cases.libs
@@ -120,6 +167,7 @@ class FixturePrefixTest extends Model {
 	public $useDbConfig = 'test';
 }
 
+
 /**
  * Test case for CakeTestFixture
  *
@@ -203,7 +251,7 @@ class CakeTestFixtureTest extends CakeTestCase {
 		$Fixture->init();
 		$this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created'));
 		$this->assertEqual($Fixture->table, 'fixture_tests');
-		
+
 		$keys = array_flip(ClassRegistry::keys());
 		$this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys));
 
@@ -302,7 +350,7 @@ class CakeTestFixtureTest extends CakeTestCase {
 	}
 
 /**
- * test that importing with records works.  Make sure to try with postgres as its 
+ * test that importing with records works.  Make sure to try with postgres as its
  * handling of aliases is a workaround at best.
  *
  * @return void
@@ -334,7 +382,7 @@ class CakeTestFixtureTest extends CakeTestCase {
 
 		$defaultDb->config = $defaultConfig;
 
-		$Source->drop($newTestSuiteDb);	
+		$Source->drop($newTestSuiteDb);
 	}
 
 /**
@@ -364,11 +412,62 @@ class CakeTestFixtureTest extends CakeTestCase {
  */
 	function testInsert() {
 		$Fixture = new CakeTestFixtureTestFixture();
-		$this->criticDb->expects($this->atLeastOnce())->method('insertMulti')->will($this->returnValue(true));
+		$this->criticDb->expects($this->atLeastOnce())
+			->method('insertMulti')
+			->will($this->returnCallback(array($this, '_insertCallback')));
+
+		$return = $Fixture->insert($this->criticDb);
+		$this->assertTrue(!empty($this->insertMulti));
+		$this->assertTrue($this->criticDb->fullDebug);
+		$this->assertTrue($return);
+		$this->assertEqual('fixture_tests', $this->insertMulti['table']);
+		$this->assertEqual(array('name', 'created'), $this->insertMulti['fields']);
+		$expected = array(
+			array('Gandalf', '2009-04-28 19:20:00'),
+			array('Captain Picard', '2009-04-28 19:20:00'),
+			array('Chewbacca', '2009-04-28 19:20:00')
+		);
+		$this->assertEqual($expected, $this->insertMulti['values']);
+	}
+
+/**
+ * Helper function to be used as callback and store the parameters of an insertMulti call
+ *
+ * @param string $table 
+ * @param string $fields 
+ * @param string $values 
+ * @return boolean true
+ */
+	function _insertCallback($table, $fields, $values) {
+		$this->insertMulti['table'] = $table;
+		$this->insertMulti['fields'] = $fields;
+		$this->insertMulti['values'] = $values;
+		return true;
+	}
+
+/**
+ * test the insert method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsertStrings() {
+		$Fixture = new StringsTestFixture();
+		$this->criticDb->expects($this->atLeastOnce())
+			->method('insertMulti')
+			->will($this->returnCallback(array($this, '_insertCallback')));
 
 		$return = $Fixture->insert($this->criticDb);
 		$this->assertTrue($this->criticDb->fullDebug);
 		$this->assertTrue($return);
+		$this->assertEqual('strings', $this->insertMulti['table']);
+		$this->assertEqual(array('email', 'name', 'age'), $this->insertMulti['fields']);
+		$expected = array(
+			array('Mark Doe', 'mark.doe@email.com', null),
+			array('John Doe', 'john.doe@email.com', 20),
+			array('Jane Doe', 'jane.doe@email.com', 30),
+		);
+		$this->assertEqual($expected, $this->insertMulti['values']);
 	}
 
 /**

+ 34 - 0
lib/Cake/tests/Case/Utility/SetTest.php

@@ -957,6 +957,40 @@ class SetTest extends CakeTestCase {
 		$result = Set::extract('/ParentNode/name', $hasMany);
 		$expected = array('Second');
 		$this->assertEqual($result, $expected);
+		
+		$data = array(
+			array(
+				'Category' => array(
+					'id' => 1,
+					'name' => 'First'
+				),
+				0 => array(
+					'value' => 50
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => 2,
+					'name' => 'Second'
+				),
+				0 => array(
+					'value' => 60
+				)
+			)
+		);
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => 1,
+					'name' => 'First'
+				),
+				0 => array(
+					'value' => 50
+				)
+			)
+		);
+		$result = Set::extract('/Category[id=1]/..', $data);
+		$this->assertEqual($result, $expected);
 	}
 
 /**

+ 75 - 0
lib/Cake/tests/Case/View/Helper/FormHelperTest.php

@@ -3451,6 +3451,26 @@ class FormHelperTest extends CakeTestCase {
 			'/select'
 		);
 		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field', $options, array('multiple' => false, 'value' => array(0, 1))
+		);
+		$expected = array(
+			'select' => array(
+				'name' => 'data[Model][multi_field]', 'id' => 'ModelMultiField'
+			),
+			array('option' => array('value' => '0', 'selected' => 'selected')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1', 'selected' => 'selected')),
+			'second',
+			'/option',
+			array('option' => array('value' => '2')),
+			'third',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
 	}
 
 /**
@@ -3844,6 +3864,61 @@ class FormHelperTest extends CakeTestCase {
 		);
 		$this->assertTags($result, $expected);
 	}
+/**
+ * testSelectHiddenFieldOmission method
+ *
+ * test that select() with 'hiddenField' => false omits the hidden field
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectHiddenFieldOmission() {
+		$result = $this->Form->select('Model.multi_field',
+			array('first', 'second'),
+			array('multiple' => 'checkbox', 'hiddenField' => false, 'value' => null)
+		);
+		$expected = array(
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('first', 'second'),
+			'multiple' => 'checkbox',
+			'hiddenField' => false
+		));
+		$expected = array(
+			array('div' => array('class' => 'input select')),
+			array('label' => array('for' => 'ModelMultiField')),
+			'Multi Field',
+			'/label',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
 
 /**
  * test that select() with multiple = checkbox works with overriding name attribute.

+ 11 - 0
lib/Cake/tests/Case/View/Helper/TextHelperTest.php

@@ -315,6 +315,17 @@ class TextHelperTest extends CakeTestCase {
 	}
 
 /**
+ * test invalid email addresses.
+ *
+ * @return void
+ */
+	function testAutoLinkEmailInvalid() {
+		$result = $this->Text->autoLinkEmails('this is a myaddress@gmx-de test');
+		$expected = 'this is a myaddress@gmx-de test';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
  * testHighlightCaseInsensitivity method
  *
  * @access public