Browse Source

Merge branch 'master' into 2.3

mark_story 13 years ago
parent
commit
d94cdc67fe

+ 1 - 0
lib/Cake/Console/Command/Task/TestTask.php

@@ -479,6 +479,7 @@ class TestTask extends BakeTask {
  */
 	public function generateUses($type, $realType, $className) {
 		$uses = array();
+		$type = strtolower($type);
 		if ($type == 'component') {
 			$uses[] = array('ComponentCollection', 'Controller');
 			$uses[] = array('Component', 'Controller');

+ 1 - 4
lib/Cake/Controller/Component/AuthComponent.php

@@ -565,10 +565,7 @@ class AuthComponent extends Component {
 		if ($key === null) {
 			return $user;
 		}
-		if ($value = Hash::get($user, $key)) {
-			return $value;
-		}
-		return null;
+		return Hash::get($user, $key);
 	}
 
 /**

+ 158 - 157
lib/Cake/Model/Behavior/TranslateBehavior.php

@@ -59,53 +59,53 @@ class TranslateBehavior extends ModelBehavior {
  * $config could be empty - and translations configured dynamically by
  * bindTranslation() method
  *
- * @param Model $model Model the behavior is being attached to.
+ * @param Model $Model Model the behavior is being attached to.
  * @param array $config Array of configuration information.
  * @return mixed
  */
-	public function setup(Model $model, $config = array()) {
-		$db = ConnectionManager::getDataSource($model->useDbConfig);
+	public function setup(Model $Model, $config = array()) {
+		$db = ConnectionManager::getDataSource($Model->useDbConfig);
 		if (!$db->connected) {
 			trigger_error(
-				__d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $model->useDbConfig, $model->alias),
+				__d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $Model->useDbConfig, $Model->alias),
 				E_USER_ERROR
 			);
 			return false;
 		}
 
-		$this->settings[$model->alias] = array();
-		$this->runtime[$model->alias] = array('fields' => array());
-		$this->translateModel($model);
-		return $this->bindTranslation($model, $config, false);
+		$this->settings[$Model->alias] = array();
+		$this->runtime[$Model->alias] = array('fields' => array());
+		$this->translateModel($Model);
+		return $this->bindTranslation($Model, $config, false);
 	}
 
 /**
  * Cleanup Callback unbinds bound translations and deletes setting information.
  *
- * @param Model $model Model being detached.
+ * @param Model $Model Model being detached.
  * @return void
  */
-	public function cleanup(Model $model) {
-		$this->unbindTranslation($model);
-		unset($this->settings[$model->alias]);
-		unset($this->runtime[$model->alias]);
+	public function cleanup(Model $Model) {
+		$this->unbindTranslation($Model);
+		unset($this->settings[$Model->alias]);
+		unset($this->runtime[$Model->alias]);
 	}
 
 /**
  * beforeFind Callback
  *
- * @param Model $model Model find is being run on.
+ * @param Model $Model Model find is being run on.
  * @param array $query Array of Query parameters.
  * @return array Modified query
  */
-	public function beforeFind(Model $model, $query) {
-		$this->runtime[$model->alias]['virtualFields'] = $model->virtualFields;
-		$locale = $this->_getLocale($model);
+	public function beforeFind(Model $Model, $query) {
+		$this->runtime[$Model->alias]['virtualFields'] = $Model->virtualFields;
+		$locale = $this->_getLocale($Model);
 		if (empty($locale)) {
 			return $query;
 		}
-		$db = $model->getDataSource();
-		$RuntimeModel = $this->translateModel($model);
+		$db = $Model->getDataSource();
+		$RuntimeModel = $this->translateModel($Model);
 
 		if (!empty($RuntimeModel->tablePrefix)) {
 			$tablePrefix = $RuntimeModel->tablePrefix;
@@ -120,27 +120,27 @@ class TranslateBehavior extends ModelBehavior {
 		$this->_joinTable = $joinTable;
 		$this->_runtimeModel = $RuntimeModel;
 
-		if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) {
-			$query['fields'] = 'COUNT(DISTINCT(' . $db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
+		if (is_string($query['fields']) && "COUNT(*) AS {$db->name('count')}" == $query['fields']) {
+			$query['fields'] = "COUNT(DISTINCT({$db->name($Model->escapeField())})) {$db->alias}count";
 			$query['joins'][] = array(
 				'type' => 'INNER',
 				'alias' => $RuntimeModel->alias,
 				'table' => $joinTable,
 				'conditions' => array(
-					$model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias . '.foreign_key'),
-					$RuntimeModel->alias . '.model' => $model->name,
-					$RuntimeModel->alias . '.locale' => $locale
+					$Model->escapeField() => $db->identifier($RuntimeModel->escapeField('foreign_key')),
+					$RuntimeModel->escapeField('model') => $Model->name,
+					$RuntimeModel->escapeField('locale')  => $locale
 				)
 			);
-			$conditionFields = $this->_checkConditions($model, $query);
+			$conditionFields = $this->_checkConditions($Model, $query);
 			foreach ($conditionFields as $field) {
-				$query = $this->_addJoin($model, $query, $field, $field, $locale);
+				$query = $this->_addJoin($Model, $query, $field, $field, $locale);
 			}
 			unset($this->_joinTable, $this->_runtimeModel);
 			return $query;
 		}
 
-		$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
+		$fields = array_merge($this->settings[$Model->alias], $this->runtime[$Model->alias]['fields']);
 		$addFields = array();
 		if (empty($query['fields'])) {
 			$addFields = $fields;
@@ -148,28 +148,28 @@ class TranslateBehavior extends ModelBehavior {
 			foreach ($fields as $key => $value) {
 				$field = (is_numeric($key)) ? $value : $key;
 
-				if (in_array($model->alias . '.*', $query['fields']) || in_array($model->alias . '.' . $field, $query['fields']) || in_array($field, $query['fields'])) {
+				if (in_array($Model->escapeField('*'), $query['fields']) || in_array($Model->alias . '.' . $field, $query['fields']) || in_array($field, $query['fields'])) {
 					$addFields[] = $field;
 				}
 			}
 		}
 
-		$this->runtime[$model->alias]['virtualFields'] = $model->virtualFields;
+		$this->runtime[$Model->alias]['virtualFields'] = $Model->virtualFields;
 		if ($addFields) {
 			foreach ($addFields as $_f => $field) {
 				$aliasField = is_numeric($_f) ? $field : $_f;
 
-				foreach (array($aliasField, $model->alias . '.' . $aliasField) as $_field) {
+				foreach (array($aliasField, $Model->alias . '.' . $aliasField) as $_field) {
 					$key = array_search($_field, (array)$query['fields']);
 
 					if ($key !== false) {
 						unset($query['fields'][$key]);
 					}
 				}
-				$query = $this->_addJoin($model, $query, $field, $aliasField, $locale);
+				$query = $this->_addJoin($Model, $query, $field, $aliasField, $locale);
 			}
 		}
-		$this->runtime[$model->alias]['beforeFind'] = $addFields;
+		$this->runtime[$Model->alias]['beforeFind'] = $addFields;
 		unset($this->_joinTable, $this->_runtimeModel);
 		return $query;
 	}
@@ -178,17 +178,17 @@ class TranslateBehavior extends ModelBehavior {
  * Check a query's conditions for translated fields.
  * Return an array of translated fields found in the conditions.
  *
- * @param Model $model The model being read.
+ * @param Model $Model The model being read.
  * @param array $query The query array.
  * @return array The list of translated fields that are in the conditions.
  */
-	protected function _checkConditions(Model $model, $query) {
+	protected function _checkConditions(Model $Model, $query) {
 		$conditionFields = array();
 		if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions'])) ) {
 			return $conditionFields;
 		}
 		foreach ($query['conditions'] as $col => $val) {
-			foreach ($this->settings[$model->alias] as $field => $assoc) {
+			foreach ($this->settings[$Model->alias] as $field => $assoc) {
 				if (is_numeric($field)) {
 					$field = $assoc;
 				}
@@ -201,55 +201,56 @@ class TranslateBehavior extends ModelBehavior {
 	}
 
 /**
- * Appends a join for translated fields and possibly a field.
+ * Appends a join for translated fields.
  *
- * @param Model $model The model being worked on.
+ * @param Model $Model The model being worked on.
  * @param object $joinTable The jointable object.
  * @param array $query The query array to append a join to.
  * @param string $field The field name being joined.
  * @param string $aliasField The aliased field name being joined.
  * @param string|array $locale The locale(s) having joins added.
- * @param boolean $addField Whether or not to add a field.
  * @return array The modfied query
  */
-	protected function _addJoin(Model $model, $query, $field, $aliasField, $locale, $addField = false) {
-		$db = ConnectionManager::getDataSource($model->useDbConfig);
-
+	protected function _addJoin(Model $Model, $query, $field, $aliasField, $locale) {
+		$db = ConnectionManager::getDataSource($Model->useDbConfig);
 		$RuntimeModel = $this->_runtimeModel;
 		$joinTable = $this->_joinTable;
-
+		$aliasVirtual = "i18n_{$field}";
+		$alias = "I18n__{$field}";
 		if (is_array($locale)) {
 			foreach ($locale as $_locale) {
-				$model->virtualFields['i18n_' . $field . '_' . $_locale] = 'I18n__' . $field . '__' . $_locale . '.content';
+				$aliasVirtualLocale = "{$aliasVirtual}_{$_locale}";
+				$aliasLocale = "{$alias}__{$_locale}";
+				$Model->virtualFields[$aliasVirtualLocale] = "{$aliasLocale}.content";
 				if (!empty($query['fields']) && is_array($query['fields'])) {
-					$query['fields'][] = 'i18n_' . $field . '_' . $_locale;
+					$query['fields'][] = $aliasVirtualLocale;
 				}
 				$query['joins'][] = array(
 					'type' => 'LEFT',
-					'alias' => 'I18n__' . $field . '__' . $_locale,
+					'alias' => $aliasLocale,
 					'table' => $joinTable,
 					'conditions' => array(
-						$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
-						'I18n__' . $field . '__' . $_locale . '.model' => $model->name,
-						'I18n__' . $field . '__' . $_locale . '.' . $RuntimeModel->displayField => $aliasField,
-						'I18n__' . $field . '__' . $_locale . '.locale' => $_locale
+						$Model->escapeField() => $db->identifier("{$aliasLocale}.foreign_key"),
+						"{$aliasLocale}.model" => $Model->name,
+						"{$aliasLocale}.{$RuntimeModel->displayField}" => $aliasField,
+						"{$aliasLocale}.locale" => $_locale
 					)
 				);
 			}
 		} else {
-			$model->virtualFields['i18n_' . $field] = 'I18n__' . $field . '.content';
+			$Model->virtualFields[$aliasVirtual] = "{$alias}.content";
 			if (!empty($query['fields']) && is_array($query['fields'])) {
-				$query['fields'][] = 'i18n_' . $field;
+				$query['fields'][] = $aliasVirtual;
 			}
 			$query['joins'][] = array(
 				'type' => 'INNER',
-				'alias' => 'I18n__' . $field,
+				'alias' => $alias,
 				'table' => $joinTable,
 				'conditions' => array(
-					$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
-					'I18n__' . $field . '.model' => $model->name,
-					'I18n__' . $field . '.' . $RuntimeModel->displayField => $aliasField,
-					'I18n__' . $field . '.locale' => $locale
+					"{$Model->alias}.{$Model->primaryKey}" => $db->identifier("{$alias}.foreign_key"),
+					"{$alias}.model" => $Model->name,
+					"{$alias}.{$RuntimeModel->displayField}" => $aliasField,
+					"{$alias}.locale" => $locale
 				)
 			);
 		}
@@ -259,45 +260,46 @@ class TranslateBehavior extends ModelBehavior {
 /**
  * afterFind Callback
  *
- * @param Model $model Model find was run on
+ * @param Model $Model Model find was run on
  * @param array $results Array of model results.
  * @param boolean $primary Did the find originate on $model.
  * @return array Modified results
  */
-	public function afterFind(Model $model, $results, $primary) {
-		$model->virtualFields = $this->runtime[$model->alias]['virtualFields'];
-		$this->runtime[$model->alias]['virtualFields'] = $this->runtime[$model->alias]['fields'] = array();
-		$locale = $this->_getLocale($model);
+	public function afterFind(Model $Model, $results, $primary) {
+		$Model->virtualFields = $this->runtime[$Model->alias]['virtualFields'];
+		$this->runtime[$Model->alias]['virtualFields'] = $this->runtime[$Model->alias]['fields'] = array();
+		$locale = $this->_getLocale($Model);
 
-		if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
+		if (empty($locale) || empty($results) || empty($this->runtime[$Model->alias]['beforeFind'])) {
 			return $results;
 		}
-		$beforeFind = $this->runtime[$model->alias]['beforeFind'];
+		$beforeFind = $this->runtime[$Model->alias]['beforeFind'];
 
 		foreach ($results as $key => &$row) {
-			$results[$key][$model->alias]['locale'] = (is_array($locale)) ? current($locale) : $locale;
+			$results[$key][$Model->alias]['locale'] = (is_array($locale)) ? current($locale) : $locale;
 			foreach ($beforeFind as $_f => $field) {
 				$aliasField = is_numeric($_f) ? $field : $_f;
-
+				$aliasVirtual = "i18n_{$field}";
 				if (is_array($locale)) {
 					foreach ($locale as $_locale) {
-						if (!isset($row[$model->alias][$aliasField]) && !empty($row[$model->alias]['i18n_' . $field . '_' . $_locale])) {
-							$row[$model->alias][$aliasField] = $row[$model->alias]['i18n_' . $field . '_' . $_locale];
-							$row[$model->alias]['locale'] = $_locale;
+						$aliasVirtualLocale = "{$aliasVirtual}_{$_locale}";
+						if (!isset($row[$Model->alias][$aliasField]) && !empty($row[$Model->alias][$aliasVirtualLocale])) {
+							$row[$Model->alias][$aliasField] = $row[$Model->alias][$aliasVirtualLocale];
+							$row[$Model->alias]['locale'] = $_locale;
 						}
-						unset($row[$model->alias]['i18n_' . $field . '_' . $_locale]);
+						unset($row[$Model->alias][$aliasVirtualLocale]);
 					}
 
-					if (!isset($row[$model->alias][$aliasField])) {
-						$row[$model->alias][$aliasField] = '';
+					if (!isset($row[$Model->alias][$aliasField])) {
+						$row[$Model->alias][$aliasField] = '';
 					}
 				} else {
 					$value = '';
-					if (!empty($row[$model->alias]['i18n_' . $field])) {
-						$value = $row[$model->alias]['i18n_' . $field];
+					if (!empty($row[$Model->alias][$aliasVirtual])) {
+						$value = $row[$Model->alias][$aliasVirtual];
 					}
-					$row[$model->alias][$aliasField] = $value;
-					unset($row[$model->alias]['i18n_' . $field]);
+					$row[$Model->alias][$aliasField] = $value;
+					unset($row[$Model->alias][$aliasVirtual]);
 				}
 			}
 		}
@@ -307,12 +309,12 @@ class TranslateBehavior extends ModelBehavior {
 /**
  * beforeValidate Callback
  *
- * @param Model $model Model invalidFields was called on.
+ * @param Model $Model Model invalidFields was called on.
  * @return boolean
  */
-	public function beforeValidate(Model $model) {
-		unset($this->runtime[$model->alias]['beforeSave']);
-		$this->_setRuntimeData($model);
+	public function beforeValidate(Model $Model) {
+		unset($this->runtime[$Model->alias]['beforeSave']);
+		$this->_setRuntimeData($Model);
 		return true;
 	}
 
@@ -322,17 +324,17 @@ class TranslateBehavior extends ModelBehavior {
  * Copies data into the runtime property when `$options['validate']` is
  * disabled.  Or the runtime data hasn't been set yet.
  *
- * @param Model $model Model save was called on.
+ * @param Model $Model Model save was called on.
  * @return boolean true.
  */
-	public function beforeSave(Model $model, $options = array()) {
+	public function beforeSave(Model $Model, $options = array()) {
 		if (isset($options['validate']) && $options['validate'] == false) {
-			unset($this->runtime[$model->alias]['beforeSave']);
+			unset($this->runtime[$Model->alias]['beforeSave']);
 		}
-		if (isset($this->runtime[$model->alias]['beforeSave'])) {
+		if (isset($this->runtime[$Model->alias]['beforeSave'])) {
 			return true;
 		}
-		$this->_setRuntimeData($model);
+		$this->_setRuntimeData($Model);
 		return true;
 	}
 
@@ -343,58 +345,58 @@ class TranslateBehavior extends ModelBehavior {
  * and to allow translations to be persisted even when validation
  * is disabled.
  *
- * @param Model $model
+ * @param Model $Model
  * @return void
  */
-	protected function _setRuntimeData(Model $model) {
-		$locale = $this->_getLocale($model);
+	protected function _setRuntimeData(Model $Model) {
+		$locale = $this->_getLocale($Model);
 		if (empty($locale)) {
 			return true;
 		}
-		$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
+		$fields = array_merge($this->settings[$Model->alias], $this->runtime[$Model->alias]['fields']);
 		$tempData = array();
 
 		foreach ($fields as $key => $value) {
 			$field = (is_numeric($key)) ? $value : $key;
 
-			if (isset($model->data[$model->alias][$field])) {
-				$tempData[$field] = $model->data[$model->alias][$field];
-				if (is_array($model->data[$model->alias][$field])) {
-					if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) {
-						$model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale];
+			if (isset($Model->data[$Model->alias][$field])) {
+				$tempData[$field] = $Model->data[$Model->alias][$field];
+				if (is_array($Model->data[$Model->alias][$field])) {
+					if (is_string($locale) && !empty($Model->data[$Model->alias][$field][$locale])) {
+						$Model->data[$Model->alias][$field] = $Model->data[$Model->alias][$field][$locale];
 					} else {
-						$values = array_values($model->data[$model->alias][$field]);
-						$model->data[$model->alias][$field] = $values[0];
+						$values = array_values($Model->data[$Model->alias][$field]);
+						$Model->data[$Model->alias][$field] = $values[0];
 					}
 				}
 			}
 		}
-		$this->runtime[$model->alias]['beforeSave'] = $tempData;
+		$this->runtime[$Model->alias]['beforeSave'] = $tempData;
 	}
 
 /**
  * afterSave Callback
  *
- * @param Model $model Model the callback is called on
+ * @param Model $Model Model the callback is called on
  * @param boolean $created Whether or not the save created a record.
  * @return void
  */
-	public function afterSave(Model $model, $created) {
-		if (!isset($this->runtime[$model->alias]['beforeValidate']) && !isset($this->runtime[$model->alias]['beforeSave'])) {
+	public function afterSave(Model $Model, $created) {
+		if (!isset($this->runtime[$Model->alias]['beforeValidate']) && !isset($this->runtime[$Model->alias]['beforeSave'])) {
 			return true;
 		}
-		$locale = $this->_getLocale($model);
-		if (isset($this->runtime[$model->alias]['beforeValidate'])) {
-			$tempData = $this->runtime[$model->alias]['beforeValidate'];
+		$locale = $this->_getLocale($Model);
+		if (isset($this->runtime[$Model->alias]['beforeValidate'])) {
+			$tempData = $this->runtime[$Model->alias]['beforeValidate'];
 		} else {
-			$tempData = $this->runtime[$model->alias]['beforeSave'];
+			$tempData = $this->runtime[$Model->alias]['beforeSave'];
 		}
 
-		unset($this->runtime[$model->alias]['beforeValidate'], $this->runtime[$model->alias]['beforeSave']);
-		$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
-		$RuntimeModel = $this->translateModel($model);
+		unset($this->runtime[$Model->alias]['beforeValidate'], $this->runtime[$Model->alias]['beforeSave']);
+		$conditions = array('model' => $Model->alias, 'foreign_key' => $Model->id);
+		$RuntimeModel = $this->translateModel($Model);
 
-		$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
+		$fields = array_merge($this->settings[$Model->alias], $this->runtime[$Model->alias]['fields']);
 		if ($created) {
 			// set each field value to an empty string
 			foreach ($fields as $key => $field) {
@@ -437,29 +439,29 @@ class TranslateBehavior extends ModelBehavior {
 /**
  * afterDelete Callback
  *
- * @param Model $model Model the callback was run on.
+ * @param Model $Model Model the callback was run on.
  * @return void
  */
-	public function afterDelete(Model $model) {
-		$RuntimeModel = $this->translateModel($model);
-		$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
+	public function afterDelete(Model $Model) {
+		$RuntimeModel = $this->translateModel($Model);
+		$conditions = array('model' => $Model->alias, 'foreign_key' => $Model->id);
 		$RuntimeModel->deleteAll($conditions);
 	}
 
 /**
  * Get selected locale for model
  *
- * @param Model $model Model the locale needs to be set/get on.
+ * @param Model $Model Model the locale needs to be set/get on.
  * @return mixed string or false
  */
-	protected function _getLocale(Model $model) {
-		if (!isset($model->locale) || is_null($model->locale)) {
+	protected function _getLocale(Model $Model) {
+		if (!isset($Model->locale) || is_null($Model->locale)) {
 			$I18n = I18n::getInstance();
 			$I18n->l10n->get(Configure::read('Config.language'));
-			$model->locale = $I18n->l10n->locale;
+			$Model->locale = $I18n->l10n->locale;
 		}
 
-		return $model->locale;
+		return $Model->locale;
 	}
 
 /**
@@ -468,25 +470,25 @@ class TranslateBehavior extends ModelBehavior {
  * If the model has a translateModel property set, this will be used as the class
  * name to find/use.  If no translateModel property is found 'I18nModel' will be used.
  *
- * @param Model $model Model to get a translatemodel for.
+ * @param Model $Model Model to get a translatemodel for.
  * @return Model
  */
-	public function translateModel(Model $model) {
-		if (!isset($this->runtime[$model->alias]['model'])) {
-			if (!isset($model->translateModel) || empty($model->translateModel)) {
+	public function translateModel(Model $Model) {
+		if (!isset($this->runtime[$Model->alias]['model'])) {
+			if (!isset($Model->translateModel) || empty($Model->translateModel)) {
 				$className = 'I18nModel';
 			} else {
-				$className = $model->translateModel;
+				$className = $Model->translateModel;
 			}
 
-			$this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
+			$this->runtime[$Model->alias]['model'] = ClassRegistry::init($className, 'Model');
 		}
-		if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) {
-			$this->runtime[$model->alias]['model']->setSource($model->translateTable);
-		} elseif (empty($model->translateTable) && empty($model->translateModel)) {
-			$this->runtime[$model->alias]['model']->setSource('i18n');
+		if (!empty($Model->translateTable) && $Model->translateTable !== $this->runtime[$Model->alias]['model']->useTable) {
+			$this->runtime[$Model->alias]['model']->setSource($Model->translateTable);
+		} elseif (empty($Model->translateTable) && empty($Model->translateModel)) {
+			$this->runtime[$Model->alias]['model']->setSource('i18n');
 		}
-		return $this->runtime[$model->alias]['model'];
+		return $this->runtime[$Model->alias]['model'];
 	}
 
 /**
@@ -496,7 +498,7 @@ class TranslateBehavior extends ModelBehavior {
  * *Note* You should avoid binding translations that overlap existing model properties.
  * This can cause un-expected and un-desirable behavior.
  *
- * @param Model $model instance of model
+ * @param Model $Model instance of model
  * @param string|array $fields string with field or array(field1, field2=>AssocName, field3)
  * @param boolean $reset Leave true to have the fields only modified for the next operation.
  *   if false the field will be added for all future queries.
@@ -504,12 +506,12 @@ class TranslateBehavior extends ModelBehavior {
  * @throws CakeException when attempting to bind a translating called name.  This is not allowed
  *   as it shadows Model::$name.
  */
-	public function bindTranslation(Model $model, $fields, $reset = true) {
+	public function bindTranslation(Model $Model, $fields, $reset = true) {
 		if (is_string($fields)) {
 			$fields = array($fields);
 		}
 		$associations = array();
-		$RuntimeModel = $this->translateModel($model);
+		$RuntimeModel = $this->translateModel($Model);
 		$default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
 
 		foreach ($fields as $key => $value) {
@@ -526,39 +528,39 @@ class TranslateBehavior extends ModelBehavior {
 				);
 			}
 
-			$this->_removeField($model, $field);
+			$this->_removeField($Model, $field);
 
 			if (is_null($association)) {
 				if ($reset) {
-					$this->runtime[$model->alias]['fields'][] = $field;
+					$this->runtime[$Model->alias]['fields'][] = $field;
 				} else {
-					$this->settings[$model->alias][] = $field;
+					$this->settings[$Model->alias][] = $field;
 				}
 			} else {
 				if ($reset) {
-					$this->runtime[$model->alias]['fields'][$field] = $association;
+					$this->runtime[$Model->alias]['fields'][$field] = $association;
 				} else {
-					$this->settings[$model->alias][$field] = $association;
+					$this->settings[$Model->alias][$field] = $association;
 				}
 
 				foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
-					if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
+					if (isset($Model->{$type}[$association]) || isset($Model->__backAssociation[$type][$association])) {
 						trigger_error(
-							__d('cake_dev', 'Association %s is already bound to model %s', $association, $model->alias),
+							__d('cake_dev', 'Association %s is already bound to model %s', $association, $Model->alias),
 							E_USER_ERROR
 						);
 						return false;
 					}
 				}
 				$associations[$association] = array_merge($default, array('conditions' => array(
-					'model' => $model->alias,
+					'model' => $Model->alias,
 					$RuntimeModel->displayField => $field
 				)));
 			}
 		}
 
 		if (!empty($associations)) {
-			$model->bindModel(array('hasMany' => $associations), $reset);
+			$Model->bindModel(array('hasMany' => $associations), $reset);
 		}
 		return true;
 	}
@@ -568,17 +570,17 @@ class TranslateBehavior extends ModelBehavior {
  *
  * @param string $field The field to update.
  */
-	protected function _removeField(Model $model, $field) {
-		if (array_key_exists($field, $this->settings[$model->alias])) {
-			unset($this->settings[$model->alias][$field]);
-		} elseif (in_array($field, $this->settings[$model->alias])) {
-			$this->settings[$model->alias] = array_merge(array_diff($this->settings[$model->alias], array($field)));
+	protected function _removeField(Model $Model, $field) {
+		if (array_key_exists($field, $this->settings[$Model->alias])) {
+			unset($this->settings[$Model->alias][$field]);
+		} elseif (in_array($field, $this->settings[$Model->alias])) {
+			$this->settings[$Model->alias] = array_merge(array_diff($this->settings[$Model->alias], array($field)));
 		}
 
-		if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
-			unset($this->runtime[$model->alias]['fields'][$field]);
-		} elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
-			$this->runtime[$model->alias]['fields'] = array_merge(array_diff($this->runtime[$model->alias]['fields'], array($field)));
+		if (array_key_exists($field, $this->runtime[$Model->alias]['fields'])) {
+			unset($this->runtime[$Model->alias]['fields'][$field]);
+		} elseif (in_array($field, $this->runtime[$Model->alias]['fields'])) {
+			$this->runtime[$Model->alias]['fields'] = array_merge(array_diff($this->runtime[$Model->alias]['fields'], array($field)));
 		}
 	}
 
@@ -586,23 +588,23 @@ class TranslateBehavior extends ModelBehavior {
  * Unbind translation for fields, optionally unbinds hasMany association for
  * fake field
  *
- * @param Model $model instance of model
+ * @param Model $Model instance of model
  * @param string|array $fields string with field, or array(field1, field2=>AssocName, field3), or null for
  *    unbind all original translations
  * @return boolean
  */
-	public function unbindTranslation(Model $model, $fields = null) {
-		if (empty($fields) && empty($this->settings[$model->alias])) {
+	public function unbindTranslation(Model $Model, $fields = null) {
+		if (empty($fields) && empty($this->settings[$Model->alias])) {
 			return false;
 		}
 		if (empty($fields)) {
-			return $this->unbindTranslation($model, $this->settings[$model->alias]);
+			return $this->unbindTranslation($Model, $this->settings[$Model->alias]);
 		}
 
 		if (is_string($fields)) {
 			$fields = array($fields);
 		}
-		$RuntimeModel = $this->translateModel($model);
+		$RuntimeModel = $this->translateModel($Model);
 		$associations = array();
 
 		foreach ($fields as $key => $value) {
@@ -614,18 +616,17 @@ class TranslateBehavior extends ModelBehavior {
 				$association = $value;
 			}
 
-			$this->_removeField($model, $field);
+			$this->_removeField($Model, $field);
 
-			if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
+			if (!is_null($association) && (isset($Model->hasMany[$association]) || isset($Model->__backAssociation['hasMany'][$association]))) {
 				$associations[] = $association;
 			}
 		}
 
 		if (!empty($associations)) {
-			$model->unbindModel(array('hasMany' => $associations), false);
+			$Model->unbindModel(array('hasMany' => $associations), false);
 		}
 		return true;
 	}
 
 }
-

+ 14 - 1
lib/Cake/Model/Datasource/DboSource.php

@@ -2953,11 +2953,24 @@ class DboSource extends DataSource {
 				$primary = null;
 				$table = $this->fullTableName($curTable);
 
+				$primaryCount = 0;
+				foreach ($columns as $col) {
+					if (isset($col['key']) && $col['key'] === 'primary') {
+						$primaryCount++;
+					}
+				}
+
 				foreach ($columns as $name => $col) {
 					if (is_string($col)) {
 						$col = array('type' => $col);
 					}
-					if (isset($col['key']) && $col['key'] === 'primary') {
+					$isPrimary = isset($col['key']) && $col['key'] === 'primary';
+					// Multi-column primary keys are not supported.
+					if ($isPrimary && $primaryCount > 1) {
+						unset($col['key']);
+						$isPrimary = false;
+					}
+					if ($isPrimary) {
 						$primary = $name;
 					}
 					if ($name !== 'indexes' && $name !== 'tableParameters') {

+ 4 - 1
lib/Cake/Routing/Router.php

@@ -182,7 +182,10 @@ class Router {
  * @throws RouterException
  */
 	protected static function _validateRouteClass($routeClass) {
-		if (!class_exists($routeClass) || !is_subclass_of($routeClass, 'CakeRoute')) {
+		if (
+			$routeClass != 'CakeRoute' &&
+			(!class_exists($routeClass) || !is_subclass_of($routeClass, 'CakeRoute'))
+		) {
 			throw new RouterException(__d('cake_dev', 'Route classes must extend CakeRoute'));
 		}
 		return $routeClass;

+ 9 - 0
lib/Cake/Test/Case/BasicsTest.php

@@ -225,6 +225,15 @@ class BasicsTest extends CakeTestCase {
 			'n' => ' '
 		);
 		$this->assertEquals($expected, $result);
+		
+		// Test that boolean values are not converted to strings
+		$result = h(false);
+		$this->assertFalse($result);
+
+		$arr = array('foo' => false, 'bar' => true);
+		$result = h($arr);
+		$this->assertFalse($result['foo']);
+		$this->assertTrue($result['bar']);
 
 		$obj = new stdClass();
 		$result = h($obj);

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

@@ -117,10 +117,10 @@ class ControllerTaskTest extends CakeTestCase {
 
 		$this->Task->connection = 'test';
 		$this->Task->interactive = true;
-		$this->Task->expects($this->at(1))->method('out')->with('1. BakeArticles');
-		$this->Task->expects($this->at(2))->method('out')->with('2. BakeArticlesBakeTags');
-		$this->Task->expects($this->at(3))->method('out')->with('3. BakeComments');
-		$this->Task->expects($this->at(4))->method('out')->with('4. BakeTags');
+		$this->Task->expects($this->at(2))->method('out')->with(' 1. BakeArticles');
+		$this->Task->expects($this->at(3))->method('out')->with(' 2. BakeArticlesBakeTags');
+		$this->Task->expects($this->at(4))->method('out')->with(' 3. BakeComments');
+		$this->Task->expects($this->at(5))->method('out')->with(' 4. BakeTags');
 
 		$expected = array('BakeArticles', 'BakeArticlesBakeTags', 'BakeComments', 'BakeTags');
 		$result = $this->Task->listAll('test');

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

@@ -491,6 +491,8 @@ class TestTaskTest extends CakeTestCase {
 
 		$result = $this->Task->bake('Component', 'Example');
 
+		$this->assertContains("App::uses('Component', 'Controller')", $result);
+		$this->assertContains("App::uses('ComponentCollection', 'Controller')", $result);
 		$this->assertContains("App::uses('ExampleComponent', 'Controller/Component')", $result);
 		$this->assertContains('class ExampleComponentTest extends CakeTestCase', $result);
 

+ 4 - 0
lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php

@@ -1269,6 +1269,7 @@ class AuthComponentTest extends CakeTestCase {
 					'id' => '1',
 					'name' => 'Members'
 				),
+				'is_admin' => false,
 		));
 		$this->Auth->Session->write('Auth', $data);
 
@@ -1286,5 +1287,8 @@ class AuthComponentTest extends CakeTestCase {
 
 		$result = $this->Auth->user('Company.invalid');
 		$this->assertEquals(null, $result);
+
+		$result = $this->Auth->user('is_admin');
+		$this->assertFalse($result);
 	}
 }

+ 39 - 0
lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php

@@ -839,6 +839,45 @@ class MysqlTest extends CakeTestCase {
 	}
 
 /**
+ * Test that two columns with key => primary doesn't create invalid sql.
+ *
+ * @return void
+ */
+	public function testTwoColumnsWithPrimaryKey() {
+		$schema = new CakeSchema(array(
+			'connection' => 'test',
+			'roles_users' => array(
+				'role_id' => array(
+					'type' => 'integer',
+					'null' => false,
+					'default' => null,
+					'key' => 'primary'
+				),
+				'user_id' => array(
+					'type' => 'integer',
+					'null' => false,
+					'default' => null,
+					'key' => 'primary'
+				),
+				'indexes' => array(
+					'user_role_index' => array(
+						'column' => array('role_id', 'user_id'),
+						'unique' => 1
+					),
+					'user_index' => array(
+						'column' => 'user_id',
+						'unique' => 0
+					)
+				),
+			)
+		));
+
+		$result = $this->Dbo->createSchema($schema);
+		$this->assertContains('`role_id` int(11) NOT NULL,', $result);
+		$this->assertContains('`user_id` int(11) NOT NULL,', $result);
+	}
+
+/**
  * Tests that listSources method sends the correct query and parses the result accordingly
  * @return void
  */

+ 0 - 5
lib/Cake/Test/Case/Utility/DebuggerTest.php

@@ -1,9 +1,5 @@
 <?php
 /**
- * DebuggerTest file
- *
- * PHP 5
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -12,7 +8,6 @@
  *
  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  * @link          http://cakephp.org CakePHP Project
- * @package       Cake.Test.Case.Utility
  * @since         CakePHP(tm) v 1.2.0.5432
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */

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

@@ -5125,6 +5125,45 @@ class FormHelperTest extends CakeTestCase {
 	}
 
 /**
+ * testDateTimeEmptyAsArray
+ *
+ * @return void
+ */
+	public function testDateTimeEmptyAsArray() {
+		$result = $this->Form->dateTime('Contact.date',
+			'DMY',
+			'12',
+			array(
+				'empty' => array('day' => 'DAY', 'month' => 'MONTH', 'year' => 'YEAR',
+					'hour' => 'HOUR', 'minute' => 'MINUTE', 'meridian' => false
+				)
+			)
+		);
+
+		$this->assertRegExp('/<option value="">DAY<\/option>/', $result);
+		$this->assertRegExp('/<option value="">MONTH<\/option>/', $result);
+		$this->assertRegExp('/<option value="">YEAR<\/option>/', $result);
+		$this->assertRegExp('/<option value="">HOUR<\/option>/', $result);
+		$this->assertRegExp('/<option value="">MINUTE<\/option>/', $result);
+		$this->assertNotRegExp('/<option value=""><\/option>/', $result);
+
+		$result = $this->Form->dateTime('Contact.date',
+			'DMY',
+			'12',
+			array(
+				'empty' => array('day' => 'DAY', 'month' => 'MONTH', 'year' => 'YEAR')
+			)
+		);
+
+		$this->assertRegExp('/<option value="">DAY<\/option>/', $result);
+		$this->assertRegExp('/<option value="">MONTH<\/option>/', $result);
+		$this->assertRegExp('/<option value="">YEAR<\/option>/', $result);
+		$this->assertRegExp('/<select[^<>]+id="ContactDateHour">\s<option value=""><\/option>/', $result);
+		$this->assertRegExp('/<select[^<>]+id="ContactDateMin">\s<option value=""><\/option>/', $result);
+		$this->assertRegExp('/<select[^<>]+id="ContactDateMeridian">\s<option value=""><\/option>/', $result);
+	}
+
+/**
  * testFormDateTimeMulti method
  *
  * test multiple datetime element generation

+ 9 - 3
lib/Cake/Utility/Debugger.php

@@ -710,8 +710,14 @@ class Debugger {
 
 		$files = $this->trace(array('start' => $data['start'], 'format' => 'points'));
 		$code = '';
-		if (isset($files[1]['file'])) {
-			$code = $this->excerpt($files[1]['file'], $files[1]['line'] - 1, 1);
+		$file = null;
+		if (isset($files[0]['file'])) {
+			$file = $files[0];
+		} elseif (isset($files[1]['file'])) {
+			$file = $files[1];
+		}
+		if ($file) {
+			$code = $this->excerpt($file['file'], $file['line'] - 1, 1);
 		}
 		$trace = $this->trace(array('start' => $data['start'], 'depth' => '20'));
 		$insertOpts = array('before' => '{:', 'after' => '}');
@@ -720,7 +726,7 @@ class Debugger {
 		$info = '';
 
 		foreach ((array)$data['context'] as $var => $value) {
-			$context[] = "\${$var} = " . $this->exportVar($value, 1);
+			$context[] = "\${$var} = " . $this->exportVar($value, 3);
 		}
 
 		switch ($this->_outputFormat) {

+ 1 - 1
lib/Cake/View/Helper.php

@@ -691,7 +691,7 @@ class Helper extends Object {
 		$data = $this->request->data;
 
 		$entity = $this->entity();
-		if (!empty($data) && !empty($entity)) {
+		if (!empty($data) && is_array($data) && !empty($entity)) {
 			$result = Hash::get($data, implode('.', $entity));
 		}
 

+ 11 - 0
lib/Cake/View/Helper/FormHelper.php

@@ -2259,6 +2259,17 @@ class FormHelper extends AppHelper {
 			}
 		}
 
+		if (is_array($attributes['empty'])) {
+			$attributes['empty'] += array(
+				'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)];
+			}
+		}
+
 		$selects = array();
 		foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
 			switch ($char) {

+ 2 - 0
lib/Cake/basics.php

@@ -175,6 +175,8 @@ function h($text, $double = true, $charset = null) {
 		} else {
 			$text = '(object)' . get_class($text);
 		}
+	} elseif (is_bool($text)) {
+		return $text;
 	}
 
 	static $defaultCharset = false;