Browse Source

use Hash instead of Set and cleanup

euromark 11 years ago
parent
commit
f1cb6e425d

+ 30 - 0
Lib/Utility/Utility.php

@@ -278,6 +278,7 @@ class Utility {
 	 * Do not use this for querystrings. Those will escape automatically.
 	 * This is only useful for named or passed params.
 	 *
+	 * @deprecated Use query strings instead
 	 * @param string $string Unsafe string
 	 * @return string Encoded string
 	 */
@@ -292,6 +293,7 @@ class Utility {
 	 * Do not use this for querystrings. Those will escape automatically.
 	 * This is only useful for named or passed params.
 	 *
+	 * @deprecated Use query strings instead
 	 * @param string $string Safe string
 	 * @return string Decoded string
 	 */
@@ -437,6 +439,34 @@ class Utility {
 	}
 
 	/**
+	 * Counts the dimensions of an array. If $all is set to false (which is the default) it will
+	 * only consider the dimension of the first element in the array.
+	 *
+	 * @param array $array Array to count dimensions on
+	 * @param boolean $all Set to true to count the dimension considering all elements in array
+	 * @param integer $count Start the dimension count at this number
+	 * @return integer The number of dimensions in $array
+	 */
+	public static function countDim($array = null, $all = false, $count = 0) {
+		if ($all) {
+			$depth = array($count);
+			if (is_array($array) && reset($array) !== false) {
+				foreach ($array as $value) {
+					$depth[] = self::countDim($value, true, $count + 1);
+				}
+			}
+			$return = max($depth);
+		} else {
+			if (is_array(reset($array))) {
+				$return = self::countDim(reset($array)) + 1;
+			} else {
+				$return = 1;
+			}
+		}
+		return $return;
+	}
+
+	/**
 	 * Expands the values of an array of strings into a deep array.
 	 * Opposite of flattenList().
 	 *

+ 40 - 31
Model/Behavior/RevisionBehavior.php

@@ -1,7 +1,7 @@
 <?php
 
 App::uses('ModelBehavior', 'Model');
-App::uses('Set', 'Utility'); // TODO: use Hash instead of Set
+App::uses('Hash', 'Utility');
 
 /**
  * Revision Behavior
@@ -27,7 +27,7 @@ App::uses('Set', 'Utility'); // TODO: use Hash instead of Set
  *
  * Install instructions :
  *
- * - Place the newest version of RevisionBehavior in your app/models/behaviors folder
+ * - Place the newest version of RevisionBehavior in your APP/Model/Behaviors folder
  * - Add the behavior to AppModel (or single models if you prefer)
  * - Create a shadow table for each model that you want revision for.
  * - Behavior will gracefully do nothing for models that has behavior, without table
@@ -98,11 +98,14 @@ App::uses('Set', 'Utility'); // TODO: use Hash instead of Set
  *
  * 2.0.5 => CakePHP 2.x
  *
+ * 2.0.6 => use alias to map shadow tables to a different alias as each alias is only allowed once
+ * per ClassRegistry.
+ *
  * @author Ronny Vindenes
  * @author Alexander 'alkemann' Morland
  * @license MIT
  * @modifed 27. march 2009
- * @version 2.0.5
+ * @version 2.0.6
  * @modified 2012-07-28 Mark Scherer (2.x ready)
  * @cakephp 2.x
  */
@@ -126,7 +129,8 @@ class RevisionBehavior extends ModelBehavior {
 		'auto' => true,
 		'ignore' => array(),
 		'useDbConfig' => null,
-		'model' => null);
+		'model' => null,
+		'alias' => null);
 
 	/**
 	 * Old data, used to detect changes.
@@ -143,13 +147,13 @@ class RevisionBehavior extends ModelBehavior {
 	 * @return void
 	 */
 	public function setup(Model $Model, $config = array()) {
-		if (!empty($config)) {
-			$this->settings[$Model->alias] = array_merge($this->_defaults, $config);
-		} else {
-			$this->settings[$Model->alias] = $this->_defaults;
+		$defaults = (array)Configure::read('Revision') + $this->_defaults;
+		$this->settings[$Model->alias] = $config + $defaults;
+
+		$this->_createShadowModel($Model);
+		if (!$Model->Behaviors->loaded('Containable')) {
+			$Model->Behaviors->load('Containable');
 		}
-		$this->createShadowModel($Model);
-		$Model->Behaviors->load('Containable');
 	}
 
 	/**
@@ -175,12 +179,13 @@ class RevisionBehavior extends ModelBehavior {
 				$habtm[] = $assocAlias;
 			}
 		}
-		$data = $Model->find('first', array('conditions' => array($Model->alias . '.' . $Model->primaryKey => $Model->id),
+		$data = $Model->find('first', array(
+				'conditions' => array($Model->alias . '.' . $Model->primaryKey => $Model->id),
 				'contain' => $habtm));
 		$Model->ShadowModel->create($data);
 		$Model->ShadowModel->set('version_created', date('Y-m-d H:i:s'));
 		foreach ($habtm as $assocAlias) {
-			$foreignKeys = Set::extract($data, '/' . $assocAlias . '/' . $Model->{$assocAlias}->primaryKey);
+			$foreignKeys = Hash::extract($data, '{n}.' . $assocAlias . '.' . $Model->{$assocAlias}->primaryKey);
 			$Model->ShadowModel->set($assocAlias, implode(',', $foreignKeys));
 		}
 		return (bool)$Model->ShadowModel->save();
@@ -244,7 +249,7 @@ class RevisionBehavior extends ModelBehavior {
 		$unified = array();
 		$keys = array_keys($all[0][$Model->alias]);
 		foreach ($keys as $field) {
-			$allValues = Set::extract($all, '/' . $Model->alias . '/' . $field);
+			$allValues = Hash::extract($all, '{n}.' . $Model->alias . '.' . $field);
 			$allValues = array_reverse(array_unique(array_reverse($allValues, true)), true);
 			if (sizeof($allValues) == 1) {
 				$unified[$field] = reset($allValues);
@@ -284,7 +289,7 @@ class RevisionBehavior extends ModelBehavior {
 			$remaining = $count;
 			for ($p = 1; true; $p++) {
 
-				$this->init($Model, $p, $limit);
+				$this->_init($Model, $p, $limit);
 
 				$remaining = $remaining - $limit;
 				if ($remaining <= 0) {
@@ -292,7 +297,7 @@ class RevisionBehavior extends ModelBehavior {
 				}
 			}
 		} else {
-			$this->init($Model, 1, $count);
+			$this->_init($Model, 1, $count);
 		}
 		return true;
 	}
@@ -305,7 +310,7 @@ class RevisionBehavior extends ModelBehavior {
 	 * @param integer $limit
 	 * @return void
 	 */
-	protected function init(Model $Model, $page, $limit) {
+	protected function _init(Model $Model, $page, $limit) {
 		$habtm = array();
 		$allHabtm = $Model->getAssociated('hasAndBelongsToMany');
 		foreach ($allHabtm as $assocAlias) {
@@ -436,7 +441,7 @@ class RevisionBehavior extends ModelBehavior {
 		// leave model rows not edited since date alone
 
 		$all = $Model->find('all', array('conditions' => $options['conditions'], 'fields' => $Model->primaryKey));
-		$allIds = Set::extract($all, '/' . $Model->alias . '/' . $Model->primaryKey);
+		$allIds = Hash::extract($all, '{n}.' . $Model->alias . '.' . $Model->primaryKey);
 
 		$cond = $options['conditions'];
 		$cond['version_created <'] = $options['date'];
@@ -444,7 +449,7 @@ class RevisionBehavior extends ModelBehavior {
 			'order' => $Model->primaryKey,
 			'conditions' => $cond,
 			'fields' => array('version_id', $Model->primaryKey)));
-		$createdBeforeDateIds = Set::extract($createdBeforeDate, '/' . $Model->alias . '/' . $Model->primaryKey);
+		$createdBeforeDateIds = Hash::extract($createdBeforeDate, '{n}.' . $Model->alias . '.' . $Model->primaryKey);
 
 		$deleteIds = array_diff($allIds, $createdBeforeDateIds);
 
@@ -457,7 +462,7 @@ class RevisionBehavior extends ModelBehavior {
 			'order' => $Model->primaryKey,
 			'conditions' => $cond,
 			'fields' => array('version_id', $Model->primaryKey)));
-		$createdAfterDateIds = Set::extract($createdAfterDate, '/' . $Model->alias . '/' . $Model->primaryKey);
+		$createdAfterDateIds = Hash::extract($createdAfterDate, '{n}.' . $Model->alias . '.' . $Model->primaryKey);
 		$updateIds = array_diff($createdAfterDateIds, $deleteIds);
 
 		$revertSuccess = true;
@@ -552,7 +557,7 @@ class RevisionBehavior extends ModelBehavior {
 					'conditions' => array($data['foreignKey'] => $Model->id, 'NOT' => array($Model->primaryKey => $ids)),
 					));
 				if (!empty($revisionChildren)) {
-					$ids = array_merge($ids, Set::extract($revisionChildren, '/' . $assoc . '/' . $Model->$assoc->primaryKey));
+					$ids = array_merge($ids, Hash::extract($revisionChildren, '{n}.' . $assoc . '.' . $Model->$assoc->primaryKey));
 				}
 
 				/* Revert all children */
@@ -589,7 +594,7 @@ class RevisionBehavior extends ModelBehavior {
 			$Model->logableAction['Revision'] = 'revertToDate(' . $datetime . ') edit';
 			foreach ($Model->getAssociated('hasAndBelongsToMany') as $assocAlias) {
 				if (isset($Model->ShadowModel->_schema[$assocAlias])) {
-					$ids = Set::extract($liveData, '/' . $assocAlias . '/' . $Model->$assocAlias->primaryKey);
+					$ids = Hash::extract($liveData, '{n}.' . $assocAlias . '.' . $Model->$assocAlias->primaryKey);
 					if (empty($ids) || is_string($ids)) {
 						$liveData[$Model->alias][$assocAlias] = '';
 					} else {
@@ -848,12 +853,12 @@ class RevisionBehavior extends ModelBehavior {
 				if (in_array($assocAlias, $this->settings[$Model->alias]['ignore'])) {
 					continue;
 				}
-				$oldIds = Set::extract($this->_oldData[$Model->alias], $assocAlias . '.{n}.id');
+				$oldIds = Hash::extract($this->_oldData[$Model->alias], $assocAlias . '.{n}.id');
 				if (!isset($Model->data[$assocAlias])) {
 					$Model->ShadowModel->set($assocAlias, implode(',', $oldIds));
 					continue;
 				}
-				$currentIds = Set::extract($data, $assocAlias . '.{n}.id');
+				$currentIds = Hash::extract($data, $assocAlias . '.{n}.id');
 				$idChanges = array_diff($currentIds, $oldIds);
 				if (!empty($idChanges)) {
 					$Model->ShadowModel->set($assocAlias, implode(',', $currentIds));
@@ -902,7 +907,7 @@ class RevisionBehavior extends ModelBehavior {
 			if (isset($Model->{$assocAlias}->ShadowModel->_schema[$Model->alias])) {
 				$joins = $Model->{$a['with']}->find('all', array('recursive' => -1, 'conditions' => array($a['foreignKey'] => $Model->
 							id)));
-				$this->deleteUpdates[$Model->alias][$assocAlias] = Set::extract($joins, '/' . $a['with'] . '/' . $a['associationForeignKey']);
+				$this->deleteUpdates[$Model->alias][$assocAlias] = Hash::extract($joins, '{n}.' . $a['with'] . '.' . $a['associationForeignKey']);
 			}
 		}
 		return true;
@@ -944,7 +949,7 @@ class RevisionBehavior extends ModelBehavior {
 	 * @param object $Model
 	 * @return boolean Success
 	 */
-	protected function createShadowModel(Model $Model) {
+	protected function _createShadowModel(Model $Model) {
 		if ($this->settings[$Model->alias]['useDbConfig'] === null) {
 			$dbConfig = $Model->useDbConfig;
 		} else {
@@ -965,17 +970,21 @@ class RevisionBehavior extends ModelBehavior {
 			$Model->ShadowModel = false;
 			return false;
 		}
-		$useShadowModel = $this->settings[$Model->alias]['model'];
-		//TOOD: use App::uses() and ClassRegistry::init() - also allow Plugin.Model syntax
-		if (is_string($useShadowModel) && App::import('Model', $useShadowModel)) {
-			$Model->ShadowModel = new $useShadowModel(false, $shadowTable, $dbConfig);
+		$shadowModel = $this->settings[$Model->alias]['model'];
+		if ($shadowModel) {
+			$options = array('class' => $shadowModel, 'table' => $fullTableName, 'ds' => $dbConfig);
+			$Model->ShadowModel = ClassRegistry::init($options);
 		} else {
-			$Model->ShadowModel = new Model(false, $shadowTable, $dbConfig);
+			$Model->ShadowModel = new Model(false, $fullTableName, $dbConfig);
 		}
 		if ($Model->tablePrefix) {
 			$Model->ShadowModel->tablePrefix = $Model->tablePrefix;
 		}
-		$Model->ShadowModel->alias = $Model->alias; // . 'Shadow';
+		$alias = $this->settings[$Model->alias]['alias'] ?: null;
+		if ($alias === true) {
+			$alias = 'Shadow';
+		}
+		$Model->ShadowModel->alias = $Model->alias . $alias;
 		$Model->ShadowModel->primaryKey = 'version_id';
 		$Model->ShadowModel->order = 'version_created DESC, version_id DESC';
 		return true;

+ 6 - 19
Model/MyModel.php

@@ -1,6 +1,7 @@
 <?php
 App::uses('Model', 'Model');
 App::uses('Utility', 'Tools.Utility');
+App::uses('Hash', 'Utility');
 
 /**
  * Model enhancements for Cake2
@@ -635,7 +636,7 @@ class MyModel extends Model {
 						$tmpPath2[] = '{n}.' . $this->alias . '.' . $field[$i];
 					}
 					//do the magic?? read the code...
-					$res = Set::combine($list, '{n}.' . $this->alias . '.' . $this->primaryKey, $tmpPath2);
+					$res = Hash::combine($list, '{n}.' . $this->alias . '.' . $this->primaryKey, $tmpPath2);
 					break;
 				default:
 					$res = parent::find($type, $options);
@@ -726,17 +727,17 @@ class MyModel extends Model {
 		$return = array();
 
 		if (!empty($qryOptions['conditions'])) {
-			$findOptions['conditions'] = Set::merge($findOptions['conditions'], $qryOptions['conditions']);
+			$findOptions['conditions'] = Hash::merge($findOptions['conditions'], $qryOptions['conditions']);
 		}
 
 		$options = $findOptions;
-		$options['conditions'] = Set::merge($options['conditions'], array($this->alias . '.' . $sortField . ' ' . $sortDirSymb[1] => $field));
+		$options['conditions'] = Hash::merge($options['conditions'], array($this->alias . '.' . $sortField . ' ' . $sortDirSymb[1] => $field));
 		$options['order'] = array($this->alias . '.' . $sortField . '' => $sortDirWord[1]);
 		$this->id = $id;
 		$return['prev'] = $this->find('first', $options);
 
 		$options = $findOptions;
-		$options['conditions'] = Set::merge($options['conditions'], array($this->alias . '.' . $sortField . ' ' . $sortDirSymb[0] => $field));
+		$options['conditions'] = Hash::merge($options['conditions'], array($this->alias . '.' . $sortField . ' ' . $sortDirSymb[0] => $field));
 		$options['order'] = array($this->alias . '.' . $sortField . '' => $sortDirWord[0]); // ??? why 0 instead of 1
 		$this->id = $id;
 		$return['next'] = $this->find('first', $options);
@@ -1375,7 +1376,7 @@ class MyModel extends Model {
 			return $guaranteedFields;
 		}
 		if (!empty($guaranteedFields)) {
-			$data = Set::merge($guaranteedFields, $data);
+			$data = Hash::merge($guaranteedFields, $data);
 		}
 		return $data;
 	}
@@ -1554,20 +1555,6 @@ class MyModel extends Model {
 	}
 
 	/**
-	 * Automagic increasing of a field with e.g.:
-	 * $this->id = ID; $this->inc('weight',3);
-	 *
-	 * @deprecated use atomic updateAll() instead!
-	 * @param string fieldname
-	 * @param integer factor: defaults to 1 (could be negative as well - if field is signed and can be < 0)
-	 */
-	public function inc($field, $factor = 1) {
-		$value = Set::extract($this->read($field), $this->alias . '.' . $field);
-		$value += $factor;
-		return $this->saveField($field, $value);
-	}
-
-	/**
 	 * Toggles Field (Important/Deleted/Primary etc)
 	 *
 	 * @param STRING fieldName

+ 2 - 1
Model/Qurl.php

@@ -1,5 +1,6 @@
 <?php
 App::uses('ToolsAppModel', 'Tools.Model');
+App::uses('Hash', 'Utility');
 
 /**
  * Manage Quick Urls
@@ -192,7 +193,7 @@ class Qurl extends ToolsAppModel {
 		$keys['used_invalid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 1, $this->alias . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
 
 		$urls = $this->find('all', array('conditions' => array(), 'fields' => array('DISTINCT url')));
-		$keys['urls'] = !empty($urls) ? Set::extract('/' . $this->alias . '/url', $urls) : array();
+		$keys['urls'] = !empty($urls) ? Hash::extract('{n}.' . $this->alias . '.url', $urls) : array();
 		return $keys;
 	}
 

+ 2 - 1
Model/Token.php

@@ -1,6 +1,7 @@
 <?php
 App::uses('ToolsAppModel', 'Tools.Model');
 App::uses('CommonComponent', 'Tools.Controller/Component');
+App::uses('Hash', 'Utility');
 
 /**
  * A generic model to hold tokens
@@ -180,7 +181,7 @@ class Token extends ToolsAppModel {
 		$keys['used_invalid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 1, $this->alias . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
 
 		$types = $this->find('all', array('conditions' => array(), 'fields' => array('DISTINCT type')));
-		$keys['types'] = !empty($types) ? Set::extract('/' . $this->alias . '/type', $types) : array();
+		$keys['types'] = !empty($types) ? Hash::extract('{n}.' . $this->alias . '.type', $types) : array();
 		return $keys;
 	}
 

+ 55 - 1
Test/Case/Lib/Utility/UtilityTest.php

@@ -207,7 +207,7 @@ class UtilityTest extends MyCakeTestCase {
 
 		$_SERVER['HTTP_REFERER'] = '/foo/bar';
 		$res = Utility::getReferer(true);
-		$base = HTTP_BASE;
+		$base = Configure::read('App.fullBaseUrl');
 		if (!$base) {
 			$base = 'http://localhost';
 		}
@@ -306,6 +306,60 @@ class UtilityTest extends MyCakeTestCase {
 	}
 
 	/**
+	 * testCountDim method
+	 *
+	 * @return void
+	 */
+	public function testCountDim() {
+		$data = array('one', '2', 'three');
+		$result = Utility::countDim($data);
+		$this->assertEquals(1, $result);
+
+		$data = array('1' => '1.1', '2', '3');
+		$result = Utility::countDim($data);
+		$this->assertEquals(1, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => '3.1.1'));
+		$result = Utility::countDim($data);
+		$this->assertEquals(2, $result);
+
+		$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));
+		$result = Utility::countDim($data);
+		$this->assertEquals(1, $result);
+
+		$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));
+		$result = Utility::countDim($data, true);
+		$this->assertEquals(2, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($data);
+		$this->assertEquals(2, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($data, true);
+		$this->assertEquals(3, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => '2.1.1.1'))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($data, true);
+		$this->assertEquals(4, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($data, true);
+		$this->assertEquals(5, $result);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($data, true);
+		$this->assertEquals(5, $result);
+
+		$set = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Utility::countDim($set, false, 0);
+		$this->assertEquals(2, $result);
+
+		$result = Utility::countDim($set, true);
+		$this->assertEquals(5, $result);
+	}
+
+	/**
 	 * UtilityTest::testExpand()
 	 *
 	 * @return void

+ 277 - 0
Test/Case/Model/Behavior/RevisionBehaviorTest.php

@@ -1,5 +1,8 @@
 <?php
 App::uses('RevisionBehavior', 'Tools.Model/Behavior');
+App::uses('Controller', 'Controller');
+App::uses('CakeRequest', 'Network');
+App::uses('CakeResponse', 'Network');
 
 class RevisionBehaviorTest extends CakeTestCase {
 
@@ -37,6 +40,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		parent::tearDown($method);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testSavePost()
+	 *
+	 * @return void
+	 */
 	public function testSavePost() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -58,6 +66,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testSaveWithoutChange()
+	 *
+	 * @return void
+	 */
 	public function testSaveWithoutChange() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -79,6 +92,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($count, 2);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testEditPost()
+	 *
+	 * @return void
+	 */
 	public function testEditPost() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -105,6 +123,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testShadow()
+	 *
+	 * @return void
+	 */
 	public function testShadow() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -152,6 +175,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testCurrentPost()
+	 *
+	 * @return void
+	 */
 	public function testCurrentPost() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -179,6 +207,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevisionsPost()
+	 *
+	 * @return void
+	 */
 	public function testRevisionsPost() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -249,6 +282,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testDiff()
+	 *
+	 * @return void
+	 */
 	public function testDiff() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -283,6 +321,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testDiffMultipleFields()
+	 *
+	 * @return void
+	 */
 	public function testDiffMultipleFields() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -316,6 +359,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testPrevious()
+	 *
+	 * @return void
+	 */
 	public function testPrevious() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -341,6 +389,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndoEdit()
+	 *
+	 * @return void
+	 */
 	public function testUndoEdit() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -368,6 +421,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndoCreate()
+	 *
+	 * @return void
+	 */
 	public function testUndoCreate() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -390,6 +448,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result['Post']['title'], 'New post');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertTo()
+	 *
+	 * @return void
+	 */
 	public function testRevertTo() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -414,6 +477,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result['Post']['title'], 'Edited Post 2');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testLimit()
+	 *
+	 * @return void
+	 */
 	public function testLimit() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -471,6 +539,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testTree()
+	 *
+	 * @return void
+	 */
 	public function testTree() {
 		$this->loadFixtures('RevisionArticle', 'RevisionArticlesRev');
 
@@ -526,6 +599,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result[3]['Article'], $expected);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testIgnore()
+	 *
+	 * @return void
+	 */
 	public function testIgnore() {
 		$this->loadFixtures('RevisionArticle', 'RevisionArticlesRev');
 
@@ -553,6 +631,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testWithoutShadowTable()
+	 *
+	 * @return void
+	 */
 	public function testWithoutShadowTable() {
 		$this->loadFixtures('RevisionUser');
 
@@ -564,6 +647,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertTrue((bool)$success);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertToDate()
+	 *
+	 * @return void
+	 */
 	public function testRevertToDate() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -587,6 +675,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testCascade()
+	 *
+	 * @return void
+	 */
 	public function testCascade() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionVote', 'RevisionVotesRev');
 
@@ -626,6 +719,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($originalComments, $revertedComments);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testCreateRevision()
+	 *
+	 * @return void
+	 */
 	public function testCreateRevision() {
 		$this->loadFixtures('RevisionArticle', 'RevisionArticlesRev');
 
@@ -667,6 +765,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndelete()
+	 *
+	 * @return void
+	 */
 	public function testUndelete() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -696,6 +799,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndeleteCallbacks()
+	 *
+	 * @return void
+	 */
 	public function testUndeleteCallbacks() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -726,6 +834,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors();
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndeleteTree1()
+	 *
+	 * @return void
+	 */
 	public function testUndeleteTree1() {
 		$this->loadFixtures('RevisionArticle', 'RevisionArticlesRev');
 
@@ -752,6 +865,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result[2]['Article']['rght'], 5);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testUndeleteTree2()
+	 *
+	 * @return void
+	 */
 	public function testUndeleteTree2() {
 		$this->loadFixtures('RevisionArticle', 'RevisionArticlesRev');
 
@@ -799,6 +917,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result[4]['Article']['rght'], 7);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testInitializeRevisionsWithLimit()
+	 *
+	 * @return void
+	 */
 	public function testInitializeRevisionsWithLimit() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev', 'RevisionArticle', 'RevisionArticlesRev', 'RevisionComment',
 			'RevisionCommentsRev', 'RevisionCommentsRevisionTag', 'RevisionVote', 'RevisionVotesRev', 'RevisionTag',
@@ -818,6 +941,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertFalse($Comment->Tag->initializeRevisions());
 	}
 
+	/**
+	 * RevisionBehaviorTest::testInitializeRevisions()
+	 *
+	 * @return void
+	 */
 	public function testInitializeRevisions() {
 		$this->loadFixtures('RevisionPost');
 
@@ -830,6 +958,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals(sizeof($result), 3);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertAll()
+	 *
+	 * @return void
+	 */
 	public function testRevertAll() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -855,6 +988,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals(sizeof($result), 3);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertAllConditions()
+	 *
+	 * @return void
+	 */
 	public function testRevertAllConditions() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -883,6 +1021,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals(sizeof($result), 3);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testOnWithModel()
+	 *
+	 * @return void
+	 */
 	public function testOnWithModel() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -898,6 +1041,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result['Tag'][2]['title'], 'Trick');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHABTMRelatedUndoed()
+	 *
+	 * @return void
+	 */
 	public function testHABTMRelatedUndoed() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -912,6 +1060,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result['Tag'][2]['title'], 'Tricks');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testOnWithModelUndoed()
+	 *
+	 * @return void
+	 */
 	public function testOnWithModelUndoed() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -937,6 +1090,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors('Third Tag not back : %s');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevSave()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevSave() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -973,6 +1131,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $result['Comment']['Tag']);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevCreate()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevCreate() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -995,6 +1158,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals('2,4', $result['Comment']['Tag']);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevIgnore()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevIgnore() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1015,6 +1183,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($originalResult, $result);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevUndo()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevUndo() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1036,6 +1209,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors('3 tags : %s');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevUndoJustHabtmChanges()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevUndoJustHabtmChanges() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1056,6 +1234,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors('3 tags : %s');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevRevert()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevRevert() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1077,6 +1260,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors('3 tags : %s');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertToHabtm2()
+	 *
+	 * @return void
+	 */
 	public function testRevertToHabtm2() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1129,6 +1317,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($revOne['Comment']['Tag'], '1,2,3');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testHabtmRevRevertToDate()
+	 *
+	 * @return void
+	 */
 	public function testHabtmRevRevertToDate() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1151,6 +1344,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertNoErrors('3 tags : %s');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertToTheTagsCommentHadBefore()
+	 *
+	 * @return void
+	 */
 	public function testRevertToTheTagsCommentHadBefore() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1191,6 +1389,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($result['Tag'][1]['title'], 'Fun');
 	}
 
+	/**
+	 * RevisionBehaviorTest::testSaveWithOutTags()
+	 *
+	 * @return void
+	 */
 	public function testSaveWithOutTags() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1208,6 +1411,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals($newest['Comment']['Tag'], $result['Comment']['Tag']);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevertToDeletedTag()
+	 *
+	 * @return void
+	 */
 	public function testRevertToDeletedTag() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1228,6 +1436,7 @@ class RevisionBehaviorTest extends CakeTestCase {
 
 	/**
 	 * @expectedException PHPUNIT_FRAMEWORK_ERROR_WARNING
+	 * @return void
 	 */
 	public function testBadKittyForgotId() {
 		$Comment = new RevisionComment();
@@ -1254,6 +1463,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertError(true);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testBadKittyMakesUpStuff()
+	 *
+	 * @return void
+	 */
 	public function testBadKittyMakesUpStuff() {
 		$this->loadFixtures('RevisionComment', 'RevisionCommentsRev', 'RevisionCommentsRevisionTag',
 			'RevisionCommentsRevisionTagsRev', 'RevisionTag', 'RevisionTagsRev');
@@ -1268,6 +1482,7 @@ class RevisionBehaviorTest extends CakeTestCase {
 
 	/**
 	 * @expectedException PHPUNIT_FRAMEWORK_ERROR_WARNING
+	 * @return void
 	 */
 	public function testMethodsOnNonRevisedModel() {
 		$User = new RevisionUser();
@@ -1301,6 +1516,11 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertError();
 	}
 
+	/**
+	 * RevisionBehaviorTest::testRevisions()
+	 *
+	 * @return void
+	 */
 	public function testRevisions() {
 		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
 
@@ -1341,6 +1561,63 @@ class RevisionBehaviorTest extends CakeTestCase {
 		$this->assertEquals('Stuff (1)', $result[2]['Post']['title']);
 	}
 
+	/**
+	 * RevisionBehaviorTest::testFoo()
+	 *
+	 * @return void
+	 */
+	public function testNoAlias() {
+		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
+
+		$this->Controller = new Controller(new CakeRequest(null, false), new CakeResponse());
+		$this->Controller->loadModel('RevisionPost');
+		$this->Controller->RevisionPost->validate['title'] = array(
+			'minLength' => array(
+				'rule' => array('minLength', 6),
+				'message' => 'mL',
+			)
+		);
+		$data = array('Post' => array('title' => 'S (1)', 'content' => 'abc'));
+		$this->Controller->RevisionPost->create();
+		$result = $this->Controller->RevisionPost->save($data);
+		$this->assertFalse($result);
+		$expected = array('title' => array('mL'));
+		$this->assertEquals($expected, $this->Controller->RevisionPost->validationErrors);
+
+		$this->Controller->render(false, false);
+		$this->assertEquals(array(), $this->Controller->View->validationErrors['Post']);
+	}
+
+	/**
+	 * RevisionBehaviorTest::testFoo()
+	 *
+	 * @return void
+	 */
+	public function testAlias() {
+		$this->loadFixtures('RevisionPost', 'RevisionPostsRev');
+		Configure::write('Revision.alias', true);
+
+		$this->Controller = new Controller(new CakeRequest(null, false), new CakeResponse());
+		$this->Controller->loadModel('RevisionPost');
+
+		$this->Controller->RevisionPost->validate['title'] = array(
+			'minLength' => array(
+				'rule' => array('minLength', 6),
+				'message' => 'mL',
+			)
+		);
+		$data = array('Post' => array('title' => 'S (1)', 'content' => 'abc'));
+		$this->Controller->RevisionPost->create();
+		$result = $this->Controller->RevisionPost->save($data);
+		$this->assertFalse($result);
+		$expected = array('title' => array('mL'));
+		$this->assertEquals($expected, $this->Controller->RevisionPost->validationErrors);
+
+		$this->Controller->render(false, false);
+		$this->assertEquals($expected, $this->Controller->View->validationErrors['Post']);
+		$this->assertEquals(array(), $this->Controller->View->validationErrors['PostShadow']);
+	}
+
 }
 
 class RevisionTestModel extends CakeTestModel {

+ 2 - 1
View/RssView.php

@@ -13,6 +13,7 @@ App::uses('View', 'View');
 App::uses('Xml', 'Utility');
 App::uses('CakeTime', 'Utility');
 App::uses('Routing', 'Router');
+App::uses('Hash', 'Utility');
 
 /**
  * A view class that is used for creating RSS feeds.
@@ -187,7 +188,7 @@ class RssView extends View {
 			}
 		} else {
 			$data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
-			if (is_array($data) && Set::numeric(array_keys($data))) {
+			if (is_array($data) && Hash::numeric(array_keys($data))) {
 				$data = array($rootNode => array($serialize => $data));
 			}
 		}