Browse Source

Merge branch 'master' into 2.3

Conflicts:
	lib/Cake/View/Helper/FormHelper.php
mark_story 13 years ago
parent
commit
c24bec679b

+ 5 - 1
lib/Cake/Controller/CakeErrorController.php

@@ -51,7 +51,11 @@ class CakeErrorController extends AppController {
  */
 	public function __construct($request = null, $response = null) {
 		parent::__construct($request, $response);
-		if (count(Router::extensions())) {
+		if (
+			count(Router::extensions()) &&
+			!array_key_exists('RequestHandler', $this->components) &&
+			!in_array('RequestHandler', $this->components, true)
+		) {
 			$this->components[] = 'RequestHandler';
 		}
 		$this->constructClasses();

+ 9 - 6
lib/Cake/Model/Model.php

@@ -2623,9 +2623,12 @@ class Model extends Object implements CakeEventListener {
  *
  *  Note: find(list) + database views have issues with MySQL 5.0. Try upgrading to MySQL 5.1 if you
  *  have issues with database views.
+ *
+ *  Note: find(count) has its own return values.
+ *
  * @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
  * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
- * @return array Array of records
+ * @return array Array of records, or Null on failure.
  * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall
  */
 	public function find($type = 'first', $query = array()) {
@@ -2648,10 +2651,10 @@ class Model extends Object implements CakeEventListener {
 
 		if ($type === 'all') {
 			return $results;
-		} else {
-			if ($this->findMethods[$type] === true) {
-				return $this->{'_find' . ucfirst($type)}('after', $query, $results);
-			}
+		}
+
+		if ($this->findMethods[$type] === true) {
+			return $this->{'_find' . ucfirst($type)}('after', $query, $results);
 		}
 	}
 
@@ -2717,7 +2720,7 @@ class Model extends Object implements CakeEventListener {
 			return $query;
 		} elseif ($state === 'after') {
 			if (empty($results[0])) {
-				return false;
+				return array();
 			}
 			return $results[0];
 		}

+ 2 - 3
lib/Cake/Network/CakeRequest.php

@@ -15,8 +15,7 @@
  * @since         CakePHP(tm) v 2.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-
-App::uses('Set', 'Utility');
+App::uses('Hash', 'Utility');
 
 /**
  * A class that helps wrap Request information and particulars about a single request.
@@ -353,7 +352,7 @@ class CakeRequest implements ArrayAccess {
 				$this->_processFileData($newPath, $fields, $field);
 			} else {
 				$newPath .= '.' . $field;
-				$this->data = Set::insert($this->data, $newPath, $fields);
+				$this->data = Hash::insert($this->data, $newPath, $fields);
 			}
 		}
 	}

+ 1 - 1
lib/Cake/Test/Case/BasicsTest.php

@@ -67,7 +67,7 @@ class BasicsTest extends CakeTestCase {
 		$one = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true);
 		$two = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true);
 		$result = array_diff_key($one, $two);
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**

+ 1 - 2
lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php

@@ -229,12 +229,11 @@ class MemcacheEngineTest extends CakeTestCase {
 		$result = Cache::write('other_test', $data, 'memcache');
 		$this->assertTrue($result);
 
-		sleep(2);
+		sleep(3);
 		$result = Cache::read('other_test', 'memcache');
 		$this->assertFalse($result);
 
 		Cache::config('memcache', array('duration' => '+1 second'));
-		sleep(2);
 
 		$result = Cache::read('other_test', 'memcache');
 		$this->assertFalse($result);

+ 1 - 1
lib/Cake/Test/Case/Console/Command/AclShellTest.php

@@ -177,7 +177,7 @@ class AclShellTest extends CakeTestCase {
 
 		$Aro = ClassRegistry::init('Aro');
 		$result = $Aro->findById(3);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 	}
 
 /**

+ 2 - 2
lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php

@@ -181,7 +181,7 @@ class ControllerTaskTest extends CakeTestCase {
 	public function testDoHelpersNo() {
 		$this->Task->expects($this->any())->method('in')->will($this->returnValue('n'));
 		$result = $this->Task->doHelpers();
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**
@@ -218,7 +218,7 @@ class ControllerTaskTest extends CakeTestCase {
 	public function testDoComponentsNo() {
 		$this->Task->expects($this->any())->method('in')->will($this->returnValue('n'));
 		$result = $this->Task->doComponents();
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**

+ 3 - 3
lib/Cake/Test/Case/Core/AppTest.php

@@ -347,7 +347,7 @@ class AppTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 
 		$result = App::objects('NonExistingType');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		App::build(array(
 			'plugins' => array(
@@ -414,9 +414,9 @@ class AppTest extends CakeTestCase {
 		$this->assertTrue(in_array('OtherComponent', $result));
 
 		$result = App::objects('TestPluginTwo.behavior');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 		$result = App::objects('TestPluginTwo.Model/Behavior');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		$result = App::objects('model', null, false);
 		$this->assertTrue(in_array('Comment', $result));

+ 1 - 1
lib/Cake/Test/Case/Log/CakeLogTest.php

@@ -175,7 +175,7 @@ class CakeLogTest extends CakeTestCase {
 
 		CakeLog::drop('file');
 		$result = CakeLog::configured();
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**

+ 3 - 3
lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php

@@ -422,7 +422,7 @@ class TranslateBehaviorTest extends CakeTestCase {
 		$TestModel = new TranslatedItem();
 		$TestModel->locale = 'rus';
 		$result = $TestModel->read(null, 1);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$TestModel->locale = array('rus');
 		$result = $TestModel->read(null, 1);
@@ -460,10 +460,10 @@ class TranslateBehaviorTest extends CakeTestCase {
 			Configure::write('debug', 0);
 
 			$result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => false));
-			$this->assertEquals(array(), $result);
+			$this->assertSame(array(), $result);
 
 			$result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => 'after'));
-			$this->assertEquals(array(), $result);
+			$this->assertSame(array(), $result);
 			Configure::write('debug', $debug);
 		}
 

+ 1 - 1
lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php

@@ -582,7 +582,7 @@ class SqlserverTest extends CakeTestCase {
 
 		$indexes = array('client_id' => array('column' => 'client_id'));
 		$result = $this->db->buildIndex($indexes, 'items');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		$indexes = array('client_id' => array('column' => array('client_id', 'period_id'), 'unique' => 1));
 		$result = $this->db->buildIndex($indexes, 'items');

+ 8 - 8
lib/Cake/Test/Case/Model/ModelDeleteTest.php

@@ -107,12 +107,12 @@ class ModelDeleteTest extends BaseModelTest {
 		$result = $Portfolio->find('first', array(
 			'conditions' => array('Portfolio.id' => 1)
 		));
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$result = $Portfolio->ItemsPortfolio->find('all', array(
 			'conditions' => array('ItemsPortfolio.portfolio_id' => 1)
 		));
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**
@@ -195,7 +195,7 @@ class ModelDeleteTest extends BaseModelTest {
 		$this->assertTrue($result);
 
 		$result = $TestModel->read(null, 2);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$TestModel->recursive = -1;
 		$result = $TestModel->find('all', array(
@@ -216,7 +216,7 @@ class ModelDeleteTest extends BaseModelTest {
 		$this->assertTrue($result);
 
 		$result = $TestModel->read(null, 3);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$TestModel->recursive = -1;
 		$result = $TestModel->find('all', array(
@@ -448,16 +448,16 @@ class ModelDeleteTest extends BaseModelTest {
 
 		$TestModel->recursive = 2;
 		$result = $TestModel->read(null, 2);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$result = $TestModel->Comment->read(null, 5);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$result = $TestModel->Comment->read(null, 6);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$result = $TestModel->Comment->Attachment->read(null, 1);
-		$this->assertFalse($result);
+		$this->assertSame(array(), $result);
 
 		$result = $TestModel->find('count');
 		$this->assertEquals(2, $result);

+ 1 - 1
lib/Cake/Test/Case/Model/ModelReadTest.php

@@ -4290,7 +4290,7 @@ class ModelReadTest extends BaseModelTest {
 
 		$TestModel->resetAssociations();
 		$result = $TestModel->hasMany;
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		$result = $TestModel->bindModel(array('hasMany' => array('Comment')), false);
 		$this->assertTrue($result);

+ 2 - 2
lib/Cake/Test/Case/Model/ModelWriteTest.php

@@ -4536,7 +4536,7 @@ class ModelWriteTest extends BaseModelTest {
 		$this->assertFalse($result);
 
 		$result = $model->find('all');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 		$expected = array('Comment' => array(
 			1 => array('comment' => array('This field cannot be left blank'))
 		));
@@ -5940,7 +5940,7 @@ class ModelWriteTest extends BaseModelTest {
 		$this->assertFalse($result);
 
 		$result = $model->find('all');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 		$expected = array('Comment' => array(
 			1 => array('comment' => array('This field cannot be left blank'))
 		));

+ 3 - 3
lib/Cake/Test/Case/Routing/RouterTest.php

@@ -111,7 +111,7 @@ class RouterTest extends CakeTestCase {
 
 		$_SERVER['REQUEST_METHOD'] = 'GET';
 		$result = Router::parse('/posts/add');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		Router::reload();
 		$resources = Router::mapResources('Posts', array('id' => '[a-z0-9_]+'));
@@ -1907,7 +1907,7 @@ class RouterTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 
 		$result = Router::parse('/blog/foobar');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		$result = Router::url(array('controller' => 'blog_posts', 'action' => 'foo'));
 		$this->assertEquals('/blog_posts/foo', $result);
@@ -2117,7 +2117,7 @@ class RouterTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 
 		$result = Router::parse('/badness/test/test_action');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		Router::reload();
 		Router::connect('/:locale/:controller/:action/*', array(), array('locale' => 'dan|eng'));

+ 13 - 12
lib/Cake/Test/Case/Utility/CakeTimeTest.php

@@ -474,7 +474,7 @@ class CakeTimeTest extends CakeTestCase {
 
 		date_default_timezone_set('UTC');
 
-		$serverTime = new DateTime('now');
+		$serverTime = new DateTime('2012-12-11 14:15:20');
 
 		$timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu');
 		foreach ($timezones as $timezone) {
@@ -509,17 +509,18 @@ class CakeTimeTest extends CakeTestCase {
  * @return void
  */
 	public function testToRss() {
-		$this->assertEquals(date('r'), $this->Time->toRss(time()));
-
-		if (!$this->skipIf(!class_exists('DateTimeZone'), '%s DateTimeZone class not available.')) {
-			$timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu');
-			foreach ($timezones as $timezone) {
-				$yourTimezone = new DateTimeZone($timezone);
-				$yourTime = new DateTime('now', $yourTimezone);
-				$userOffset = $yourTimezone->getOffset($yourTime) / HOUR;
-				$this->assertEquals($yourTime->format('r'), $this->Time->toRss(time(), $userOffset));
-				$this->assertEquals($yourTime->format('r'), $this->Time->toRss(time(), $timezone));
-			}
+		$date = '2012-08-12 12:12:45';
+		$time = strtotime($date);
+		$this->assertEquals(date('r', $time), $this->Time->toRss($time));
+
+		$timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu');
+		foreach ($timezones as $timezone) {
+			$yourTimezone = new DateTimeZone($timezone);
+			$yourTime = new DateTime($date, $yourTimezone);
+			$userOffset = $yourTimezone->getOffset($yourTime) / HOUR;
+			$time = $yourTime->getTimestamp();
+			$this->assertEquals($yourTime->format('r'), $this->Time->toRss($time, $userOffset), "Failed on $timezone");
+			$this->assertEquals($yourTime->format('r'), $this->Time->toRss($time, $timezone), "Failed on $timezone");
 		}
 	}
 

+ 1 - 1
lib/Cake/Test/Case/Utility/SetTest.php

@@ -1967,7 +1967,7 @@ class SetTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 
 		$result = Set::combine($a, 'fail', 'fail');
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 	}
 
 /**

+ 58 - 0
lib/Cake/Test/Case/View/Helper/FormHelperTest.php

@@ -119,6 +119,11 @@ class Contact extends CakeTestModel {
 			'between' => array('rule' => array('between', 5, 30), 'allowEmpty' => true),
 		),
 		'imnotrequiredeither' => array('required' => true, 'rule' => array('between', 5, 30), 'allowEmpty' => true),
+		'iamrequiredalways' => array(
+			'email' => array('rule' => 'email'),
+			'rule_on_create' => array('rule' => array('maxLength', 50), 'on' => 'create'),
+			'rule_on_update' => array('rule' => array('between', 1, 50), 'on' => 'update'),
+		),
 	);
 
 /**
@@ -2256,6 +2261,15 @@ class FormHelperTest extends CakeTestCase {
 		$result = $this->Form->input('Model.start_time', array(
 			'type' => 'time',
 			'interval' => 15,
+			'selected' => array('hour' => '3', 'min' => '57', 'meridian' => 'pm')
+		));
+		$this->assertContains('<option value="04" selected="selected">4</option>', $result);
+		$this->assertContains('<option value="00" selected="selected">00</option>', $result);
+		$this->assertContains('<option value="pm" selected="selected">pm</option>', $result);
+
+		$result = $this->Form->input('Model.start_time', array(
+			'type' => 'time',
+			'interval' => 15,
 			'selected' => '2012-10-23 15:57:00'
 		));
 		$this->assertContains('<option value="04" selected="selected">4</option>', $result);
@@ -2714,6 +2728,36 @@ class FormHelperTest extends CakeTestCase {
 	}
 
 /**
+ * Test that magic input() selects are created for type=number
+ *
+ * @return void
+ */
+	public function testInputMagicSelectForTypeNumber() {
+		$this->View->viewVars['balances'] = array(0 => 'nothing', 1 => 'some', 100 => 'a lot');
+		$this->Form->request->data = array('ValidateUser' => array('balance' => 1));
+		$result = $this->Form->input('ValidateUser.balance');
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ValidateUserBalance'),
+			'Balance',
+			'/label',
+			'select' => array('name' => 'data[ValidateUser][balance]', 'id' => 'ValidateUserBalance'),
+			array('option' => array('value' => '0')),
+			'nothing',
+			'/option',
+			array('option' => array('value' => '1', 'selected' => 'selected')),
+			'some',
+			'/option',
+			array('option' => array('value' => '100')),
+			'a lot',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
  * Test that magic input() selects can easily be converted into radio types without error.
  *
  * @return void
@@ -7131,6 +7175,20 @@ class FormHelperTest extends CakeTestCase {
 			'/div'
 		);
 		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.iamrequiredalways');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactIamrequiredalways'),
+			'Iamrequiredalways',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][iamrequiredalways]',
+				'id' => 'ContactIamrequiredalways'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
 	}
 
 /**

+ 1 - 1
lib/Cake/Test/Case/View/Helper/JsHelperTest.php

@@ -904,7 +904,7 @@ class JsBaseEngineTest extends CakeTestCase {
 	public function testOptionMapping() {
 		$JsEngine = new OptionEngineHelper($this->View);
 		$result = $JsEngine->testMap();
-		$this->assertEquals(array(), $result);
+		$this->assertSame(array(), $result);
 
 		$result = $JsEngine->testMap(array('foo' => 'bar', 'baz' => 'sho'));
 		$this->assertEquals(array('foo' => 'bar', 'baz' => 'sho'), $result);

+ 115 - 94
lib/Cake/View/Helper/FormHelper.php

@@ -249,11 +249,11 @@ class FormHelper extends AppHelper {
 		}
 		foreach ($validationRules as $rule) {
 			$rule->isUpdate($this->requestType === 'put');
-			if ($rule->isEmptyAllowed()) {
-				return false;
+			if (!$rule->isEmptyAllowed()) {
+				return true;
 			}
 		}
-		return true;
+		return false;
 	}
 
 /**
@@ -994,7 +994,7 @@ class FormHelper extends AppHelper {
 
 		if (
 			(!isset($options['options']) && in_array($options['type'], $types)) ||
-			(isset($magicType) && $options['type'] == 'text')
+			(isset($magicType) && in_array($options['type'], array('text', 'number')))
 		) {
 			$varName = Inflector::variable(
 				Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
@@ -1006,12 +1006,17 @@ class FormHelper extends AppHelper {
 				}
 				$options['options'] = $varOptions;
 			}
+
+			if ($options['type'] === 'select' && array_key_exists('step', $options)) {
+				unset($options['step']);
+			}
 		}
 
 		$autoLength = (
 			!array_key_exists('maxlength', $options) &&
 			isset($fieldDef['length']) &&
-			is_scalar($fieldDef['length'])
+			is_scalar($fieldDef['length']) &&
+			$options['type'] !== 'select'
 		);
 		if ($autoLength && $options['type'] == 'text') {
 			$options['maxlength'] = $fieldDef['length'];
@@ -2207,47 +2212,12 @@ class FormHelper extends AppHelper {
 		}
 
 		if (!empty($attributes['value'])) {
-			if (is_array($attributes['value'])) {
-				extract($attributes['value']);
-			} else {
-				if (is_numeric($attributes['value'])) {
-					$attributes['value'] = strftime('%Y-%m-%d %H:%M:%S', $attributes['value']);
-				}
-				$meridian = 'am';
-				$pos = strpos($attributes['value'], '-');
-				if ($pos !== false) {
-					$date = explode('-', $attributes['value']);
-					$days = explode(' ', $date[2]);
-					$day = $days[0];
-					$month = $date[1];
-					$year = $date[0];
-				} else {
-					$days[1] = $attributes['value'];
-				}
-
-				if (!empty($timeFormat)) {
-					$time = explode(':', $days[1]);
-
-					if ($time[0] >= '12' && $timeFormat == '12') {
-						$meridian = 'pm';
-					} elseif ($time[0] == '00' && $timeFormat == '12') {
-						$time[0] = 12;
-					} elseif ($time[0] >= 12) {
-						$meridian = 'pm';
-					}
-					if (!$time[0] && $timeFormat == '12') {
-						$time[0] = 12;
-					}
-					$hour = $min = null;
-					if (isset($time[1])) {
-						$hour = $time[0];
-						$min = $time[1];
-					}
-				}
-			}
+			list($year, $month, $day, $hour, $min, $meridian) = $this->_getDateTimeValue(
+				$attributes['value'],
+				$timeFormat
+			);
 		}
 
-		$elements = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian');
 		$defaults = array(
 			'minYear' => null, 'maxYear' => null, 'separator' => '-',
 			'interval' => 1, 'monthNames' => true
@@ -2278,42 +2248,42 @@ class FormHelper extends AppHelper {
 			list($year, $month, $day, $hour, $min, $meridian) = $newTime;
 		}
 
-		if (isset($attributes['id'])) {
-			if (is_string($attributes['id'])) {
-				// build out an array version
-				foreach ($elements as $element) {
-					$selectAttrName = 'select' . $element . 'Attr';
-					${$selectAttrName} = $attributes;
-					${$selectAttrName}['id'] = $attributes['id'] . $element;
-				}
-			} elseif (is_array($attributes['id'])) {
-				// check for missing ones and build selectAttr for each element
-				$attributes['id'] += array(
-					'month' => '', 'year' => '', 'day' => '',
-					'hour' => '', 'minute' => '', 'meridian' => ''
-				);
-				foreach ($elements as $element) {
-					$selectAttrName = 'select' . $element . 'Attr';
-					${$selectAttrName} = $attributes;
-					${$selectAttrName}['id'] = $attributes['id'][strtolower($element)];
-				}
+		$keys = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian');
+		$attrs = array_fill_keys($keys, $attributes);
+
+		$hasId = isset($attributes['id']);
+		if ($hasId && is_array($attributes['id'])) {
+			// check for missing ones and build selectAttr for each element
+			$attributes['id'] += array(
+				'month' => '',
+				'year' => '',
+				'day' => '',
+				'hour' => '',
+				'minute' => '',
+				'meridian' => ''
+			);
+			foreach ($keys as $key) {
+				$attrs[$key]['id'] = $attributes['id'][strtolower($key)];
 			}
-		} else {
-			// build the selectAttrName with empty id's to pass
-			foreach ($elements as $element) {
-				$selectAttrName = 'select' . $element . 'Attr';
-				${$selectAttrName} = $attributes;
+		}
+		if ($hasId && is_string($attributes['id'])) {
+			// build out an array version
+			foreach ($keys as $key) {
+				$attrs[$key]['id'] = $attributes['id'] . $key;
 			}
 		}
 
 		if (is_array($attributes['empty'])) {
 			$attributes['empty'] += array(
-				'month' => true, 'year' => true, 'day' => true,
-				'hour' => true, 'minute' => true, 'meridian' => true
+				'month' => true,
+				'year' => true,
+				'day' => true,
+				'hour' => true,
+				'minute' => true,
+				'meridian' => true
 			);
-			foreach ($elements as $element) {
-				$selectAttrName = 'select' . $element . 'Attr';
-				${$selectAttrName}['empty'] = $attributes['empty'][strtolower($element)];
+			foreach ($keys as $key) {
+				$attrs[$key]['empty'] = $attributes['empty'][strtolower($key)];
 			}
 		}
 
@@ -2321,48 +2291,99 @@ class FormHelper extends AppHelper {
 		foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
 			switch ($char) {
 				case 'Y':
-					$selectYearAttr['value'] = $year;
+					$attrs['Year']['value'] = $year;
 					$selects[] = $this->year(
-						$fieldName, $minYear, $maxYear, $selectYearAttr
+						$fieldName, $minYear, $maxYear, $attrs['Year']
 					);
 				break;
 				case 'M':
-					$selectMonthAttr['value'] = $month;
-					$selectMonthAttr['monthNames'] = $monthNames;
-					$selects[] = $this->month($fieldName, $selectMonthAttr);
+					$attrs['Month']['value'] = $month;
+					$attrs['Month']['monthNames'] = $monthNames;
+					$selects[] = $this->month($fieldName, $attrs['Month']);
 				break;
 				case 'D':
-					$selectDayAttr['value'] = $day;
-					$selects[] = $this->day($fieldName, $selectDayAttr);
+					$attrs['Day']['value'] = $day;
+					$selects[] = $this->day($fieldName, $attrs['Day']);
 				break;
 			}
 		}
 		$opt = implode($separator, $selects);
 
-		$selectMinuteAttr['interval'] = $interval;
+		$attrs['Minute']['interval'] = $interval;
 		switch ($timeFormat) {
 			case '24':
-				$selectHourAttr['value'] = $hour;
-				$selectMinuteAttr['value'] = $min;
-				$opt .= $this->hour($fieldName, true, $selectHourAttr) . ':' .
-				$this->minute($fieldName, $selectMinuteAttr);
+				$attrs['Hour']['value'] = $hour;
+				$attrs['Minute']['value'] = $min;
+				$opt .= $this->hour($fieldName, true, $attrs['Hour']) . ':' .
+				$this->minute($fieldName, $attrs['Minute']);
 			break;
 			case '12':
-				$selectHourAttr['value'] = $hour;
-				$selectMinuteAttr['value'] = $min;
-				$selectMeridianAttr['value'] = $meridian;
-				$opt .= $this->hour($fieldName, false, $selectHourAttr) . ':' .
-				$this->minute($fieldName, $selectMinuteAttr) . ' ' .
-				$this->meridian($fieldName, $selectMeridianAttr);
-			break;
-			default:
-				$opt .= '';
+				$attrs['Hour']['value'] = $hour;
+				$attrs['Minute']['value'] = $min;
+				$attrs['Meridian']['value'] = $meridian;
+				$opt .= $this->hour($fieldName, false, $attrs['Hour']) . ':' .
+				$this->minute($fieldName, $attrs['Minute']) . ' ' .
+				$this->meridian($fieldName, $attrs['Meridian']);
 			break;
 		}
 		return $opt;
 	}
 
 /**
+ * Parse the value for a datetime selected value
+ *
+ * @param string|array $value The selected value.
+ * @param integer $timeFormat The time format
+ * @return array Array of selected value.
+ */
+	protected function _getDateTimeValue($value, $timeFormat) {
+		$year = $month = $day = $hour = $min = $meridian = null;
+		if (is_array($value)) {
+			extract($value);
+			if ($meridian === 'pm') {
+				$hour += 12;
+			}
+			return array($year, $month, $day, $hour, $min, $meridian);
+		}
+
+		if (is_numeric($value)) {
+			$value = strftime('%Y-%m-%d %H:%M:%S', $value);
+		}
+		$meridian = 'am';
+		$pos = strpos($value, '-');
+		if ($pos !== false) {
+			$date = explode('-', $value);
+			$days = explode(' ', $date[2]);
+			$day = $days[0];
+			$month = $date[1];
+			$year = $date[0];
+		} else {
+			$days[1] = $value;
+		}
+
+		if (!empty($timeFormat)) {
+			$time = explode(':', $days[1]);
+
+			if ($time[0] >= '12' && $timeFormat == '12') {
+				$meridian = 'pm';
+			} elseif ($time[0] == '00' && $timeFormat == '12') {
+				$time[0] = 12;
+			} elseif ($time[0] >= 12) {
+				$meridian = 'pm';
+			}
+			if ($time[0] == 0 && $timeFormat == '12') {
+				$time[0] = 12;
+			}
+			$hour = $min = null;
+			if (isset($time[1])) {
+				$hour = $time[0];
+				$min = $time[1];
+			}
+		}
+		return array($year, $month, $day, $hour, $min, $meridian);
+	}
+
+/**
  * Gets the input field name for the current tag
  *
  * @param array $options