Index: app/locale/eng/LC_MESSAGES/empty
===================================================================
Index: app/tmp/tests/empty
===================================================================
Index: app/tmp/cache/persistent/empty
===================================================================
Index: app/tmp/cache/models/empty
===================================================================
Index: app/tmp/cache/views/empty
===================================================================
Index: app/tmp/logs/empty
===================================================================
Index: app/tmp/sessions/empty
===================================================================
Index: app/vendors/shells/tasks/empty
===================================================================
Index: app/vendors/shells/templates/empty
===================================================================
Index: app/plugins/empty
===================================================================
Index: app/webroot/test.php
===================================================================
--- app/webroot/test.php	(revision 0)
+++ app/webroot/test.php	(revision 0)
@@ -0,0 +1,181 @@
+<?php
+/* SVN FILE: $Id: test.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+error_reporting(E_ALL);
+set_time_limit(0);
+ini_set('memory_limit','128M');
+ini_set('display_errors', 1);
+/**
+ * Use the DS to separate the directories in other defines
+ */
+	if (!defined('DS')) {
+		define('DS', DIRECTORY_SEPARATOR);
+	}
+/**
+ * These defines should only be edited if you have cake installed in
+ * a directory layout other than the way it is distributed.
+ * When using custom settings be sure to use the DS and do not add a trailing DS.
+ */
+
+/**
+ * The full path to the directory which holds "app", WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('ROOT')) {
+		define('ROOT', dirname(dirname(dirname(__FILE__))));
+	}
+/**
+ * The actual directory name for the "app".
+ *
+ */
+	if (!defined('APP_DIR')) {
+		define('APP_DIR', basename(dirname(dirname(__FILE__))));
+	}
+/**
+ * The absolute path to the "cake" directory, WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+
+/**
+ * Editing below this line should not be necessary.
+ * Change at your own risk.
+ *
+ */
+if (!defined('WEBROOT_DIR')) {
+	define('WEBROOT_DIR', basename(dirname(__FILE__)));
+}
+if (!defined('WWW_ROOT')) {
+	define('WWW_ROOT', dirname(__FILE__) . DS);
+}
+if (!defined('CORE_PATH')) {
+	if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
+		define('APP_PATH', null);
+		define('CORE_PATH', null);
+	} else {
+		define('APP_PATH', ROOT . DS . APP_DIR . DS);
+		define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+	}
+}
+if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
+	trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
+}
+
+$corePath = Configure::corePaths('cake');
+if (isset($corePath[0])) {
+	define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS);
+} else {
+	define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH);
+}
+
+require_once CAKE_TESTS_LIB . 'test_manager.php';
+
+if (Configure::read('debug') < 1) {
+	die(__('Debug setting does not allow access to this url.', true));
+}
+
+if (!isset($_SERVER['SERVER_NAME'])) {
+	$_SERVER['SERVER_NAME'] = '';
+}
+if (empty( $_GET['output'])) {
+	$_GET['output'] = 'html';
+}
+/**
+ *
+ * Used to determine output to display
+ */
+define('CAKE_TEST_OUTPUT_HTML', 1);
+define('CAKE_TEST_OUTPUT_TEXT', 2);
+
+if (isset($_GET['output']) && $_GET['output'] == 'html') {
+	define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_HTML);
+} else {
+	Debugger::output('txt');
+	define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_TEXT);
+}
+
+if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
+	CakePHPTestHeader();
+	include CAKE_TESTS_LIB . 'simpletest.php';
+	CakePHPTestSuiteFooter();
+	exit();
+}
+
+$analyzeCodeCoverage = false;
+if (isset($_GET['code_coverage'])) {
+	$analyzeCodeCoverage = true;
+	require_once CAKE_TESTS_LIB . 'code_coverage_manager.php';
+	if (!extension_loaded('xdebug')) {
+		CakePHPTestHeader();
+		include CAKE_TESTS_LIB . 'xdebug.php';
+		CakePHPTestSuiteFooter();
+		exit();
+	}
+}
+
+CakePHPTestHeader();
+CakePHPTestSuiteHeader();
+define('RUN_TEST_LINK', $_SERVER['PHP_SELF']);
+
+if (isset($_GET['group'])) {
+	if ('all' == $_GET['group']) {
+		TestManager::runAllTests(CakeTestsGetReporter());
+	} else {
+		if ($analyzeCodeCoverage) {
+			CodeCoverageManager::start($_GET['group'], CakeTestsGetReporter());
+		}
+		TestManager::runGroupTest(ucfirst($_GET['group']), CakeTestsGetReporter());
+		if ($analyzeCodeCoverage) {
+			CodeCoverageManager::report();
+		}
+	}
+
+	CakePHPTestRunMore();
+	CakePHPTestAnalyzeCodeCoverage();
+} elseif (isset($_GET['case'])) {
+	if ($analyzeCodeCoverage) {
+		CodeCoverageManager::start($_GET['case'], CakeTestsGetReporter());
+	}
+
+	TestManager::runTestCase($_GET['case'], CakeTestsGetReporter());
+
+	if ($analyzeCodeCoverage) {
+		CodeCoverageManager::report();
+	}
+
+	CakePHPTestRunMore();
+	CakePHPTestAnalyzeCodeCoverage();
+} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') {
+	CakePHPTestCaseList();
+} else {
+	CakePHPTestGroupTestList();
+}
+CakePHPTestSuiteFooter();
+$output = ob_get_clean();
+echo $output;
+?>
\ No newline at end of file
Index: app/webroot/css/cake.generic.css
===================================================================
--- app/webroot/css/cake.generic.css	(revision 0)
+++ app/webroot/css/cake.generic.css	(revision 0)
@@ -0,0 +1,479 @@
+/* SVN FILE: $Id: cake.generic.css 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.css
+ * @since         CakePHP(tm)
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+* {
+	margin:0;
+	padding:0;
+}
+
+/* General Style Info */
+body {
+	background: #003d4c;
+	color: #fff;
+	font-family:'lucida grande',verdana,helvetica,arial,sans-serif;
+	font-size:90%;
+	margin: 0;
+}
+a {
+	background:#fff;
+	color: #003d4c;
+	text-decoration: underline;
+	font-weight: bold;
+}
+a:hover {
+	background:#fff;
+	color: #003d4c;
+	text-decoration:none;
+}
+a img {
+	border:none;
+}
+h1, h2, h3, h4 {
+	font-weight: normal;
+}
+h1 {
+	background:#fff;
+	color: #003d4c;
+	font-size: 100%;
+	margin: 0.1em 0;
+}
+h2 {
+	background:#fff;
+	color: #e32;
+	font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
+	font-size: 190%;
+	margin: 0.3em 0;
+	padding-top: 0.8em;
+}
+h3 {
+	color: #993;
+	font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
+	font-size: 165%;
+	padding-top: 1.5em;
+}
+h4 {
+	color: #993;
+	font-weight: normal;
+	padding-top: 0.5em;
+}
+ul, li {
+	margin: 0 12px;
+}
+
+/* Layout */
+#container {
+	text-align: left;
+}
+
+#header{
+	padding: 10px 20px;
+}
+#header h1 {
+	background: #003d4c url('../img/cake.icon.gif') no-repeat left;
+	color: #fff;
+	padding: 0px 30px;
+}
+#header h1 a {
+	color: #fff;
+	background: #003d4c;
+	font-weight: normal;
+	text-decoration: none;
+}
+#header h1 a:hover {
+	color: #fff;
+	background: #003d4c;
+	text-decoration: underline;
+}
+#content{
+	background: #fff;
+	clear: both;
+	color: #333;
+	padding: 10px 20px 40px 20px;
+	overflow: auto;
+}
+#footer {
+	clear: both;
+	padding: 6px 10px;
+	text-align: right;
+}
+
+/* Tables */
+table {
+	background: #fff;
+	border:1px solid #ccc;
+	border-right:0;
+	clear: both;
+	color: #333;
+	margin-bottom: 10px;
+	width: 100%;
+}
+th {
+	background: #f2f2f2;
+	border:1px solid #bbb;
+	border-top: 1px solid #fff;
+	border-left: 1px solid #fff;
+	text-align: center;
+}
+th a {
+	background:#f2f2f2;
+	display: block;
+	padding: 2px 4px;
+	text-decoration: none;
+}
+th a:hover {
+	background: #ccc;
+	color: #333;
+	text-decoration: none;
+}
+table tr td {
+	background: #fff;
+	border-right: 1px solid #ccc;
+	padding: 4px;
+	text-align: center;
+	vertical-align: top;
+}
+table tr.altrow td {
+	background: #f4f4f4;
+}
+td.actions {
+	text-align: center;
+	white-space: nowrap;
+}
+td.actions a {
+	margin: 0px 6px;
+}
+.cake-sql-log table {
+	background: #f4f4f4;
+}
+.cake-sql-log td {
+	padding: 4px 8px;
+	text-align: left;
+}
+
+/* Paging */
+div.paging {
+	background:#fff;
+	color: #ccc;
+	margin-bottom: 2em;
+}
+div.paging div.disabled {
+	color: #ddd;
+	display: inline;
+}
+div.paging span {
+}
+div.paging span.current {
+	color: #000;
+}
+div.paging span a {
+}
+
+/* Scaffold View */
+dl {
+	line-height: 2em;
+	margin: 0em 0em;
+	width: 60%;
+}
+dl.altrow {
+	background: #f4f4f4;
+}
+dt {
+	font-weight: bold;
+	padding-left: 4px;
+	vertical-align: top;
+}
+dd {
+	margin-left: 10em;
+	margin-top: -2em;
+	vertical-align: top;
+}
+
+/* Forms */
+form {
+	clear: both;
+	margin-right: 20px;
+	padding: 0;
+	width: 80%;
+}
+fieldset {
+	border: 1px solid #ccc;
+	margin-top: 30px;
+	padding: 16px 20px;
+}
+fieldset legend {
+	background:#fff;
+	color: #e32;
+	font-size: 160%;
+	font-weight: bold;
+}
+fieldset fieldset {
+	margin-top: 0px;
+	margin-bottom: 20px;
+	padding: 16px 10px;
+}
+fieldset fieldset legend {
+	font-size: 120%;
+	font-weight: normal;
+}
+fieldset fieldset div {
+	clear: left;
+	margin: 0 20px;
+}
+form div {
+	clear: both;
+	margin-bottom: 1em;
+	padding: .5em;
+	vertical-align: text-top;
+}
+form div.input {
+	color: #444;
+}
+form div.required {
+	color: #333;
+	font-weight: bold;
+}
+form div.submit {
+	border: 0;
+	clear: both;
+	margin-top: 10px;
+	margin-left: 140px;
+}
+label {
+	display: block;
+	font-size: 110%;
+	padding-right: 20px;
+}
+input, textarea {
+	clear: both;
+	font-size: 140%;
+	font-family: "frutiger linotype", "lucida grande", "verdana", sans-serif;
+	padding: 2px;
+	width: 100%;
+}
+select {
+	clear: both;
+	font-size: 120%;
+	vertical-align: text-bottom;
+}
+select[multiple=multiple] {
+	width: 100%;
+}
+option {
+	font-size: 120%;
+	padding: 0 3px;
+}
+input[type=checkbox] {
+	clear: left;
+	float: left;
+	margin: 0px 6px 7px 2px;
+	width: auto;
+}
+input[type=radio] {
+	float:left;
+	width:auto;
+	margin: 0 3px 7px 0;
+}
+div.radio label {
+	margin: 0 0 6px 20px;
+}
+input[type=submit] {
+	display: inline;
+	font-size: 110%;
+	padding: 2px 5px;
+	width: auto;
+	vertical-align: bottom;
+}
+
+/* Notices and Errors */
+div.message {
+	clear: both;
+	color: #900;
+	font-size: 140%;
+	font-weight: bold;
+	margin: 1em 0;
+}
+div.error-message {
+	clear: both;
+	color: #900;
+	font-weight: bold;
+}
+p.error {
+	background-color: #e32;
+	color: #fff;
+	font-family: Courier, monospace;
+	font-size: 120%;
+	line-height: 140%;
+	padding: 0.8em;
+	margin: 1em 0;
+}
+p.error em {
+	color: #000;
+	font-weight: normal;
+	line-height: 140%;
+}
+.notice {
+	background: #ffcc00;
+	color: #000;
+	display: block;
+	font-family: Courier, monospace;
+	font-size: 120%;
+	line-height: 140%;
+	padding: 0.8em;
+	margin: 1em 0;
+}
+.success {
+	background: green;
+	color: #fff;
+}
+
+/*  Actions  */
+div.actions ul {
+	margin: 0px 0;
+	padding: 0;
+}
+div.actions li {
+	display: inline;
+	list-style-type: none;
+	line-height: 2em;
+	margin: 0 2em 0 0;
+	white-space: nowrap;
+}
+div.actions ul li a {
+	background:#fff;
+	color: #003d4c;
+	text-decoration: none;
+}
+div.actions ul li a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+/* Related */
+div.related {
+	clear: both;
+	display: block;
+}
+
+/* Debugging */
+pre {
+	color: #000;
+	background: #f0f0f0;
+	padding: 1em;
+}
+pre.cake-debug {
+	background: #ffcc00;
+	font-size: 120%;
+	line-height: 140%;
+	margin-top: 1em;
+	overflow: auto;
+	position: relative;
+}
+div.cake-stack-trace {
+	background: #fff;
+	border: 4px dotted #ffcc00;
+	color: #333;
+	margin: 0px;
+	padding: 6px;
+	font-size: 120%;
+	line-height: 140%;
+	overflow: auto;
+	position: relative;
+}
+div.cake-code-dump pre {
+	position: relative;
+	overflow: auto;
+}
+div.cake-stack-trace pre, div.cake-code-dump pre {
+	color: #000;
+	background-color: #F0F0F0;
+	margin: 0px;
+	padding: 1em;
+	overflow: auto;
+}
+div.cake-code-dump pre, div.cake-code-dump pre code {
+	clear: both;
+	font-size: 12px;
+	line-height: 15px;
+	margin: 4px 2px;
+	padding: 4px;
+	overflow: auto;
+}
+div.cake-code-dump span.code-highlight {
+	background-color: #ff0;
+	padding: 4px;
+}
+div.code-coverage-results div.code-line {
+	padding-left:5px;
+	display:block;
+	margin-left:10px;
+}
+div.code-coverage-results div.uncovered span.content {
+	background:#ecc;
+}
+div.code-coverage-results div.covered span.content {
+	background:#cec;
+}
+div.code-coverage-results div.ignored span.content {
+	color:#aaa;
+}
+div.code-coverage-results span.line-num {
+	color:#666;
+	display:block;
+	float:left;
+	width:20px;
+	text-align:right;
+	margin-right:5px;
+}
+div.code-coverage-results span.line-num strong {
+	color:#666;
+}
+div.code-coverage-results div.start {
+	border:1px solid #aaa;
+	border-width:1px 1px 0px 1px;
+	margin-top:30px;
+	padding-top:5px;
+}
+div.code-coverage-results div.end {
+	border:1px solid #aaa;
+	border-width:0px 1px 1px 1px;
+	margin-bottom:30px;
+	padding-bottom:5px;
+}
+div.code-coverage-results div.realstart {
+	margin-top:0px;
+}
+div.code-coverage-results p.note {
+	color:#bbb;
+	padding:5px;
+	margin:5px 0 10px;
+	font-size:10px;
+}
+div.code-coverage-results span.result-bad {
+	color: #a00;
+}
+div.code-coverage-results span.result-ok {
+	color: #fa0;
+}
+div.code-coverage-results span.result-good {
+	color: #0a0;
+}
\ No newline at end of file
Index: app/webroot/.htaccess
===================================================================
--- app/webroot/.htaccess	(revision 0)
+++ app/webroot/.htaccess	(revision 0)
@@ -0,0 +1,6 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
+</IfModule>
\ No newline at end of file
Index: app/webroot/img/cake.power.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: app/webroot/img/cake.power.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: app/webroot/img/cake.icon.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: app/webroot/img/cake.icon.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: app/webroot/js/vendors.php
===================================================================
--- app/webroot/js/vendors.php	(revision 0)
+++ app/webroot/js/vendors.php	(revision 0)
@@ -0,0 +1,42 @@
+<?php
+/* SVN FILE: $Id: vendors.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file includes js vendor-files from /vendor/ directory if they need to
+ * be accessible to the public.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.js
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Enter description here...
+ */
+if (isset($_GET['file'])) {
+	$file = $_GET['file'];
+	$pos = strpos($file, '..');
+	if ($pos === false) {
+		if (is_file('../../vendors/javascript/'.$file) && (preg_match('/(\/.+)\\.js/', $file))) {
+			readfile('../../vendors/javascript/'.$file);
+			return;
+		}
+	}
+}
+header('HTTP/1.1 404 Not Found');
+?>
\ No newline at end of file
Index: app/webroot/index.php
===================================================================
--- app/webroot/index.php	(revision 0)
+++ app/webroot/index.php	(revision 0)
@@ -0,0 +1,93 @@
+<?php
+/* SVN FILE: $Id: index.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Use the DS to separate the directories in other defines
+ */
+	if (!defined('DS')) {
+		define('DS', DIRECTORY_SEPARATOR);
+	}
+/**
+ * These defines should only be edited if you have cake installed in
+ * a directory layout other than the way it is distributed.
+ * When using custom settings be sure to use the DS and do not add a trailing DS.
+ */
+
+/**
+ * The full path to the directory which holds "app", WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('ROOT')) {
+		define('ROOT', dirname(dirname(dirname(__FILE__))));
+	}
+/**
+ * The actual directory name for the "app".
+ *
+ */
+	if (!defined('APP_DIR')) {
+		define('APP_DIR', basename(dirname(dirname(__FILE__))));
+	}
+/**
+ * The absolute path to the "cake" directory, WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+
+/**
+ * Editing below this line should NOT be necessary.
+ * Change at your own risk.
+ *
+ */
+	if (!defined('WEBROOT_DIR')) {
+		define('WEBROOT_DIR', basename(dirname(__FILE__)));
+	}
+	if (!defined('WWW_ROOT')) {
+		define('WWW_ROOT', dirname(__FILE__) . DS);
+	}
+	if (!defined('CORE_PATH')) {
+		if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
+			define('APP_PATH', null);
+			define('CORE_PATH', null);
+		} else {
+			define('APP_PATH', ROOT . DS . APP_DIR . DS);
+			define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+		}
+	}
+	if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
+		trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
+	}
+	if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') {
+		return;
+	} else {
+		$Dispatcher = new Dispatcher();
+		$Dispatcher->dispatch($url);
+	}
+	if (Configure::read() > 0) {
+		echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->";
+	}
+?>
\ No newline at end of file
Index: app/webroot/css.php
===================================================================
--- app/webroot/css.php	(revision 0)
+++ app/webroot/css.php	(revision 0)
@@ -0,0 +1,102 @@
+<?php
+/* SVN FILE: $Id: css.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+	header('HTTP/1.1 404 Not Found');
+	exit('File Not Found');
+}
+/**
+ * Enter description here...
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $path
+ * @param unknown_type $name
+ * @return unknown
+ */
+	function make_clean_css($path, $name) {
+		App::import('Vendor', 'csspp' . DS . 'csspp');
+		$data = file_get_contents($path);
+		$csspp = new csspp();
+		$output = $csspp->compress($data);
+		$ratio = 100 - (round(strlen($output) / strlen($data), 3) * 100);
+		$output = " /* file: $name, ratio: $ratio% */ " . $output;
+		return $output;
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $path
+ * @param unknown_type $content
+ * @return unknown
+ */
+	function write_css_cache($path, $content) {
+		if (!is_dir(dirname($path))) {
+			mkdir(dirname($path));
+		}
+		$cache = new File($path);
+		return $cache->write($content);
+	}
+
+	if (preg_match('|\.\.|', $url) || !preg_match('|^ccss/(.+)$|i', $url, $regs)) {
+		die('Wrong file name.');
+	}
+
+	$filename = 'css/' . $regs[1];
+	$filepath = CSS . $regs[1];
+	$cachepath = CACHE . 'css' . DS . str_replace(array('/','\\'), '-', $regs[1]);
+
+	if (!file_exists($filepath)) {
+		die('Wrong file name.');
+	}
+
+	if (file_exists($cachepath)) {
+		$templateModified = filemtime($filepath);
+		$cacheModified = filemtime($cachepath);
+
+		if ($templateModified > $cacheModified) {
+			$output = make_clean_css($filepath, $filename);
+			write_css_cache($cachepath, $output);
+		} else {
+			$output = file_get_contents($cachepath);
+		}
+	} else {
+		$output = make_clean_css($filepath, $filename);
+		write_css_cache($cachepath, $output);
+		$templateModified = time();
+	}
+
+	header("Date: " . date("D, j M Y G:i:s ", $templateModified) . 'GMT');
+	header("Content-Type: text/css");
+	header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
+	header("Cache-Control: max-age=86400, must-revalidate"); // HTTP/1.1
+	header("Pragma: cache");        // HTTP/1.0
+	print $output;
+?>
\ No newline at end of file
Index: app/webroot/favicon.ico
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: app/webroot/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: app/tests/groups/empty
===================================================================
Index: app/tests/fixtures/empty
===================================================================
Index: app/tests/cases/behaviors/empty
===================================================================
Index: app/tests/cases/helpers/empty
===================================================================
Index: app/tests/cases/models/empty
===================================================================
Index: app/tests/cases/components/empty
===================================================================
Index: app/tests/cases/controllers/empty
===================================================================
Index: app/models/behaviors/empty
===================================================================
Index: app/models/datasources/empty
===================================================================
Index: app/config/sql/db_acl.sql
===================================================================
--- app/config/sql/db_acl.sql	(revision 0)
+++ app/config/sql/db_acl.sql	(revision 0)
@@ -0,0 +1,40 @@
+# $Id: db_acl.sql 7945 2008-12-19 02:16:01Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE acos (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  parent_id INTEGER(10) DEFAULT NULL,
+  model VARCHAR(255) DEFAULT '',
+  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
+  alias VARCHAR(255) DEFAULT '',
+  lft INTEGER(10) DEFAULT NULL,
+  rght INTEGER(10) DEFAULT NULL,
+  PRIMARY KEY  (id)
+);
+
+CREATE TABLE aros_acos (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  aro_id INTEGER(10) UNSIGNED NOT NULL,
+  aco_id INTEGER(10) UNSIGNED NOT NULL,
+  _create CHAR(2) NOT NULL DEFAULT 0,
+  _read CHAR(2) NOT NULL DEFAULT 0,
+  _update CHAR(2) NOT NULL DEFAULT 0,
+  _delete CHAR(2) NOT NULL DEFAULT 0,
+  PRIMARY KEY(id)
+);
+
+CREATE TABLE aros (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  parent_id INTEGER(10) DEFAULT NULL,
+  model VARCHAR(255) DEFAULT '',
+  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
+  alias VARCHAR(255) DEFAULT '',
+  lft INTEGER(10) DEFAULT NULL,
+  rght INTEGER(10) DEFAULT NULL,
+  PRIMARY KEY  (id)
+);
Index: app/config/sql/sessions.sql
===================================================================
--- app/config/sql/sessions.sql	(revision 0)
+++ app/config/sql/sessions.sql	(revision 0)
@@ -0,0 +1,16 @@
+# $Id: sessions.sql 7118 2008-06-04 20:49:29Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#								1785 E. Sahara Avenue, Suite 490-204
+#								Las Vegas, Nevada 89104
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE cake_sessions (
+  id varchar(255) NOT NULL default '',
+  data text,
+  expires int(11) default NULL,
+  PRIMARY KEY  (id)
+);
\ No newline at end of file
Index: app/config/sql/i18n.php
===================================================================
--- app/config/sql/i18n.php	(revision 0)
+++ app/config/sql/i18n.php	(revision 0)
@@ -0,0 +1,56 @@
+<?php
+/* SVN FILE: $Id: i18n.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*i18n schema generated on: 2007-11-25 07:11:25 : 1196004805*/
+/**
+ * This is i18n Schema file
+ *
+ * Use it to configure database for i18n
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create i18n
+ *
+ */
+class i18nSchema extends CakeSchema {
+
+	var $name = 'i18n';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $i18n = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'locale' => array('type'=>'string', 'null' => false, 'length' => 6, 'key' => 'index'),
+			'model' => array('type'=>'string', 'null' => false, 'key' => 'index'),
+			'foreign_key' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'),
+			'field' => array('type'=>'string', 'null' => false, 'key' => 'index'),
+			'content' => array('type'=>'text', 'null' => true, 'default' => NULL),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'locale' => array('column' => 'locale', 'unique' => 0), 'model' => array('column' => 'model', 'unique' => 0), 'row_id' => array('column' => 'foreign_key', 'unique' => 0), 'field' => array('column' => 'field', 'unique' => 0))
+		);
+
+}
+?>
\ No newline at end of file
Index: app/config/sql/db_acl.php
===================================================================
--- app/config/sql/db_acl.php	(revision 0)
+++ app/config/sql/db_acl.php	(revision 0)
@@ -0,0 +1,79 @@
+<?php
+/* SVN FILE: $Id: db_acl.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*DbAcl schema generated on: 2007-11-24 15:11:13 : 1195945453*/
+/**
+ * This is Acl Schema file
+ *
+ * Use it to configure database for ACL
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create DbAcl
+ *
+ */
+class DbAclSchema extends CakeSchema {
+
+	var $name = 'DbAcl';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $acos = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'model' => array('type'=>'string', 'null' => true),
+			'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'alias' => array('type'=>'string', 'null' => true),
+			'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+	var $aros = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'model' => array('type'=>'string', 'null' => true),
+			'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'alias' => array('type'=>'string', 'null' => true),
+			'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+	var $aros_acos = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'aro_id' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'),
+			'aco_id' => array('type'=>'integer', 'null' => false, 'length' => 10),
+			'_create' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_read' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_update' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_delete' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'ARO_ACO_KEY' => array('column' => array('aro_id', 'aco_id'), 'unique' => 1))
+		);
+
+}
+?>
\ No newline at end of file
Index: app/config/sql/sessions.php
===================================================================
--- app/config/sql/sessions.php	(revision 0)
+++ app/config/sql/sessions.php	(revision 0)
@@ -0,0 +1,53 @@
+<?php
+/* SVN FILE: $Id: sessions.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*Sessions schema generated on: 2007-11-25 07:11:54 : 1196004714*/
+/**
+ * This is Sessions Schema file
+ *
+ * Use it to configure database for Sessions
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create Sessions
+ *
+ */
+class SessionsSchema extends CakeSchema {
+
+	var $name = 'Sessions';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $cake_sessions = array(
+			'id' => array('type'=>'string', 'null' => false, 'key' => 'primary'),
+			'data' => array('type'=>'text', 'null' => true, 'default' => NULL),
+			'expires' => array('type'=>'integer', 'null' => true, 'default' => NULL),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+}
+?>
\ No newline at end of file
Index: app/config/sql/i18n.sql
===================================================================
--- app/config/sql/i18n.sql	(revision 0)
+++ app/config/sql/i18n.sql	(revision 0)
@@ -0,0 +1,26 @@
+# $Id: i18n.sql 7945 2008-12-19 02:16:01Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE i18n (
+	id int(10) NOT NULL auto_increment,
+	locale varchar(6) NOT NULL,
+	model varchar(255) NOT NULL,
+	foreign_key int(10) NOT NULL,
+	field varchar(255) NOT NULL,
+	content mediumtext,
+	PRIMARY KEY	(id),
+#	UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
+#	INDEX I18N_LOCALE_ROW(locale, model, foreign_key),
+#	INDEX I18N_LOCALE_MODEL(locale, model),
+#	INDEX I18N_FIELD(model, foreign_key, field),
+#	INDEX I18N_ROW(model, foreign_key),
+	INDEX locale (locale),
+	INDEX model (model),
+	INDEX row_id (foreign_key),
+	INDEX field (field)
+);
Index: app/config/acl.ini.php
===================================================================
--- app/config/acl.ini.php	(revision 0)
+++ app/config/acl.ini.php	(revision 0)
@@ -0,0 +1,74 @@
+;<?php die() ?>
+; SVN FILE: $Id: acl.ini.php 7945 2008-12-19 02:16:01Z gwoo $
+;/**
+; * Short description for file.
+; *
+; *
+; * PHP versions 4 and 5
+; *
+; * CakePHP(tm) :  Rapid Development Framework http://www.cakephp.org/
+; * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @filesource
+; * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+; * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+; * @package       cake
+; * @subpackage    cake.app.config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @version       $Revision: 7945 $
+; * @modifiedby    $LastChangedBy: gwoo $
+; * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+; * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+; */
+
+; acl.ini.php - Cake ACL Configuration
+; ---------------------------------------------------------------------
+; Use this file to specify user permissions.
+; aco = access control object (something in your application)
+; aro = access request object (something requesting access)
+;
+; User records are added as follows:
+;
+; [uid]
+; groups = group1, group2, group3
+; allow = aco1, aco2, aco3
+; deny = aco4, aco5, aco6
+;
+; Group records are added in a similar manner:
+;
+; [gid]
+; allow = aco1, aco2, aco3
+; deny = aco4, aco5, aco6
+;
+; The allow, deny, and groups sections are all optional.
+; NOTE: groups names *cannot* ever be the same as usernames!
+;
+; ACL permissions are checked in the following order:
+; 1. Check for user denies (and DENY if specified)
+; 2. Check for user allows (and ALLOW if specified)
+; 3. Gather user's groups
+; 4. Check group denies (and DENY if specified)
+; 5. Check group allows (and ALLOW if specified)
+; 6. If no aro, aco, or group information is found, DENY
+;
+; ---------------------------------------------------------------------
+
+;-------------------------------------
+;Users
+;-------------------------------------
+
+[username-goes-here]
+groups = group1, group2
+deny = aco1, aco2
+allow = aco3, aco4
+
+;-------------------------------------
+;Groups
+;-------------------------------------
+
+[groupname-goes-here]
+deny = aco5, aco6
+allow = aco7, aco8
\ No newline at end of file
Index: app/config/bootstrap.php
===================================================================
--- app/config/bootstrap.php	(revision 0)
+++ app/config/bootstrap.php	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: bootstrap.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.10.8.2117
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ *
+ * This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded
+ * This is an application wide file to load any function that is not used within a class define.
+ * You can also use this to include or require any files in your application.
+ *
+ */
+/**
+ * The settings below can be used to set additional paths to models, views and controllers.
+ * This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
+ *
+ * $modelPaths = array('full path to models', 'second full path to models', 'etc...');
+ * $viewPaths = array('this path to views', 'second full path to views', 'etc...');
+ * $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...');
+ *
+ */
+//EOF
+?>
\ No newline at end of file
Index: app/config/inflections.php
===================================================================
--- app/config/inflections.php	(revision 0)
+++ app/config/inflections.php	(revision 0)
@@ -0,0 +1,70 @@
+<?php
+/* SVN FILE: $Id: inflections.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Custom Inflected Words.
+ *
+ * This file is used to hold words that are not matched in the normail Inflector::pluralize() and
+ * Inflector::singularize()
+ *
+ * PHP versions 4 and %
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 1.0.0.2312
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * This is a key => value array of regex used to match words.
+ * If key matches then the value is returned.
+ *
+ *  $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice');
+ */
+	$pluralRules = array();
+/**
+ * This is a key only array of plural words that should not be inflected.
+ * Notice the last comma
+ *
+ * $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
+ */
+	$uninflectedPlural = array();
+/**
+ * This is a key => value array of plural irregular words.
+ * If key matches then the value is returned.
+ *
+ *  $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers')
+ */
+	$irregularPlural = array();
+/**
+ * This is a key => value array of regex used to match words.
+ * If key matches then the value is returned.
+ *
+ *  $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i')
+ */
+	$singularRules = array();
+/**
+ * This is a key only array of singular words that should not be inflected.
+ * You should not have to change this value below if you do change it use same format
+ * as the $uninflectedPlural above.
+ */
+	$uninflectedSingular = $uninflectedPlural;
+/**
+ * This is a key => value array of singular irregular words.
+ * Most of the time this will be a reverse of the above $irregularPlural array
+ * You should not have to change this value below if you do change it use same format
+ *
+ * $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother')
+ */
+	$irregularSingular = array_flip($irregularPlural);
+?>
\ No newline at end of file
Index: app/config/routes.php
===================================================================
--- app/config/routes.php	(revision 0)
+++ app/config/routes.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+/* SVN FILE: $Id: routes.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * In this file, you set up routes to your controllers and their actions.
+ * Routes are very important mechanism that allows you to freely connect
+ * different urls to chosen controllers and their actions (functions).
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Here, we are connecting '/' (base path) to controller called 'Pages',
+ * its action called 'display', and we pass a param to select the view file
+ * to use (in this case, /app/views/pages/home.ctp)...
+ */
+	Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+/**
+ * ...and connect the rest of 'Pages' controller's urls.
+ */
+	Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+?>
\ No newline at end of file
Index: app/config/database.php.default
===================================================================
--- app/config/database.php.default	(revision 0)
+++ app/config/database.php.default	(revision 0)
@@ -0,0 +1,101 @@
+<?php
+/* SVN FILE: $Id: database.php.default 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * In this file you set up your database connection details.
+ *
+ * @package       cake
+ * @subpackage    cake.config
+ */
+/**
+ * Database configuration class.
+ * You can specify multiple configurations for production, development and testing.
+ *
+ * driver => The name of a supported driver; valid options are as follows:
+ *		mysql 		- MySQL 4 & 5,
+ *		mysqli 		- MySQL 4 & 5 Improved Interface (PHP5 only),
+ *		sqlite		- SQLite (PHP5 only),
+ *		postgres	- PostgreSQL 7 and higher,
+ *		mssql		- Microsoft SQL Server 2000 and higher,
+ *		db2			- IBM DB2, Cloudscape, and Apache Derby (http://php.net/ibm-db2)
+ *		oracle		- Oracle 8 and higher
+ *		firebird	- Firebird/Interbase
+ *		sybase		- Sybase ASE
+ *		adodb-[drivername]	- ADOdb interface wrapper (see below),
+ *		odbc		- ODBC DBO driver
+ *
+ * You can add custom database drivers (or override existing drivers) by adding the
+ * appropriate file to app/models/datasources/dbo.  Drivers should be named 'dbo_x.php',
+ * where 'x' is the name of the database.
+ *
+ * persistent => true / false
+ * Determines whether or not the database should use a persistent connection
+ *
+ * connect =>
+ * ADOdb set the connect to one of these
+ *	(http://phplens.com/adodb/supported.databases.html) and
+ *	append it '|p' for persistent connection. (mssql|p for example, or just mssql for not persistent)
+ * For all other databases, this setting is deprecated.
+ *
+ * host =>
+ * the host you connect to the database.  To add a socket or port number, use 'port' => #
+ *
+ * prefix =>
+ * Uses the given prefix for all the tables in this database.  This setting can be overridden
+ * on a per-table basis with the Model::$tablePrefix property.
+ *
+ * schema =>
+ * For Postgres and DB2, specifies which schema you would like to use the tables in. Postgres defaults to
+ * 'public', DB2 defaults to empty.
+ *
+ * encoding =>
+ * For MySQL, MySQLi, Postgres and DB2, specifies the character encoding to use when connecting to the
+ * database.  Uses database default.
+ *
+ */
+class DATABASE_CONFIG {
+
+	var $default = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'database_name',
+		'prefix' => '',
+	);
+
+	var $test = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'test_database_name',
+		'prefix' => '',
+	);
+}
+?>
\ No newline at end of file
Index: app/config/core.php
===================================================================
--- app/config/core.php	(revision 0)
+++ app/config/core.php	(revision 0)
@@ -0,0 +1,227 @@
+<?php
+/* SVN FILE: $Id: core.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behavior of Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * CakePHP Debug Level:
+ *
+ * Production Mode:
+ * 	0: No error messages, errors, or warnings shown. Flash messages redirect.
+ *
+ * Development Mode:
+ * 	1: Errors and warnings shown, model caches refreshed, flash messages halted.
+ * 	2: As in 1, but also with full debug messages and SQL output.
+ * 	3: As in 2, but also with full controller dump.
+ *
+ * In production mode, flash messages redirect after a time interval.
+ * In development mode, you need to click the flash message to continue.
+ */
+	Configure::write('debug', 2);
+/**
+ * Application wide charset encoding
+ */
+	Configure::write('App.encoding', 'UTF-8');
+/**
+ * To configure CakePHP *not* to use mod_rewrite and to
+ * use CakePHP pretty URLs, remove these .htaccess
+ * files:
+ *
+ * /.htaccess
+ * /app/.htaccess
+ * /app/webroot/.htaccess
+ *
+ * And uncomment the App.baseUrl below:
+ */
+	//Configure::write('App.baseUrl', env('SCRIPT_NAME'));
+/**
+ * Uncomment the define below to use CakePHP admin routes.
+ *
+ * The value of the define determines the name of the route
+ * and its associated controller actions:
+ *
+ * 'admin' 		-> admin_index() and /admin/controller/index
+ * 'superuser' -> superuser_index() and /superuser/controller/index
+ */
+	//Configure::write('Routing.admin', 'admin');
+
+/**
+ * Turn off all caching application-wide.
+ *
+ */
+	//Configure::write('Cache.disable', true);
+/**
+ * Enable cache checking.
+ *
+ * If set to true, for view caching you must still use the controller
+ * var $cacheAction inside your controllers to define caching settings.
+ * You can either set it controller-wide by setting var $cacheAction = true,
+ * or in each action using $this->cacheAction = true.
+ *
+ */
+	//Configure::write('Cache.check', true);
+/**
+ * Defines the default error type when using the log() function. Used for
+ * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
+ */
+	define('LOG_ERROR', 2);
+/**
+ * The preferred session handling method. Valid values:
+ *
+ * 'php'	 		Uses settings defined in your php.ini.
+ * 'cake'		Saves session files in CakePHP's /tmp directory.
+ * 'database'	Uses CakePHP's database sessions.
+ *
+ * To define a custom session handler, save it at /app/config/<name>.php.
+ * Set the value of 'Session.save' to <name> to utilize it in CakePHP.
+ *
+ * To use database sessions, execute the SQL file found at /app/config/sql/sessions.sql.
+ *
+ */
+	Configure::write('Session.save', 'php');
+/**
+ * The name of the table used to store CakePHP database sessions.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ *
+ * The table name set here should *not* include any table prefix defined elsewhere.
+ */
+	//Configure::write('Session.table', 'cake_sessions');
+/**
+ * The DATABASE_CONFIG::$var to use for database session handling.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ */
+	//Configure::write('Session.database', 'default');
+/**
+ * The name of CakePHP's session cookie.
+ */
+	Configure::write('Session.cookie', 'CAKEPHP');
+/**
+ * Session time out time (in seconds).
+ * Actual value depends on 'Security.level' setting.
+ */
+	Configure::write('Session.timeout', '120');
+/**
+ * If set to false, sessions are not automatically started.
+ */
+	Configure::write('Session.start', true);
+/**
+ * When set to false, HTTP_USER_AGENT will not be checked
+ * in the session
+ */
+	Configure::write('Session.checkAgent', true);
+/**
+ * The level of CakePHP security. The session timeout time defined
+ * in 'Session.timeout' is multiplied according to the settings here.
+ * Valid values:
+ *
+ * 'high'	Session timeout in 'Session.timeout' x 10
+ * 'medium'	Session timeout in 'Session.timeout' x 100
+ * 'low'		Session timeout in 'Session.timeout' x 300
+ *
+ * CakePHP session IDs are also regenerated between requests if
+ * 'Security.level' is set to 'high'.
+ */
+	Configure::write('Security.level', 'high');
+/**
+ * A random string used in security hashing methods.
+ */
+	Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
+/**
+ * Compress CSS output by removing comments, whitespace, repeating tags, etc.
+ * This requires a/var/cache directory to be writable by the web server for caching.
+ * and /vendors/csspp/csspp.php
+ *
+ * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css().
+ */
+	//Configure::write('Asset.filter.css', 'css.php');
+/**
+ * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the
+ * output, and setting the config below to the name of the script.
+ *
+ * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link().
+ */
+	//Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php');
+/**
+ * The classname and database used in CakePHP's
+ * access control lists.
+ */
+	Configure::write('Acl.classname', 'DbAcl');
+	Configure::write('Acl.database', 'default');
+/**
+ *
+ * Cache Engine Configuration
+ * Default settings provided below
+ *
+ * File storage engine.
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'File', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path
+ * 		'prefix' => 'cake_', //[optional]  prefix every cache file with this string
+ * 		'lock' => false, //[optional]  use file locking
+ * 		'serialize' => true, [optional]
+ *	));
+ *
+ *
+ * APC (http://pecl.php.net/package/APC)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Apc', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional]  prefix every cache file with this string
+ *	));
+ *
+ * Xcache (http://xcache.lighttpd.net/)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Xcache', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ *		'user' => 'user', //user from xcache.admin.user settings
+ *      'password' => 'password', //plaintext password (xcache.admin.pass)
+ *	));
+ *
+ *
+ * Memcache (http://www.danga.com/memcached/)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Memcache', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional]  prefix every cache file with this string
+ * 		'servers' => array(
+ * 			'127.0.0.1:11211' // localhost, default port 11211
+ * 		), //[optional]
+ * 		'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory)
+ *	));
+ *
+ */
+	Cache::config('default', array('engine' => 'File'));
+?>
\ No newline at end of file
Index: app/controllers/components/empty
===================================================================
Index: app/.htaccess
===================================================================
--- app/.htaccess	(revision 0)
+++ app/.htaccess	(revision 0)
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine on
+    RewriteRule    ^$    webroot/    [L]
+    RewriteRule    (.*) webroot/$1    [L]
+ </IfModule>
\ No newline at end of file
Index: app/index.php
===================================================================
--- app/index.php	(revision 0)
+++ app/index.php	(revision 0)
@@ -0,0 +1,24 @@
+<?php
+/* SVN FILE: $Id: index.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
+?>
\ No newline at end of file
Index: app/views/layouts/xml/empty
===================================================================
Index: app/views/layouts/rss/empty
===================================================================
Index: app/views/layouts/js/empty
===================================================================
Index: app/views/helpers/empty
===================================================================
Index: app/views/scaffolds/empty
===================================================================
Index: app/views/elements/empty
===================================================================
Index: app/views/errors/empty
===================================================================
Index: vendors/css/empty
===================================================================
Index: vendors/shells/tasks/empty
===================================================================
Index: vendors/shells/templates/empty
===================================================================
Index: vendors/js/empty
===================================================================
Index: cake/LICENSE.txt
===================================================================
--- cake/LICENSE.txt	(revision 0)
+++ cake/LICENSE.txt	(revision 0)
@@ -0,0 +1,22 @@
+The MIT License
+
+CakePHP(tm) : The Rapid Development PHP Framework (http://www.cakephp.org)
+Copyright 2005-2007, Cake Software Foundation, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
\ No newline at end of file
Index: cake/console/error.php
===================================================================
--- cake/console/error.php	(revision 0)
+++ cake/console/error.php	(revision 0)
@@ -0,0 +1,254 @@
+<?php
+/* SVN FILE: $Id: error.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * ErrorHandler for Console Shells
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5074
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Error Handler for Cake console.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console
+ */
+class ErrorHandler extends Object {
+/**
+ * Standard output stream.
+ *
+ * @var filehandle
+ * @access public
+ */
+	var $stdout;
+/**
+ * Standard error stream.
+ *
+ * @var filehandle
+ * @access public
+ */
+	var $stderr;
+/**
+ * Class constructor.
+ *
+ * @param string $method Method dispatching an error
+ * @param array $messages Error messages
+ */
+	function __construct($method, $messages) {
+		$this->stdout = fopen('php://stdout', 'w');
+		$this->stderr = fopen('php://stderr', 'w');
+		if (Configure::read() > 0 || $method == 'error') {
+			call_user_func_array(array(&$this, $method), $messages);
+		} else {
+			call_user_func_array(array(&$this, 'error404'), $messages);
+		}
+	}
+/**
+ * Displays an error page (e.g. 404 Not found).
+ *
+ * @param array $params Parameters (code, name, and message)
+ * @access public
+ */
+	function error($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr($code . $name . $message."\n");
+		$this->_stop();
+	}
+/**
+ * Convenience method to display a 404 page.
+ *
+ * @param array $params Parameters (url, message)
+ * @access public
+ */
+	function error404($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->error(array('code' => '404',
+							'name' => 'Not found',
+							'message' => sprintf(__("The requested address %s was not found on this server.", true), $url, $message)));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Controller web page.
+ *
+ * @param array $params Parameters (className)
+ * @access public
+ */
+	function missingController($params) {
+		extract($params, EXTR_OVERWRITE);
+		$controllerName = str_replace('Controller', '', $className);
+		$this->stderr(sprintf(__("Missing Controller '%s'", true), $controllerName));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Action web page.
+ *
+ * @param array $params Parameters (action, className)
+ * @access public
+ */
+	function missingAction($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Method '%s' in '%s'", true), $action, $className));
+		$this->_stop();
+	}
+/**
+ * Renders the Private Action web page.
+ *
+ * @param array $params Parameters (action, className)
+ * @access public
+ */
+	function privateAction($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Trying to access private method '%s' in '%s'", true), $action, $className));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Table web page.
+ *
+ * @param array $params Parameters (table, className)
+ * @access public
+ */
+	function missingTable($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing database table '%s' for model '%s'", true), $table, $className));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Database web page.
+ *
+ * @param array $params Parameters
+ * @access public
+ */
+	function missingDatabase($params = array()) {
+		$this->stderr(__("Missing Database", true));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing View web page.
+ *
+ * @param array $params Parameters (file, action, className)
+ * @access public
+ */
+	function missingView($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing View '%s' for '%s' in '%s'", true), $file, $action, $className));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Layout web page.
+ *
+ * @param array $params Parameters (file)
+ * @access public
+ */
+	function missingLayout($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Layout '%s'", true), $file));
+		$this->_stop();
+	}
+/**
+ * Renders the Database Connection web page.
+ *
+ * @param array $params Parameters
+ * @access public
+ */
+	function missingConnection($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(__("Missing Database Connection. Try 'cake bake'", true));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Helper file web page.
+ *
+ * @param array $params Parameters (file, helper)
+ * @access public
+ */
+	function missingHelperFile($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Helper file '%s' for '%s'", true), $file, Inflector::camelize($helper)));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Helper class web page.
+ *
+ * @param array $params Parameters (file, helper)
+ * @access public
+ */
+	function missingHelperClass($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Helper class '%s' in '%s'", true), Inflector::camelize($helper), $file));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Component file web page.
+ *
+ * @param array $params Parameters (file, component)
+ * @access public
+ */
+	function missingComponentFile($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Component file '%s' for '%s'", true), $file, Inflector::camelize($component)));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Component class web page.
+ *
+ * @param array $params Parameters (file, component)
+ * @access public
+ */
+	function missingComponentClass($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing Component class '%s' in '%s'", true), Inflector::camelize($component), $file));
+		$this->_stop();
+	}
+/**
+ * Renders the Missing Model class web page.
+ *
+ * @param array $params Parameters (className)
+ * @access public
+ */
+	function missingModel($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->stderr(sprintf(__("Missing model '%s'", true), $className));
+		$this->_stop();
+	}
+/**
+ * Outputs to the stdout filehandle.
+ *
+ * @param string $string String to output.
+ * @param boolean $newline If true, the outputs gets an added newline.
+ * @access public
+ */
+	function stdout($string, $newline = true) {
+		if ($newline) {
+			fwrite($this->stdout, $string . "\n");
+		} else {
+			fwrite($this->stdout, $string);
+		}
+	}
+/**
+ * Outputs to the stderr filehandle.
+ *
+ * @param string $string Error text to output.
+ * @access public
+ */
+	function stderr($string) {
+		fwrite($this->stderr, "Error: ". $string . "\n");
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/cake
===================================================================
--- cake/console/cake	(revision 0)
+++ cake/console/cake	(revision 0)
@@ -0,0 +1,32 @@
+#!/bin/bash
+################################################################################
+#
+# Bake is a shell script for running CakePHP bake script
+# PHP versions 4 and 5
+#
+# CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+# Copyright 2005-2007, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+#
+# @filesource
+# @copyright		Copyright 2005-2007, Cake Software Foundation, Inc.
+# @link				http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+# @package			cake
+# @subpackage		cake.cake.console
+# @since				CakePHP(tm) v 1.2.0.5012
+# @version			$Revision: 7945 $
+# @modifiedby		$LastChangedBy: gwoo $
+# @lastmodified	$Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+# @license			http://www.opensource.org/licenses/mit-license.php The MIT License
+#
+################################################################################
+clear
+
+LIB=${0/%cake/}
+APP=`pwd`
+
+exec php -q ${LIB}cake.php -working "${APP}" "$@"
+
+exit;
\ No newline at end of file

Property changes on: cake/console/cake
___________________________________________________________________
Added: svn:executable
   + *

Index: cake/console/cake.bat
===================================================================
--- cake/console/cake.bat	(revision 0)
+++ cake/console/cake.bat	(revision 0)
@@ -0,0 +1,35 @@
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Bake is a shell script for running CakePHP bake script
+:: PHP versions 4 and 5
+::
+:: CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+:: Copyright 2005-2007, Cake Software Foundation, Inc.
+::
+:: Licensed under The MIT License
+:: Redistributions of files must retain the above copyright notice.
+::
+:: @filesource
+:: @copyright		Copyright 2005-2007, Cake Software Foundation, Inc.
+:: @link				http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+:: @package			cake
+:: @subpackage		cake.cake.console
+:: @since			CakePHP(tm) v 1.2.0.5012
+:: @version			$Revision: 7945 $
+:: @modifiedby		$LastChangedBy: gwoo $
+:: @lastmodified	$Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+:: @license			http://www.opensource.org/licenses/mit-license.php The MIT License
+::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:: In order for this script to work as intended, the cake\console\ folder must be in your PATH
+
+@echo.
+@echo off
+
+SET app=%0
+SET lib=%~dp0
+
+php -q "%lib%cake.php" -working "%CD%" %*
+
+echo.
\ No newline at end of file
Index: cake/console/cake.php
===================================================================
--- cake/console/cake.php	(revision 0)
+++ cake/console/cake.php	(revision 0)
@@ -0,0 +1,581 @@
+#!/usr/bin/php -q
+<?php
+/* SVN FILE: $Id: cake.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * Shell dispatcher class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008,	Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Shell dispatcher
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console
+ */
+class ShellDispatcher {
+/**
+ * Standard input stream.
+ *
+ * @var filehandle
+ * @access public
+ */
+	var $stdin;
+/**
+ * Standard output stream.
+ *
+ * @var filehandle
+ * @access public
+ */
+	var $stdout;
+/**
+ * Standard error stream.
+ *
+ * @var filehandle
+ * @access public
+ */
+	var $stderr;
+/**
+ * Contains command switches parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	var $params = array();
+/**
+ * Contains arguments parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	var $args = array();
+/**
+ * The file name of the shell that was invoked.
+ *
+ * @var string
+ * @access public
+ */
+	var $shell = null;
+/**
+ * The class name of the shell that was invoked.
+ *
+ * @var string
+ * @access public
+ */
+	var $shellClass = null;
+/**
+ * The command called if public methods are available.
+ *
+ * @var string
+ * @access public
+ */
+	var $shellCommand = null;
+/**
+ * The path locations of shells.
+ *
+ * @var array
+ * @access public
+ */
+	var $shellPaths = array();
+/**
+ * The path to the current shell location.
+ *
+ * @var string
+ * @access public
+ */
+	var $shellPath = null;
+/**
+ * The name of the shell in camelized.
+ *
+ * @var string
+ * @access public
+ */
+	var $shellName = null;
+/**
+ * Constructs this ShellDispatcher instance.
+ *
+ * @param array $args the argv.
+ */
+	function ShellDispatcher($args = array()) {
+		$this->__construct($args);
+	}
+/**
+ * Constructor
+ *
+ * @param array $args the argv.
+ */
+	function __construct($args = array()) {
+		set_time_limit(0);
+		$this->__initConstants();
+		$this->parseParams($args);
+		$this->_initEnvironment();
+		$this->__buildPaths();
+		$this->_stop($this->dispatch());
+	}
+/**
+ * Defines core configuration.
+ *
+ * @access private
+ */
+	function __initConstants() {
+		if (function_exists('ini_set')) {
+			ini_set('display_errors', '1');
+			ini_set('error_reporting', E_ALL);
+			ini_set('html_errors', false);
+			ini_set('implicit_flush', true);
+			ini_set('max_execution_time', 0);
+		}
+
+		if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+			define('PHP5', (PHP_VERSION >= 5));
+			define('DS', DIRECTORY_SEPARATOR);
+			define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
+			define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+			define('DISABLE_DEFAULT_ERROR_HANDLING', false);
+			define('CAKEPHP_SHELL', true);
+		}
+		require_once(CORE_PATH . 'cake' . DS . 'basics.php');
+	}
+/**
+ * Defines current working environment.
+ *
+ * @access protected
+ */
+	function _initEnvironment() {
+		$this->stdin = fopen('php://stdin', 'r');
+		$this->stdout = fopen('php://stdout', 'w');
+		$this->stderr = fopen('php://stderr', 'w');
+
+		if (!$this->__bootstrap()) {
+			$this->stderr("\nCakePHP Console: ");
+			$this->stderr("\nUnable to load Cake core:");
+			$this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH);
+			$this->_stop();
+		}
+
+		if (!isset($this->args[0]) || !isset($this->params['working'])) {
+			$this->stderr("\nCakePHP Console: ");
+			$this->stderr('This file has been loaded incorrectly and cannot continue.');
+			$this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,');
+			$this->stderr('and check the manual for the correct usage of this command.');
+			$this->stderr('(http://manual.cakephp.org/)');
+			$this->_stop();
+		}
+
+		if (basename(__FILE__) !=  basename($this->args[0])) {
+			$this->stderr("\nCakePHP Console: ");
+			$this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...');
+			if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') {
+				$this->_stop();
+			}
+		}
+
+		$this->shiftArgs();
+	}
+/**
+ * Builds the shell paths.
+ *
+ * @access private
+ * @return void
+ */
+	function __buildPaths() {
+		$paths = array();
+
+		$pluginPaths = Configure::read('pluginPaths');
+		foreach ($pluginPaths as $pluginPath) {
+			$plugins = Configure::listObjects('plugin', $pluginPath);
+			foreach ((array)$plugins as $plugin) {
+				$path = $pluginPath . Inflector::underscore($plugin) . DS . 'vendors' . DS . 'shells' . DS;
+				if (file_exists($path)) {
+					$paths[] = $path;
+				}
+			}
+		}
+
+		$vendorPaths = array_values(Configure::read('vendorPaths'));
+		foreach ($vendorPaths as $vendorPath) {
+			$path = rtrim($vendorPath, DS) . DS . 'shells' . DS;
+			if (file_exists($path)) {
+				$paths[] = $path;
+			}
+		}
+
+		$this->shellPaths = array_values(array_unique(array_merge($paths, Configure::read('shellPaths'))));
+	}
+/**
+ * Initializes the environment and loads the Cake core.
+ *
+ * @return boolean Success.
+ * @access private
+ */
+	function __bootstrap() {
+
+		define('ROOT', $this->params['root']);
+		define('APP_DIR', $this->params['app']);
+		define('APP_PATH', $this->params['working'] . DS);
+		define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
+
+		$includes = array(
+			CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'object.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'inflector.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'configure.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'file.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'cache.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'string.php',
+			CORE_PATH . 'cake' . DS . 'libs' . DS . 'class_registry.php',
+			CORE_PATH . 'cake' . DS . 'console' . DS . 'error.php'
+		);
+
+		foreach ($includes as $inc) {
+			if (!require($inc)) {
+				$this->stderr("Failed to load Cake core file {$inc}");
+				return false;
+			}
+		}
+
+		Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php'));
+
+		if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
+			include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
+			Configure::buildPaths(array());
+		}
+
+		Configure::write('debug', 1);
+		return true;
+	}
+
+/**
+ * Dispatches a CLI request
+ *
+ * @access public
+ */
+	function dispatch() {
+		if (isset($this->args[0])) {
+			$plugin = null;
+			$shell = $this->args[0];
+			if (strpos($shell, '.') !== false)  {
+				list($plugin, $shell) = explode('.', $this->args[0]);
+			}
+
+			$this->shell = $shell;
+			$this->shiftArgs();
+			$this->shellName = Inflector::camelize($this->shell);
+			$this->shellClass = $this->shellName . 'Shell';
+
+			if ($this->shell === 'help') {
+				$this->help();
+			} else {
+				$loaded = false;
+				foreach ($this->shellPaths as $path) {
+					$this->shellPath = $path . $this->shell . '.php';
+
+					$isPlugin = ($plugin && strpos($path, DS . $plugin . DS . 'vendors' . DS . 'shells' . DS) !== false);
+					if (($isPlugin && file_exists($this->shellPath)) || (!$plugin && file_exists($this->shellPath))) {
+						$loaded = true;
+						break;
+					}
+				}
+
+				if ($loaded) {
+					if (!class_exists('Shell')) {
+						require CONSOLE_LIBS . 'shell.php';
+					}
+					require $this->shellPath;
+					if (class_exists($this->shellClass)) {
+						$command = null;
+						if (isset($this->args[0])) {
+							$command = $this->args[0];
+						}
+						$this->shellCommand = Inflector::variable($command);
+						$shell = new $this->shellClass($this);
+
+						if (strtolower(get_parent_class($shell)) == 'shell') {
+							$shell->initialize();
+							$shell->loadTasks();
+
+							foreach ($shell->taskNames as $task) {
+								if (strtolower(get_parent_class($shell)) == 'shell') {
+									$shell->{$task}->initialize();
+									$shell->{$task}->loadTasks();
+								}
+							}
+
+							$task = Inflector::camelize($command);
+							if (in_array($task, $shell->taskNames)) {
+								$this->shiftArgs();
+								$shell->{$task}->startup();
+								if (isset($this->args[0]) && $this->args[0] == 'help') {
+									if (method_exists($shell->{$task}, 'help')) {
+										$shell->{$task}->help();
+										$this->_stop();
+									} else {
+										$this->help();
+									}
+								}
+								return $shell->{$task}->execute();
+							}
+						}
+
+						$classMethods = get_class_methods($shell);
+
+						$privateMethod = $missingCommand = false;
+						if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) {
+							$privateMethod = true;
+						}
+
+						if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) {
+							$missingCommand = true;
+						}
+
+						$protectedCommands = array(
+							'initialize','in','out','err','hr',
+							'createfile', 'isdir','copydir','object','tostring',
+							'requestaction','log','cakeerror', 'shelldispatcher',
+							'__initconstants','__initenvironment','__construct',
+							'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs'
+						);
+
+						if (in_array(strtolower($command), $protectedCommands)) {
+							$missingCommand = true;
+						}
+
+						if ($missingCommand && method_exists($shell, 'main')) {
+							$shell->startup();
+							return $shell->main();
+						} elseif (!$privateMethod && method_exists($shell, $command)) {
+							$this->shiftArgs();
+							$shell->startup();
+							return $shell->{$command}();
+						} else {
+							$this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n");
+						}
+					} else {
+						$this->stderr('Class '.$this->shellClass.' could not be loaded');
+					}
+				} else {
+					$this->help();
+				}
+			}
+		} else {
+			$this->help();
+		}
+	}
+/**
+ * Prompts the user for input, and returns it.
+ *
+ * @param string $prompt Prompt text.
+ * @param mixed $options Array or string of options.
+ * @param string $default Default input value.
+ * @return Either the default value, or the user-provided input.
+ * @access public
+ */
+	function getInput($prompt, $options = null, $default = null) {
+		if (!is_array($options)) {
+			$printOptions = '';
+		} else {
+			$printOptions = '(' . implode('/', $options) . ')';
+		}
+
+		if ($default == null) {
+			$this->stdout($prompt . " $printOptions \n" . '> ', false);
+		} else {
+			$this->stdout($prompt . " $printOptions \n" . "[$default] > ", false);
+		}
+		$result = fgets($this->stdin);
+
+		if ($result === false) {
+			exit;
+		}
+		$result = trim($result);
+
+		if ($default != null && empty($result)) {
+			return $default;
+		}
+		return $result;
+	}
+/**
+ * Outputs to the stdout filehandle.
+ *
+ * @param string $string String to output.
+ * @param boolean $newline If true, the outputs gets an added newline.
+ * @access public
+ */
+	function stdout($string, $newline = true) {
+		if ($newline) {
+			fwrite($this->stdout, $string . "\n");
+		} else {
+			fwrite($this->stdout, $string);
+		}
+	}
+/**
+ * Outputs to the stderr filehandle.
+ *
+ * @param string $string Error text to output.
+ * @access public
+ */
+	function stderr($string) {
+		fwrite($this->stderr, 'Error: '. $string);
+	}
+/**
+ * Parses command line options
+ *
+ * @param array $params Parameters to parse
+ * @access public
+ */
+	function parseParams($params) {
+		$this->__parseParams($params);
+
+		$defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot');
+
+		$params = array_merge($defaults, array_intersect_key($this->params, $defaults));
+
+		$isWin = array_filter(array_map('strpos', $params, array('\\')));
+
+		$params = str_replace('\\', '/', $params);
+
+		if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
+			if (empty($this->params['app']) && $params['working'] != $params['root']) {
+				$params['root'] = dirname($params['working']);
+				$params['app'] = basename($params['working']);
+			} else {
+				$params['root'] = $params['working'];
+			}
+		}
+
+		if ($params['app'][0] == '/' || preg_match('/([a-zA-Z])(:)/i', $params['app'], $matches)) {
+			$params['root'] = dirname($params['app']);
+		} elseif (strpos($params['app'], '/')) {
+			$params['root'] .= '/' . dirname($params['app']);
+		}
+
+		$params['app'] = basename($params['app']);
+		$params['working'] = rtrim($params['root'], '/') . '/' . $params['app'];
+
+		if (!empty($matches[0]) || !empty($isWin)) {
+			$params = str_replace('/', '\\', $params);
+		}
+
+		$this->params = array_merge($this->params, $params);
+	}
+/**
+ * Helper for recursively paraing params
+ *
+ * @return array params
+ * @access private
+ */
+	function __parseParams($params) {
+		$count = count($params);
+		for ($i = 0; $i < $count; $i++) {
+			if (isset($params[$i])) {
+				if ($params[$i]{0} === '-') {
+					$key = substr($params[$i], 1);
+					$this->params[$key] = true;
+					unset($params[$i]);
+					if (isset($params[++$i])) {
+						if ($params[$i]{0} !== '-') {
+							$this->params[$key] = str_replace('"', '', $params[$i]);
+							unset($params[$i]);
+						} else {
+							$i--;
+							$this->__parseParams($params);
+						}
+					}
+				} else {
+					$this->args[] = $params[$i];
+					unset($params[$i]);
+				}
+
+			}
+		}
+	}
+/**
+ * Removes first argument and shifts other arguments up
+ *
+ * @return boolean False if there are no arguments
+ * @access public
+ */
+	function shiftArgs() {
+		if (empty($this->args)) {
+			return false;
+		}
+		unset($this->args[0]);
+		$this->args = array_values($this->args);
+		return true;
+	}
+/**
+ * Shows console help
+ *
+ * @access public
+ */
+	function help() {
+		$this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console");
+		$this->stdout("---------------------------------------------------------------");
+		$this->stdout("Current Paths:");
+		$this->stdout(" -app: ". $this->params['app']);
+		$this->stdout(" -working: " . rtrim($this->params['working'], DS));
+		$this->stdout(" -root: " . rtrim($this->params['root'], DS));
+		$this->stdout(" -core: " . rtrim(CORE_PATH, DS));
+		$this->stdout("");
+		$this->stdout("Changing Paths:");
+		$this->stdout("your working path should be the same as your application path");
+		$this->stdout("to change your path use the '-app' param.");
+		$this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp");
+
+		$this->stdout("\nAvailable Shells:");
+		$_shells = array();
+		foreach ($this->shellPaths as $path) {
+			if (is_dir($path)) {
+				$shells = Configure::listObjects('file', $path);
+				$path = str_replace(CORE_PATH, 'CORE/', $path);
+				$path = str_replace(ROOT, 'ROOT', $path);
+				$path = rtrim($path, DS);
+				$this->stdout("\n " . $path . ":");
+				if (empty($shells)) {
+					$this->stdout("\t - none");
+				} else {
+					foreach ($shells as $shell) {
+						if ($shell !== 'shell.php') {
+							$this->stdout("\t " . str_replace('.php', '', $shell));
+						}
+					}
+				}
+			}
+		}
+		$this->stdout("\nTo run a command, type 'cake shell_name [args]'");
+		$this->stdout("To get help on a specific command, type 'cake shell_name help'");
+		$this->_stop();
+	}
+/**
+ * Stop execution of the current script
+ *
+ * @param $status see http://php.net/exit for values
+ * @return void
+ * @access protected
+ */
+	function _stop($status = 0) {
+		exit($status);
+	}
+}
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	$dispatcher = new ShellDispatcher($argv);
+}
+?>
\ No newline at end of file
Index: cake/console/libs/api.php
===================================================================
--- cake/console/libs/api.php	(revision 0)
+++ cake/console/libs/api.php	(revision 0)
@@ -0,0 +1,216 @@
+<?php
+/* SVN FILE: $Id: api.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * API shell to get CakePHP core method signatures.
+ *
+ * Implementation of a Cake Shell to show CakePHP core method signatures.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+/**
+ * API shell to show method signatures of CakePHP core classes.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class ApiShell extends Shell {
+/**
+ * Map between short name for paths and real paths.
+ *
+ * @var array
+ * @access public
+ */
+	var $paths = array();
+/**
+ * Override intialize of the Shell
+ *
+ * @access public
+ */
+	function initialize () {
+		$this->paths = array_merge($this->paths, array(
+			'behavior' => LIBS . 'model' . DS . 'behaviors' . DS,
+			'cache' => LIBS . 'cache' . DS,
+			'controller' => LIBS . 'controller' . DS,
+			'component' => LIBS . 'controller' . DS . 'components' . DS,
+			'helper' => LIBS . 'view' . DS . 'helpers' . DS,
+			'model' => LIBS . 'model' . DS,
+			'view' => LIBS . 'view' . DS,
+			'core' => LIBS
+		));
+	}
+/**
+ * Override main() to handle action
+ *
+ * @access public
+ */
+	function main() {
+		if (empty($this->args)) {
+			return $this->help();
+		}
+
+		$type = low($this->args[0]);
+
+		if (isset($this->paths[$type])) {
+			$path = $this->paths[$type];
+		} else {
+			$path = $this->paths['core'];
+		}
+
+		if (count($this->args) == 1) {
+			$file = $type;
+			$class = Inflector::camelize($type);
+		} elseif (count($this->args) > 1) {
+			$file = Inflector::underscore($this->args[1]);
+			$class = Inflector::camelize($file);
+		}
+
+		$objects = Configure::listObjects('class', $path);
+		if (in_array($class, $objects)) {
+			if (in_array($type, array('behavior', 'component', 'helper')) && $type !== $file) {
+				if (!preg_match('/' . Inflector::camelize($type) . '$/', $class)) {
+					$class .= Inflector::camelize($type);
+				}
+			}
+
+		} else {
+			$this->err(sprintf(__("%s not found", true), $class));
+			$this->_stop();
+		}
+
+		$parsed = $this->__parseClass($path . $file .'.php');
+
+		if (!empty($parsed)) {
+			if (isset($this->params['m'])) {
+				if (!isset($parsed[$this->params['m']])) {
+					$this->err(sprintf(__("%s::%s() could not be found", true), $class, $this->params['m']));
+					$this->_stop();
+				}
+				$method = $parsed[$this->params['m']];
+				$this->out($class .'::'.$method['method'] . $method['parameters']);
+				$this->hr();
+				$this->out($method['comment'], true);
+			} else {
+				$this->out(ucwords($class));
+				$this->hr();
+				$i = 0;
+				foreach ($parsed as $method) {
+					$list[] = ++$i . ". " . $method['method'] . $method['parameters'];
+				}
+				$this->out($list);
+
+				$methods = array_keys($parsed);
+				while ($number = $this->in(__('Select a number to see the more information about a specific method. q to quit. l to list.', true), null, 'q')) {
+					if ($number === 'q') {
+						$this->out(__('Done', true));
+						$this->_stop();
+					}
+
+					if ($number === 'l') {
+						$this->out($list);
+					}
+
+					if (isset($methods[--$number])) {
+						$method = $parsed[$methods[$number]];
+						$this->hr();
+						$this->out($class .'::'.$method['method'] . $method['parameters']);
+						$this->hr();
+						$this->out($method['comment'], true);
+					}
+				}
+			}
+		}
+	}
+
+/**
+ * Show help for this shell.
+ *
+ * @access public
+ */
+	function help() {
+		$head  = "Usage: cake api [<type>] <className> [-m <method>]\n";
+		$head .= "-----------------------------------------------\n";
+		$head .= "Parameters:\n\n";
+
+		$commands = array(
+			'path' => "\t<type>\n" .
+						"\t\tEither a full path or type of class (model, behavior, controller, component, view, helper).\n".
+						"\t\tAvailable values:\n\n".
+						"\t\tbehavior\tLook for class in CakePHP behavior path\n".
+						"\t\tcache\tLook for class in CakePHP cache path\n".
+						"\t\tcontroller\tLook for class in CakePHP controller path\n".
+						"\t\tcomponent\tLook for class in CakePHP component path\n".
+						"\t\thelper\tLook for class in CakePHP helper path\n".
+						"\t\tmodel\tLook for class in CakePHP model path\n".
+						"\t\tview\tLook for class in CakePHP view path\n",
+			'className' => "\t<className>\n" .
+						"\t\tA CakePHP core class name (e.g: Component, HtmlHelper).\n"
+		);
+
+		$this->out($head);
+		if (!isset($this->args[1])) {
+			foreach ($commands as $cmd) {
+				$this->out("{$cmd}\n\n");
+			}
+		} elseif (isset($commands[low($this->args[1])])) {
+			$this->out($commands[low($this->args[1])] . "\n\n");
+		} else {
+			$this->out("Command '" . $this->args[1] . "' not found");
+		}
+	}
+
+/**
+ * Parse a given class (located on given file) and get public methods and their
+ * signatures.
+ *
+ * @param object $File File object
+ * @param string $class Class name
+ * @return array Methods and signatures indexed by method name
+ * @access private
+ */
+	function __parseClass($path) {
+		$parsed = array();
+
+		$File = new File($path);
+		if (!$File->exists()) {
+			$this->err(sprintf(__("%s could not be found", true), $File->name));
+			$this->_stop();
+		}
+
+		$contents = $File->read();
+
+		if (preg_match_all('%(/\\*\\*[\\s\\S]*?\\*/)(\\s+function\\s+\\w+)(\\(.+\\))%', $contents, $result, PREG_PATTERN_ORDER)) {
+			foreach ($result[2] as $key => $method) {
+				$method = str_replace('function ', '', trim($method));
+
+				if (strpos($method, '__') === false && $method[0] != '_') {
+					$parsed[$method] = array(
+											'comment' => r(array('/*', '*/', '*'), '', trim($result[1][$key])),
+											'method' => $method,
+											'parameters' => trim($result[3][$key]),
+											);
+				}
+			}
+		}
+		ksort($parsed);
+		return $parsed;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/testsuite.php
===================================================================
--- cake/console/libs/testsuite.php	(revision 0)
+++ cake/console/libs/testsuite.php	(revision 0)
@@ -0,0 +1,352 @@
+<?php
+/* SVN FILE: $Id: testsuite.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Test Suite Shell
+ *
+ * This Shell allows the running of test suites via the cake command line
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestSuiteShell extends Shell {
+/**
+ * The test category, "app", "core" or the name of a plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $category = '';
+/**
+ * "group", "case" or "all"
+ *
+ * @var string
+ * @access public
+ */
+	var $type = '';
+/**
+ * Path to the test case/group file
+ *
+ * @var string
+ * @access public
+ */
+	var $file = '';
+/**
+ * Storage for plugins that have tests
+ *
+ * @var string
+ * @access public
+ */
+	var $plugins = array();
+/**
+ * Convenience variable to avoid duplicated code
+ *
+ * @var string
+ * @access public
+ */
+	var $isPluginTest = false;
+/**
+ * Stores if the user wishes to get a code coverage analysis report
+ *
+ * @var string
+ * @access public
+ */
+	var $doCoverage = false;
+/**
+ * The headline for the test output
+ *
+ * @var string
+ * @access public
+ */
+	var $headline = 'CakePHP Test Shell';
+/**
+ * Initialization method installs Simpletest and loads all plugins
+ *
+ * @return void
+ * @access public
+ */
+	function initialize() {
+		$corePath = Configure::corePaths('cake');
+		if (isset($corePath[0])) {
+			define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS);
+		} else {
+			define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH);
+		}
+
+		$this->__installSimpleTest();
+
+		require_once CAKE . 'tests' . DS . 'lib' . DS . 'test_manager.php';
+		require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php';
+
+		$plugins = Configure::listObjects('plugin');
+		foreach ($plugins as $p) {
+			$this->plugins[] = Inflector::underscore($p);
+		}
+	}
+/**
+ * Main entry point to this shell
+ *
+ * @return void
+ * @access public
+ */
+	function main() {
+		$this->out($this->headline);
+		$this->hr();
+
+		if (count($this->args) > 0) {
+			$this->category = $this->args[0];
+
+			if (!in_array($this->category, array('app', 'core'))) {
+				$this->isPluginTest = true;
+			}
+
+			if (isset($this->args[1])) {
+				$this->type = $this->args[1];
+			}
+
+			if (isset($this->args[2])) {
+				if ($this->args[2] == 'cov') {
+					$this->doCoverage = true;
+				} else {
+					$this->file = Inflector::underscore($this->args[2]);
+				}
+			}
+
+			if (isset($this->args[3]) && $this->args[3] == 'cov') {
+				$this->doCoverage = true;
+			}
+		} else {
+			$this->err('Sorry, you did not pass any arguments!');
+		}
+
+		if ($this->__canRun()) {
+			$this->out('Running '.$this->category.' '.$this->type.' '.$this->file);
+
+			$exitCode = 0;
+			if (!$this->__run()) {
+				$exitCode = 1;
+			}
+			exit($exitCode);
+		} else {
+			$this->err('Sorry, the tests could not be found.');
+			exit(1);
+		}
+	}
+/**
+ * Help screen
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->out('Usage: ');
+		$this->out("\tcake testsuite category test_type file");
+		$this->out("\t\t - category - \"app\", \"core\" or name of a plugin");
+		$this->out("\t\t - test_type - \"case\", \"group\" or \"all\"");
+		$this->out("\t\t - test_file - file name with folder prefix and without the (test|group).php suffix");
+		$this->out('');
+		$this->out('Examples: ');
+		$this->out("\t\t cake testsuite app all");
+		$this->out("\t\t cake testsuite core all");
+		$this->out('');
+		$this->out("\t\t cake testsuite app case behaviors/debuggable");
+		$this->out("\t\t cake testsuite app case models/my_model");
+		$this->out("\t\t cake testsuite app case controllers/my_controller");
+		$this->out('');
+		$this->out("\t\t cake testsuite core case file");
+		$this->out("\t\t cake testsuite core case router");
+		$this->out("\t\t cake testsuite core case set");
+		$this->out('');
+		$this->out("\t\t cake testsuite app group mygroup");
+		$this->out("\t\t cake testsuite core group acl");
+		$this->out("\t\t cake testsuite core group socket");
+		$this->out('');
+		$this->out("\t\t cake testsuite bugs case models/bug  // for the plugin 'bugs' and its test case 'bug'");
+		$this->out("\t\t cake testsuite bugs group bug  // for the plugin bugs and its test group 'bug'");
+		$this->out("\t\t cake testsuite bugs_me case models/bug  // for the plugin 'bugs_me' and its test case 'bug'");
+		$this->out("\t\t cake testsuite bugs_me group bug  // for the plugin bugs_me and its test group 'bug'");
+		$this->out('');
+		$this->out('Code Coverage Analysis: ');
+		$this->out("\n\nAppend 'cov' to any of the above in order to enable code coverage analysis");
+	}
+/**
+ * Checks if the arguments supplied point to a valid test file and thus the shell can be run.
+ *
+ * @return bool true if it's a valid test file, false otherwise
+ * @access private
+ */
+	function __canRun() {
+		$isNeitherAppNorCore = !in_array($this->category, array('app', 'core'));
+		$isPlugin = in_array(Inflector::underscore($this->category), $this->plugins);
+
+		if ($isNeitherAppNorCore && !$isPlugin) {
+			$this->err($this->category.' is an invalid test category (either "app", "core" or name of a plugin)');
+			return false;
+		}
+
+		$folder = $this->__findFolderByCategory($this->category);
+		if (!file_exists($folder)) {
+			$this->err($folder . ' not found');
+			return false;
+		}
+
+		if (!in_array($this->type, array('all', 'group', 'case'))) {
+			$this->err($this->type.' is invalid. Should be case, group or all');
+			return false;
+		}
+
+		switch ($this->type) {
+			case 'all':
+				return true;
+				break;
+			case 'group':
+				if (file_exists($folder.DS.'groups'.DS.$this->file.'.group.php')) {
+					return true;
+				}
+				break;
+			case 'case':
+				if ($this->category == 'app' && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) {
+					return true;
+				}
+
+				if ($this->category == 'core' && file_exists($folder.DS.'cases'.DS.'libs'.DS.$this->file.'.test.php')) {
+					return true;
+				}
+
+				if ($isPlugin && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) {
+					return true;
+				}
+				break;
+		}
+
+		$this->err($this->category.' '.$this->type.' '.$this->file.' is an invalid test identifier');
+		return false;
+	}
+/**
+ * Executes the tests depending on our settings
+ *
+ * @return void
+ * @access private
+ */
+	function __run() {
+		$reporter = new CLIReporter();
+		$this->__setGetVars();
+
+		if ($this->type == 'all') {
+			return TestManager::runAllTests($reporter);
+		}
+
+		if ($this->doCoverage) {
+			if (!extension_loaded('xdebug')) {
+				$this->out('You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation. Download it from http://www.xdebug.org/docs/install');
+				exit(0);
+			}
+		}
+
+		if ($this->type == 'group') {
+			$ucFirstGroup = ucfirst($this->file);
+
+			$path = CORE_TEST_GROUPS;
+			if ($this->category == 'app') {
+				$path = APP_TEST_GROUPS;
+			} elseif ($this->isPluginTest) {
+				$path = APP.'plugins'.DS.$this->category.DS.'tests'.DS.'groups';
+			}
+
+			if ($this->doCoverage) {
+				require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
+				CodeCoverageManager::start($ucFirstGroup, $reporter);
+			}
+			$result = TestManager::runGroupTest($ucFirstGroup, $reporter);
+			if ($this->doCoverage) {
+				CodeCoverageManager::report();
+			}
+			return $result;
+		}
+
+		$case = 'libs'.DS.$this->file.'.test.php';
+		if ($this->category == 'app') {
+			$case = $this->file.'.test.php';
+		} elseif ($this->isPluginTest) {
+			$case = $this->file.'.test.php';
+		}
+
+		if ($this->doCoverage) {
+			require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
+			CodeCoverageManager::start($case, $reporter);
+		}
+
+		$result = TestManager::runTestCase($case, $reporter);
+		if ($this->doCoverage) {
+			CodeCoverageManager::report();
+		}
+
+		return $result;
+	}
+/**
+ * Finds the correct folder to look for tests for based on the input category
+ *
+ * @return string the folder path
+ * @access private
+ */
+	function __findFolderByCategory($category) {
+		$folder = '';
+		$paths = array(
+			'core' => CAKE,
+			'app'  => APP
+		);
+
+		if (array_key_exists($category, $paths)) {
+			$folder = $paths[$category];
+		} else {
+			$folder = APP.'plugins'.DS.Inflector::underscore($category).DS;
+		}
+		return $folder.'tests';
+	}
+/**
+ * Sets some get vars needed for TestManager
+ *
+ * @return void
+ * @access private
+ */
+	function __setGetVars() {
+		if (in_array($this->category, $this->plugins)) {
+			$_GET['plugin'] = $this->category;
+		} elseif (in_array(Inflector::Humanize($this->category), $this->plugins)) {
+			$_GET['plugin'] = Inflector::Humanize($this->category);
+		} elseif ($this->category == 'app') {
+			$_GET['app'] = true;
+		}
+		if ($this->type == 'group') {
+			$_GET['group'] = true;
+		}
+	}
+/**
+ * tries to install simpletest and exits gracefully if it is not there
+ *
+ * @return void
+ * @access private
+ */
+	function __installSimpleTest() {
+		if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
+			$this->err('Sorry, Simpletest could not be found. Download it from http://simpletest.org and install it to your vendors directory.');
+			exit;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/acl.php
===================================================================
--- cake/console/libs/acl.php	(revision 0)
+++ cake/console/libs/acl.php	(revision 0)
@@ -0,0 +1,517 @@
+<?php
+/* SVN FILE: $Id: acl.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Component', 'Acl');
+App::import('Model', 'DbAcl');
+/**
+ * Shell for ACL management.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class AclShell extends Shell {
+/**
+ * Contains instance of AclComponent
+ *
+ * @var object
+ * @access public
+ */
+	var $Acl;
+/**
+ * Contains arguments parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	var $args;
+/**
+ * Contains database source to use
+ *
+ * @var string
+ * @access public
+ */
+	var $dataSource = 'default';
+/**
+ * Contains tasks to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig');
+/**
+ * Override startup of the Shell
+ *
+ * @access public
+ */
+	function startup() {
+		$this->dataSource = 'default';
+
+		if (isset($this->params['datasource'])) {
+			$this->dataSource = $this->params['datasource'];
+		}
+
+		if (!in_array(Configure::read('Acl.classname'), array('DbAcl', 'DB_ACL'))) {
+			$out = "--------------------------------------------------\n";
+			$out .= __("Error: Your current Cake configuration is set to", true) . "\n";
+			$out .= __("an ACL implementation other than DB. Please change", true) . "\n";
+			$out .= __("your core config to reflect your decision to use", true) . "\n";
+			$out .= __("DbAcl before attempting to use this script", true) . ".\n";
+			$out .= "--------------------------------------------------\n";
+			$out .= sprintf(__("Current ACL Classname: %s", true), Configure::read('Acl.classname')) . "\n";
+			$out .= "--------------------------------------------------\n";
+			$this->err($out);
+			$this->_stop();
+		}
+
+		if ($this->command && !in_array($this->command, array('help'))) {
+			if (!config('database')) {
+				$this->out(__("Your database configuration was not found. Take a moment to create one.", true), true);
+				$this->args = null;
+				return $this->DbConfig->execute();
+			}
+			require_once (CONFIGS.'database.php');
+
+			if (!in_array($this->command, array('initdb'))) {
+				$this->Acl = new AclComponent();
+				$controller = null;
+				$this->Acl->startup($controller);
+			}
+		}
+	}
+/**
+ * Override main() for help message hook
+ *
+ * @access public
+ */
+	function main() {
+		$out  = __("Available ACL commands:", true) . "\n";
+		$out .= "\t - create\n";
+		$out .= "\t - delete\n";
+		$out .= "\t - setParent\n";
+		$out .= "\t - getPath\n";
+		$out .= "\t - check\n";
+		$out .= "\t - grant\n";
+		$out .= "\t - deny\n";
+		$out .= "\t - inherit\n";
+		$out .= "\t - view\n";
+		$out .= "\t - initdb\n";
+		$out .= "\t - help\n\n";
+		$out .= __("For help, run the 'help' command.  For help on a specific command, run 'help <command>'", true);
+		$this->out($out);
+	}
+/**
+ * Creates an ARO/ACO node
+ *
+ * @access public
+ */
+	function create() {
+
+		$this->_checkArgs(3, 'create');
+		$this->checkNodeType();
+		extract($this->__dataVars());
+
+		$class = ucfirst($this->args[0]);
+		$object = new $class();
+
+		if (preg_match('/^([\w]+)\.(.*)$/', $this->args[1], $matches) && count($matches) == 3) {
+			$parent = array(
+				'model' => $matches[1],
+				'foreign_key' => $matches[2],
+			);
+		} else {
+			$parent = $this->args[1];
+		}
+
+		if (!empty($parent) && $parent != '/' && $parent != 'root') {
+			@$parent = $object->node($parent);
+			if (empty($parent)) {
+				$this->err(sprintf(__('Could not find parent node using reference "%s"', true), $this->args[1]));
+				return;
+			} else {
+				$parent = Set::extract($parent, "0.{$class}.id");
+			}
+		} else {
+			$parent = null;
+		}
+
+		if (preg_match('/^([\w]+)\.(.*)$/', $this->args[2], $matches) && count($matches) == 3) {
+			$data = array(
+				'model' => $matches[1],
+				'foreign_key' => $matches[2],
+			);
+		} else {
+			if (!($this->args[2] == '/')) {
+				$data = array('alias' => $this->args[2]);
+			} else {
+				$this->error(__('/ can not be used as an alias!', true), __('\t/ is the root, please supply a sub alias', true));
+			}
+		}
+
+		$data['parent_id'] = $parent;
+		$object->create();
+
+		if ($object->save($data)) {
+			$this->out(sprintf(__("New %s '%s' created.\n", true), $class, $this->args[2]), true);
+		} else {
+			$this->err(sprintf(__("There was a problem creating a new %s '%s'.", true), $class, $this->args[2]));
+		}
+	}
+/**
+ * Delete an ARO/ACO node.
+ *
+ * @access public
+ */
+	function delete() {
+		$this->_checkArgs(2, 'delete');
+		$this->checkNodeType();
+		extract($this->__dataVars());
+		if (!$this->Acl->{$class}->delete($this->args[1])) {
+			$this->error(__("Node Not Deleted", true), sprintf(__("There was an error deleting the %s. Check that the node exists", true), $class) . ".\n");
+		}
+		$this->out(sprintf(__("%s deleted", true), $class) . ".\n", true);
+	}
+
+/**
+ * Set parent for an ARO/ACO node.
+ *
+ * @access public
+ */
+	function setParent() {
+		$this->_checkArgs(3, 'setParent');
+		$this->checkNodeType();
+		extract($this->__dataVars());
+		$data = array(
+			$class => array(
+				'id' 		=> $this->args[1],
+				'parent_id' => $this->args[2]
+			)
+		);
+		$this->Acl->{$class}->create();
+		if (!$this->Acl->{$class}->save($data)) {
+			$this->out(__("Error in setting new parent. Please make sure the parent node exists, and is not a descendant of the node specified.", true), true);
+		} else {
+			$this->out(sprintf(__("Node parent set to %s", true), $this->args[2]) . "\n", true);
+		}
+	}
+/**
+ * Get path to specified ARO/ACO node.
+ *
+ * @access public
+ */
+	function getPath() {
+		$this->_checkArgs(2, 'getPath');
+		$this->checkNodeType();
+		extract($this->__dataVars());
+		$id = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]);
+		$nodes = $this->Acl->{$class}->getPath($id);
+		if (empty($nodes)) {
+			$this->error(sprintf(__("Supplied Node '%s' not found", true), $this->args[1]), __("No tree returned.", true));
+		}
+		for ($i = 0; $i < count($nodes); $i++) {
+			$this->out(str_repeat('  ', $i) . "[" . $nodes[$i][$class]['id'] . "]" . $nodes[$i][$class]['alias'] . "\n");
+		}
+	}
+/**
+ * Check permission for a given ARO to a given ACO.
+ *
+ * @access public
+ */
+	function check() {
+		$this->_checkArgs(3, 'check');
+		extract($this->__getParams());
+
+		if ($this->Acl->check($aro, $aco, $action)) {
+			$this->out(sprintf(__("%s is allowed.", true), $aro), true);
+		} else {
+			$this->out(sprintf(__("%s is not allowed.", true), $aro), true);
+		}
+	}
+/**
+ * Grant permission for a given ARO to a given ACO.
+ *
+ * @access public
+ */
+	function grant() {
+		$this->_checkArgs(3, 'grant');
+		extract($this->__getParams());
+
+		if ($this->Acl->allow($aro, $aco, $action)) {
+			$this->out(__("Permission granted.", true), true);
+		} else {
+			$this->out(__("Permission was not granted.", true), true);
+		}
+	}
+/**
+ * Deny access for an ARO to an ACO.
+ *
+ * @access public
+ */
+	function deny() {
+		$this->_checkArgs(3, 'deny');
+		extract($this->__getParams());
+
+		if ($this->Acl->deny($aro, $aco, $action)) {
+			$this->out(__("Permission denied.", true), true);
+		} else {
+			$this->out(__("Permission was not denied.", true), true);
+		}
+	}
+/**
+ * Set an ARO to inhermit permission to an ACO.
+ *
+ * @access public
+ */
+	function inherit() {
+		$this->_checkArgs(3, 'inherit');
+		extract($this->__getParams());
+
+		if ($this->Acl->inherit($aro, $aco, $action)) {
+			$this->out(__("Permission inherited.", true), true);
+		} else {
+			$this->out(__("Permission was not inherited.", true), true);
+		}
+	}
+/**
+ * Show a specific ARO/ACO node.
+ *
+ * @access public
+ */
+	function view() {
+		$this->_checkArgs(1, 'view');
+		$this->checkNodeType();
+		extract($this->__dataVars());
+		if (isset($this->args[1]) && !is_null($this->args[1])) {
+			$key = ife(is_numeric($this->args[1]), $secondary_id, 'alias');
+			$conditions = array($class . '.' . $key => $this->args[1]);
+		} else {
+			$conditions = null;
+		}
+		$nodes = $this->Acl->{$class}->find('all', array('conditions' => $conditions, 'order' => 'lft ASC'));
+		if (empty($nodes)) {
+			if (isset($this->args[1])) {
+				$this->error(sprintf(__("%s not found", true), $this->args[1]), __("No tree returned.", true));
+			} elseif (isset($this->args[0])) {
+				$this->error(sprintf(__("%s not found", true), $this->args[0]), __("No tree returned.", true));
+			}
+		}
+		$this->out($class . " tree:");
+		$this->hr();
+		$stack = array();
+		$last  = null;
+		foreach ($nodes as $n) {
+			$stack[] = $n;
+			if (!empty($last)) {
+				$end = end($stack);
+				if ($end[$class]['rght'] > $last) {
+					foreach ($stack as $k => $v) {
+						$end = end($stack);
+						if ($v[$class]['rght'] < $end[$class]['rght']) {
+							unset($stack[$k]);
+						}
+					}
+				}
+			}
+			$last   = $n[$class]['rght'];
+			$count  = count($stack);
+			$this->out(str_repeat('  ', $count) . "[" . $n[$class]['id'] . "]" . $n[$class]['alias']."\n");
+		}
+		$this->hr();
+	}
+/**
+ * Initialize ACL database.
+ *
+ * @access public
+ */
+	function initdb() {
+		$this->Dispatch->args = array('schema', 'run', 'create', 'DbAcl');
+		$this->Dispatch->dispatch();
+	}
+/**
+ * Show help screen.
+ *
+ * @access public
+ */
+	function help() {
+		$head  = __("Usage: cake acl <command> <arg1> <arg2>...", true) . "\n";
+		$head .= "-----------------------------------------------\n";
+		$head .= __("Commands:", true) . "\n\n";
+
+		$commands = array(
+			'create' => "\tcreate aro|aco <parent> <node>\n" .
+						"\t\t" . __("Creates a new ACL object <node> under the parent specified by <parent>, an id/alias.", true) . "\n" .
+						"\t\t" . __("The <parent> and <node> references can be in one of the following formats:", true) . "\n" .
+						"\t\t\t- " . __("<model>.<id> - The node will be bound to a specific record of the given model", true) . "\n" .
+						"\t\t\t- " . __("<alias> - The node will be given a string alias (or path, in the case of <parent>),", true) . "\n" .
+						"\t\t\t  " . __("i.e. 'John'.  When used with <parent>, this takes the form of an alias path,", true) . "\n" .
+						"\t\t\t  " . __("i.e. <group>/<subgroup>/<parent>.", true) . "\n" .
+						"\t\t" . __("To add a node at the root level, enter 'root' or '/' as the <parent> parameter.", true) . "\n",
+
+			'delete' =>	"\tdelete aro|aco <node>\n" .
+						"\t\t" . __("Deletes the ACL object with the given <node> reference (see 'create' for info on node references).", true) . "\n",
+
+			'setparent' => "\tsetParent aro|aco <node> <parent>\n" .
+							"\t\t" . __("Moves the ACL object specified by <node> beneath the parent ACL object specified by <parent>.", true) . "\n" .
+							"\t\t" . __("To identify the node and parent, use the row id.", true) . "\n",
+
+			'getpath' => "\tgetPath aro|aco <node>\n" .
+						"\t\t" . __("Returns the path to the ACL object specified by <node>. This command", true) . "\n" .
+						"\t\t" . __("is useful in determining the inhertiance of permissions for a certain", true) . "\n" .
+						"\t\t" . __("object in the tree.", true) . "\n" .
+						"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'check' =>	"\tcheck <aro_id> <aco_id> [<aco_action>] " . __("or", true) . " all\n" .
+						"\t\t" . __("Use this command to check ACL permissions.", true) . "\n" .
+						"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'grant' =>	"\tgrant <aro_id> <aco_id> [<aco_action>] " . __("or", true) . " all\n" .
+						"\t\t" . __("Use this command to grant ACL permissions. Once executed, the ARO", true) . "\n" .
+						"\t\t" . __("specified (and its children, if any) will have ALLOW access to the", true) . "\n" .
+						"\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
+						"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'deny' =>	"\tdeny <aro_id> <aco_id> [<aco_action>]" . __("or", true) . " all\n" .
+						"\t\t" . __("Use this command to deny ACL permissions. Once executed, the ARO", true) . "\n" .
+						"\t\t" . __("specified (and its children, if any) will have DENY access to the", true) . "\n" .
+						"\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
+						"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'inherit' =>	"\tinherit <aro_id> <aco_id> [<aco_action>]" . __("or", true) . " all\n" .
+							"\t\t" . __("Use this command to force a child ARO object to inherit its", true) . "\n" .
+							"\t\t" . __("permissions settings from its parent.", true) . "\n" .
+							"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'view' =>	"\tview aro|aco [<node>]\n" .
+						"\t\t" . __("The view command will return the ARO or ACO tree. The optional", true) . "\n" .
+						"\t\t" . __("id/alias parameter allows you to return only a portion of the requested tree.", true) . "\n" .
+						"\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n",
+
+			'initdb' =>	"\tinitdb\n".
+						"\t\t" . __("Uses this command : cake schema run create DbAcl", true) . "\n",
+
+			'help' => 	"\thelp [<command>]\n" .
+						"\t\t" . __("Displays this help message, or a message on a specific command.", true) . "\n"
+		);
+
+		$this->out($head);
+		if (!isset($this->args[0])) {
+			foreach ($commands as $cmd) {
+				$this->out("{$cmd}\n\n");
+			}
+		} elseif (isset($commands[low($this->args[0])])) {
+			$this->out($commands[low($this->args[0])] . "\n\n");
+		} else {
+			$this->out(sprintf(__("Command '%s' not found", true), $this->args[0]));
+		}
+	}
+/**
+ * Check that first argument specifies a valid Node type (ARO/ACO)
+ *
+ * @access public
+ */
+	function checkNodeType() {
+		if (!isset($this->args[0])) {
+			return false;
+		}
+		if ($this->args[0] != 'aco' && $this->args[0] != 'aro') {
+			$this->error(sprintf(__("Missing/Unknown node type: '%s'", true), $this->args[1]), __('Please specify which ACL object type you wish to create.', true));
+		}
+	}
+/**
+ * Checks that given node exists
+ *
+ * @param string $type Node type (ARO/ACO)
+ * @param integer $id Node id
+ * @return boolean Success
+ * @access public
+ */
+	function nodeExists() {
+		if (!$this->checkNodeType() && !isset($this->args[1])) {
+			return false;
+		}
+		extract($this->__dataVars($this->args[0]));
+		$key = (ife(is_numeric($this->args[1]), $secondary_id, 'alias'));
+		$conditions = array($class . '.' . $key => $this->args[1]);
+		$possibility = $this->Acl->{$class}->find('all', compact('conditions'));
+		if (empty($possibility)) {
+			$this->error(sprintf(__("%s not found", true), $this->args[1]), __("No tree returned.", true));
+		}
+		return $possibility;
+	}
+/**
+ * get params for standard Acl methods
+ *
+ * @return array aro, aco, action
+ * @access private
+ */
+	function __getParams() {
+		$aro = ife(is_numeric($this->args[0]), intval($this->args[0]), $this->args[0]);
+		$aco = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]);
+
+		if (is_string($aro) && preg_match('/^([\w]+)\.(.*)$/', $aro, $matches)) {
+			$aro = array(
+				'model' => $matches[1],
+				'foreign_key' => $matches[2],
+			);
+		}
+
+		if (is_string($aco) && preg_match('/^([\w]+)\.(.*)$/', $aco, $matches)) {
+			$aco = array(
+				'model' => $matches[1],
+				'foreign_key' => $matches[2],
+			);
+		}
+
+		$action = null;
+		if (isset($this->args[2])) {
+			$action = $this->args[2];
+			if ($action == '' || $action == 'all') {
+				$action = '*';
+			}
+		}
+		return compact('aro', 'aco', 'action');
+	}
+
+/**
+ * Build data parameters based on node type
+ *
+ * @param string $type Node type  (ARO/ACO)
+ * @return array Variables
+ * @access private
+ */
+	function __dataVars($type = null) {
+		if ($type == null) {
+			$type = $this->args[0];
+		}
+		$vars = array();
+		$class = ucwords($type);
+		$vars['secondary_id'] = ife(strtolower($class) == 'aro', 'foreign_key', 'object_id');
+		$vars['data_name'] = $type;
+		$vars['table_name'] = $type . 's';
+		$vars['class'] = $class;
+		return $vars;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/i18n.php
===================================================================
--- cake/console/libs/i18n.php	(revision 0)
+++ cake/console/libs/i18n.php	(revision 0)
@@ -0,0 +1,129 @@
+<?php
+/* SVN FILE: $Id: i18n.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Shell for I18N management.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class I18nShell extends Shell {
+/**
+ * Contains database source to use
+ *
+ * @var string
+ * @access public
+ */
+	var $dataSource = 'default';
+/**
+ * Contains tasks to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig', 'Extract');
+/**
+ * Override startup of the Shell
+ *
+ * @access public
+ */
+	function startup() {
+		$this->_welcome();
+		if (isset($this->params['datasource'])) {
+			$this->dataSource = $this->params['datasource'];
+		}
+
+		if ($this->command && !in_array($this->command, array('help'))) {
+			if (!config('database')) {
+				$this->out(__('Your database configuration was not found. Take a moment to create one.', true), true);
+				return $this->DbConfig->execute();
+			}
+		}
+	}
+/**
+ * Override main() for help message hook
+ *
+ * @access public
+ */
+	function main() {
+		$this->out(__('I18n Shell', true));
+		$this->hr();
+		$this->out(__('[E]xtract POT file from sources', true));
+		$this->out(__('[I]nitialize i18n database table', true));
+		$this->out(__('[H]elp', true));
+		$this->out(__('[Q]uit', true));
+
+		$choice = strtoupper($this->in(__('What would you like to do?', true), array('E', 'I', 'H', 'Q')));
+		switch ($choice) {
+			case 'E':
+				$this->Extract->execute();
+			break;
+			case 'I':
+				$this->initdb();
+			break;
+			case 'H':
+				$this->help();
+			break;
+			case 'Q':
+				exit(0);
+			break;
+			default:
+				$this->out(__('You have made an invalid selection. Please choose a command to execute by entering E, I, H, or Q.', true));
+		}
+		$this->hr();
+		$this->main();
+	}
+/**
+ * Initialize I18N database.
+ *
+ * @access public
+ */
+	function initdb() {
+		$this->Dispatch->args = array('schema', 'run', 'create', 'i18n');
+		$this->Dispatch->dispatch();
+	}
+/**
+ * Show help screen.
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out(__('I18n Shell:', true));
+		$this->hr();
+		$this->out(__('I18n Shell initializes i18n database table for your application', true));
+		$this->out(__('and generates .pot file(s) with translations.', true));
+		$this->hr();
+		$this->out(__('usage:', true));
+		$this->out('   cake i18n help');
+		$this->out('   cake i18n initdb [-datasource custom]');
+		$this->out('');
+		$this->hr();
+
+		$this->Extract->help();
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/db_config.php
===================================================================
--- cake/console/libs/tasks/db_config.php	(revision 0)
+++ cake/console/libs/tasks/db_config.php	(revision 0)
@@ -0,0 +1,353 @@
+<?php
+/* SVN FILE: $Id: db_config.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The DbConfig Task handles creating and updating the database.php
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+/**
+ * Task class for creating and updating the database configuration file.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class DbConfigTask extends Shell {
+/**
+ * path to CONFIG directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+/**
+ * Default configuration settings to use
+ *
+ * @var array
+ * @access private
+ */
+	var $__defaultConfig = array(
+		'name' => 'default', 'driver'=> 'mysql', 'persistent'=> 'false', 'host'=> 'localhost',
+		'login'=> 'root', 'password'=> 'password', 'database'=> 'project_name',
+		'schema'=> null, 'prefix'=> null, 'encoding' => null, 'port' => null
+	);
+/**
+ * initialization callback
+ *
+ * @var string
+ * @access public
+ */
+	function initialize() {
+		$this->path = $this->params['working'] . DS . 'config' . DS;
+	}
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+			$this->_stop();
+		}
+	}
+/**
+ * Interactive interface
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->hr();
+		$this->out('Database Configuration:');
+		$this->hr();
+		$done = false;
+		$dbConfigs = array();
+
+		while ($done == false) {
+			$name = '';
+
+			while ($name == '') {
+				$name = $this->in("Name:", null, 'default');
+				if (preg_match('/[^a-z0-9_]/i', $name)) {
+					$name = '';
+					$this->out('The name may only contain unaccented latin characters, numbers or underscores');
+				}
+				else if (preg_match('/^[^a-z_]/i', $name)) {
+					$name = '';
+					$this->out('The name must start with an unaccented latin character or an underscore');
+				}
+			}
+			$driver = '';
+
+			while ($driver == '') {
+				$driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql');
+			}
+			$persistent = '';
+
+			while ($persistent == '') {
+				$persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n');
+			}
+
+			if (low($persistent) == 'n') {
+				$persistent = 'false';
+			} else {
+				$persistent = 'true';
+			}
+			$host = '';
+
+			while ($host == '') {
+				$host = $this->in('Database Host:', null, 'localhost');
+			}
+			$port = '';
+
+			while ($port == '') {
+				$port = $this->in('Port?', null, 'n');
+			}
+
+			if (low($port) == 'n') {
+				$port = null;
+			}
+			$login = '';
+
+			while ($login == '') {
+				$login = $this->in('User:', null, 'root');
+			}
+			$password = '';
+			$blankPassword = false;
+
+			while ($password == '' && $blankPassword == false) {
+				$password = $this->in('Password:');
+
+				if ($password == '') {
+					$blank = $this->in('The password you supplied was empty. Use an empty password?', array('y', 'n'), 'n');
+					if ($blank == 'y')
+					{
+						$blankPassword = true;
+					}
+				}
+			}
+			$database = '';
+
+			while ($database == '') {
+				$database = $this->in('Database Name:', null, 'cake');
+			}
+			$prefix = '';
+
+			while ($prefix == '') {
+				$prefix = $this->in('Table Prefix?', null, 'n');
+			}
+
+			if (low($prefix) == 'n') {
+				$prefix = null;
+			}
+			$encoding = '';
+
+			while ($encoding == '') {
+				$encoding = $this->in('Table encoding?', null, 'n');
+			}
+
+			if (low($encoding) == 'n') {
+				$encoding = null;
+			}
+			$schema = '';
+
+			if ($driver == 'postgres') {
+				while ($schema == '') {
+					$schema = $this->in('Table schema?', null, 'n');
+				}
+			}
+
+			if (low($schema) == 'n') {
+				$schema = null;
+			}
+
+			$config = compact('name', 'driver', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema');
+
+			while ($this->__verify($config) == false) {
+				$this->__interactive();
+			}
+			$dbConfigs[] = $config;
+			$doneYet = $this->in('Do you wish to add another database configuration?', null, 'n');
+
+			if (low($doneYet == 'n')) {
+				$done = true;
+			}
+		}
+
+		$this->bake($dbConfigs);
+		config('database');
+		return true;
+	}
+/**
+ * Output verification message and bake if it looks good
+ *
+ * @return boolean True if user says it looks good, false otherwise
+ * @access private
+ */
+	function __verify($config) {
+		$config = array_merge($this->__defaultConfig, $config);
+		extract($config);
+		$this->out('');
+		$this->hr();
+		$this->out('The following database configuration will be created:');
+		$this->hr();
+		$this->out("Name:         $name");
+		$this->out("Driver:       $driver");
+		$this->out("Persistent:   $persistent");
+		$this->out("Host:         $host");
+
+		if ($port) {
+			$this->out("Port:         $port");
+		}
+
+		$this->out("User:         $login");
+		$this->out("Pass:         " . str_repeat('*', strlen($password)));
+		$this->out("Database:     $database");
+
+		if ($prefix) {
+			$this->out("Table prefix: $prefix");
+		}
+
+		if ($schema) {
+			$this->out("Schema:       $schema");
+		}
+
+		if ($encoding) {
+			$this->out("Encoding:     $encoding");
+		}
+
+		$this->hr();
+		$looksGood = $this->in('Look okay?', array('y', 'n'), 'y');
+
+		if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+			return $config;
+		}
+		return false;
+	}
+/**
+ * Assembles and writes database.php
+ *
+ * @param array $configs Configuration settings to use
+ * @return boolean Success
+ * @access public
+ */
+	function bake($configs) {
+		if (!is_dir($this->path)) {
+			$this->err($this->path . ' not found');
+			return false;
+		}
+
+		$filename = $this->path . 'database.php';
+		$oldConfigs = array();
+
+		if (file_exists($filename)) {
+			$db = new DATABASE_CONFIG;
+			$temp = get_class_vars(get_class($db));
+
+			foreach ($temp as $configName => $info) {
+				$info = array_merge($this->__defaultConfig, $info);
+
+				if (!isset($info['schema'])) {
+					$info['schema'] = null;
+				}
+				if (!isset($info['encoding'])) {
+					$info['encoding'] = null;
+				}
+				if (!isset($info['port'])) {
+					$info['port'] = null;
+				}
+
+				if ($info['persistent'] === false) {
+					$info['persistent'] = 'false';
+				} else {
+					$info['persistent'] = 'false';
+				}
+
+				$oldConfigs[] = array(
+					'name' => $configName,
+					'driver' => $info['driver'],
+					'persistent' => $info['persistent'],
+					'host' => $info['host'],
+					'port' => $info['port'],
+					'login' => $info['login'],
+					'password' => $info['password'],
+					'database' => $info['database'],
+					'prefix' => $info['prefix'],
+					'schema' => $info['schema'],
+					'encoding' => $info['encoding']
+				);
+			}
+		}
+
+		foreach ($oldConfigs as $key => $oldConfig) {
+			foreach ($configs as $key1 => $config) {
+				if ($oldConfig['name'] == $config['name']) {
+					unset($oldConfigs[$key]);
+				}
+			}
+		}
+
+		$configs = array_merge($oldConfigs, $configs);
+		$out = "<?php\n";
+		$out .= "class DATABASE_CONFIG {\n\n";
+
+		foreach ($configs as $config) {
+			$config = array_merge($this->__defaultConfig, $config);
+			extract($config);
+
+			$out .= "\tvar \${$name} = array(\n";
+			$out .= "\t\t'driver' => '{$driver}',\n";
+			$out .= "\t\t'persistent' => {$persistent},\n";
+			$out .= "\t\t'host' => '{$host}',\n";
+
+			if ($port) {
+				$out .= "\t\t'port' => {$port},\n";
+			}
+
+			$out .= "\t\t'login' => '{$login}',\n";
+			$out .= "\t\t'password' => '{$password}',\n";
+			$out .= "\t\t'database' => '{$database}',\n";
+
+			if ($schema) {
+				$out .= "\t\t'schema' => '{$schema}',\n";
+			}
+
+			if ($prefix) {
+				$out .= "\t\t'prefix' => '{$prefix}',\n";
+			}
+
+			if ($encoding) {
+				$out .= "\t\t'encoding' => '{$encoding}'\n";
+			}
+
+			$out .= "\t);\n";
+		}
+
+		$out .= "}\n";
+		$out .= "?>";
+		$filename = $this->path.'database.php';
+		return $this->createFile($filename, $out);
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/extract.php
===================================================================
--- cake/console/libs/tasks/extract.php	(revision 0)
+++ cake/console/libs/tasks/extract.php	(revision 0)
@@ -0,0 +1,682 @@
+<?php
+/* SVN FILE: $Id: extract.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Only used when -debug option
+ */
+	ob_start();
+
+	$singularReturn = __('Singular string  return __()', true);
+	$singularEcho = __('Singular string  echo __()');
+
+	$pluralReturn = __n('% apple in the bowl (plural string return __n())', '% apples in the blowl (plural string 2 return __n())', 3, true);
+	$pluralEcho = __n('% apple in the bowl (plural string 2 echo __n())', '% apples in the blowl (plural string 2 echo __n()', 3);
+
+	$singularDomainReturn = __d('controllers', 'Singular string domain lookup return __d()', true);
+	$singularDomainEcho = __d('controllers', 'Singular string domain lookup echo __d()');
+
+	$pluralDomainReturn = __dn('controllers', '% pears in the bowl (plural string domain lookup return __dn())', '% pears in the blowl (plural string domain lookup return __dn())', 3, true);
+	$pluralDomainEcho = __dn('controllers', '% pears in the bowl (plural string domain lookup echo __dn())', '% pears in the blowl (plural string domain lookup echo __dn())', 3);
+
+	$singularDomainCategoryReturn = __dc('controllers', 'Singular string domain and category lookup return __dc()', 5, true);
+	$singularDomainCategoryEcho = __dc('controllers', 'Singular string domain and category lookup echo __dc()', 5);
+
+	$pluralDomainCategoryReturn = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup return __dcn())', '% apples in the blowl (plural string 2 domain and category lookup return __dcn())', 3, 5, true);
+	$pluralDomainCategoryEcho = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup echo __dcn())', '% apples in the blowl (plural string 2 domain and category lookup echo __dcn())', 3, 5);
+
+	$categoryReturn = __c('Category string lookup line return __c()', 5, true);
+	$categoryEcho = __c('Category string  lookup line echo __c()', 5);
+
+	ob_end_clean();
+/**
+ * Language string extractor
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class ExtractTask extends Shell{
+/**
+ * Path to use when looking for strings
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+/**
+ * Files from where to extract
+ *
+ * @var array
+ * @access public
+ */
+	var $files = array();
+/**
+ * Filename where to deposit translations
+ *
+ * @var string
+ * @access private
+ */
+	var $__filename = 'default';
+/**
+ * True if all strings should be merged into one file
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__oneFile = true;
+/**
+ * Current file being processed
+ *
+ * @var string
+ * @access private
+ */
+	var $__file = null;
+/**
+ * Extracted tokens
+ *
+ * @var array
+ * @access private
+ */
+	var $__tokens = array();
+/**
+ * Extracted strings
+ *
+ * @var array
+ * @access private
+ */
+	var $__strings = array();
+/**
+ * History of file versions
+ *
+ * @var array
+ * @access private
+ */
+	var $__fileVersions = array();
+/**
+ * Destination path
+ *
+ * @var string
+ * @access private
+ */
+	var $__output = null;
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (isset($this->params['files']) && !is_array($this->params['files'])) {
+			$this->files = explode(',', $this->params['files']);
+		}
+		if (isset($this->params['path'])) {
+			$this->path = $this->params['path'];
+		} else {
+			$response = '';
+			while ($response == '') {
+				$response = $this->in("What is the full path you would like to extract?\nExample: " . $this->params['root'] . DS . "myapp\n[Q]uit", null, 'Q');
+				if (strtoupper($response) === 'Q') {
+					$this->out('Extract Aborted');
+					$this->_stop();
+				}
+			}
+
+			if (is_dir($response)) {
+				$this->path = $response;
+			} else {
+				$this->err('The directory path you supplied was not found. Please try again.');
+				$this->execute();
+			}
+		}
+
+		if (isset($this->params['debug'])) {
+			$this->path = ROOT;
+			$this->files = array(__FILE__);
+		}
+
+		if (isset($this->params['output'])) {
+			$this->__output = $this->params['output'];
+		} else {
+			$response = '';
+			while ($response == '') {
+				$response = $this->in("What is the full path you would like to output?\nExample: " . $this->path . DS . "locale\n[Q]uit", null, $this->path . DS . "locale");
+				if (strtoupper($response) === 'Q') {
+					$this->out('Extract Aborted');
+					$this->_stop();
+				}
+			}
+
+			if (is_dir($response)) {
+				$this->__output = $response . DS;
+			} else {
+				$this->err('The directory path you supplied was not found. Please try again.');
+				$this->execute();
+			}
+		}
+
+		if (empty($this->files)) {
+			$this->files = $this->__searchDirectory();
+		}
+		$this->__extract();
+	}
+/**
+ * Extract text
+ *
+ * @access private
+ */
+	function __extract() {
+		$this->out('');
+		$this->out('');
+		$this->out(__('Extracting...', true));
+		$this->hr();
+		$this->out(__('Path: ', true). $this->path);
+		$this->out(__('Output Directory: ', true). $this->__output);
+		$this->hr();
+
+		$response = '';
+		$filename = '';
+		while ($response == '') {
+			$response = $this->in(__('Would you like to merge all translations into one file?', true), array('y','n'), 'y');
+			if (strtolower($response) == 'n') {
+				$this->__oneFile = false;
+			} else {
+				while ($filename == '') {
+					$filename = $this->in(__('What should we name this file?', true), null, $this->__filename);
+					if ($filename == '') {
+						$this->out(__('The filesname you supplied was empty. Please try again.', true));
+					}
+				}
+				$this->__filename = $filename;
+			}
+		}
+		$this->__extractTokens();
+	}
+/**
+ * Show help options
+ *
+ * @access public
+ */
+	function help() {
+		$this->out(__('CakePHP Language String Extraction:', true));
+		$this->hr();
+		$this->out(__('The Extract script generates .pot file(s) with translations', true));
+		$this->out(__('By default the .pot file(s) will be place in the locale directory of -app', true));
+		$this->out(__('By default -app is ROOT/app', true));
+		$this->hr();
+		$this->out(__('usage: cake i18n extract [command] [path...]', true));
+		$this->out('');
+		$this->out(__('commands:', true));
+		$this->out(__('   -app [path...]: directory where your application is located', true));
+		$this->out(__('   -root [path...]: path to install', true));
+		$this->out(__('   -core [path...]: path to cake directory', true));
+		$this->out(__('   -path [path...]: Full path to directory to extract strings', true));
+		$this->out(__('   -output [path...]: Full path to output directory', true));
+		$this->out(__('   -files: [comma separated list of files, full path to file is needed]', true));
+		$this->out(__('   cake i18n extract help: Shows this help message.', true));
+		$this->out(__('   -debug: Perform self test.', true));
+		$this->out('');
+	}
+/**
+ * Extract tokens out of all files to be processed
+ *
+ * @access private
+ */
+	function __extractTokens() {
+		foreach ($this->files as $file) {
+			$this->__file = $file;
+			$this->out(sprintf(__('Processing %s...', true), $file));
+
+			$code = file_get_contents($file);
+
+			$this->__findVersion($code, $file);
+			$allTokens = token_get_all($code);
+			$this->__tokens = array();
+			$lineNumber = 1;
+
+			foreach ($allTokens as $token) {
+				if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) {
+					if (is_array($token)) {
+						$token[] = $lineNumber;
+					}
+					$this->__tokens[] = $token;
+				}
+
+				if (is_array($token)) {
+					$lineNumber += count(split("\n", $token[1])) - 1;
+				} else {
+					$lineNumber += count(split("\n", $token)) - 1;
+				}
+			}
+			unset($allTokens);
+			$this->basic();
+			$this->basic('__c');
+			$this->extended();
+			$this->extended('__dc', 2);
+			$this->extended('__n', 0, true);
+			$this->extended('__dn', 2, true);
+			$this->extended('__dcn', 4, true);
+		}
+		$this->__buildFiles();
+		$this->__writeFiles();
+		$this->out('Done.');
+	}
+/**
+ * Will parse  __(), __c() functions
+ *
+ * @param string $functionName Function name that indicates translatable string (e.g: '__')
+ * @access public
+ */
+	function basic($functionName = '__') {
+		$count = 0;
+		$tokenCount = count($this->__tokens);
+
+		while (($tokenCount - $count) > 3) {
+			list($countToken, $parenthesis, $middle, $right) = array($this->__tokens[$count], $this->__tokens[$count + 1], $this->__tokens[$count + 2], $this->__tokens[$count + 3]);
+			if (!is_array($countToken)) {
+				$count++;
+				continue;
+			}
+
+			list($type, $string, $line) = $countToken;
+			if (($type == T_STRING) && ($string == $functionName) && ($parenthesis == '(')) {
+
+				if (in_array($right, array(')', ','))
+				&& (is_array($middle) && ($middle[0] == T_CONSTANT_ENCAPSED_STRING))) {
+
+					if ($this->__oneFile === true) {
+						$this->__strings[$this->__formatString($middle[1])][$this->__file][] = $line;
+					} else {
+						$this->__strings[$this->__file][$this->__formatString($middle[1])][] = $line;
+					}
+				} else {
+					$this->__markerError($this->__file, $line, $functionName, $count);
+				}
+			}
+			$count++;
+		}
+	}
+/**
+ * Will parse __d(), __dc(), __n(), __dn(), __dcn()
+ *
+ * @param string $functionName Function name that indicates translatable string (e.g: '__')
+ * @param integer $shift Number of parameters to shift to find translateable string
+ * @param boolean $plural Set to true if function supports plural format, false otherwise
+ * @access public
+ */
+	function extended($functionName = '__d', $shift = 0, $plural = false) {
+		$count = 0;
+		$tokenCount = count($this->__tokens);
+
+		while (($tokenCount - $count) > 7) {
+			list($countToken, $firstParenthesis) = array($this->__tokens[$count], $this->__tokens[$count + 1]);
+			if (!is_array($countToken)) {
+				$count++;
+				continue;
+			}
+
+			list($type, $string, $line) = $countToken;
+			if (($type == T_STRING) && ($string == $functionName) && ($firstParenthesis == '(')) {
+				$position = $count;
+				$depth = 0;
+
+				while ($depth == 0) {
+					if ($this->__tokens[$position] == '(') {
+						$depth++;
+					} elseif ($this->__tokens[$position] == ')') {
+						$depth--;
+					}
+					$position++;
+				}
+
+				if ($plural) {
+					$end = $position + $shift + 7;
+
+					if ($this->__tokens[$position + $shift + 5] === ')') {
+						$end = $position + $shift + 5;
+					}
+
+					if (empty($shift)) {
+						list($singular, $firstComma, $plural, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$end]);
+						$condition = ($seoncdComma == ',');
+					} else {
+						list($domain, $firstComma, $singular, $seoncdComma, $plural, $comma3, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$position + 4], $this->__tokens[$position + 5], $this->__tokens[$end]);
+						$condition = ($comma3 == ',');
+					}
+					$condition = $condition &&
+						(is_array($singular) && ($singular[0] == T_CONSTANT_ENCAPSED_STRING)) &&
+						(is_array($plural) && ($plural[0] == T_CONSTANT_ENCAPSED_STRING));
+				} else {
+					if ($this->__tokens[$position + $shift + 5] === ')') {
+						$comma = $this->__tokens[$position + $shift + 3];
+						$end = $position + $shift + 5;
+					} else {
+						$comma = null;
+						$end = $position + $shift + 3;
+					}
+
+					list($domain, $firstComma, $text, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $comma, $this->__tokens[$end]);
+					$condition = ($seoncdComma == ',' || $seoncdComma === null) &&
+						(is_array($domain) && ($domain[0] == T_CONSTANT_ENCAPSED_STRING)) &&
+						(is_array($text) && ($text[0] == T_CONSTANT_ENCAPSED_STRING));
+				}
+
+				if (($endParenthesis == ')') && $condition) {
+					if ($this->__oneFile === true) {
+						if ($plural) {
+							$this->__strings[$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][$this->__file][] = $line;
+						} else {
+							$this->__strings[$this->__formatString($text[1])][$this->__file][] = $line;
+						}
+					} else {
+						if ($plural) {
+							$this->__strings[$this->__file][$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][] = $line;
+						} else {
+							$this->__strings[$this->__file][$this->__formatString($text[1])][] = $line;
+						}
+					}
+				} else {
+					$this->__markerError($this->__file, $line, $functionName, $count);
+				}
+			}
+			$count++;
+		}
+	}
+/**
+ * Build the translate template file contents out of obtained strings
+ *
+ * @access private
+ */
+	function __buildFiles() {
+		foreach ($this->__strings as $str => $fileInfo) {
+			$output = '';
+			$occured = $fileList = array();
+
+			if ($this->__oneFile === true) {
+				foreach ($fileInfo as $file => $lines) {
+					$occured[] = "$file:" . join(';', $lines);
+
+					if (isset($this->__fileVersions[$file])) {
+						$fileList[] = $this->__fileVersions[$file];
+					}
+				}
+				$occurances = join("\n#: ", $occured);
+				$occurances = str_replace($this->path, '', $occurances);
+				$output = "#: $occurances\n";
+				$filename = $this->__filename;
+
+				if (strpos($str, "\0") === false) {
+					$output .= "msgid \"$str\"\n";
+					$output .= "msgstr \"\"\n";
+				} else {
+					list($singular, $plural) = explode("\0", $str);
+					$output .= "msgid \"$singular\"\n";
+					$output .= "msgid_plural \"$plural\"\n";
+					$output .= "msgstr[0] \"\"\n";
+					$output .= "msgstr[1] \"\"\n";
+				}
+				$output .= "\n";
+			} else {
+				foreach ($fileInfo as $file => $lines) {
+					$filename = $str;
+					$occured = array("$str:" . join(';', $lines));
+
+					if (isset($this->__fileVersions[$str])) {
+						$fileList[] = $this->__fileVersions[$str];
+					}
+					$occurances = join("\n#: ", $occured);
+					$occurances = str_replace($this->path, '', $occurances);
+					$output .= "#: $occurances\n";
+
+					if (strpos($file, "\0") === false) {
+						$output .= "msgid \"$file\"\n";
+						$output .= "msgstr \"\"\n";
+					} else {
+						list($singular, $plural) = explode("\0", $file);
+						$output .= "msgid \"$singular\"\n";
+						$output .= "msgid_plural \"$plural\"\n";
+						$output .= "msgstr[0] \"\"\n";
+						$output .= "msgstr[1] \"\"\n";
+					}
+					$output .= "\n";
+				}
+			}
+			$this->__store($filename, $output, $fileList);
+		}
+	}
+/**
+ * Prepare a file to be stored
+ *
+ * @param string $file Filename
+ * @param string $input What to store
+ * @param array $fileList File list
+ * @param integer $get Set to 1 to get files to store, false to set
+ * @return mixed If $get == 1, files to store, otherwise void
+ * @access private
+ */
+	function __store($file = 0, $input = 0, $fileList = array(), $get = 0) {
+		static $storage = array();
+
+		if (!$get) {
+			if (isset($storage[$file])) {
+				$storage[$file][1] = array_unique(array_merge($storage[$file][1], $fileList));
+				$storage[$file][] = $input;
+			} else {
+				$storage[$file] = array();
+				$storage[$file][0] = $this->__writeHeader();
+				$storage[$file][1] = $fileList;
+				$storage[$file][2] = $input;
+			}
+		} else {
+			return $storage;
+		}
+	}
+/**
+ * Write the files that need to be stored
+ *
+ * @access private
+ */
+	function __writeFiles() {
+		$output = $this->__store(0, 0, array(), 1);
+		$output = $this->__mergeFiles($output);
+
+		foreach ($output as $file => $content) {
+			$tmp = str_replace(array($this->path, '.php','.ctp','.thtml', '.inc','.tpl' ), '', $file);
+			$tmp = str_replace(DS, '.', $tmp);
+			$file = str_replace('.', '-', $tmp) .'.pot';
+			$fileList = $content[1];
+
+			unset($content[1]);
+
+			$fileList = str_replace(array($this->path), '', $fileList);
+
+			if (count($fileList) > 1) {
+				$fileList = "Generated from files:\n#  " . join("\n#  ", $fileList);
+			} elseif (count($fileList) == 1) {
+				$fileList = 'Generated from file: ' . join('', $fileList);
+			} else {
+				$fileList = 'No version information was available in the source files.';
+			}
+
+			if (is_file($this->__output . $file)) {
+				$response = '';
+				while ($response == '') {
+					$response = $this->in("\n\nError: ".$file . ' already exists in this location. Overwrite?', array('y','n', 'q'), 'n');
+					if (strtoupper($response) === 'Q') {
+						$this->out('Extract Aborted');
+						$this->_stop();
+					} elseif (strtoupper($response) === 'N') {
+						$response = '';
+						while ($response == '') {
+							$response = $this->in("What would you like to name this file?\nExample: new_" . $file, null, "new_" . $file);
+							$file = $response;
+						}
+					}
+				}
+			}
+			$fp = fopen($this->__output . $file, 'w');
+			fwrite($fp, str_replace('--VERSIONS--', $fileList, join('', $content)));
+			fclose($fp);
+		}
+	}
+/**
+ * Merge output files
+ *
+ * @param array $output Output to merge
+ * @return array Merged output
+ * @access private
+ */
+	function __mergeFiles($output) {
+		foreach ($output as $file => $content) {
+			if (count($content) <= 1 && $file != $this->__filename) {
+				@$output[$this->__filename][1] = array_unique(array_merge($output[$this->__filename][1], $content[1]));
+
+				if (!isset($output[$this->__filename][0])) {
+					$output[$this->__filename][0] = $content[0];
+				}
+				unset($content[0]);
+				unset($content[1]);
+
+				foreach ($content as $msgid) {
+					$output[$this->__filename][] = $msgid;
+				}
+				unset($output[$file]);
+			}
+		}
+		return $output;
+	}
+/**
+ * Build the translation template header
+ *
+ * @return string Translation template header
+ * @access private
+ */
+	function __writeHeader() {
+		$output  = "# LANGUAGE translation of CakePHP Application\n";
+		$output .= "# Copyright YEAR NAME <EMAIL@ADDRESS>\n";
+		$output .= "# --VERSIONS--\n";
+		$output .= "#\n";
+		$output .= "#, fuzzy\n";
+		$output .= "msgid \"\"\n";
+		$output .= "msgstr \"\"\n";
+		$output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
+		$output .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
+		$output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
+		$output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
+		$output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
+		$output .= "\"MIME-Version: 1.0\\n\"\n";
+		$output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
+		$output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
+		$output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n";
+		return $output;
+	}
+/**
+ * Find the version number of a file looking for SVN commands
+ *
+ * @param string $code Source code of file
+ * @param string $file File
+ * @access private
+ */
+	function __findVersion($code, $file) {
+		$header = '$Id' . ':';
+		if (preg_match('/\\' . $header . ' [\\w.]* ([\\d]*)/', $code, $versionInfo)) {
+			$version = str_replace(ROOT, '', 'Revision: ' . $versionInfo[1] . ' ' .$file);
+			$this->__fileVersions[$file] = $version;
+		}
+	}
+/**
+ * Format a string to be added as a translateable string
+ *
+ * @param string $string String to format
+ * @return string Formatted string
+ * @access private
+ */
+	function __formatString($string) {
+		$quote = substr($string, 0, 1);
+		$string = substr($string, 1, -1);
+		if ($quote == '"') {
+			$string = stripcslashes($string);
+		} else {
+			$string = strtr($string, array("\\'" => "'", "\\\\" => "\\"));
+		}
+		$string = str_replace("\r\n", "\n", $string);
+		return addcslashes($string, "\0..\37\\\"");
+	}
+/**
+ * Indicate an invalid marker on a processed file
+ *
+ * @param string $file File where invalid marker resides
+ * @param integer $line Line number
+ * @param string $marker Marker found
+ * @param integer $count Count
+ * @access private
+ */
+	function __markerError($file, $line, $marker, $count) {
+		$this->out("Invalid marker content in $file:$line\n* $marker(", true);
+		$count += 2;
+		$tokenCount = count($this->__tokens);
+		$parenthesis = 1;
+
+		while ((($tokenCount - $count) > 0) && $parenthesis) {
+			if (is_array($this->__tokens[$count])) {
+				$this->out($this->__tokens[$count][1], false);
+			} else {
+				$this->out($this->__tokens[$count], false);
+				if ($this->__tokens[$count] == '(') {
+					$parenthesis++;
+				}
+
+				if ($this->__tokens[$count] == ')') {
+					$parenthesis--;
+				}
+			}
+			$count++;
+		}
+		$this->out("\n", true);
+	}
+/**
+ * Search the specified path for files that may contain translateable strings
+ *
+ * @param string $path Path (or set to null to use current)
+ * @return array Files
+ * @access private
+ */
+	function __searchDirectory($path = null) {
+		if ($path === null) {
+			$path = $this->path .DS;
+		}
+		$files = glob("$path*.{php,ctp,thtml,inc,tpl}", GLOB_BRACE);
+		$dirs = glob("$path*", GLOB_ONLYDIR);
+
+		foreach ($dirs as $dir) {
+			if (!preg_match("!(^|.+/)(CVS|.svn)$!", $dir)) {
+				$files = array_merge($files, $this->__searchDirectory("$dir" . DS));
+				if (($id = array_search($dir . DS . 'extract.php', $files)) !== FALSE) {
+					unset($files[$id]);
+				}
+			}
+		}
+		return $files;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/view.php
===================================================================
--- cake/console/libs/tasks/view.php	(revision 0)
+++ cake/console/libs/tasks/view.php	(revision 0)
@@ -0,0 +1,389 @@
+<?php
+/* SVN FILE: $Id: view.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The View Tasks handles creating and updating view files.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'Controller');
+/**
+ * Task class for creating and updating view files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ViewTask extends Shell {
+/**
+ * Name of plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * Tasks to be loaded by this Task
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Project', 'Controller');
+/**
+ * path to VIEWS directory
+ *
+ * @var array
+ * @access public
+ */
+	var $path = VIEWS;
+/**
+ * Name of the controller being used
+ *
+ * @var string
+ * @access public
+ */
+	var $controllerName = null;
+/**
+ * Path to controller to put views
+ *
+ * @var string
+ * @access public
+ */
+	var $controllerPath = null;
+/**
+ * The template file to use
+ *
+ * @var string
+ * @access public
+ */
+	var $template = null;
+/**
+ * Actions to use for scaffolding
+ *
+ * @var array
+ * @access public
+ */
+	var $scaffoldActions = array('index', 'view', 'add', 'edit');
+/**
+ * Override initialize
+ *
+ * @access public
+ */
+	function initialize() {
+	}
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (isset($this->args[0])) {
+			$controller = $action = $alias = null;
+			$this->controllerName = Inflector::camelize($this->args[0]);
+			$this->controllerPath = Inflector::underscore($this->controllerName);
+
+			if (isset($this->args[1])) {
+				$this->template = $this->args[1];
+			}
+
+			if (isset($this->args[2])) {
+				$action = $this->args[2];
+			}
+
+			if (!$action) {
+				$action = $this->template;
+			}
+
+			if (in_array($action, $this->scaffoldActions)) {
+				$this->bake($action, true);
+			} elseif ($action) {
+				$this->bake($action, true);
+			} else {
+				$vars = $this->__loadController();
+				if ($vars) {
+
+					$methods =  array_diff(
+						array_map('strtolower', get_class_methods($this->controllerName . 'Controller')),
+						array_map('strtolower', get_class_methods('appcontroller'))
+					);
+					if (empty($methods)) {
+						$methods = $this->scaffoldActions;
+					}
+					$adminDelete = null;
+
+					$adminRoute = Configure::read('Routing.admin');
+					if (!empty($adminRoute)) {
+						$adminDelete = $adminRoute.'_delete';
+					}
+					foreach ($methods as $method) {
+						if ($method{0} != '_' && !in_array($method, array('delete', $adminDelete))) {
+							$content = $this->getContent($method, $vars);
+							$this->bake($method, $content);
+						}
+					}
+				}
+			}
+		}
+	}
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->hr();
+		$this->out(sprintf("Bake View\nPath: %s", $this->path));
+		$this->hr();
+		$wannaDoAdmin = 'n';
+		$wannaDoScaffold = 'y';
+		$this->interactive = false;
+
+		$this->controllerName = $this->Controller->getName();
+
+		$this->controllerPath = low(Inflector::underscore($this->controllerName));
+
+		$interactive = $this->in("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite {$this->controllerName} views if it exist.", array('y','n'), 'y');
+
+		if (low($interactive) == 'y' || low($interactive) == 'yes') {
+			$this->interactive = true;
+			$wannaDoScaffold = $this->in("Would you like to create some scaffolded views (index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller and model classes (including associated models).", array('y','n'), 'n');
+		}
+
+		if (low($wannaDoScaffold) == 'y' || low($wannaDoScaffold) == 'yes') {
+			$wannaDoAdmin = $this->in("Would you like to create the views for admin routing?", array('y','n'), 'y');
+		}
+		$admin = false;
+
+		if ((low($wannaDoAdmin) == 'y' || low($wannaDoAdmin) == 'yes')) {
+			$admin = $this->getAdmin();
+		}
+
+		if (low($wannaDoScaffold) == 'y' || low($wannaDoScaffold) == 'yes') {
+			$actions = $this->scaffoldActions;
+			if ($admin) {
+				foreach ($actions as $action) {
+					$actions[] = $admin . $action;
+				}
+			}
+			$vars = $this->__loadController();
+			if ($vars) {
+				foreach ($actions as $action) {
+					$content = $this->getContent($action, $vars);
+					$this->bake($action, $content);
+				}
+			}
+			$this->hr();
+			$this->out('');
+			$this->out('View Scaffolding Complete.'."\n");
+		} else {
+			$action = '';
+			while ($action == '') {
+				$action = $this->in('Action Name? (use camelCased function name)');
+				if ($action == '') {
+					$this->out('The action name you supplied was empty. Please try again.');
+				}
+			}
+			$this->out('');
+			$this->hr();
+			$this->out('The following view will be created:');
+			$this->hr();
+			$this->out("Controller Name: {$this->controllerName}");
+			$this->out("Action Name:	 {$action}");
+			$this->out("Path:			 ".$this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp");
+			$this->hr();
+			$looksGood = $this->in('Look okay?', array('y','n'), 'y');
+			if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+				$this->bake($action);
+				$this->_stop();
+			} else {
+				$this->out('Bake Aborted.');
+			}
+		}
+	}
+/**
+ * Loads Controller and sets variables for the template
+ * Available template variables
+ *	'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
+ *	'singularHumanName', 'pluralHumanName', 'fields', 'foreignKeys',
+ *	'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'
+ *
+ * @return array Returns an variables to be made available to a view template
+ * @access private
+ */
+	function __loadController() {
+		if (!$this->controllerName) {
+			$this->err(__('Controller not found', true));
+		}
+
+		$import = $this->controllerName;
+		if ($this->plugin) {
+			$import = $this->plugin . '.' . $this->controllerName;
+		}
+
+		if (!App::import('Controller', $import)) {
+			$file = $this->controllerPath . '_controller.php';
+			$this->err(sprintf(__("The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller.", true), $file));
+			$this->_stop();
+		}
+		$controllerClassName = $this->controllerName . 'Controller';
+		$controllerObj = & new $controllerClassName();
+		$controllerObj->constructClasses();
+		$modelClass = $controllerObj->modelClass;
+		$modelObj =& ClassRegistry::getObject($controllerObj->modelKey);
+
+		if ($modelObj) {
+			$primaryKey = $modelObj->primaryKey;
+			$displayField = $modelObj->displayField;
+			$singularVar = Inflector::variable($modelClass);
+			$pluralVar = Inflector::variable($this->controllerName);
+			$singularHumanName = Inflector::humanize($modelClass);
+			$pluralHumanName = Inflector::humanize($this->controllerName);
+			$schema = $modelObj->schema();
+			$fields = array_keys($schema);
+			$associations = $this->__associations($modelObj);
+		} else {
+			$primaryKey = null;
+			$displayField = null;
+			$singularVar = Inflector::variable(Inflector::singularize($this->controllerName));
+			$pluralVar = Inflector::variable($this->controllerName);
+			$singularHumanName = Inflector::humanize(Inflector::singularize($this->controllerName));
+			$pluralHumanName = Inflector::humanize($this->controllerName);
+			$fields = array();
+			$schema = array();
+			$associations = array();
+		}
+
+		return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
+				'singularHumanName', 'pluralHumanName', 'fields','associations');
+	}
+/**
+ * Assembles and writes bakes the view file.
+ *
+ * @param string $action Action to bake
+ * @param string $content Content to write
+ * @return boolean Success
+ * @access public
+ */
+	function bake($action, $content = '') {
+		if ($content === true) {
+			$content = $this->getContent();
+		}
+		$filename = $this->path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp';
+		$Folder =& new Folder($this->path . $this->controllerPath, true);
+		$errors = $Folder->errors();
+		if (empty($errors)) {
+			$path = $Folder->slashTerm($Folder->pwd());
+			return $this->createFile($filename, $content);
+		} else {
+			foreach ($errors as $error) {
+				$this->err($error);
+			}
+		}
+		return false;
+	}
+/**
+ * Builds content from template and variables
+ *
+ * @param string $template file to use
+ * @param array $vars passed for use in templates
+ * @return string content from template
+ * @access public
+ */
+	function getContent($template = null, $vars = null) {
+		if (!$template) {
+			$template = $this->template;
+		}
+		$action = $template;
+
+		$adminRoute = Configure::read('Routing.admin');
+		if (!empty($adminRoute) && strpos($template, $adminRoute) !== false) {
+			$template = str_replace($adminRoute.'_', '', $template);
+		}
+		if (in_array($template, array('add', 'edit'))) {
+			$action = $template;
+			$template = 'form';
+		}
+		$loaded = false;
+		foreach ($this->Dispatch->shellPaths as $path) {
+			$templatePath = $path . 'templates' . DS . 'views' . DS .Inflector::underscore($template).'.ctp';
+			if (file_exists($templatePath) && is_file($templatePath)) {
+				$loaded = true;
+				break;
+			}
+		}
+		if (!$vars) {
+			$vars = $this->__loadController();
+		}
+		if ($loaded) {
+			extract($vars);
+			ob_start();
+			ob_implicit_flush(0);
+			include($templatePath);
+			$content = ob_get_clean();
+			return $content;
+		}
+		$this->hr();
+		$this->err(sprintf(__('Template for %s could not be found', true), $template));
+		return false;
+	}
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake view <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("\n\tview <controller>\n\t\twill read the given controller for methods\n\t\tand bake corresponding views.\n\t\tIf var scaffold is found it will bake the scaffolded actions\n\t\t(index,view,add,edit)");
+		$this->out("\n\tview <controller> <action>\n\t\twill bake a template. core templates: (index, add, edit, view)");
+		$this->out("\n\tview <controller> <template> <alias>\n\t\twill use the template specified but name the file based on the alias");
+		$this->out("");
+		$this->_stop();
+	}
+/**
+ * Returns associations for controllers models.
+ *
+ * @return  array $associations
+ * @access private
+ */
+	function __associations($model) {
+		$keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+		$associations = array();
+
+		foreach ($keys as $key => $type) {
+			foreach ($model->{$type} as $assocKey => $assocData) {
+				$associations[$type][$assocKey]['primaryKey'] = $model->{$assocKey}->primaryKey;
+				$associations[$type][$assocKey]['displayField'] = $model->{$assocKey}->displayField;
+				$associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey'];
+				$associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($assocData['className']));
+				$associations[$type][$assocKey]['fields'] =  array_keys($model->{$assocKey}->schema());
+			}
+		}
+		return $associations;
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/plugin.php
===================================================================
--- cake/console/libs/tasks/plugin.php	(revision 0)
+++ cake/console/libs/tasks/plugin.php	(revision 0)
@@ -0,0 +1,202 @@
+<?php
+/* SVN FILE: $Id: plugin.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The Plugin Task handles creating an empty plugin, ready to be used
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+/**
+ * Task class for creating a plugin
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class PluginTask extends Shell {
+/**
+ * Tasks
+ *
+ */
+	var $tasks = array('Model', 'Controller', 'View');
+/**
+ * path to CONTROLLERS directory
+ *
+ * @var array
+ * @access public
+ */
+	var $path = null;
+/**
+ * initialize
+ *
+ * @return void
+ */
+	function initialize() {
+		$this->path = APP . 'plugins' . DS;
+	}
+/**
+ * Execution method always used for tasks
+ *
+ * @return void
+ */
+	function execute() {
+		if (empty($this->params['skel'])) {
+			$this->params['skel'] = '';
+			if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) {
+				$this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel';
+			}
+		}
+
+		$plugin = null;
+
+		if (isset($this->args[0])) {
+			$plugin = Inflector::camelize($this->args[0]);
+			$pluginPath = Inflector::underscore($plugin) . DS;
+			$this->Dispatch->shiftArgs();
+			if (is_dir($this->path . $pluginPath)) {
+				$this->out(sprintf('Plugin: %s', $plugin));
+				$this->out(sprintf('Path: %s', $this->path . $pluginPath));
+				$this->hr();
+			} elseif (isset($this->args[0])) {
+				$this->err(sprintf('%s in path %s not found.', $plugin, $this->path . $pluginPath));
+				$this->_stop();
+			} else {
+				$this->__interactive($plugin);
+			}
+		}
+
+		if (isset($this->args[0])) {
+			$task = Inflector::classify($this->args[0]);
+			$this->Dispatch->shiftArgs();
+			if (in_array($task, $this->tasks)) {
+				$this->{$task}->plugin = $plugin;
+				$this->{$task}->path = $this->path . $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS;
+
+				if (!is_dir($this->{$task}->path)) {
+					$this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path));
+				}
+				$this->{$task}->loadTasks();
+				$this->{$task}->execute();
+			}
+		}
+	}
+
+/**
+ * Interactive interface
+ *
+ * @access private
+ * @return void
+ */
+	function __interactive($plugin = null) {
+		while ($plugin === null) {
+			$plugin = $this->in(__('Enter the name of the plugin in CamelCase format', true));
+		}
+
+		if (!$this->bake($plugin)) {
+			$this->err(sprintf(__("An error occured trying to bake: %s in %s", true), $plugin, $this->path . $pluginPath));
+		}
+	}
+
+/**
+ * Bake the plugin, create directories and files
+ *
+ * @params $plugin name of the plugin in CamelCased format
+ * @access public
+ * @return bool
+ */
+	function bake($plugin) {
+
+		$pluginPath = Inflector::underscore($plugin);
+
+		$this->hr();
+		$this->out("Plugin Name: $plugin");
+		$this->out("Plugin Directory: {$this->path}{$pluginPath}");
+		$this->hr();
+
+
+		$looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y');
+
+		if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+			$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
+
+			$Folder = new Folder($this->path . $pluginPath);
+			$directories = array('models' . DS . 'behaviors', 'controllers' . DS . 'components', 'views' . DS . 'helpers');
+
+			foreach ($directories as $directory) {
+				$Folder->create($this->path . $pluginPath . DS . $directory);
+			}
+
+			if (low($verbose) == 'y' || low($verbose) == 'yes') {
+				foreach ($Folder->messages() as $message) {
+					$this->out($message);
+				}
+			}
+
+			$errors = $Folder->errors();
+			if (!empty($errors)) {
+				return false;
+			}
+
+			$controllerFileName = $pluginPath . '_app_controller.php';
+
+			$out = "<?php\n\n";
+			$out .= "class {$plugin}AppController extends AppController {\n\n";
+			$out .= "}\n\n";
+			$out .= "?>";
+			$this->createFile($this->path . $pluginPath. DS . $controllerFileName, $out);
+
+			$modelFileName = $pluginPath . '_app_model.php';
+
+			$out = "<?php\n\n";
+			$out .= "class {$plugin}AppModel extends AppModel {\n\n";
+			$out .= "}\n\n";
+			$out .= "?>";
+			$this->createFile($this->path . $pluginPath . DS . $modelFileName, $out);
+
+			$this->hr();
+			$this->out(sprintf(__("Created: %s in %s", true), $plugin, $this->path . $pluginPath));
+			$this->hr();
+		}
+
+		return true;
+	}
+/**
+ * Help
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake plugin <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("\n\tplugin <name>\n\t\tbakes plugin directory structure");
+		$this->out("\n\tplugin <name> model\n\t\tbakes model. Run 'cake bake model help' for more info.");
+		$this->out("\n\tplugin <name> controller\n\t\tbakes controller. Run 'cake bake controller help' for more info.");
+		$this->out("\n\tplugin <name> view\n\t\tbakes view. Run 'cake bake view help' for more info.");
+		$this->out("");
+		$this->_stop();
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/test.php
===================================================================
--- cake/console/libs/tasks/test.php	(revision 0)
+++ cake/console/libs/tasks/test.php	(revision 0)
@@ -0,0 +1,203 @@
+<?php
+/* SVN FILE: $Id: test.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The TestTask handles creating and updating test files.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Task class for creating and updating test files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class TestTask extends Shell {
+/**
+ * Name of plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * path to TESTS directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = TESTS;
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (count($this->args) == 1) {
+			$this->__interactive($this->args[0]);
+		}
+
+		if (count($this->args) > 1) {
+			$class = Inflector::underscore($this->args[0]);
+			if ($this->bake($class, $this->args[1])) {
+				$this->out('done');
+			}
+		}
+	}
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive($class = null) {
+		$this->hr();
+		$this->out(sprintf("Bake Tests\nPath: %s", $this->path));
+		$this->hr();
+
+		$key = null;
+		$options = array('Behavior', 'Helper', 'Component', 'Model', 'Controller');
+
+		if ($class !== null) {
+			$class = Inflector::camelize($class);
+			if (in_array($class, $options)) {
+				$key = array_search($class);
+			}
+		}
+
+		while ($class == null) {
+
+				$this->hr();
+				$this->out("Select a class:");
+				$this->hr();
+
+				$keys = array();
+				foreach ($options as $key => $option) {
+					$this->out(++$key . '. ' . $option);
+					$keys[] = $key;
+				}
+				$keys[] = 'q';
+
+				$key = $this->in(__("Enter the class to test or (q)uit", true), $keys, 'q');
+
+			if ($key != 'q') {
+				if (isset($options[--$key])) {
+					$class = $options[$key];
+				}
+
+				if ($class) {
+					$name = $this->in(__("Enter the name for the test or (q)uit", true), null, 'q');
+					if ($name !== 'q') {
+						$case = null;
+						while ($case !== 'q') {
+							$case = $this->in(__("Enter a test case or (q)uit", true), null, 'q');
+							if ($case !== 'q') {
+								$cases[] = $case;
+							}
+						}
+						if ($this->bake($class, $name, $cases)) {
+							$this->out(__("Test baked\n", true));
+							$type = null;
+						}
+						$class = null;
+					}
+				}
+			} else {
+				$this->_stop();
+			}
+		}
+	}
+/**
+ * Writes File
+ *
+ * @access public
+ */
+	function bake($class, $name = null, $cases = array()) {
+		if (!$name) {
+			return false;
+		}
+
+		if (!is_array($cases)) {
+			$cases = array($cases);
+		}
+
+		if (strpos($this->path, $class) === false) {
+			$this->path .= 'cases' . DS . Inflector::tableize($class) . DS;
+		}
+
+		$class = Inflector::classify($class);
+		$name = Inflector::classify($name);
+
+		$import = $name;
+		if (isset($this->plugin)) {
+			$import = $this->plugin . '.' . $name;
+		}
+		$extras = $this->__extras($class);
+		$out = "App::import('$class', '$import');\n";
+		if ($class == 'Model') {
+			$class = null;
+		}
+		$out .= "class Test{$name} extends {$name}{$class} {\n";
+		$out .= "{$extras}";
+		$out .= "}\n\n";
+		$out .= "class {$name}{$class}Test extends CakeTestCase {\n";
+		$out .= "\n\tfunction startTest() {";
+		$out .= "\n\t\t\$this->{$name} = new Test{$name}();";
+		$out .= "\n\t}\n";
+		$out .= "\n\tfunction test{$name}Instance() {\n";
+		$out .= "\t\t\$this->assertTrue(is_a(\$this->{$name}, '{$name}{$class}'));\n\t}\n";
+		foreach ($cases as $case) {
+			$case = Inflector::classify($case);
+			$out .= "\n\tfunction test{$case}() {\n\n\t}\n";
+		}
+		$out .= "}\n";
+
+		$this->out("Baking unit test for $name...");
+		$this->out($out);
+		$ok = $this->in(__('Is this correct?'), array('y', 'n'), 'y');
+		if ($ok == 'n') {
+			return false;
+		}
+
+		$header = '$Id';
+		$content = "<?php \n/* SVN FILE: $header$ */\n/* ". $name ." Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+		return $this->createFile($this->path . Inflector::underscore($name) . '.test.php', $content);
+	}
+/**
+ * Handles the extra stuff needed
+ *
+ * @access private
+ */
+	function __extras($class) {
+		$extras = null;
+		switch ($class) {
+			case 'Model':
+				$extras = "\n\tvar \$cacheSources = false;";
+				$extras .= "\n\tvar \$useDbConfig = 'test_suite';\n";
+			break;
+		}
+		return $extras;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/model.php
===================================================================
--- cake/console/libs/tasks/model.php	(revision 0)
+++ cake/console/libs/tasks/model.php	(revision 0)
@@ -0,0 +1,938 @@
+<?php
+/* SVN FILE: $Id: model.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The ModelTask handles creating and updating models files.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Model', 'ConnectionManager');
+/**
+ * Task class for creating and updating model files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ModelTask extends Shell {
+/**
+ * Name of plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * path to MODELS directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = MODELS;
+/**
+ * tasks
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig');
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (!empty($this->args[0])) {
+			$model = Inflector::camelize($this->args[0]);
+			if ($this->bake($model)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeTest($model);
+				}
+			}
+		}
+	}
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->hr();
+		$this->out(sprintf("Bake Model\nPath: %s", $this->path));
+		$this->hr();
+		$this->interactive = true;
+
+		$useTable = null;
+		$primaryKey = 'id';
+		$validate = array();
+		$associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array());
+
+		$useDbConfig = 'default';
+		$configs = get_class_vars('DATABASE_CONFIG');
+
+		if (!is_array($configs)) {
+			return $this->DbConfig->execute();
+		}
+
+		$connections = array_keys($configs);
+		if (count($connections) > 1) {
+			$useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default');
+		}
+
+		$currentModelName = $this->getName($useDbConfig);
+		$db =& ConnectionManager::getDataSource($useDbConfig);
+		$useTable = Inflector::tableize($currentModelName);
+		$fullTableName = $db->fullTableName($useTable, false);
+		$tableIsGood = false;
+
+		if (array_search($useTable, $this->__tables) === false) {
+			$this->out('');
+			$this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName));
+			$tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
+		}
+
+		if (low($tableIsGood) == 'n' || low($tableIsGood) == 'no') {
+			$useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
+		}
+
+		while ($tableIsGood == false && low($useTable) != 'null') {
+			if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) {
+				$fullTableName = $db->fullTableName($useTable, false);
+				$this->out($fullTableName . ' does not exist.');
+				$useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
+				$tableIsGood = false;
+			} else {
+				$tableIsGood = true;
+			}
+		}
+
+		$wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y');
+
+		if (in_array($useTable, $this->__tables)) {
+			App::import('Model');
+			$tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig));
+
+			$fields = $tempModel->schema();
+			if (!array_key_exists('id', $fields)) {
+				foreach ($fields as $name => $field) {
+					if (isset($field['key']) && $field['key'] == 'primary') {
+						break;
+					}
+				}
+				$primaryKey = $this->in(__('What is the primaryKey?', true), null, $name);
+			}
+		}
+
+		if (array_search($useTable, $this->__tables) !== false && (low($wannaDoValidation) == 'y' || low($wannaDoValidation) == 'yes')) {
+			$validate = $this->doValidation($tempModel);
+		}
+
+		$wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y');
+		if ((low($wannaDoAssoc) == 'y' || low($wannaDoAssoc) == 'yes')) {
+			$associations = $this->doAssociations($tempModel);
+		}
+
+		$this->out('');
+		$this->hr();
+		$this->out(__('The following Model will be created:', true));
+		$this->hr();
+		$this->out("Name:       " . $currentModelName);
+
+		if ($useDbConfig !== 'default') {
+			$this->out("DB Config:  " . $useDbConfig);
+		}
+		if ($fullTableName !== Inflector::tableize($currentModelName)) {
+			$this->out("DB Table:   " . $fullTableName);
+		}
+		if ($primaryKey != 'id') {
+			$this->out("Primary Key: " . $primaryKey);
+		}
+		if (!empty($validate)) {
+			$this->out("Validation: " . print_r($validate, true));
+		}
+		if (!empty($associations)) {
+			$this->out("Associations:");
+
+			if (!empty($associations['belongsTo'])) {
+				for ($i = 0; $i < count($associations['belongsTo']); $i++) {
+					$this->out("			$currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}");
+				}
+			}
+
+			if (!empty($associations['hasOne'])) {
+				for ($i = 0; $i < count($associations['hasOne']); $i++) {
+					$this->out("			$currentModelName hasOne	{$associations['hasOne'][$i]['alias']}");
+				}
+			}
+
+			if (!empty($associations['hasMany'])) {
+				for ($i = 0; $i < count($associations['hasMany']); $i++) {
+					$this->out("			$currentModelName hasMany	{$associations['hasMany'][$i]['alias']}");
+				}
+			}
+
+			if (!empty($associations['hasAndBelongsToMany'])) {
+				for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) {
+					$this->out("			$currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}");
+				}
+			}
+		}
+		$this->hr();
+		$looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
+
+		if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+			if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeTest($currentModelName, $useTable, $associations);
+				}
+			}
+		} else {
+			return false;
+		}
+	}
+/**
+ * Handles associations
+ *
+ * @param object $model
+ * @param boolean $interactive
+ * @return array $validate
+ * @access public
+ */
+	function doValidation(&$model, $interactive = true) {
+		if (!is_object($model)) {
+			return false;
+		}
+		$fields = $model->schema();
+
+		if (empty($fields)) {
+			return false;
+		}
+
+		$validate = array();
+
+		$options = array();
+
+		if (class_exists('Validation')) {
+			$parent = get_class_methods(get_parent_class('Validation'));
+			$options = array_diff(get_class_methods('Validation'), $parent);
+		}
+
+		foreach ($fields as $fieldName => $field) {
+			$prompt = 'Field: ' . $fieldName . "\n";
+			$prompt .= 'Type: ' . $field['type'] . "\n";
+			$prompt .= '---------------------------------------------------------------'."\n";
+			$prompt .= 'Please select one of the following validation options:'."\n";
+			$prompt .= '---------------------------------------------------------------'."\n";
+
+			sort($options);
+
+			$skip = 1;
+			foreach ($options as $key => $option) {
+				if ($option{0} != '_' && strtolower($option) != 'getinstance') {
+					$prompt .= "{$skip} - {$option}\n";
+					$choices[$skip] = strtolower($option);
+					$skip++;
+				}
+			}
+
+			$methods = array_flip($choices);
+
+			$prompt .=  "{$skip} - Do not do any validation on this field.\n";
+			$prompt .= "... or enter in a valid regex validation string.\n";
+
+			$guess = $skip;
+			if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) {
+				if ($fieldName == 'email') {
+					$guess = $methods['email'];
+				} elseif ($field['type'] == 'string') {
+					$guess = $methods['notempty'];
+				} elseif ($field['type'] == 'integer') {
+					$guess = $methods['numeric'];
+				} elseif ($field['type'] == 'boolean') {
+					$guess = $methods['numeric'];
+				} elseif ($field['type'] == 'datetime') {
+					$guess = $methods['date'];
+				}
+			}
+
+			if ($interactive === true) {
+				$this->out('');
+				$choice = $this->in($prompt, null, $guess);
+			} else {
+				$choice = $guess;
+			}
+			if ($choice != $skip) {
+				if (is_numeric($choice) && isset($choices[$choice])) {
+					$validate[$fieldName] = $choices[$choice];
+				} else {
+					$validate[$fieldName] = $choice;
+				}
+			}
+		}
+		return $validate;
+	}
+
+/**
+ * Handles associations
+ *
+ * @param object $model
+ * @param boolean $interactive
+ * @return array $assocaitons
+ * @access public
+ */
+	function doAssociations(&$model, $interactive = true) {
+
+		if (!is_object($model)) {
+			return false;
+		}
+		$this->out(__('One moment while the associations are detected.', true));
+
+		$fields = $model->schema();
+
+		if (empty($fields)) {
+			return false;
+		}
+
+		$primaryKey = $model->primaryKey;
+		$foreignKey = $this->_modelKey($model->name);
+
+		$associations = array('belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array());
+		$possibleKeys = array();
+
+		//Look for belongsTo
+		$i = 0;
+		foreach ($fields as $fieldName => $field) {
+			$offset = strpos($fieldName, '_id');
+			if ($fieldName != $model->primaryKey && $offset !== false) {
+				$tmpModelName = $this->_modelNameFromKey($fieldName);
+				$associations['belongsTo'][$i]['alias'] = $tmpModelName;
+				$associations['belongsTo'][$i]['className'] = $tmpModelName;
+				$associations['belongsTo'][$i]['foreignKey'] = $fieldName;
+				$i++;
+			}
+		}
+		//Look for hasOne and hasMany and hasAndBelongsToMany
+		$i = $j = 0;
+
+		foreach ($this->__tables as $otherTable) {
+			App::import('Model');
+			$tmpModelName = $this->_modelName($otherTable);
+			$tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig));
+			$modelFieldsTemp = $tempOtherModel->schema();
+
+			$offset = strpos($otherTable, $model->table . '_');
+			$otherOffset = strpos($otherTable, '_' . $model->table);
+
+			foreach ($modelFieldsTemp as $fieldName => $field) {
+				if ($field['type'] == 'integer' || $field['type'] == 'string') {
+					$possibleKeys[$otherTable][] = $fieldName;
+				}
+				if ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) {
+					$associations['hasOne'][$j]['alias'] = $tempOtherModel->name;
+					$associations['hasOne'][$j]['className'] = $tempOtherModel->name;
+					$associations['hasOne'][$j]['foreignKey'] = $fieldName;
+
+					$associations['hasMany'][$j]['alias'] = $tempOtherModel->name;
+					$associations['hasMany'][$j]['className'] = $tempOtherModel->name;
+					$associations['hasMany'][$j]['foreignKey'] = $fieldName;
+					$j++;
+				}
+			}
+
+			if ($offset !== false) {
+				$offset = strlen($model->table . '_');
+				$tmpModelName = $this->_modelName(substr($otherTable, $offset));
+				$associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
+				$associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
+				$associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
+				$associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
+				$associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
+				$i++;
+			}
+
+			if ($otherOffset !== false) {
+				$tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset));
+				$associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
+				$associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
+				$associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
+				$associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
+				$associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
+				$i++;
+			}
+		}
+
+		if ($interactive !== true) {
+			unset($associations['hasOne']);
+		}
+
+		if ($interactive === true) {
+			$this->hr();
+			if (empty($associations)) {
+				$this->out(__('None found.', true));
+			} else {
+				$this->out(__('Please confirm the following associations:', true));
+				$this->hr();
+				foreach ($associations as $type => $settings) {
+					if (!empty($associations[$type])) {
+						$count = count($associations[$type]);
+						$response = 'y';
+						for ($i = 0; $i < $count; $i++) {
+							$prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}";
+							$response = $this->in("{$prompt}?", array('y','n'), 'y');
+
+							if ('n' == low($response) || 'no' == low($response)) {
+								unset($associations[$type][$i]);
+							} else {
+								if ($model->name === $associations[$type][$i]['alias']) {
+									if ($type === 'belongsTo') {
+										$alias = 'Parent' . $associations[$type][$i]['alias'];
+									}
+									if ($type === 'hasOne' || $type === 'hasMany') {
+										$alias = 'Child' . $associations[$type][$i]['alias'];
+									}
+
+									$alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y');
+
+									if ('n' == low($alternateAlias) || 'no' == low($alternateAlias)) {
+										$associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true));
+									} else {
+										$associations[$type][$i]['alias'] = $alias;
+									}
+								}
+							}
+						}
+						$associations[$type] = array_merge($associations[$type]);
+					}
+				}
+			}
+
+			$wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n');
+
+			while ((low($wannaDoMoreAssoc) == 'y' || low($wannaDoMoreAssoc) == 'yes')) {
+				$assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany');
+				$bad = true;
+				while ($bad) {
+					$this->out(__('What is the association type?', true));
+					$prompt = "1. belongsTo\n";
+					$prompt .= "2. hasOne\n";
+					$prompt .= "3. hasMany\n";
+					$prompt .= "4. hasAndBelongsToMany\n";
+					$assocType = intval($this->in($prompt, null, __("Enter a number", true)));
+
+					if (intval($assocType) < 1 || intval($assocType) > 4) {
+						$this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true));
+					} else {
+						$bad = false;
+					}
+				}
+				$this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true));
+				$this->hr();
+				$alias = $this->in(__('What is the alias for this association?', true));
+				$className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias );
+				$suggestedForeignKey = null;
+				if ($assocType == '1') {
+					$showKeys = $possibleKeys[$model->table];
+					$suggestedForeignKey = $this->_modelKey($alias);
+				} else {
+					$otherTable = Inflector::tableize($className);
+					if (in_array($otherTable, $this->__tables)) {
+						if ($assocType < '4') {
+							$showKeys = $possibleKeys[$otherTable];
+						} else {
+							$showKeys = null;
+						}
+					} else {
+						$otherTable = $this->in(__('What is the table for this model?', true));
+						$showKeys = $possibleKeys[$otherTable];
+					}
+					$suggestedForeignKey = $this->_modelKey($model->name);
+				}
+				if (!empty($showKeys)) {
+					$this->out(__('A helpful List of possible keys', true));
+					for ($i = 0; $i < count($showKeys); $i++) {
+						$this->out($i + 1 . ". " . $showKeys[$i]);
+					}
+					$foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true));
+					if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) {
+						$foreignKey = $showKeys[intval($foreignKey) - 1];
+					}
+				}
+				if (!isset($foreignKey)) {
+					$foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
+				}
+				if ($assocType == '4') {
+					$associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name));
+					$joinTable = $this->in(__('What is the joinTable?', true));
+				}
+				$associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
+				$count = count($associations[$assocs[$assocType]]);
+				$i = ($count > 0) ? $count : 0;
+				$associations[$assocs[$assocType]][$i]['alias'] = $alias;
+				$associations[$assocs[$assocType]][$i]['className'] = $className;
+				$associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
+				if ($assocType == '4') {
+					$associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
+					$associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
+				}
+				$wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y');
+			}
+		}
+		return $associations;
+	}
+/**
+ * Assembles and writes a Model file.
+ *
+ * @param mixed $name Model name or object
+ * @param mixed $associations if array and $name is not an object assume Model associations array otherwise boolean interactive
+ * @param array $validate Validation rules
+ * @param string $primaryKey Primary key to use
+ * @param string $useTable Table to use
+ * @param string $useDbConfig Database configuration setting to use
+ * @access private
+ */
+	function bake($name, $associations = array(),  $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') {
+
+		if (is_object($name)) {
+			if (!is_array($associations)) {
+				$associations = $this->doAssociations($name, $associations);
+				$validate = $this->doValidation($name, $associations);
+			}
+			$primaryKey = $name->primaryKey;
+			$useTable = $name->table;
+			$useDbConfig = $name->useDbConfig;
+			$name = $name->name;
+		}
+
+		$out = "<?php\n";
+		$out .= "class {$name} extends {$this->plugin}AppModel {\n\n";
+		$out .= "\tvar \$name = '{$name}';\n";
+
+		if ($useDbConfig !== 'default') {
+			$out .= "\tvar \$useDbConfig = '$useDbConfig';\n";
+		}
+
+		if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) {
+			$table = "'$useTable'";
+			if (!$useTable) {
+				$table = 'false';
+			}
+			$out .= "\tvar \$useTable = $table;\n";
+		}
+
+		if ($primaryKey !== 'id') {
+			$out .= "\tvar \$primaryKey = '$primaryKey';\n";
+		}
+
+		$validateCount = count($validate);
+		if (is_array($validate) && $validateCount > 0) {
+			$out .= "\tvar \$validate = array(\n";
+			$keys = array_keys($validate);
+			for ($i = 0; $i < $validateCount; $i++) {
+				$val = "'" . $validate[$keys[$i]] . "'";
+				$out .= "\t\t'" . $keys[$i] . "' => array({$val})";
+				if ($i + 1 < $validateCount) {
+					$out .= ",";
+				}
+				$out .= "\n";
+			}
+			$out .= "\t);\n";
+		}
+		$out .= "\n";
+
+		if (!empty($associations)) {
+			if (!empty($associations['belongsTo']) || !empty($associations['hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) {
+				$out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n";
+			}
+
+			if (!empty($associations['belongsTo'])) {
+				$out .= "\tvar \$belongsTo = array(\n";
+				$belongsToCount = count($associations['belongsTo']);
+
+				for ($i = 0; $i < $belongsToCount; $i++) {
+					$out .= "\t\t\t'{$associations['belongsTo'][$i]['alias']}' => ";
+					$out .= "array('className' => '{$associations['belongsTo'][$i]['className']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['foreignKey']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'order' => ''\n";
+					$out .= "\t\t\t)";
+					if ($i + 1 < $belongsToCount) {
+						$out .= ",";
+					}
+					$out .= "\n";
+
+				}
+				$out .= "\t);\n\n";
+			}
+
+			if (!empty($associations['hasOne'])) {
+				$out .= "\tvar \$hasOne = array(\n";
+				$hasOneCount = count($associations['hasOne']);
+
+				for ($i = 0; $i < $hasOneCount; $i++) {
+					$out .= "\t\t\t'{$associations['hasOne'][$i]['alias']}' => ";
+					$out .= "array('className' => '{$associations['hasOne'][$i]['className']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['foreignKey']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'dependent' => false,\n";
+					$out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'order' => ''\n";
+					$out .= "\t\t\t)";
+					if ($i + 1 < $hasOneCount) {
+						$out .= ",";
+					}
+					$out .= "\n";
+
+				}
+				$out .= "\t);\n\n";
+			}
+
+			if (!empty($associations['hasMany'])) {
+				$out .= "\tvar \$hasMany = array(\n";
+				$hasManyCount = count($associations['hasMany']);
+
+				for ($i = 0; $i < $hasManyCount; $i++) {
+					$out .= "\t\t\t'{$associations['hasMany'][$i]['alias']}' => ";
+					$out .= "array('className' => '{$associations['hasMany'][$i]['className']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['foreignKey']}',\n";
+					$out .= "\t\t\t\t\t\t\t\t'dependent' => false,\n";
+					$out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'order' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'limit' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'offset' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'exclusive' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'finderQuery' => '',\n";
+					$out .= "\t\t\t\t\t\t\t\t'counterQuery' => ''\n";
+					$out .= "\t\t\t)";
+					if ($i + 1 < $hasManyCount) {
+						$out .= ",";
+					}
+					$out .= "\n";
+				}
+				$out .= "\t);\n\n";
+			}
+
+			if (!empty($associations['hasAndBelongsToMany'])) {
+				$out .= "\tvar \$hasAndBelongsToMany = array(\n";
+				$hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']);
+
+				for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) {
+					$out .= "\t\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => ";
+					$out .= "array('className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n";
+					$out .= "\t\t\t\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n";
+					$out .= "\t\t\t\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n";
+					$out .= "\t\t\t\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['associationForeignKey']}',\n";
+					$out .= "\t\t\t\t\t\t'unique' => true,\n";
+					$out .= "\t\t\t\t\t\t'conditions' => '',\n";
+					$out .= "\t\t\t\t\t\t'fields' => '',\n";
+					$out .= "\t\t\t\t\t\t'order' => '',\n";
+					$out .= "\t\t\t\t\t\t'limit' => '',\n";
+					$out .= "\t\t\t\t\t\t'offset' => '',\n";
+					$out .= "\t\t\t\t\t\t'finderQuery' => '',\n";
+					$out .= "\t\t\t\t\t\t'deleteQuery' => '',\n";
+					$out .= "\t\t\t\t\t\t'insertQuery' => ''\n";
+					$out .= "\t\t\t)";
+					if ($i + 1 < $hasAndBelongsToManyCount) {
+						$out .= ",";
+					}
+					$out .= "\n";
+				}
+				$out .= "\t);\n\n";
+			}
+		}
+		$out .= "}\n";
+		$out .= "?>";
+		$filename = $this->path . Inflector::underscore($name) . '.php';
+		$this->out("\nBaking model class for $name...");
+		return $this->createFile($filename, $out);
+	}
+
+/**
+ * Assembles and writes a unit test file
+ *
+ * @param string $className Model class name
+ * @access private
+ */
+	function bakeTest($className, $useTable = null, $associations = array()) {
+		$results = $this->fixture($className, $useTable);
+
+		if ($results) {
+			$fixtureInc = 'app';
+			if ($this->plugin) {
+				$fixtureInc = 'plugin.'.Inflector::underscore($this->plugin);
+			}
+
+			$fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'";
+
+			if (!empty($associations)) {
+				$assoc[] = Set::extract($associations, 'belongsTo.{n}.className');
+				$assoc[] = Set::extract($associations, 'hasOne.{n}.className');
+				$assoc[] = Set::extract($associations, 'hasMany.{n}.className');
+				foreach ($assoc as $key => $value) {
+					if (is_array($value)) {
+						foreach ($value as $class) {
+							$fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'";
+						}
+					}
+				}
+			}
+			$fixture = join(", ", $fixture);
+
+			$import = $className;
+			if (isset($this->plugin)) {
+				$import = $this->plugin . '.' . $className;
+			}
+
+			$out = "App::import('Model', '$import');\n\n";
+			$out .= "class {$className}TestCase extends CakeTestCase {\n";
+			$out .= "\tvar \${$className} = null;\n";
+			$out .= "\tvar \$fixtures = array($fixture);\n\n";
+			$out .= "\tfunction startTest() {\n";
+			$out .= "\t\t\$this->{$className} =& ClassRegistry::init('{$className}');\n";
+			$out .= "\t}\n\n";
+			$out .= "\tfunction test{$className}Instance() {\n";
+			$out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n";
+			$out .= "\t}\n\n";
+			$out .= "\tfunction test{$className}Find() {\n";
+			$out .= "\t\t\$this->{$className}->recursive = -1;\n";
+			$out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n";
+			$out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t\t));\n";
+			$out .= "\t\t\$this->assertEqual(\$results, \$expected);\n";
+			$out .= "\t}\n";
+			$out .= "}\n";
+
+			$path = MODEL_TESTS;
+			if (isset($this->plugin)) {
+				$pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
+				$path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS;
+			}
+
+			$filename = Inflector::underscore($className).'.test.php';
+			$this->out("\nBaking unit test for $className...");
+
+			$header = '$Id';
+			$content = "<?php \n/* SVN FILE: $header$ */\n/* ". $className ." Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+			return $this->createFile($path . $filename, $content);
+		}
+		return false;
+	}
+/**
+ * outputs the a list of possible models or controllers from database
+ *
+ * @param string $useDbConfig Database configuration name
+ * @access public
+ */
+	function listAll($useDbConfig = 'default', $interactive = true) {
+		$db =& ConnectionManager::getDataSource($useDbConfig);
+		$usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
+		if ($usePrefix) {
+			$tables = array();
+			foreach ($db->listSources() as $table) {
+				if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
+					$tables[] = substr($table, strlen($usePrefix));
+				}
+			}
+		} else {
+			$tables = $db->listSources();
+		}
+		if (empty($tables)) {
+			$this->err(__('Your database does not have any tables.', true));
+			$this->_stop();
+		}
+
+		$this->__tables = $tables;
+
+		if ($interactive === true) {
+			$this->out(__('Possible Models based on your current database:', true));
+			$this->_modelNames = array();
+			$count = count($tables);
+			for ($i = 0; $i < $count; $i++) {
+				$this->_modelNames[] = $this->_modelName($tables[$i]);
+				$this->out($i + 1 . ". " . $this->_modelNames[$i]);
+			}
+		}
+	}
+/**
+ * Forces the user to specify the model he wants to bake, and returns the selected model name.
+ *
+ * @return string the model name
+ * @access public
+ */
+	function getName($useDbConfig) {
+		$this->listAll($useDbConfig);
+
+		$enteredModel = '';
+
+		while ($enteredModel == '') {
+			$enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q');
+
+			if ($enteredModel === 'q') {
+				$this->out(__("Exit", true));
+				$this->_stop();
+			}
+
+			if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
+				$this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true));
+				$enteredModel = '';
+			}
+		}
+
+		if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
+			$currentModelName = $this->_modelNames[intval($enteredModel) - 1];
+		} else {
+			$currentModelName = $enteredModel;
+		}
+
+		return $currentModelName;
+	}
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake model <arg1>");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("\n\tmodel\n\t\tbakes model in interactive mode.");
+		$this->out("\n\tmodel <name>\n\t\tbakes model file with no associations or validation");
+		$this->out("");
+		$this->_stop();
+	}
+/**
+ * Builds the tests fixtures for the model and create the file
+ *
+ * @param string $model the name of the model
+ * @param string $useTable table name
+ * @return array $records, used in ModelTask::bakeTest() to create $expected
+ * @todo move this to a task
+ */
+	function fixture($model, $useTable = null) {
+		if (!class_exists('CakeSchema')) {
+			App::import('Model', 'Schema');
+		}
+		$out = "\nclass {$model}Fixture extends CakeTestFixture {\n";
+		$out .= "\tvar \$name = '$model';\n";
+
+		if (!$useTable) {
+			$useTable = Inflector::tableize($model);
+		} else {
+			$out .= "\tvar \$table = '$useTable';\n";
+		}
+		$schema = new CakeSchema();
+		$data = $schema->read(array('models' => false));
+
+		if (!isset($data['tables'][$useTable])) {
+			return false;
+		}
+		$tables[$model] = $data['tables'][$useTable];
+
+		foreach ($tables as $table => $fields) {
+			if (!is_numeric($table) && $table !== 'missing') {
+				$out .= "\tvar \$fields = array(\n";
+				$records = array();
+				if (is_array($fields)) {
+					$cols = array();
+					foreach ($fields as $field => $value) {
+						if ($field != 'indexes') {
+							if (is_string($value)) {
+								$type = $value;
+								$value = array('type'=> $type);
+							}
+							$col = "\t\t\t'{$field}' => array('type'=>'" . $value['type'] . "', ";
+
+							switch ($value['type']) {
+								case 'integer':
+									$insert = 1;
+								break;
+								case 'string';
+									$insert = "Lorem ipsum dolor sit amet";
+									if (!empty($value['length'])) {
+										$insert = substr($insert, 0, (int)$value['length'] - 2);
+									}
+									$insert = "'$insert'";
+								break;
+								case 'datetime':
+									$ts = date('Y-m-d H:i:s');
+									$insert = "'$ts'";
+								break;
+								case 'date':
+									$ts = date('Y-m-d');
+									$insert = "'$ts'";
+								break;
+								case 'time':
+									$ts = date('H:i:s');
+									$insert = "'$ts'";
+								break;
+								case 'boolean':
+									$insert = 1;
+								break;
+								case 'text':
+									$insert =
+									"'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,";
+									$insert .= "phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,";
+									$insert .= "vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,";
+									$insert .= "feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.'";
+								break;
+							}
+							$records[] = "\t\t\t'$field'  => $insert";
+							unset($value['type']);
+							$col .= join(', ',  $schema->__values($value));
+						} else {
+							$col = "\t\t\t'indexes' => array(";
+							$props = array();
+							foreach ((array)$value as $key => $index) {
+								$props[] = "'{$key}' => array(".join(', ',  $schema->__values($index)).")";
+							}
+							$col .= join(', ', $props);
+						}
+						$col .= ")";
+						$cols[] = $col;
+					}
+					$out .= join(",\n", $cols);
+				}
+				$out .= "\n\t\t\t);\n";
+			}
+		}
+		$records = join(",\n", $records);
+		$out .= "\tvar \$records = array(array(\n$records\n\t\t\t));\n";
+		$out .= "}\n";
+		$path = TESTS . DS . 'fixtures' . DS;
+		if (isset($this->plugin)) {
+			$pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
+			$path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS;
+		}
+		$filename = Inflector::underscore($model).'_fixture.php';
+		$header = '$Id';
+		$content = "<?php \n/* SVN FILE: $header$ */\n/* ". $model ." Fixture generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+		$this->out("\nBaking test fixture for $model...");
+		if ($this->createFile($path . $filename, $content)) {
+			return $records;
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/tasks/controller.php
===================================================================
--- cake/console/libs/tasks/controller.php	(revision 0)
+++ cake/console/libs/tasks/controller.php	(revision 0)
@@ -0,0 +1,576 @@
+<?php
+/* SVN FILE: $Id: controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The ControllerTask handles creating and updating controller files.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008,	Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Task class for creating and updating controller files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ControllerTask extends Shell {
+/**
+ * Name of plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * Tasks to be loaded by this Task
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Project');
+/**
+ * path to CONTROLLERS directory
+ *
+ * @var array
+ * @access public
+ */
+	var $path = CONTROLLERS;
+/**
+ * Override initialize
+ *
+ * @access public
+ */
+	function initialize() {
+	}
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (isset($this->args[0])) {
+			$controller = Inflector::camelize($this->args[0]);
+			$actions = null;
+			if (isset($this->args[1]) && $this->args[1] == 'scaffold') {
+				$this->out('Baking scaffold for ' . $controller);
+				$actions = $this->bakeActions($controller);
+			} else {
+				$actions = 'scaffold';
+			}
+			if ((isset($this->args[1]) && $this->args[1] == 'admin') || (isset($this->args[2]) && $this->args[2] == 'admin')) {
+				if ($admin = $this->getAdmin()) {
+					$this->out('Adding ' . Configure::read('Routing.admin') .' methods');
+					if ($actions == 'scaffold') {
+						$actions = $this->bakeActions($controller, $admin);
+					} else {
+						$actions .= $this->bakeActions($controller, $admin);
+					}
+				}
+			}
+			if ($this->bake($controller, $actions)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeTest($controller);
+				}
+			}
+		}
+	}
+/**
+ * Interactive
+ *
+ * @access private
+ */
+	function __interactive($controllerName = false) {
+		if (!$controllerName) {
+			$this->interactive = true;
+			$this->hr();
+			$this->out(sprintf("Bake Controller\nPath: %s", $this->path));
+			$this->hr();
+			$actions = '';
+			$uses = array();
+			$helpers = array();
+			$components = array();
+			$wannaUseSession = 'y';
+			$wannaDoAdmin = 'n';
+			$wannaUseScaffold = 'n';
+			$wannaDoScaffolding = 'y';
+			$controllerName = $this->getName();
+		}
+		$this->hr();
+		$this->out("Baking {$controllerName}Controller");
+		$this->hr();
+
+		$controllerFile = low(Inflector::underscore($controllerName));
+
+		$question[] = __("Would you like to build your controller interactively?", true);
+		if (file_exists($this->path . $controllerFile .'_controller.php')) {
+			$question[] = sprintf(__("Warning: Choosing no will overwrite the %sController.", true), $controllerName);
+		}
+		$doItInteractive = $this->in(join("\n", $question), array('y','n'), 'y');
+
+		if (low($doItInteractive) == 'y' || low($doItInteractive) == 'yes') {
+			$this->interactive = true;
+
+			$wannaUseScaffold = $this->in(__("Would you like to use scaffolding?", true), array('y','n'), 'n');
+
+			if (low($wannaUseScaffold) == 'n' || low($wannaUseScaffold) == 'no') {
+
+				$wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'n');
+
+				if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') {
+					$wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'n');
+				}
+
+				$wannaDoHelpers = $this->in(__("Would you like this controller to use other helpers besides HtmlHelper and FormHelper?", true), array('y','n'), 'n');
+
+				if (low($wannaDoHelpers) == 'y' || low($wannaDoHelpers) == 'yes') {
+					$helpersList = $this->in(__("Please provide a comma separated list of the other helper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true));
+					$helpersListTrimmed = str_replace(' ', '', $helpersList);
+					$helpers = explode(',', $helpersListTrimmed);
+				}
+				$wannaDoComponents = $this->in(__("Would you like this controller to use any components?", true), array('y','n'), 'n');
+
+				if (low($wannaDoComponents) == 'y' || low($wannaDoComponents) == 'yes') {
+					$componentsList = $this->in(__("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true));
+					$componentsListTrimmed = str_replace(' ', '', $componentsList);
+					$components = explode(',', $componentsListTrimmed);
+				}
+
+				$wannaUseSession = $this->in(__("Would you like to use Sessions?", true), array('y','n'), 'y');
+			} else {
+				$wannaDoScaffolding = 'n';
+			}
+		} else {
+			$wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'y');
+
+			if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') {
+				$wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'y');
+			}
+		}
+		$admin = false;
+
+		if ((low($wannaDoAdmin) == 'y' || low($wannaDoAdmin) == 'yes')) {
+			$admin = $this->getAdmin();
+		}
+
+		if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') {
+			$actions = $this->bakeActions($controllerName, null, in_array(low($wannaUseSession), array('y', 'yes')));
+			if ($admin) {
+				$actions .= $this->bakeActions($controllerName, $admin, in_array(low($wannaUseSession), array('y', 'yes')));
+			}
+		}
+
+		if ($this->interactive === true) {
+			$this->out('');
+			$this->hr();
+			$this->out('The following controller will be created:');
+			$this->hr();
+			$this->out("Controller Name:  $controllerName");
+
+			if (low($wannaUseScaffold) == 'y' || low($wannaUseScaffold) == 'yes') {
+				$this->out("		   var \$scaffold;");
+				$actions = 'scaffold';
+			}
+
+			if (count($helpers)) {
+				$this->out("Helpers:      ", false);
+
+				foreach ($helpers as $help) {
+					if ($help != $helpers[count($helpers) - 1]) {
+						$this->out(ucfirst($help) . ", ", false);
+					} else {
+						$this->out(ucfirst($help));
+					}
+				}
+			}
+
+			if (count($components)) {
+				$this->out("Components:      ", false);
+
+				foreach ($components as $comp) {
+					if ($comp != $components[count($components) - 1]) {
+						$this->out(ucfirst($comp) . ", ", false);
+					} else {
+						$this->out(ucfirst($comp));
+					}
+				}
+			}
+			$this->hr();
+			$looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
+
+			if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+				$baked = $this->bake($controllerName, $actions, $helpers, $components, $uses);
+				if ($baked && $this->_checkUnitTest()) {
+					$this->bakeTest($controllerName);
+				}
+			} else {
+				$this->__interactive($controllerName);
+			}
+		} else {
+			$baked = $this->bake($controllerName, $actions, $helpers, $components, $uses);
+			if ($baked && $this->_checkUnitTest()) {
+				$this->bakeTest($controllerName);
+			}
+		}
+	}
+/**
+ * Bake scaffold actions
+ *
+ * @param string $controllerName Controller name
+ * @param string $admin Admin route to use
+ * @param boolean $wannaUseSession Set to true to use sessions, false otherwise
+ * @return string Baked actions
+ * @access private
+ */
+	function bakeActions($controllerName, $admin = null, $wannaUseSession = true) {
+		$currentModelName = $this->_modelName($controllerName);
+		if (!App::import('Model', $currentModelName)) {
+			$this->err(__('You must have a model for this class to build scaffold methods. Please try again.', true));
+			exit;
+		}
+		$actions = null;
+		$modelObj =& new $currentModelName();
+		$controllerPath = $this->_controllerPath($controllerName);
+		$pluralName = $this->_pluralName($currentModelName);
+		$singularName = Inflector::variable($currentModelName);
+		$singularHumanName = Inflector::humanize($currentModelName);
+		$pluralHumanName = Inflector::humanize($controllerName);
+		$actions .= "\n";
+		$actions .= "\tfunction {$admin}index() {\n";
+		$actions .= "\t\t\$this->{$currentModelName}->recursive = 0;\n";
+		$actions .= "\t\t\$this->set('{$pluralName}', \$this->paginate());\n";
+		$actions .= "\t}\n";
+		$actions .= "\n";
+		$actions .= "\tfunction {$admin}view(\$id = null) {\n";
+		$actions .= "\t\tif (!\$id) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}.', true));\n";
+			$actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t}\n";
+		$actions .= "\t\t\$this->set('".$singularName."', \$this->{$currentModelName}->read(null, \$id));\n";
+		$actions .= "\t}\n";
+		$actions .= "\n";
+
+		/* ADD ACTION */
+		$compact = array();
+		$actions .= "\tfunction {$admin}add() {\n";
+		$actions .= "\t\tif (!empty(\$this->data)) {\n";
+		$actions .= "\t\t\t\$this->{$currentModelName}->create();\n";
+		$actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\t\$this->Session->setFlash(__('The ".$singularHumanName." has been saved', true));\n";
+			$actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\t\$this->flash(__('{$currentModelName} saved.', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t\t} else {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n";
+		}
+		$actions .= "\t\t\t}\n";
+		$actions .= "\t\t}\n";
+		foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) {
+			if (!empty($associationName)) {
+				$habtmModelName = $this->_modelName($associationName);
+				$habtmSingularName = $this->_singularName($associationName);
+				$habtmPluralName = $this->_pluralName($associationName);
+				$actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n";
+				$compact[] = "'{$habtmPluralName}'";
+			}
+		}
+		foreach ($modelObj->belongsTo as $associationName => $relation) {
+			if (!empty($associationName)) {
+				$belongsToModelName = $this->_modelName($associationName);
+				$belongsToPluralName = $this->_pluralName($associationName);
+				$actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n";
+				$compact[] = "'{$belongsToPluralName}'";
+			}
+		}
+		if (!empty($compact)) {
+			$actions .= "\t\t\$this->set(compact(".join(', ', $compact)."));\n";
+		}
+		$actions .= "\t}\n";
+		$actions .= "\n";
+
+		/* EDIT ACTION */
+		$compact = array();
+		$actions .= "\tfunction {$admin}edit(\$id = null) {\n";
+		$actions .= "\t\tif (!\$id && empty(\$this->data)) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}', true));\n";
+			$actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t}\n";
+		$actions .= "\t\tif (!empty(\$this->data)) {\n";
+		$actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\t\$this->Session->setFlash(__('The ".$singularHumanName." has been saved', true));\n";
+			$actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\t\$this->flash(__('The ".$singularHumanName." has been saved.', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t\t} else {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n";
+		}
+		$actions .= "\t\t\t}\n";
+		$actions .= "\t\t}\n";
+		$actions .= "\t\tif (empty(\$this->data)) {\n";
+		$actions .= "\t\t\t\$this->data = \$this->{$currentModelName}->read(null, \$id);\n";
+		$actions .= "\t\t}\n";
+
+		foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) {
+			if (!empty($associationName)) {
+				$habtmModelName = $this->_modelName($associationName);
+				$habtmSingularName = $this->_singularName($associationName);
+				$habtmPluralName = $this->_pluralName($associationName);
+				$actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n";
+				$compact[] = "'{$habtmPluralName}'";
+			}
+		}
+		foreach ($modelObj->belongsTo as $associationName => $relation) {
+			if (!empty($associationName)) {
+				$belongsToModelName = $this->_modelName($associationName);
+				$belongsToPluralName = $this->_pluralName($associationName);
+				$actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n";
+				$compact[] = "'{$belongsToPluralName}'";
+			}
+		}
+		if (!empty($compact)) {
+			$actions .= "\t\t\$this->set(compact(".join(',', $compact)."));\n";
+		}
+		$actions .= "\t}\n";
+		$actions .= "\n";
+		$actions .= "\tfunction {$admin}delete(\$id = null) {\n";
+		$actions .= "\t\tif (!\$id) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\$this->Session->setFlash(__('Invalid id for {$singularHumanName}', true));\n";
+			$actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t}\n";
+		$actions .= "\t\tif (\$this->{$currentModelName}->del(\$id)) {\n";
+		if ($wannaUseSession) {
+			$actions .= "\t\t\t\$this->Session->setFlash(__('{$singularHumanName} deleted', true));\n";
+			$actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n";
+		} else {
+			$actions .= "\t\t\t\$this->flash(__('{$singularHumanName} deleted', true), array('action'=>'index'));\n";
+		}
+		$actions .= "\t\t}\n";
+		$actions .= "\t}\n";
+		$actions .= "\n";
+		return $actions;
+	}
+
+
+/**
+ * Assembles and writes a Controller file
+ *
+ * @param string $controllerName Controller name
+ * @param string $actions Actions to add, or set the whole controller to use $scaffold (set $actions to 'scaffold')
+ * @param array $helpers Helpers to use in controller
+ * @param array $components Components to use in controller
+ * @param array $uses Models to use in controller
+ * @return string Baked controller
+ * @access private
+ */
+	function bake($controllerName, $actions = '', $helpers = null, $components = null, $uses = null) {
+		$out = "<?php\n";
+		$out .= "class $controllerName" . "Controller extends {$this->plugin}AppController {\n\n";
+		$out .= "\tvar \$name = '$controllerName';\n";
+
+		if (low($actions) == 'scaffold') {
+			$out .= "\tvar \$scaffold;\n";
+		} else {
+			if (count($uses)) {
+				$out .= "\tvar \$uses = array('" . $this->_modelName($controllerName) . "', ";
+
+				foreach ($uses as $use) {
+					if ($use != $uses[count($uses) - 1]) {
+						$out .= "'" . $this->_modelName($use) . "', ";
+					} else {
+						$out .= "'" . $this->_modelName($use) . "'";
+					}
+				}
+				$out .= ");\n";
+			}
+
+			$out .= "\tvar \$helpers = array('Html', 'Form'";
+			if (count($helpers)) {
+				foreach ($helpers as $help) {
+					$out .= ", '" . Inflector::camelize($help) . "'";
+				}
+			}
+			$out .= ");\n";
+
+			if (count($components)) {
+				$out .= "\tvar \$components = array(";
+
+				foreach ($components as $comp) {
+					if ($comp != $components[count($components) - 1]) {
+						$out .= "'" . Inflector::camelize($comp) . "', ";
+					} else {
+						$out .= "'" . Inflector::camelize($comp) . "'";
+					}
+				}
+				$out .= ");\n";
+			}
+			$out .= $actions;
+		}
+		$out .= "}\n";
+		$out .= "?>";
+		$filename = $this->path . $this->_controllerPath($controllerName) . '_controller.php';
+		return $this->createFile($filename, $out);
+	}
+/**
+ * Assembles and writes a unit test file
+ *
+ * @param string $className Controller class name
+ * @return string Baked test
+ * @access private
+ */
+	function bakeTest($className) {
+		$import = $className;
+		if ($this->plugin) {
+			$import = $this->plugin . '.' . $className;
+		}
+		$out = "App::import('Controller', '$import');\n\n";
+		$out .= "class Test{$className} extends {$className}Controller {\n";
+		$out .= "\tvar \$autoRender = false;\n}\n\n";
+		$out .= "class {$className}ControllerTest extends CakeTestCase {\n";
+		$out .= "\tvar \${$className} = null;\n\n";
+		$out .= "\tfunction setUp() {\n\t\t\$this->{$className} = new Test{$className}();";
+		$out .= "\n\t\t\$this->{$className}->constructClasses();\n\t}\n\n";
+		$out .= "\tfunction test{$className}ControllerInstance() {\n";
+		$out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}Controller'));\n\t}\n\n";
+		$out .= "\tfunction tearDown() {\n\t\tunset(\$this->{$className});\n\t}\n}\n";
+
+		$path = CONTROLLER_TESTS;
+		if (isset($this->plugin)) {
+			$pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
+			$path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'controllers' . DS;
+		}
+
+		$filename = Inflector::underscore($className).'_controller.test.php';
+		$this->out("\nBaking unit test for $className...");
+
+		$header = '$Id';
+		$content = "<?php \n/* SVN FILE: $header$ */\n/* ". $className ."Controller Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+		return $this->createFile($path . $filename, $content);
+	}
+/**
+ * Outputs and gets the list of possible models or controllers from database
+ *
+ * @param string $useDbConfig Database configuration name
+ * @return array Set of controllers
+ * @access public
+ */
+	function listAll($useDbConfig = 'default') {
+		$db =& ConnectionManager::getDataSource($useDbConfig);
+		$usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
+		if ($usePrefix) {
+			$tables = array();
+			foreach ($db->listSources() as $table) {
+				if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
+					$tables[] = substr($table, strlen($usePrefix));
+				}
+			}
+		} else {
+			$tables = $db->listSources();
+		}
+
+		if (empty($tables)) {
+			$this->err(__('Your database does not have any tables.', true));
+			$this->_stop();
+		}
+
+		$this->__tables = $tables;
+		$this->out('Possible Controllers based on your current database:');
+		$this->_controllerNames = array();
+		$count = count($tables);
+		for ($i = 0; $i < $count; $i++) {
+			$this->_controllerNames[] = $this->_controllerName($this->_modelName($tables[$i]));
+			$this->out($i + 1 . ". " . $this->_controllerNames[$i]);
+		}
+		return $this->_controllerNames;
+	}
+
+/**
+ * Forces the user to specify the controller he wants to bake, and returns the selected controller name.
+ *
+ * @return string Controller name
+ * @access public
+ */
+	function getName() {
+		$useDbConfig = 'default';
+		$controllers = $this->listAll($useDbConfig, 'Controllers');
+		$enteredController = '';
+
+		while ($enteredController == '') {
+			$enteredController = $this->in(__("Enter a number from the list above, type in the name of another controller, or 'q' to exit", true), null, 'q');
+
+			if ($enteredController === 'q') {
+				$this->out(__("Exit", true));
+				$this->_stop();
+			}
+
+			if ($enteredController == '' || intval($enteredController) > count($controllers)) {
+				$this->out(__('Error:', true));
+				$this->out(__("The Controller name you supplied was empty, or the number \nyou selected was not an option. Please try again.", true));
+				$enteredController = '';
+			}
+		}
+
+		if (intval($enteredController) > 0 && intval($enteredController) <= count($controllers) ) {
+			$controllerName = $controllers[intval($enteredController) - 1];
+		} else {
+			$controllerName = Inflector::camelize($enteredController);
+		}
+
+		return $controllerName;
+	}
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake controller <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("\n\tcontroller <name>\n\t\tbakes controller with var \$scaffold");
+		$this->out("\n\tcontroller <name> scaffold\n\t\tbakes controller with scaffold actions.\n\t\t(index, view, add, edit, delete)");
+		$this->out("\n\tcontroller <name> scaffold admin\n\t\tbakes a controller with scaffold actions for both public and Configure::read('Routing.admin')");
+		$this->out("\n\tcontroller <name> admin\n\t\tbakes a controller with scaffold actions only for Configure::read('Routing.admin')");
+		$this->out("");
+		$this->_stop();
+	}
+}
+?>
Index: cake/console/libs/tasks/project.php
===================================================================
--- cake/console/libs/tasks/project.php	(revision 0)
+++ cake/console/libs/tasks/project.php	(revision 0)
@@ -0,0 +1,282 @@
+<?php
+/* SVN FILE: $Id: project.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * The Project Task handles creating the base application
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.scripts.bake
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+/**
+ * Task class for creating new project apps and plugins
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ProjectTask extends Shell {
+/**
+ * Checks that given project path does not already exist, and
+ * finds the app directory in it. Then it calls bake() with that information.
+ *
+ * @param string $project Project path
+ * @access public
+ */
+	function execute($project = null) {
+		if ($project === null) {
+			if (isset($this->args[0])) {
+				$project = $this->args[0];
+				$this->Dispatch->shiftArgs();
+			}
+		}
+
+		if ($project) {
+			$this->Dispatch->parseParams(array('-app', $project));
+			$project = $this->params['working'];
+		}
+
+		if (empty($this->params['skel'])) {
+			$this->params['skel'] = '';
+			if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) {
+				$this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel';
+			}
+		}
+
+		while (!$project) {
+			$project = $this->in("What is the full path for this app including the app directory name?\nExample: ".$this->params['working'] . DS . "myapp", null, $this->params['working'] . DS . 'myapp');
+		}
+
+		if ($project) {
+			$response = false;
+			while ($response == false && is_dir($project) === true && file_exists($project . 'config' . 'core.php')) {
+				$response = $this->in('A project already exists in this location: '.$project.' Overwrite?', array('y','n'), 'n');
+				if (strtolower($response) === 'n') {
+					$response = $project = false;
+				}
+			}
+		}
+
+		if ($this->bake($project)) {
+			$path = Folder::slashTerm($project);
+			if ($this->createHome($path)) {
+				$this->out(__('Welcome page created', true));
+			} else {
+				$this->out(__('The Welcome page was NOT created', true));
+			}
+
+			if ($this->securitySalt($path) === true ) {
+				$this->out(__('Random hash key created for \'Security.salt\'', true));
+			} else {
+				$this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s', true), CONFIGS . 'core.php'));
+			}
+
+			$corePath = $this->corePath($path);
+			if ($corePath === true ) {
+				$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', true), CAKE_CORE_INCLUDE_PATH));
+				$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', true), CAKE_CORE_INCLUDE_PATH));
+				$this->out(__('Remember to check these value after moving to production server', true));
+			} elseif ($corePath === false) {
+				$this->err(sprintf(__('Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', true), $path . 'webroot' .DS .'index.php'));
+			}
+			$Folder = new Folder($path);
+			if (!$Folder->chmod($path . 'tmp', 0777)) {
+				$this->err(sprintf(__('Could not set permissions on %s', true), $path . DS .'tmp'));
+				$this->out(sprintf(__('chmod -R 0777 %s', true), $path . DS .'tmp'));
+			}
+
+			$this->params['working'] = $path;
+			$this->params['app'] = basename($path);
+			return true;
+		}
+	}
+/**
+ * Looks for a skeleton template of a Cake application,
+ * and if not found asks the user for a path. When there is a path
+ * this method will make a deep copy of the skeleton to the project directory.
+ * A default home page will be added, and the tmp file storage will be chmod'ed to 0777.
+ *
+ * @param string $path Project path
+ * @param string $skel Path to copy from
+ * @param string $skip array of directories to skip when copying
+ * @access private
+ */
+	function bake($path, $skel = null, $skip = array('empty')) {
+		if (!$skel) {
+			$skel = $this->params['skel'];
+		}
+
+		while (!$skel) {
+			$skel = $this->in(sprintf(__("What is the path to the directory layout you wish to copy?\nExample: %s"), APP, null, ROOT . DS . 'myapp' . DS));
+			if ($skel == '') {
+				$this->out(__('The directory path you supplied was empty. Please try again.', true));
+			} else {
+				while (is_dir($skel) === false) {
+					$skel = $this->in(__('Directory path does not exist please choose another:', true));
+				}
+			}
+		}
+
+		$app = basename($path);
+
+		$this->out('Bake Project');
+		$this->out("Skel Directory: $skel");
+		$this->out("Will be copied to: {$path}");
+		$this->hr();
+
+		$looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y');
+
+		if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
+			$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
+
+			$Folder = new Folder($skel);
+			if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
+				$this->hr();
+				$this->out(sprintf(__("Created: %s in %s", true), $app, $path));
+				$this->hr();
+			} else {
+				$this->err(" '".$app."' could not be created properly");
+				return false;
+			}
+
+			if (low($verbose) == 'y' || low($verbose) == 'yes') {
+				foreach ($Folder->messages() as $message) {
+					$this->out($message);
+				}
+			}
+
+			return true;
+		} elseif (low($looksGood) == 'q' || low($looksGood) == 'quit') {
+			$this->out('Bake Aborted.');
+		} else {
+			$this->execute(false);
+			return false;
+		}
+	}
+/**
+ * Writes a file with a default home page to the project.
+ *
+ * @param string $dir Path to project
+ * @return boolean Success
+ * @access public
+ */
+	function createHome($dir) {
+		$app = basename($dir);
+		$path = $dir . 'views' . DS . 'pages' . DS;
+		include(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'views'.DS.'home.ctp');
+		return $this->createFile($path.'home.ctp', $output);
+	}
+/**
+ * Generates and writes 'Security.salt'
+ *
+ * @param string $path Project path
+ * @return boolean Success
+ * @access public
+ */
+	function securitySalt($path) {
+		$File =& new File($path . 'config' . DS . 'core.php');
+		$contents = $File->read();
+		if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.salt\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+			if (!class_exists('Security')) {
+				uses('Security');
+			}
+			$string = Security::generateAuthKey();
+			$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \''.$string.'\');', $contents);
+			if ($File->write($result)) {
+				return true;
+			}
+			return false;
+		}
+		return false;
+	}
+/**
+ * Generates and writes CAKE_CORE_INCLUDE_PATH
+ *
+ * @param string $path Project path
+ * @return boolean Success
+ * @access public
+ */
+	function corePath($path) {
+		if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
+			$File =& new File($path . 'webroot' . DS . 'index.php');
+			$contents = $File->read();
+			if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+				$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '".CAKE_CORE_INCLUDE_PATH."');", $contents);
+				if (!$File->write($result)) {
+					return false;
+				}
+			} else {
+				return false;
+			}
+
+			$File =& new File($path . 'webroot' . DS . 'test.php');
+			$contents = $File->read();
+			if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+				$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '".CAKE_CORE_INCLUDE_PATH."');", $contents);
+				if (!$File->write($result)) {
+					return false;
+				}
+			} else {
+				return false;
+			}
+			return true;
+		}
+	}
+/**
+ * Enables Configure::read('Routing.admin') in /app/config/core.php
+ *
+ * @param string $name Name to use as admin routing
+ * @return boolean Success
+ * @access public
+ */
+	function cakeAdmin($name) {
+		$File =& new File(CONFIGS . 'core.php');
+		$contents = $File->read();
+		if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.admin\',[\\t\\x20\'a-z]*\\);)%', $contents, $match)) {
+			$result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.admin\', \''.$name.'\');', $contents);
+			if ($File->write($result)) {
+				Configure::write('Routing.admin', $name);
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	}
+/**
+ * Help
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake project <arg1>");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("\n\tproject <name>\n\t\tbakes app directory structure.\n\t\tif <name> begins with '/' path is absolute.");
+		$this->out("");
+		$this->_stop();
+	}
+
+}
+?>
\ No newline at end of file
Index: cake/console/libs/schema.php
===================================================================
--- cake/console/libs/schema.php	(revision 0)
+++ cake/console/libs/schema.php	(revision 0)
@@ -0,0 +1,429 @@
+<?php
+/* SVN FILE: $Id: schema.php 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ * Command-line database management utility to automate programmer chores.
+ *
+ * Schema is CakePHP's database management utility. This helps you maintain versions of
+ * of your database.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('File');
+App::import('Model', 'Schema');
+/**
+ * Schema is a command-line database management utility for automating programmer chores.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @link          http://book.cakephp.org/view/734/Schema-management-and-migrations
+ */
+class SchemaShell extends Shell {
+/**
+ * is this a dry run?
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__dry = null;
+/**
+ * Override initialize
+ *
+ * @access public
+ */
+	function initialize() {
+		$this->_welcome();
+		$this->out('Cake Schema Shell');
+		$this->hr();
+	}
+/**
+ * Override startup
+ *
+ * @access public
+ */
+	function startup() {
+		$name = null;
+		if (!empty($this->params['name'])) {
+			$name = $this->params['name'];
+			$this->params['file'] = Inflector::underscore($name);
+		}
+
+		$path = null;
+		if (!empty($this->params['path'])) {
+			$path = $this->params['path'];
+		}
+
+		$file = null;
+		if (empty($this->params['file'])) {
+			$this->params['file'] = 'schema.php';
+		}
+		if (strpos($this->params['file'], '.php') === false) {
+			$this->params['file'] .= '.php';
+		}
+		$file = $this->params['file'];
+
+		$connection = null;
+		if (!empty($this->params['connection'])) {
+			$connection = $this->params['connection'];
+		}
+
+		$this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection'));
+	}
+/**
+ * Override main
+ *
+ * @access public
+ */
+	function main() {
+		$this->help();
+	}
+/**
+ * Read and output contents of schema object
+ * path to read as second arg
+ *
+ * @access public
+ */
+	function view() {
+		$File = new File($this->Schema->path . DS . $this->params['file']);
+		if ($File->exists()) {
+			$this->out($File->read());
+			$this->_stop();
+		} else {
+			$this->err(__('Schema could not be found', true));
+			$this->_stop();
+		}
+	}
+/**
+ * Read database and Write schema object
+ * accepts a connection as first arg or path to save as second arg
+ *
+ * @access public
+ */
+	function generate() {
+		$this->out('Generating Schema...');
+		$options = array();
+		if (isset($this->params['f'])) {
+			$options = array('models' => false);
+		}
+
+		$snapshot = false;
+		if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
+			$snapshot = true;
+		}
+
+		if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
+			$snapshot = true;
+			$result = $this->in("Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?", array('o', 's', 'q'), 's');
+			if ($result === 'q') {
+				$this->_stop();
+			}
+			if ($result === 'o') {
+				$snapshot = false;
+			}
+		}
+
+		$content = $this->Schema->read($options);
+		$content['file'] = $this->params['file'];
+
+		if ($snapshot === true) {
+			$Folder =& new Folder($this->Schema->path);
+			$result = $Folder->read();
+
+			$numToUse = false;
+			if (isset($this->params['s'])) {
+				$numToUse = $this->params['s'];
+			}
+
+			$count = 1;
+			if (!empty($result[1])) {
+				foreach ($result[1] as $file) {
+					if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
+						$count++;
+					}
+				}
+			}
+
+			if ($numToUse !== false) {
+				if ($numToUse > $count) {
+					$count = $numToUse;
+				}
+			}
+
+			$fileName = rtrim($this->params['file'], '.php');
+			$content['file'] = $fileName . '_' . $count . '.php';
+		}
+
+		if ($this->Schema->write($content)) {
+			$this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
+			$this->_stop();
+		} else {
+			$this->err(__('Schema file: %s generated', true));
+			$this->_stop();
+		}
+	}
+/**
+ * Dump Schema object to sql file
+ * if first arg == write, file will be written to sql file
+ * or it will output sql
+ *
+ * @access public
+ */
+	function dump() {
+		$write = false;
+		$Schema = $this->Schema->load();
+		if (!$Schema) {
+			$this->err(__('Schema could not be loaded', true));
+			$this->_stop();
+		}
+		if (!empty($this->args[0])) {
+			if ($this->args[0] == 'write') {
+				$write = Inflector::underscore($this->Schema->name);
+			} else {
+				$write = $this->args[0];
+			}
+		}
+		$db =& ConnectionManager::getDataSource($this->Schema->connection);
+		$contents = "#". $Schema->name ." sql generated on: " . date('Y-m-d H:m:s') . " : ". time()."\n\n";
+		$contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
+		if ($write) {
+			if (strpos($write, '.sql') === false) {
+				$write .= '.sql';
+			}
+			$File = new File($this->Schema->path . DS . $write, true);
+			if ($File->write($contents)) {
+				$this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
+				$this->_stop();
+			} else {
+				$this->err(__('SQL dump could not be created', true));
+				$this->_stop();
+			}
+		}
+		$this->out($contents);
+		return $contents;
+	}
+/**
+ * Run database commands: create, update
+ *
+ * @access public
+ */
+	function run() {
+		if (!isset($this->args[0])) {
+			$this->err('command not found');
+			$this->_stop();
+		}
+
+		$command = $this->args[0];
+
+		$this->Dispatch->shiftArgs();
+
+		$name = null;
+		if (isset($this->args[0])) {
+			$name = $this->args[0];
+		}
+		if (isset($this->params['name'])) {
+			$name = $this->params['name'];
+		}
+
+		if (isset($this->params['dry'])) {
+			$this->__dry = true;
+			$this->out(__('Performing a dry run.', true));
+		}
+
+		$options = array('name' => $name);
+		if (isset($this->params['s'])) {
+			$fileName = rtrim($this->Schema->file, '.php');
+			$options['file'] = $fileName . '_' . $this->params['s'] . '.php';
+		}
+
+		$Schema = $this->Schema->load($options);
+
+		if (!$Schema) {
+			$this->err(sprintf(__('%s could not be loaded', true), $this->Schema->file));
+			$this->_stop();
+		}
+
+		$table = null;
+		if (isset($this->args[1])) {
+			$table = $this->args[1];
+		}
+
+		switch ($command) {
+			case 'create':
+				$this->__create($Schema, $table);
+			break;
+			case 'update':
+				$this->__update($Schema, $table);
+			break;
+			default:
+				$this->err(__('command not found', true));
+			$this->_stop();
+		}
+	}
+/**
+ * Create database from Schema object
+ * Should be called via the run method
+ *
+ * @access private
+ */
+	function __create($Schema, $table = null) {
+		$db =& ConnectionManager::getDataSource($this->Schema->connection);
+
+		$drop = $create = array();
+
+		if (!$table) {
+			foreach ($Schema->tables as $table => $fields) {
+				$drop[$table] = $db->dropSchema($Schema, $table);
+				$create[$table] = $db->createSchema($Schema, $table);
+			}
+		} elseif (isset($Schema->tables[$table])) {
+			$drop[$table] = $db->dropSchema($Schema, $table);
+			$create[$table] = $db->createSchema($Schema, $table);
+		}
+		if (empty($drop) || empty($create)) {
+			$this->out(__('Schema is up to date.', true));
+			$this->_stop();
+		}
+
+		$this->out("\n" . __('The following table(s) will be dropped.', true));
+		$this->out(array_keys($drop));
+
+		if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) {
+			$this->out('Dropping table(s).');
+			$this->__run($drop, 'drop', $Schema);
+		}
+
+		$this->out("\n" . __('The following table(s) will be created.', true));
+		$this->out(array_keys($create));
+
+		if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) {
+			$this->out('Creating table(s).');
+			$this->__run($create, 'create', $Schema);
+		}
+
+		$this->out(__('End create.', true));
+	}
+/**
+ * Update database with Schema object
+ * Should be called via the run method
+ *
+ * @access private
+ */
+	function __update($Schema, $table = null) {
+		$db =& ConnectionManager::getDataSource($this->Schema->connection);
+
+		$this->out('Comparing Database to Schema...');
+		$Old = $this->Schema->read();
+		$compare = $this->Schema->compare($Old, $Schema);
+
+		$contents = array();
+
+		if (empty($table)) {
+			foreach ($compare as $table => $changes) {
+				$contents[$table] = $db->alterSchema(array($table => $changes), $table);
+			}
+		} elseif (isset($compare[$table])) {
+			$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
+		}
+
+		if (empty($contents)) {
+			$this->out(__('Schema is up to date.', true));
+			$this->_stop();
+		}
+
+		$this->out("\n" . __('The following statements will run.', true));
+		$this->out(array_map('trim', $contents));
+		if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) {
+			$this->out('');
+			$this->out(__('Updating Database...', true));
+			$this->__run($contents, 'update', $Schema);
+		}
+
+		$this->out(__('End update.', true));
+	}
+/**
+ * Runs sql from __create() or __update()
+ *
+ * @access private
+ */
+	function __run($contents, $event, $Schema) {
+		if (empty($contents)) {
+			$this->err(__('Sql could not be run', true));
+			return;
+		}
+		Configure::write('debug', 2);
+		$db =& ConnectionManager::getDataSource($this->Schema->connection);
+		$db->fullDebug = true;
+
+		$errors = array();
+		foreach ($contents as $table => $sql) {
+			if (empty($sql)) {
+				$this->out(sprintf(__('%s is up to date.', true), $table));
+			} else {
+				if ($this->__dry === true) {
+					$this->out(sprintf(__('Dry run for %s :', true), $table));
+					$this->out($sql);
+				} else {
+					if (!$Schema->before(array($event => $table))) {
+						return false;
+					}
+					if (!$db->_execute($sql)) {
+						$error = $table . ': '  . $db->lastError();
+					}
+
+					$Schema->after(array($event => $table, 'errors'=> $errors));
+
+					if (isset($error)) {
+						$this->out($error);
+					} elseif ($this->__dry !== true) {
+						$this->out(sprintf(__('%s updated.', true), $table));
+					}
+				}
+			}
+		}
+	}
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->out("The Schema Shell generates a schema object from \n\t\tthe database and updates the database from the schema.");
+		$this->hr();
+		$this->out("Usage: cake schema <command> <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Params:');
+		$this->out("\n\t-connection <config>\n\t\tset db config <config>. uses 'default' if none is specified");
+		$this->out("\n\t-path <dir>\n\t\tpath <dir> to read and write schema.php.\n\t\tdefault path: ". $this->Schema->path);
+		$this->out("\n\t-name <name>\n\t\tclassname to use.");
+		$this->out("\n\t-file <name>\n\t\tfile <name> to read and write.\n\t\tdefault file: ". $this->Schema->file);
+		$this->out("\n\t-s <number>\n\t\tsnapshot <number> to use for run.");
+		$this->out("\n\t-dry\n\t\tPerform a dry run on 'run' commands.\n\t\tQueries will be output to window instead of executed.");
+		$this->out("\n\t-f\n\t\tforce 'generate' to create a new schema.");
+		$this->out('Commands:');
+		$this->out("\n\tschema help\n\t\tshows this help message.");
+		$this->out("\n\tschema view\n\t\tread and output contents of schema file");
+		$this->out("\n\tschema generate\n\t\treads from 'connection' writes to 'path'\n\t\tTo force generation of all tables into the schema, use the -f param.\n\t\tUse 'schema generate snapshot <number>' to generate snapshots\n\t\twhich you can use with the -s parameter in the other operations.");
+		$this->out("\n\tschema dump <filename>\n\t\tDump database sql based on schema file to <filename>. \n\t\tIf <filename> is write, schema dump will be written to a file\n\t\tthat has the same name as the app directory.");
+		$this->out("\n\tschema run create <schema> <table>\n\t\tDrop and create tables based on schema file\n\t\toptional <schema> arg for selecting schema name\n\t\toptional <table> arg for creating only one table\n\t\tpass the -s param with a number to use a snapshot\n\t\tTo see the changes, perform a dry run with the -dry param");
+		$this->out("\n\tschema run update <schema> <table>\n\t\talter tables based on schema file\n\t\toptional <schema> arg for selecting schema name.\n\t\toptional <table> arg for altering only one table.\n\t\tTo use a snapshot, pass the -s param with the snapshot number\n\t\tTo see the changes, perform a dry run with the -dry param");
+		$this->out("");
+		$this->_stop();
+	}
+}
+?>
Index: cake/console/libs/bake.php
===================================================================
--- cake/console/libs/bake.php	(revision 0)
+++ cake/console/libs/bake.php	(revision 0)
@@ -0,0 +1,211 @@
+<?php
+/* SVN FILE: $Id: bake.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * Bake is CakePHP's code generation script, which can help you kickstart
+ * application development by writing fully functional skeleton controllers,
+ * models, and views. Going further, Bake can also write Unit Tests for you.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Bake is a command-line code generation utility for automating programmer chores.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @link          http://book.cakephp.org/view/113/Code-Generation-with-Bake
+ */
+class BakeShell extends Shell {
+/**
+ * Contains tasks to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Project', 'DbConfig', 'Model', 'Controller', 'View', 'Plugin', 'Test');
+/**
+ * Override loadTasks() to handle paths
+ *
+ * @access public
+ */
+	function loadTasks() {
+		parent::loadTasks();
+		$task = Inflector::classify($this->command);
+		if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) {
+			$path = Inflector::underscore(Inflector::pluralize($this->command));
+			$this->{$task}->path = $this->params['working'] . DS . $path . DS;
+			if (!is_dir($this->{$task}->path)) {
+				$this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path));
+				$this->_stop();
+			}
+		}
+	}
+/**
+ * Override main() to handle action
+ *
+ * @access public
+ */
+	function main() {
+		if (!is_dir($this->DbConfig->path)) {
+			if ($this->Project->execute()) {
+				$this->DbConfig->path = $this->params['working'] . DS . 'config' . DS;
+			}
+		}
+
+		if (!config('database')) {
+			$this->out(__("Your database configuration was not found. Take a moment to create one.", true));
+			$this->args = null;
+			return $this->DbConfig->execute();
+		}
+		$this->out('Interactive Bake Shell');
+		$this->hr();
+		$this->out('[D]atabase Configuration');
+		$this->out('[M]odel');
+		$this->out('[V]iew');
+		$this->out('[C]ontroller');
+		$this->out('[P]roject');
+		$this->out('[Q]uit');
+
+		$classToBake = strtoupper($this->in(__('What would you like to Bake?', true), array('D', 'M', 'V', 'C', 'P', 'Q')));
+		switch ($classToBake) {
+			case 'D':
+				$this->DbConfig->execute();
+				break;
+			case 'M':
+				$this->Model->execute();
+				break;
+			case 'V':
+				$this->View->execute();
+				break;
+			case 'C':
+				$this->Controller->execute();
+				break;
+			case 'P':
+				$this->Project->execute();
+				break;
+			case 'Q':
+				exit(0);
+				break;
+			default:
+				$this->out(__('You have made an invalid selection. Please choose a type of class to Bake by entering D, M, V, or C.', true));
+		}
+		$this->hr();
+		$this->main();
+	}
+/**
+ * Quickly bake the MVC
+ *
+ * @access public
+ */
+	function all() {
+		$ds = 'default';
+		$this->hr();
+		$this->out('Bake All');
+		$this->hr();
+
+		if (isset($this->params['connection'])) {
+			$ds = $this->params['connection'];
+		}
+
+		if (empty($this->args)) {
+			$name = $this->Model->getName($ds);
+		}
+
+		if (!empty($this->args[0])) {
+			$name = $this->args[0];
+			$this->Model->listAll($ds, false);
+		}
+
+		$modelExists = false;
+		$model = $this->_modelName($name);
+		if (App::import('Model', $model)) {
+			$object = new $model();
+			$modelExists = true;
+		} else {
+			App::import('Model');
+			$object = new Model(array('name' => $name, 'ds' => $ds));
+		}
+
+		$modelBaked = $this->Model->bake($object, false);
+
+		if ($modelBaked && $modelExists === false) {
+			$this->out(sprintf(__('%s Model was baked.', true), $model));
+			if ($this->_checkUnitTest()) {
+				$this->Model->bakeTest($model);
+			}
+			$modelExists = true;
+		}
+
+		if ($modelExists === true) {
+			$controller = $this->_controllerName($name);
+			if ($this->Controller->bake($controller, $this->Controller->bakeActions($controller))) {
+				$this->out(sprintf(__('%s Controller was baked.', true), $name));
+				if ($this->_checkUnitTest()) {
+					$this->Controller->bakeTest($controller);
+				}
+			}
+			if (App::import('Controller', $controller)) {
+				$this->View->args = array($controller);
+				$this->View->execute();
+			}
+			$this->out(__('Bake All complete'));
+			array_shift($this->args);
+		} else {
+			$this->err(__('Bake All could not continue without a valid model', true));
+		}
+
+		if (empty($this->args)) {
+			$this->all();
+		}
+		$this->_stop();
+	}
+
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->out('CakePHP Bake:');
+		$this->hr();
+		$this->out('The Bake script generates controllers, views and models for your application.');
+		$this->out('If run with no command line arguments, Bake guides the user through the class');
+		$this->out('creation process. You can customize the generation process by telling Bake');
+		$this->out('where different parts of your application are using command line arguments.');
+		$this->hr();
+		$this->out("Usage: cake bake <command> <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Params:');
+		$this->out("\t-app <path> Absolute/Relative path to your app folder.\n");
+		$this->out('Commands:');
+		$this->out("\n\tbake help\n\t\tshows this help message.");
+		$this->out("\n\tbake all <name>\n\t\tbakes complete MVC. optional <name> of a Model");
+		$this->out("\n\tbake project <path>\n\t\tbakes a new app folder in the path supplied\n\t\tor in current directory if no path is specified");
+		$this->out("\n\tbake plugin <name>\n\t\tbakes a new plugin folder in the path supplied\n\t\tor in current directory if no path is specified.");
+		$this->out("\n\tbake db_config\n\t\tbakes a database.php file in config directory.");
+		$this->out("\n\tbake model\n\t\tbakes a model. run 'bake model help' for more info");
+		$this->out("\n\tbake view\n\t\tbakes views. run 'bake view help' for more info");
+		$this->out("\n\tbake controller\n\t\tbakes a controller. run 'bake controller help' for more info");
+		$this->out("");
+
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/console.php
===================================================================
--- cake/console/libs/console.php	(revision 0)
+++ cake/console/libs/console.php	(revision 0)
@@ -0,0 +1,339 @@
+<?php
+/* SVN FILE: $Id: console.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class ConsoleShell extends Shell {
+/**
+ * Available binding types
+ *
+ * @var array
+ * @access public
+ */
+	var $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany');
+/**
+ * Chars that describe invalid commands
+ *
+ * @var array
+ * @access public
+ */
+	var $badCommandChars = array('$', ';');
+/**
+ * Available models
+ *
+ * @var array
+ * @access public
+ */
+	var $models = array();
+/**
+ * Override intialize of the Shell
+ *
+ * @access public
+ */
+	function initialize() {
+		require_once CAKE . 'dispatcher.php';
+		$this->Dispatcher = new Dispatcher();
+		$this->models = Configure::listObjects('model');
+		App::import('Model', $this->models);
+
+		foreach ($this->models as $model) {
+			$class = Inflector::camelize(r('.php', '', $model));
+			$this->models[$model] = $class;
+			$this->{$class} =& new $class();
+		}
+		$this->out('Model classes:');
+		$this->out('--------------');
+
+		foreach ($this->models as $model) {
+			$this->out(" - {$model}");
+		}
+		$this->__loadRoutes();
+	}
+/**
+ * Prints the help message
+ *
+ * @access public
+ */
+	function help() {
+		$this->main('help');
+	}
+/**
+ * Override main() to handle action
+ *
+ * @access public
+ */
+	function main($command = null) {
+		while (true) {
+			if (empty($command)) {
+				$command = trim($this->in(''));
+			}
+
+			switch ($command) {
+				case 'help':
+					$this->out('Console help:');
+					$this->out('-------------');
+					$this->out('The interactive console is a tool for testing parts of your app before you commit code');
+					$this->out('');
+					$this->out('Model testing:');
+					$this->out('To test model results, use the name of your model without a leading $');
+					$this->out('e.g. Foo->find("all")');
+					$this->out('');
+					$this->out('To dynamically set associations, you can do the following:');
+					$this->out("\tModelA bind <association> ModelB");
+					$this->out("where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany");
+					$this->out('');
+					$this->out('To dynamically remove associations, you can do the following:');
+					$this->out("\t ModelA unbind <association> ModelB");
+					$this->out("where the supported associations are the same as above");
+					$this->out('');
+					$this->out("To save a new field in a model, you can do the following:");
+					$this->out("\tModelA->save(array('foo' => 'bar', 'baz' => 0))");
+					$this->out("where you are passing a hash of data to be saved in the format");
+					$this->out("of field => value pairs");
+					$this->out('');
+					$this->out("To get column information for a model, use the following:");
+					$this->out("\tModelA columns");
+					$this->out("which returns a list of columns and their type");
+					$this->out('');
+					$this->out('Route testing:');
+					$this->out('To test URLs against your app\'s route configuration, type:');
+					$this->out("\tRoute <url>");
+					$this->out("where url is the path to your your action plus any query parameters, minus the");
+					$this->out("application's base path");
+					$this->out('');
+					$this->out('To reload your routes config (config/routes.php), do the following:');
+					$this->out("\tRoutes reload");
+					$this->out('');
+					$this->out('');
+					$this->out('To show all connected routes, do the following:');
+					$this->out("\tRoutes show");
+					$this->out('');
+				break;
+				case 'quit':
+				case 'exit':
+					return true;
+				break;
+				case 'models':
+					$this->out('Model classes:');
+					$this->hr();
+					foreach ($this->models as $model) {
+						$this->out(" - {$model}");
+					}
+				break;
+				case (preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp) == true):
+					foreach ($tmp as $data) {
+						$data = strip_tags($data);
+						$data = str_replace($this->badCommandChars, "", $data);
+					}
+
+					$modelA = $tmp[1];
+					$association = $tmp[2];
+					$modelB = $tmp[3];
+
+					if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations)) {
+						$this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false);
+						$this->out("Created $association association between $modelA and $modelB");
+					} else {
+						$this->out("Please verify you are using valid models and association types");
+					}
+				break;
+				case (preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp) == true):
+					foreach ($tmp as $data) {
+						$data = strip_tags($data);
+						$data = str_replace($this->badCommandChars, "", $data);
+					}
+
+					$modelA = $tmp[1];
+					$association = $tmp[2];
+					$modelB = $tmp[3];
+
+					// Verify that there is actually an association to unbind
+					$currentAssociations = $this->{$modelA}->getAssociated();
+					$validCurrentAssociation = false;
+
+					foreach ($currentAssociations as $model => $currentAssociation) {
+						if ($model == $modelB && $association == $currentAssociation) {
+							$validCurrentAssociation = true;
+						}
+					}
+
+					if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) {
+						$this->{$modelA}->unbindModel(array($association => array($modelB)));
+						$this->out("Removed $association association between $modelA and $modelB");
+					} else {
+						$this->out("Please verify you are using valid models, valid current association, and valid association types");
+					}
+				break;
+				case (strpos($command, "->find") > 0):
+					// Remove any bad info
+					$command = strip_tags($command);
+					$command = str_replace($this->badCommandChars, "", $command);
+
+					// Do we have a valid model?
+					list($modelToCheck, $tmp) = explode('->', $command);
+
+					if ($this->__isValidModel($modelToCheck)) {
+						$findCommand = "\$data = \$this->$command;";
+						@eval($findCommand);
+
+						if (is_array($data)) {
+							foreach ($data as $idx => $results) {
+								if (is_numeric($idx)) { // findAll() output
+									foreach ($results as $modelName => $result) {
+										$this->out("$modelName");
+
+										foreach ($result as $field => $value) {
+											if (is_array($value)) {
+												foreach ($value as $field2 => $value2) {
+													$this->out("\t$field2: $value2");
+												}
+
+												$this->out("");
+											} else {
+												$this->out("\t$field: $value");
+											}
+										}
+									}
+								} else { // find() output
+									$this->out($idx);
+
+									foreach ($results as $field => $value) {
+										if (is_array($value)) {
+											foreach ($value as $field2 => $value2) {
+												$this->out("\t$field2: $value2");
+											}
+
+											$this->out("");
+										} else {
+											$this->out("\t$field: $value");
+										}
+									}
+								}
+							}
+						} else {
+							$this->out("\nNo result set found");
+						}
+					} else {
+						$this->out("$modelToCheck is not a valid model");
+					}
+
+				break;
+				case (strpos($command, '->save') > 0):
+					// Validate the model we're trying to save here
+					$command = strip_tags($command);
+					$command = str_replace($this->badCommandChars, "", $command);
+					list($modelToSave, $tmp) = explode("->", $command);
+
+					if ($this->__isValidModel($modelToSave)) {
+						// Extract the array of data we are trying to build
+						list($foo, $data) = explode("->save", $command);
+						$badChars = array("(", ")");
+						$data = str_replace($badChars, "", $data);
+						$saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));";
+						@eval($saveCommand);
+						$this->out('Saved record for ' . $modelToSave);
+					}
+				break;
+				case (preg_match("/^(\w+) columns/", $command, $tmp) == true):
+					$modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1]));
+
+					if ($this->__isValidModel($modelToCheck)) {
+						// Get the column info for this model
+						$fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
+						@eval($fieldsCommand);
+
+						if (is_array($data)) {
+							foreach ($data as $field => $type) {
+								$this->out("\t{$field}: {$type}");
+							}
+						}
+					} else {
+						$this->out("Please verify that you selected a valid model");
+					}
+				break;
+				case (preg_match("/^routes\s+reload/i", $command, $tmp) == true):
+					$router =& Router::getInstance();
+					if (!$this->__loadRoutes()) {
+						$this->out("There was an error loading the routes config.  Please check that the file");
+						$this->out("exists and is free of parse errors.");
+						break;
+					}
+					$this->out("Routes configuration reloaded, " . count($router->routes) . " routes connected");
+				break;
+				case (preg_match("/^routes\s+show/i", $command, $tmp) == true):
+					$router =& Router::getInstance();
+					$this->out(join("\n", Set::extract($router->routes, '{n}.0')));
+				break;
+				case (preg_match("/^route\s+(.*)/i", $command, $tmp) == true):
+					$this->out(var_export(Router::parse($tmp[1]), true));
+				break;
+				default:
+					$this->out("Invalid command\n");
+				break;
+			}
+			$command = '';
+		}
+	}
+/**
+ * Tells if the specified model is included in the list of available models
+ *
+ * @param string $modelToCheck
+ * @return boolean true if is an available model, false otherwise
+ * @access private
+ */
+	function __isValidModel($modelToCheck) {
+		return in_array($modelToCheck, $this->models);
+	}
+/**
+ * Reloads the routes configuration from config/routes.php, and compiles
+ * all routes found
+ *
+ * @return boolean True if config reload was a success, otherwise false
+ * @access private
+ */
+	function __loadRoutes() {
+		$router =& Router::getInstance();
+
+		$router->reload();
+		extract($router->getNamedExpressions());
+
+		if (!@include(CONFIGS . 'routes.php')) {
+			return false;
+		}
+		$router->parse('/');
+
+		foreach (array_keys($router->getNamedExpressions()) as $var) {
+			unset(${$var});
+		}
+		for ($i = 0; $i < count($router->routes); $i++) {
+			$router->compile($i);
+		}
+		return true;
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/shell.php
===================================================================
--- cake/console/libs/shell.php	(revision 0)
+++ cake/console/libs/shell.php	(revision 0)
@@ -0,0 +1,615 @@
+<?php
+/* SVN FILE: $Id: shell.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Base class for Shells
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Base class for command-line utilities for automating programmer chores.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class Shell extends Object {
+/**
+ * An instance of the ShellDispatcher object that loaded this script
+ *
+ * @var object
+ * @access public
+ */
+	var $Dispatch = null;
+/**
+ * If true, the script will ask for permission to perform actions.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $interactive = true;
+/**
+ * Holds the DATABASE_CONFIG object for the app. Null if database.php could not be found,
+ * or the app does not exist.
+ *
+ * @var object
+ * @access public
+ */
+	var $DbConfig = null;
+/**
+ * Contains command switches parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	var $params = array();
+/**
+ * Contains arguments parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	var $args = array();
+/**
+ * The file name of the shell that was invoked.
+ *
+ * @var string
+ * @access public
+ */
+	var $shell = null;
+/**
+ * The class name of the shell that was invoked.
+ *
+ * @var string
+ * @access public
+ */
+	var $className = null;
+/**
+ * The command called if public methods are available.
+ *
+ * @var string
+ * @access public
+ */
+	var $command = null;
+/**
+ * The name of the shell in camelized.
+ *
+ * @var string
+ * @access public
+ */
+	var $name = null;
+/**
+ * An alias for the shell
+ *
+ * @var string
+ * @access public
+ */
+	var $alias = null;
+/**
+ * Contains tasks to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array();
+/**
+ * Contains the loaded tasks
+ *
+ * @var array
+ * @access public
+ */
+	var $taskNames = array();
+/**
+ * Contains models to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+/**
+ *  Constructs this Shell instance.
+ *
+ */
+	function __construct(&$dispatch) {
+		$vars = array('params', 'args', 'shell', 'shellCommand' => 'command');
+		foreach ($vars as $key => $var) {
+			if (is_string($key)) {
+				$this->{$var} =& $dispatch->{$key};
+			} else {
+				$this->{$var} =& $dispatch->{$var};
+			}
+		}
+
+		if ($this->name == null) {
+			$this->name = get_class($this);
+		}
+
+		if ($this->alias == null) {
+			$this->alias = $this->name;
+		}
+
+		ClassRegistry::addObject($this->name, $this);
+		ClassRegistry::map($this->name, $this->alias);
+
+		if (!PHP5 && isset($this->args[0])) {
+			if (strpos($this->name, low(Inflector::camelize($this->args[0]))) !== false) {
+				$dispatch->shiftArgs();
+			}
+			if (low($this->command) == low(Inflector::variable($this->args[0])) && method_exists($this, $this->command)) {
+				$dispatch->shiftArgs();
+			}
+		}
+
+		$this->Dispatch =& $dispatch;
+	}
+/**
+ * Initializes the Shell
+ * acts as constructor for subclasses
+ * allows configuration of tasks prior to shell execution
+ *
+ * @access public
+ */
+	function initialize() {
+		$this->_loadModels();
+	}
+/**
+ * Starts up the the Shell
+ * allows for checking and configuring prior to command or main execution
+ * can be overriden in subclasses
+ *
+ * @access public
+ */
+	function startup() {
+		$this->_welcome();
+	}
+/**
+ * Displays a header for the shell
+ *
+ * @access protected
+ */
+	function _welcome() {
+		$this->out("\nWelcome to CakePHP v" . Configure::version() . " Console");
+		$this->out("---------------------------------------------------------------");
+		$this->out('App : '. $this->params['app']);
+		$this->out('Path: '. $this->params['working']);
+		$this->hr();
+	}
+/**
+ * Loads database file and constructs DATABASE_CONFIG class
+ * makes $this->DbConfig available to subclasses
+ *
+ * @return bool
+ * @access protected
+ */
+	function _loadDbConfig() {
+		if (config('database') && class_exists('DATABASE_CONFIG')) {
+			$this->DbConfig =& new DATABASE_CONFIG();
+			return true;
+		}
+		$this->err('Database config could not be loaded');
+		$this->out('Run \'bake\' to create the database configuration');
+		return false;
+	}
+/**
+ * if var $uses = true
+ * Loads AppModel file and constructs AppModel class
+ * makes $this->AppModel available to subclasses
+ * if var $uses is an array of models will load those models
+ *
+ * @return bool
+ * @access protected
+ */
+	function _loadModels() {
+		if ($this->uses === null || $this->uses === false) {
+			return;
+		}
+
+		if ($this->uses === true && App::import('Model', 'AppModel')) {
+			$this->AppModel =& new AppModel(false, false, false);
+			return true;
+		}
+
+		if ($this->uses !== true && !empty($this->uses)) {
+			$uses = is_array($this->uses) ? $this->uses : array($this->uses);
+
+			$modelClassName = $uses[0];
+			if (strpos($uses[0], '.') !== false) {
+				list($plugin, $modelClassName) = explode('.', $uses[0]);
+			}
+			$this->modelClass = $modelClassName;
+
+			foreach ($uses as $modelClass) {
+				$plugin = null;
+				if (strpos($modelClass, '.') !== false) {
+					list($plugin, $modelClass) = explode('.', $modelClass);
+					$plugin = $plugin . '.';
+				}
+				if (PHP5) {
+					$this->{$modelClass} = ClassRegistry::init($plugin . $modelClass);
+				} else {
+					$this->{$modelClass} =& ClassRegistry::init($plugin . $modelClass);
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+/**
+ * Loads tasks defined in var $tasks
+ *
+ * @return bool
+ * @access public
+ */
+	function loadTasks() {
+		if ($this->tasks === null || $this->tasks === false || $this->tasks === true || empty($this->tasks)) {
+			return true;
+		}
+
+		$tasks = $this->tasks;
+		if (!is_array($tasks)) {
+			$tasks = array($tasks);
+		}
+
+		foreach ($tasks as $taskName) {
+			$task = Inflector::underscore($taskName);
+			$taskClass = Inflector::camelize($taskName . 'Task');
+
+			if (!class_exists($taskClass)) {
+				foreach ($this->Dispatch->shellPaths as $path) {
+					$taskPath = $path . 'tasks' . DS . $task.'.php';
+					if (file_exists($taskPath)) {
+						require_once $taskPath;
+						break;
+					}
+				}
+			}
+			if (ClassRegistry::isKeySet($taskClass)) {
+				$this->taskNames[] = $taskName;
+				if (!PHP5) {
+					$this->{$taskName} =& ClassRegistry::getObject($taskClass);
+				} else {
+					$this->{$taskName} = ClassRegistry::getObject($taskClass);
+				}
+			} else {
+				$this->taskNames[] = $taskName;
+				if (!PHP5) {
+					$this->{$taskName} =& new $taskClass($this->Dispatch);
+				} else {
+					$this->{$taskName} = new $taskClass($this->Dispatch);
+				}
+			}
+
+			if (!isset($this->{$taskName})) {
+				$this->err("Task '".$taskName."' could not be loaded");
+				$this->_stop();
+			}
+		}
+
+		return true;
+	}
+/**
+ * Prompts the user for input, and returns it.
+ *
+ * @param string $prompt Prompt text.
+ * @param mixed $options Array or string of options.
+ * @param string $default Default input value.
+ * @return Either the default value, or the user-provided input.
+ * @access public
+ */
+	function in($prompt, $options = null, $default = null) {
+		if (!$this->interactive) {
+			return $default;
+		}
+		$in = $this->Dispatch->getInput($prompt, $options, $default);
+
+		if ($options && is_string($options)) {
+			if (strpos($options, ',')) {
+				$options = explode(',', $options);
+			} elseif (strpos($options, '/')) {
+				$options = explode('/', $options);
+			} else {
+				$options = array($options);
+			}
+		}
+		if (is_array($options)) {
+			while ($in == '' || ($in && (!in_array(low($in), $options) && !in_array(up($in), $options)) && !in_array($in, $options))) {
+				$in = $this->Dispatch->getInput($prompt, $options, $default);
+			}
+		}
+		if ($in) {
+			return $in;
+		}
+	}
+/**
+ * Outputs to the stdout filehandle.
+ *
+ * @param string $string String to output.
+ * @param boolean $newline If true, the outputs gets an added newline.
+ * @access public
+ */
+	function out($string, $newline = true) {
+		if (is_array($string)) {
+			$str = '';
+			foreach ($string as $message) {
+				$str .= $message ."\n";
+			}
+			$string = $str;
+		}
+		return $this->Dispatch->stdout($string, $newline);
+	}
+/**
+ * Outputs to the stderr filehandle.
+ *
+ * @param string $string Error text to output.
+ * @access public
+ */
+	function err($string) {
+		if (is_array($string)) {
+			$str = '';
+			foreach ($string as $message) {
+				$str .= $message ."\n";
+			}
+			$string = $str;
+		}
+		return $this->Dispatch->stderr($string."\n");
+	}
+/**
+ * Outputs a series of minus characters to the standard output, acts as a visual separator.
+ *
+ * @param boolean $newline If true, the outputs gets an added newline.
+ * @access public
+ */
+	function hr($newline = false) {
+		if ($newline) {
+			$this->out("\n");
+		}
+		$this->out('---------------------------------------------------------------');
+		if ($newline) {
+			$this->out("\n");
+		}
+	}
+/**
+ * Displays a formatted error message and exits the application
+ *
+ * @param string $title Title of the error message
+ * @param string $msg Error message
+ * @access public
+ */
+	function error($title, $msg) {
+		$out  = "$title\n";
+		$out .= "$msg\n";
+		$out .= "\n";
+		$this->err($out);
+		$this->_stop();
+	}
+/**
+ * Will check the number args matches otherwise throw an error
+ *
+ * @param integer $expectedNum Expected number of paramters
+ * @param string $command Command
+ * @access protected
+ */
+	function _checkArgs($expectedNum, $command = null) {
+		if (!$command) {
+			$command = $this->command;
+		}
+		if (count($this->args) < $expectedNum) {
+			$this->error("Wrong number of parameters: ".count($this->args), "Expected: {$expectedNum}\nPlease type 'cake {$this->shell} help' for help on usage of the {$this->name} {$command}");
+		}
+	}
+/**
+ * Creates a file at given path
+ *
+ * @param string $path Where to put the file.
+ * @param string $contents Content to put in the file.
+ * @return boolean Success
+ * @access public
+ */
+	function createFile ($path, $contents) {
+		$path = str_replace(DS . DS, DS, $path);
+		$this->out("\n" . sprintf(__("Creating file %s", true), $path));
+		if (is_file($path) && $this->interactive === true) {
+			$key = $this->in(__("File exists, overwrite?", true). " {$path}",  array('y', 'n', 'q'), 'n');
+			if (low($key) == 'q') {
+				$this->out(__("Quitting.", true) ."\n");
+				exit;
+			} elseif (low($key) != 'y') {
+				$this->out(__("Skip", true) ." {$path}\n");
+				return false;
+			}
+		}
+		if (!class_exists('File')) {
+			uses('file');
+		}
+
+		if ($File = new File($path, true)) {
+			$data = $File->prepare($contents);
+			$File->write($data);
+			$this->out(__("Wrote", true) ." {$path}");
+			return true;
+		} else {
+			$this->err(__("Error! Could not write to", true)." {$path}.\n");
+			return false;
+		}
+	}
+/**
+ * Outputs usage text on the standard output. Implement it in subclasses.
+ *
+ * @access public
+ */
+	function help() {
+		if ($this->command != null) {
+			$this->err("Unknown {$this->name} command '$this->command'.\nFor usage, try 'cake {$this->shell} help'.\n\n");
+		} else {
+			$this->Dispatch->help();
+		}
+	}
+/**
+ * Action to create a Unit Test
+ *
+ * @return boolean Success
+ * @access protected
+ */
+	function _checkUnitTest() {
+		if (App::import('vendor', 'simpletest' . DS . 'simpletest')) {
+			return true;
+		}
+		$unitTest = $this->in('Cake test suite not installed.  Do you want to bake unit test files anyway?', array('y','n'), 'y');
+		$result = low($unitTest) == 'y' || low($unitTest) == 'yes';
+
+		if ($result) {
+			$this->out("\nYou can download the Cake test suite from http://cakeforge.org/projects/testsuite/", true);
+		}
+		return $result;
+	}
+/**
+ * Makes absolute file path easier to read
+ *
+ * @param string $file Absolute file path
+ * @return sting short path
+ * @access public
+ */
+	function shortPath($file) {
+		$shortPath = str_replace(ROOT, null, $file);
+		$shortPath = str_replace('..'.DS, '', $shortPath);
+		return r(DS.DS, DS, $shortPath);
+	}
+/**
+ * Checks for Configure::read('Routing.admin') and forces user to input it if not enabled
+ *
+ * @return string Admin route to use
+ * @access public
+ */
+	function getAdmin() {
+		$admin = '';
+		$cakeAdmin = null;
+		$adminRoute = Configure::read('Routing.admin');
+		if (!empty($adminRoute)) {
+			$cakeAdmin = $adminRoute . '_';
+		} else {
+			$this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.');
+			$this->out('What would you like the admin route to be?');
+			$this->out('Example: www.example.com/admin/controller');
+			while ($admin == '') {
+				$admin = $this->in("What would you like the admin route to be?", null, 'admin');
+			}
+			if ($this->Project->cakeAdmin($admin) !== true) {
+				$this->out('Unable to write to /app/config/core.php.');
+				$this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.');
+				$this->_stop();
+			} else {
+				$cakeAdmin = $admin . '_';
+			}
+		}
+		return $cakeAdmin;
+	}
+/**
+ * Creates the proper controller path for the specified controller class name
+ *
+ * @param string $name Controller class name
+ * @return string Path to controller
+ * @access protected
+ */
+	function _controllerPath($name) {
+		return low(Inflector::underscore($name));
+	}
+/**
+ * Creates the proper controller plural name for the specified controller class name
+ *
+ * @param string $name Controller class name
+ * @return string Controller plural name
+ * @access protected
+ */
+	function _controllerName($name) {
+		return Inflector::pluralize(Inflector::camelize($name));
+	}
+/**
+ * Creates the proper controller camelized name (singularized) for the specified name
+ *
+ * @param string $name Name
+ * @return string Camelized and singularized controller name
+ * @access protected
+ */
+	function _modelName($name) {
+		return Inflector::camelize(Inflector::singularize($name));
+	}
+/**
+ * Creates the proper singular model key for associations
+ *
+ * @param string $name Controller class name
+ * @return string Singular model key
+ * @access protected
+ */
+	function _modelKey($name) {
+		return Inflector::underscore(Inflector::singularize($name)).'_id';
+	}
+/**
+ * Creates the proper model name from a foreign key
+ *
+ * @param string $key Foreign key
+ * @return string Model name
+ * @access protected
+ */
+	function _modelNameFromKey($key) {
+		$name = str_replace('_id', '',$key);
+		return Inflector::camelize($name);
+	}
+/**
+ * creates the singular name for use in views.
+ *
+ * @param string $name
+ * @return string $name
+ * @access protected
+ */
+	function _singularName($name) {
+		return Inflector::variable(Inflector::singularize($name));
+	}
+/**
+ * Creates the plural name for views
+ *
+ * @param string $name Name to use
+ * @return string Plural name for views
+ * @access protected
+ */
+	function _pluralName($name) {
+		return Inflector::variable(Inflector::pluralize($name));
+	}
+/**
+ * Creates the singular human name used in views
+ *
+ * @param string $name Controller name
+ * @return string Singular human name
+ * @access protected
+ */
+	function _singularHumanName($name) {
+		return Inflector::humanize(Inflector::underscore(Inflector::singularize($name)));
+	}
+/**
+ * Creates the plural human name used in views
+ *
+ * @param string $name Controller name
+ * @return string Plural human name
+ * @access protected
+ */
+	function _pluralHumanName($name) {
+		return Inflector::humanize(Inflector::underscore(Inflector::pluralize($name)));
+	}
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/views/view.ctp
===================================================================
--- cake/console/libs/templates/views/view.ctp	(revision 0)
+++ cake/console/libs/templates/views/view.ctp	(revision 0)
@@ -0,0 +1,150 @@
+<?php
+/* SVN FILE: $Id: view.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> view">
+<h2><?php echo "<?php  __('{$singularHumanName}');?>";?></h2>
+	<dl><?php echo "<?php \$i = 0; \$class = ' class=\"altrow\"';?>\n";?>
+<?php
+foreach ($fields as $field) {
+	$isKey = false;
+	if (!empty($associations['belongsTo'])) {
+		foreach ($associations['belongsTo'] as $alias => $details) {
+			if ($field === $details['foreignKey']) {
+				$isKey = true;
+				echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('".Inflector::humanize(Inflector::underscore($alias))."'); ?></dt>\n";
+				echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t\t\t<?php echo \$html->link(\${$singularVar}['{$alias}']['{$details['displayField']}'], array('controller'=> '{$details['controller']}', 'action'=>'view', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?>\n\t\t\t&nbsp;\n\t\t</dd>\n";
+				break;
+			}
+		}
+	}
+	if ($isKey !== true) {
+		echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('".Inflector::humanize($field)."'); ?></dt>\n";
+		echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t\t\t<?php echo \${$singularVar}['{$modelClass}']['{$field}']; ?>\n\t\t\t&nbsp;\n\t\t</dd>\n";
+	}
+}
+?>
+	</dl>
+</div>
+<div class="actions">
+	<ul>
+<?php
+	echo "\t\t<li><?php echo \$html->link(__('Edit {$singularHumanName}', true), array('action'=>'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> </li>\n";
+	echo "\t\t<li><?php echo \$html->link(__('Delete {$singularHumanName}', true), array('action'=>'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> </li>\n";
+	echo "\t\t<li><?php echo \$html->link(__('List {$pluralHumanName}', true), array('action'=>'index')); ?> </li>\n";
+	echo "\t\t<li><?php echo \$html->link(__('New {$singularHumanName}', true), array('action'=>'add')); ?> </li>\n";
+
+	$done = array();
+	foreach ($associations as $type => $data) {
+		foreach ($data as $alias => $details) {
+			if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) {
+				echo "\t\t<li><?php echo \$html->link(__('List ".Inflector::humanize($details['controller'])."', true), array('controller'=> '{$details['controller']}', 'action'=>'index')); ?> </li>\n";
+				echo "\t\t<li><?php echo \$html->link(__('New ".Inflector::humanize(Inflector::underscore($alias))."', true), array('controller'=> '{$details['controller']}', 'action'=>'add')); ?> </li>\n";
+				$done[] = $details['controller'];
+			}
+		}
+	}
+?>
+	</ul>
+</div>
+<?php
+if (!empty($associations['hasOne'])) :
+	foreach ($associations['hasOne'] as $alias => $details): ?>
+	<div class="related">
+		<h3><?php echo "<?php  __('Related ".Inflector::humanize($details['controller'])."');?>";?></h3>
+	<?php echo "<?php if (!empty(\${$singularVar}['{$alias}'])):?>\n";?>
+		<dl><?php echo "\t<?php \$i = 0; \$class = ' class=\"altrow\"';?>\n";?>
+	<?php
+			foreach ($details['fields'] as $field) {
+				echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('".Inflector::humanize($field)."');?></dt>\n";
+				echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t<?php echo \${$singularVar}['{$alias}']['{$field}'];?>\n&nbsp;</dd>\n";
+			}
+	?>
+		</dl>
+	<?php echo "<?php endif; ?>\n";?>
+		<div class="actions">
+			<ul>
+				<li><?php echo "<?php echo \$html->link(__('Edit ".Inflector::humanize(Inflector::underscore($alias))."', true), array('controller'=> '{$details['controller']}', 'action'=>'edit', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?></li>\n";?>
+			</ul>
+		</div>
+	</div>
+	<?php
+	endforeach;
+endif;
+if (empty($associations['hasMany'])) {
+	$associations['hasMany'] = array();
+}
+if (empty($associations['hasAndBelongsToMany'])) {
+	$associations['hasAndBelongsToMany'] = array();
+}
+$relations = array_merge($associations['hasMany'], $associations['hasAndBelongsToMany']);
+$i = 0;
+foreach ($relations as $alias => $details):
+	$otherSingularVar = Inflector::variable($alias);
+	$otherPluralHumanName = Inflector::humanize($details['controller']);
+	?>
+<div class="related">
+	<h3><?php echo "<?php __('Related {$otherPluralHumanName}');?>";?></h3>
+	<?php echo "<?php if (!empty(\${$singularVar}['{$alias}'])):?>\n";?>
+	<table cellpadding = "0" cellspacing = "0">
+	<tr>
+<?php
+			foreach ($details['fields'] as $field) {
+				echo "\t\t<th><?php __('".Inflector::humanize($field)."'); ?></th>\n";
+			}
+?>
+		<th class="actions"><?php echo "<?php __('Actions');?>";?></th>
+	</tr>
+<?php
+echo "\t<?php
+		\$i = 0;
+		foreach (\${$singularVar}['{$alias}'] as \${$otherSingularVar}):
+			\$class = null;
+			if (\$i++ % 2 == 0) {
+				\$class = ' class=\"altrow\"';
+			}
+		?>\n";
+		echo "\t\t<tr<?php echo \$class;?>>\n";
+
+				foreach ($details['fields'] as $field) {
+					echo "\t\t\t<td><?php echo \${$otherSingularVar}['{$field}'];?></td>\n";
+				}
+
+				echo "\t\t\t<td class=\"actions\">\n";
+				echo "\t\t\t\t<?php echo \$html->link(__('View', true), array('controller'=> '{$details['controller']}', 'action'=>'view', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n";
+				echo "\t\t\t\t<?php echo \$html->link(__('Edit', true), array('controller'=> '{$details['controller']}', 'action'=>'edit', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n";
+				echo "\t\t\t\t<?php echo \$html->link(__('Delete', true), array('controller'=> '{$details['controller']}', 'action'=>'delete', \${$otherSingularVar}['{$details['primaryKey']}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n";
+				echo "\t\t\t</td>\n";
+			echo "\t\t</tr>\n";
+
+echo "\t<?php endforeach; ?>\n";
+?>
+	</table>
+<?php echo "<?php endif; ?>\n\n";?>
+	<div class="actions">
+		<ul>
+			<li><?php echo "<?php echo \$html->link(__('New ".Inflector::humanize(Inflector::underscore($alias))."', true), array('controller'=> '{$details['controller']}', 'action'=>'add'));?>";?> </li>
+		</ul>
+	</div>
+</div>
+<?php endforeach;?>
\ No newline at end of file
Index: cake/console/libs/templates/views/form.ctp
===================================================================
--- cake/console/libs/templates/views/form.ctp	(revision 0)
+++ cake/console/libs/templates/views/form.ctp	(revision 0)
@@ -0,0 +1,69 @@
+<?php
+/* SVN FILE: $Id: form.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> form">
+<?php echo "<?php echo \$form->create('{$modelClass}');?>\n";?>
+	<fieldset>
+ 		<legend><?php echo "<?php __('".Inflector::humanize($action)." {$singularHumanName}');?>";?></legend>
+<?php
+		echo "\t<?php\n";
+		foreach ($fields as $field) {
+			if ($action == 'add' && $field == $primaryKey) {
+				continue;
+			} elseif (!in_array($field, array('created', 'modified', 'updated'))) {
+				echo "\t\techo \$form->input('{$field}');\n";
+			}
+		}
+		if (!empty($associations['hasAndBelongsToMany'])) {
+			foreach ($associations['hasAndBelongsToMany'] as $assocName => $assocData) {
+				echo "\t\techo \$form->input('{$assocName}');\n";
+			}
+		}
+		echo "\t?>\n";
+?>
+	</fieldset>
+<?php
+	echo "<?php echo \$form->end('Submit');?>\n";
+?>
+</div>
+<div class="actions">
+	<ul>
+<?php if ($action != 'add'):?>
+		<li><?php echo "<?php echo \$html->link(__('Delete', true), array('action'=>'delete', \$form->value('{$modelClass}.{$primaryKey}')), null, sprintf(__('Are you sure you want to delete # %s?', true), \$form->value('{$modelClass}.{$primaryKey}'))); ?>";?></li>
+<?php endif;?>
+		<li><?php echo "<?php echo \$html->link(__('List {$pluralHumanName}', true), array('action'=>'index'));?>";?></li>
+<?php
+		$done = array();
+		foreach ($associations as $type => $data) {
+			foreach ($data as $alias => $details) {
+				if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) {
+					echo "\t\t<li><?php echo \$html->link(__('List ".Inflector::humanize($details['controller'])."', true), array('controller'=> '{$details['controller']}', 'action'=>'index')); ?> </li>\n";
+					echo "\t\t<li><?php echo \$html->link(__('New ".Inflector::humanize(Inflector::underscore($alias))."', true), array('controller'=> '{$details['controller']}', 'action'=>'add')); ?> </li>\n";
+					$done[] = $details['controller'];
+				}
+			}
+		}
+?>
+	</ul>
+</div>
Index: cake/console/libs/templates/views/index.ctp
===================================================================
--- cake/console/libs/templates/views/index.ctp	(revision 0)
+++ cake/console/libs/templates/views/index.ctp	(revision 0)
@@ -0,0 +1,99 @@
+<?php
+/* SVN FILE: $Id: index.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> index">
+<h2><?php echo "<?php __('{$pluralHumanName}');?>";?></h2>
+<p>
+<?php echo "<?php
+echo \$paginator->counter(array(
+'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
+));
+?>";?>
+</p>
+<table cellpadding="0" cellspacing="0">
+<tr>
+<?php  foreach ($fields as $field):?>
+	<th><?php echo "<?php echo \$paginator->sort('{$field}');?>";?></th>
+<?php endforeach;?>
+	<th class="actions"><?php echo "<?php __('Actions');?>";?></th>
+</tr>
+<?php
+echo "<?php
+\$i = 0;
+foreach (\${$pluralVar} as \${$singularVar}):
+	\$class = null;
+	if (\$i++ % 2 == 0) {
+		\$class = ' class=\"altrow\"';
+	}
+?>\n";
+	echo "\t<tr<?php echo \$class;?>>\n";
+		foreach ($fields as $field) {
+			$isKey = false;
+			if (!empty($associations['belongsTo'])) {
+				foreach ($associations['belongsTo'] as $alias => $details) {
+					if ($field === $details['foreignKey']) {
+						$isKey = true;
+						echo "\t\t<td>\n\t\t\t<?php echo \$html->link(\${$singularVar}['{$alias}']['{$details['displayField']}'], array('controller'=> '{$details['controller']}', 'action'=>'view', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?>\n\t\t</td>\n";
+						break;
+					}
+				}
+			}
+			if ($isKey !== true) {
+				echo "\t\t<td>\n\t\t\t<?php echo \${$singularVar}['{$modelClass}']['{$field}']; ?>\n\t\t</td>\n";
+			}
+		}
+
+		echo "\t\t<td class=\"actions\">\n";
+		echo "\t\t\t<?php echo \$html->link(__('View', true), array('action'=>'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
+	 	echo "\t\t\t<?php echo \$html->link(__('Edit', true), array('action'=>'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
+	 	echo "\t\t\t<?php echo \$html->link(__('Delete', true), array('action'=>'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
+		echo "\t\t</td>\n";
+	echo "\t</tr>\n";
+
+echo "<?php endforeach; ?>\n";
+?>
+</table>
+</div>
+<div class="paging">
+<?php echo "\t<?php echo \$paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>\n";?>
+ | <?php echo "\t<?php echo \$paginator->numbers();?>\n"?>
+<?php echo "\t<?php echo \$paginator->next(__('next', true).' >>', array(), null, array('class'=>'disabled'));?>\n";?>
+</div>
+<div class="actions">
+	<ul>
+		<li><?php echo "<?php echo \$html->link(__('New {$singularHumanName}', true), array('action'=>'add')); ?>";?></li>
+<?php
+	$done = array();
+	foreach ($associations as $type => $data) {
+		foreach ($data as $alias => $details) {
+			if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) {
+				echo "\t\t<li><?php echo \$html->link(__('List ".Inflector::humanize($details['controller'])."', true), array('controller'=> '{$details['controller']}', 'action'=>'index')); ?> </li>\n";
+				echo "\t\t<li><?php echo \$html->link(__('New ".Inflector::humanize(Inflector::underscore($alias))."', true), array('controller'=> '{$details['controller']}', 'action'=>'add')); ?> </li>\n";
+				$done[] = $details['controller'];
+			}
+		}
+	}
+?>
+	</ul>
+</div>
Index: cake/console/libs/templates/views/home.ctp
===================================================================
--- cake/console/libs/templates/views/home.ctp	(revision 0)
+++ cake/console/libs/templates/views/home.ctp	(revision 0)
@@ -0,0 +1,82 @@
+<?php
+$output = "<h2>Sweet, \"".Inflector::humanize($app)."\" got Baked by CakePHP!</h2>\n";
+$output .="
+<?php
+if (Configure::read() > 0):
+	Debugger::checkSessionKey();
+endif;
+?>
+<p>
+<?php
+	if (is_writable(TMP)):
+		echo '<span class=\"notice success\">';
+			__('Your tmp directory is writable.');
+		echo '</span>';
+	else:
+		echo '<span class=\"notice\">';
+			__('Your tmp directory is NOT writable.');
+		echo '</span>';
+	endif;
+?>
+</p>
+<p>
+<?php
+	\$settings = Cache::settings();
+	if (!empty(\$settings)):
+		echo '<span class=\"notice success\">';
+				echo sprintf(__('The %s is being used for caching. To change the config edit APP/config/core.php ', true), '<em>'. \$settings['engine'] . 'Engine</em>');
+		echo '</span>';
+	else:
+		echo '<span class=\"notice\">';
+				__('Your cache is NOT working. Please check the settings in APP/config/core.php');
+		echo '</span>';
+	endif;
+?>
+</p>
+<p>
+<?php
+	\$filePresent = null;
+	if (file_exists(CONFIGS . 'database.php')):
+		echo '<span class=\"notice success\">';
+			__('Your database configuration file is present.');
+			\$filePresent = true;
+		echo '</span>';
+	else:
+		echo '<span class=\"notice\">';
+			__('Your database configuration file is NOT present.');
+			echo '<br/>';
+			__('Rename config/database.php.default to config/database.php');
+		echo '</span>';
+	endif;
+?>
+</p>
+<?php
+if (!empty(\$filePresent)):
+ 	uses('model' . DS . 'connection_manager');
+	\$db = ConnectionManager::getInstance();
+ 	\$connected = \$db->getDataSource('default');
+?>
+<p>
+<?php
+	if (\$connected->isConnected()):
+		echo '<span class=\"notice success\">';
+ 			__('Cake is able to connect to the database.');
+		echo '</span>';
+	else:
+		echo '<span class=\"notice\">';
+			__('Cake is NOT able to connect to the database.');
+		echo '</span>';
+	endif;
+?>
+</p>\n";
+$output .= "<?php endif;?>\n";
+$output .= "<h3><?php __('Editing this Page') ?></h3>\n";
+$output .= "<p>\n";
+$output .= "<?php\n";
+$output .= "\techo sprintf(__('To change the content of this page, edit: %s\n";
+$output .= "\t\tTo change its layout, edit: %s\n";
+$output .= "\t\tYou can also add some CSS styles for your pages at: %s', true),\n";
+$output .= "\t\tAPP . 'views' . DS . 'pages' . DS . 'home.ctp.<br />',  APP . 'views' . DS . 'layouts' . DS . 'default.ctp.<br />', APP . 'webroot' . DS . 'css');\n";
+$output .= "?>\n";
+$output .= "</p>\n";
+?>
Index: cake/console/libs/templates/skel/vendors/shells/tasks/empty
===================================================================
Index: cake/console/libs/templates/skel/vendors/shells/templates/empty
===================================================================
Index: cake/console/libs/templates/skel/plugins/empty
===================================================================
Index: cake/console/libs/templates/skel/webroot/test.php
===================================================================
--- cake/console/libs/templates/skel/webroot/test.php	(revision 0)
+++ cake/console/libs/templates/skel/webroot/test.php	(revision 0)
@@ -0,0 +1,181 @@
+<?php
+/* SVN FILE: $Id: test.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+error_reporting(E_ALL);
+set_time_limit(0);
+ini_set('memory_limit','128M');
+ini_set('display_errors', 1);
+/**
+ * Use the DS to separate the directories in other defines
+ */
+	if (!defined('DS')) {
+		define('DS', DIRECTORY_SEPARATOR);
+	}
+/**
+ * These defines should only be edited if you have cake installed in
+ * a directory layout other than the way it is distributed.
+ * When using custom settings be sure to use the DS and do not add a trailing DS.
+ */
+
+/**
+ * The full path to the directory which holds "app", WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('ROOT')) {
+		define('ROOT', dirname(dirname(dirname(__FILE__))));
+	}
+/**
+ * The actual directory name for the "app".
+ *
+ */
+	if (!defined('APP_DIR')) {
+		define('APP_DIR', basename(dirname(dirname(__FILE__))));
+	}
+/**
+ * The absolute path to the "cake" directory, WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+
+/**
+ * Editing below this line should not be necessary.
+ * Change at your own risk.
+ *
+ */
+if (!defined('WEBROOT_DIR')) {
+	define('WEBROOT_DIR', basename(dirname(__FILE__)));
+}
+if (!defined('WWW_ROOT')) {
+	define('WWW_ROOT', dirname(__FILE__) . DS);
+}
+if (!defined('CORE_PATH')) {
+	if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
+		define('APP_PATH', null);
+		define('CORE_PATH', null);
+	} else {
+		define('APP_PATH', ROOT . DS . APP_DIR . DS);
+		define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+	}
+}
+if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
+	trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
+}
+
+$corePath = Configure::corePaths('cake');
+if (isset($corePath[0])) {
+	define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS);
+} else {
+	define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH);
+}
+
+require_once CAKE_TESTS_LIB . 'test_manager.php';
+
+if (Configure::read('debug') < 1) {
+	die(__('Debug setting does not allow access to this url.', true));
+}
+
+if (!isset($_SERVER['SERVER_NAME'])) {
+	$_SERVER['SERVER_NAME'] = '';
+}
+if (empty( $_GET['output'])) {
+	$_GET['output'] = 'html';
+}
+/**
+ *
+ * Used to determine output to display
+ */
+define('CAKE_TEST_OUTPUT_HTML', 1);
+define('CAKE_TEST_OUTPUT_TEXT', 2);
+
+if (isset($_GET['output']) && $_GET['output'] == 'html') {
+	define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_HTML);
+} else {
+	Debugger::output('txt');
+	define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_TEXT);
+}
+
+if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
+	CakePHPTestHeader();
+	include CAKE_TESTS_LIB . 'simpletest.php';
+	CakePHPTestSuiteFooter();
+	exit();
+}
+
+$analyzeCodeCoverage = false;
+if (isset($_GET['code_coverage'])) {
+	$analyzeCodeCoverage = true;
+	require_once CAKE_TESTS_LIB . 'code_coverage_manager.php';
+	if (!extension_loaded('xdebug')) {
+		CakePHPTestHeader();
+		include CAKE_TESTS_LIB . 'xdebug.php';
+		CakePHPTestSuiteFooter();
+		exit();
+	}
+}
+
+CakePHPTestHeader();
+CakePHPTestSuiteHeader();
+define('RUN_TEST_LINK', $_SERVER['PHP_SELF']);
+
+if (isset($_GET['group'])) {
+	if ('all' == $_GET['group']) {
+		TestManager::runAllTests(CakeTestsGetReporter());
+	} else {
+		if ($analyzeCodeCoverage) {
+			CodeCoverageManager::start($_GET['group'], CakeTestsGetReporter());
+		}
+		TestManager::runGroupTest(ucfirst($_GET['group']), CakeTestsGetReporter());
+		if ($analyzeCodeCoverage) {
+			CodeCoverageManager::report();
+		}
+	}
+
+	CakePHPTestRunMore();
+	CakePHPTestAnalyzeCodeCoverage();
+} elseif (isset($_GET['case'])) {
+	if ($analyzeCodeCoverage) {
+		CodeCoverageManager::start($_GET['case'], CakeTestsGetReporter());
+	}
+
+	TestManager::runTestCase($_GET['case'], CakeTestsGetReporter());
+
+	if ($analyzeCodeCoverage) {
+		CodeCoverageManager::report();
+	}
+
+	CakePHPTestRunMore();
+	CakePHPTestAnalyzeCodeCoverage();
+} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') {
+	CakePHPTestCaseList();
+} else {
+	CakePHPTestGroupTestList();
+}
+CakePHPTestSuiteFooter();
+$output = ob_get_clean();
+echo $output;
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/css/cake.generic.css
===================================================================
--- cake/console/libs/templates/skel/webroot/css/cake.generic.css	(revision 0)
+++ cake/console/libs/templates/skel/webroot/css/cake.generic.css	(revision 0)
@@ -0,0 +1,480 @@
+/* SVN FILE: $Id: cake.generic.css 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.css
+ * @since         CakePHP(tm)
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+
+* {
+	margin:0;
+	padding:0;
+}
+
+/* General Style Info */
+body {
+	background: #003d4c;
+	color: #fff;
+	font-family:'lucida grande',verdana,helvetica,arial,sans-serif;
+	font-size:90%;
+	margin: 0;
+}
+a {
+	background:#fff;
+	color: #003d4c;
+	text-decoration: underline;
+	font-weight: bold;
+}
+a:hover {
+	background:#fff;
+	color: #003d4c;
+	text-decoration:none;
+}
+a img {
+	border:none;
+}
+h1, h2, h3, h4 {
+	font-weight: normal;
+}
+h1 {
+	background:#fff;
+	color: #003d4c;
+	font-size: 100%;
+	margin: 0.1em 0;
+}
+h2 {
+	background:#fff;
+	color: #e32;
+	font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
+	font-size: 190%;
+	margin: 0.3em 0;
+	padding-top: 0.8em;
+}
+h3 {
+	color: #993;
+	font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
+	font-size: 165%;
+	padding-top: 1.5em;
+}
+h4 {
+	color: #993;
+	font-weight: normal;
+	padding-top: 0.5em;
+}
+ul, li {
+	margin: 0 12px;
+}
+
+/* Layout */
+#container {
+	text-align: left;
+}
+
+#header{
+	padding: 10px 20px;
+}
+#header h1 {
+	background: #003d4c url('../img/cake.icon.gif') no-repeat left;
+	color: #fff;
+	padding: 0px 30px;
+}
+#header h1 a {
+	color: #fff;
+	background: #003d4c;
+	font-weight: normal;
+	text-decoration: none;
+}
+#header h1 a:hover {
+	color: #fff;
+	background: #003d4c;
+	text-decoration: underline;
+}
+#content{
+	background: #fff;
+	clear: both;
+	color: #333;
+	padding: 10px 20px 40px 20px;
+	overflow: auto;
+}
+#footer {
+	clear: both;
+	padding: 6px 10px;
+	text-align: right;
+}
+
+/* Tables */
+table {
+	background: #fff;
+	border:1px solid #ccc;
+	border-right:0;
+	clear: both;
+	color: #333;
+	margin-bottom: 10px;
+	width: 100%;
+}
+th {
+	background: #f2f2f2;
+	border:1px solid #bbb;
+	border-top: 1px solid #fff;
+	border-left: 1px solid #fff;
+	text-align: center;
+}
+th a {
+	background:#f2f2f2;
+	display: block;
+	padding: 2px 4px;
+	text-decoration: none;
+}
+th a:hover {
+	background: #ccc;
+	color: #333;
+	text-decoration: none;
+}
+table tr td {
+	background: #fff;
+	border-right: 1px solid #ccc;
+	padding: 4px;
+	text-align: center;
+	vertical-align: top;
+}
+table tr.altrow td {
+	background: #f4f4f4;
+}
+td.actions {
+	text-align: center;
+	white-space: nowrap;
+}
+td.actions a {
+	margin: 0px 6px;
+}
+.cake-sql-log table {
+	background: #f4f4f4;
+}
+.cake-sql-log td {
+	padding: 4px 8px;
+	text-align: left;
+}
+
+/* Paging */
+div.paging {
+	background:#fff;
+	color: #ccc;
+	margin-bottom: 2em;
+}
+div.paging div.disabled {
+	color: #ddd;
+	display: inline;
+}
+div.paging span {
+}
+div.paging span.current {
+	color: #000;
+}
+div.paging span a {
+}
+
+/* Scaffold View */
+dl {
+	line-height: 2em;
+	margin: 0em 0em;
+	width: 60%;
+}
+dl.altrow {
+	background: #f4f4f4;
+}
+dt {
+	font-weight: bold;
+	padding-left: 4px;
+	vertical-align: top;
+}
+dd {
+	margin-left: 10em;
+	margin-top: -2em;
+	vertical-align: top;
+}
+
+/* Forms */
+form {
+	clear: both;
+	margin-right: 20px;
+	padding: 0;
+	width: 80%;
+}
+fieldset {
+	border: 1px solid #ccc;
+	margin-top: 30px;
+	padding: 16px 20px;
+}
+fieldset legend {
+	background:#fff;
+	color: #e32;
+	font-size: 160%;
+	font-weight: bold;
+}
+fieldset fieldset {
+	margin-top: 0px;
+	margin-bottom: 20px;
+	padding: 16px 10px;
+}
+fieldset fieldset legend {
+	font-size: 120%;
+	font-weight: normal;
+}
+fieldset fieldset div {
+	clear: left;
+	margin: 0 20px;
+}
+form div {
+	clear: both;
+	margin-bottom: 1em;
+	padding: .5em;
+	vertical-align: text-top;
+}
+form div.input {
+	color: #444;
+}
+form div.required {
+	color: #333;
+	font-weight: bold;
+}
+form div.submit {
+	border: 0;
+	clear: both;
+	margin-top: 10px;
+	margin-left: 140px;
+}
+label {
+	display: block;
+	font-size: 110%;
+	padding-right: 20px;
+}
+input, textarea {
+	clear: both;
+	font-size: 140%;
+	font-family: "frutiger linotype", "lucida grande", "verdana", sans-serif;
+	padding: 2px;
+	width: 100%;
+}
+select {
+	clear: both;
+	font-size: 120%;
+	vertical-align: text-bottom;
+}
+select[multiple=multiple] {
+	width: 100%;
+}
+option {
+	font-size: 120%;
+	padding: 0 3px;
+}
+input[type=checkbox] {
+	clear: left;
+	float: left;
+	margin: 0px 6px 7px 2px;
+	width: auto;
+}
+input[type=radio] {
+	float:left;
+	width:auto;
+	margin: 0 3px 7px 0;
+}
+div.radio label {
+	margin: 0 0 6px 20px;
+}
+input[type=submit] {
+	display: inline;
+	font-size: 110%;
+	padding: 2px 5px;
+	width: auto;
+	vertical-align: bottom;
+}
+
+/* Notices and Errors */
+div.message {
+	clear: both;
+	color: #900;
+	font-size: 140%;
+	font-weight: bold;
+	margin: 1em 0;
+}
+div.error-message {
+	clear: both;
+	color: #900;
+	font-weight: bold;
+}
+p.error {
+	background-color: #e32;
+	color: #fff;
+	font-family: Courier, monospace;
+	font-size: 120%;
+	line-height: 140%;
+	padding: 0.8em;
+	margin: 1em 0;
+}
+p.error em {
+	color: #000;
+	font-weight: normal;
+	line-height: 140%;
+}
+.notice {
+	background: #ffcc00;
+	color: #000;
+	display: block;
+	font-family: Courier, monospace;
+	font-size: 120%;
+	line-height: 140%;
+	padding: 0.8em;
+	margin: 1em 0;
+}
+.success {
+	background: green;
+	color: #fff;
+}
+
+/*  Actions  */
+div.actions ul {
+	margin: 0px 0;
+	padding: 0;
+}
+div.actions li {
+	display: inline;
+	list-style-type: none;
+	line-height: 2em;
+	margin: 0 2em 0 0;
+	white-space: nowrap;
+}
+div.actions ul li a {
+	background:#fff;
+	color: #003d4c;
+	text-decoration: none;
+}
+div.actions ul li a:hover {
+	color: #333;
+	text-decoration: underline;
+}
+
+/* Related */
+div.related {
+	clear: both;
+	display: block;
+}
+
+/* Debugging */
+pre {
+	color: #000;
+	background: #f0f0f0;
+	padding: 1em;
+}
+pre.cake-debug {
+	background: #ffcc00;
+	font-size: 120%;
+	line-height: 140%;
+	margin-top: 1em;
+	overflow: auto;
+	position: relative;
+}
+div.cake-stack-trace {
+	background: #fff;
+	border: 4px dotted #ffcc00;
+	color: #333;
+	margin: 0px;
+	padding: 6px;
+	font-size: 120%;
+	line-height: 140%;
+	overflow: auto;
+	position: relative;
+}
+div.cake-code-dump pre {
+	position: relative;
+	overflow: auto;
+}
+div.cake-stack-trace pre, div.cake-code-dump pre {
+	color: #000;
+	background-color: #F0F0F0;
+	margin: 0px;
+	padding: 1em;
+	overflow: auto;
+}
+div.cake-code-dump pre, div.cake-code-dump pre code {
+	clear: both;
+	font-size: 12px;
+	line-height: 15px;
+	margin: 4px 2px;
+	padding: 4px;
+	overflow: auto;
+}
+div.cake-code-dump span.code-highlight {
+	background-color: #ff0;
+	padding: 4px;
+}
+div.code-coverage-results div.code-line {
+	padding-left:5px;
+	display:block;
+	margin-left:10px;
+}
+div.code-coverage-results div.uncovered span.content {
+	background:#ecc;
+}
+div.code-coverage-results div.covered span.content {
+	background:#cec;
+}
+div.code-coverage-results div.ignored span.content {
+	color:#aaa;
+}
+div.code-coverage-results span.line-num {
+	color:#666;
+	display:block;
+	float:left;
+	width:20px;
+	text-align:right;
+	margin-right:5px;
+}
+div.code-coverage-results span.line-num strong {
+	color:#666;
+}
+div.code-coverage-results div.start {
+	border:1px solid #aaa;
+	border-width:1px 1px 0px 1px;
+	margin-top:30px;
+	padding-top:5px;
+}
+div.code-coverage-results div.end {
+	border:1px solid #aaa;
+	border-width:0px 1px 1px 1px;
+	margin-bottom:30px;
+	padding-bottom:5px;
+}
+div.code-coverage-results div.realstart {
+	margin-top:0px;
+}
+div.code-coverage-results p.note {
+	color:#bbb;
+	padding:5px;
+	margin:5px 0 10px;
+	font-size:10px;
+}
+div.code-coverage-results span.result-bad {
+	color: #a00;
+}
+div.code-coverage-results span.result-ok {
+	color: #fa0;
+}
+div.code-coverage-results span.result-good {
+	color: #0a0;
+}
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/.htaccess
===================================================================
--- cake/console/libs/templates/skel/webroot/.htaccess	(revision 0)
+++ cake/console/libs/templates/skel/webroot/.htaccess	(revision 0)
@@ -0,0 +1,6 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
+</IfModule>
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/img/cake.power.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: cake/console/libs/templates/skel/webroot/img/cake.power.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: cake/console/libs/templates/skel/webroot/img/cake.icon.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: cake/console/libs/templates/skel/webroot/img/cake.icon.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: cake/console/libs/templates/skel/webroot/js/vendors.php
===================================================================
--- cake/console/libs/templates/skel/webroot/js/vendors.php	(revision 0)
+++ cake/console/libs/templates/skel/webroot/js/vendors.php	(revision 0)
@@ -0,0 +1,42 @@
+<?php
+/* SVN FILE: $Id: vendors.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file includes js vendor-files from /vendor/ directory if they need to
+ * be accessible to the public.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.js
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Enter description here...
+ */
+if (isset($_GET['file'])) {
+	$file = $_GET['file'];
+	$pos = strpos($file, '..');
+	if ($pos === false) {
+		if (is_file('../../vendors/javascript/'.$file) && (preg_match('/(\/.+)\\.js/', $file))) {
+			readfile('../../vendors/javascript/'.$file);
+			return;
+		}
+	}
+}
+header('HTTP/1.1 404 Not Found');
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/index.php
===================================================================
--- cake/console/libs/templates/skel/webroot/index.php	(revision 0)
+++ cake/console/libs/templates/skel/webroot/index.php	(revision 0)
@@ -0,0 +1,93 @@
+<?php
+/* SVN FILE: $Id: index.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Use the DS to separate the directories in other defines
+ */
+	if (!defined('DS')) {
+		define('DS', DIRECTORY_SEPARATOR);
+	}
+/**
+ * These defines should only be edited if you have cake installed in
+ * a directory layout other than the way it is distributed.
+ * When using custom settings be sure to use the DS and do not add a trailing DS.
+ */
+
+/**
+ * The full path to the directory which holds "app", WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('ROOT')) {
+		define('ROOT', dirname(dirname(dirname(__FILE__))));
+	}
+/**
+ * The actual directory name for the "app".
+ *
+ */
+	if (!defined('APP_DIR')) {
+		define('APP_DIR', basename(dirname(dirname(__FILE__))));
+	}
+/**
+ * The absolute path to the "cake" directory, WITHOUT a trailing DS.
+ *
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+
+/**
+ * Editing below this line should not be necessary.
+ * Change at your own risk.
+ *
+ */
+	if (!defined('WEBROOT_DIR')) {
+		define('WEBROOT_DIR', basename(dirname(__FILE__)));
+	}
+	if (!defined('WWW_ROOT')) {
+		define('WWW_ROOT', dirname(__FILE__) . DS);
+	}
+	if (!defined('CORE_PATH')) {
+		if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
+			define('APP_PATH', null);
+			define('CORE_PATH', null);
+		} else {
+			define('APP_PATH', ROOT . DS . APP_DIR . DS);
+			define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+		}
+	}
+	if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
+		trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
+	}
+	if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') {
+		return;
+	} else {
+		$Dispatcher = new Dispatcher();
+		$Dispatcher->dispatch($url);
+	}
+	if (Configure::read() > 0) {
+		echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->";
+	}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/css.php
===================================================================
--- cake/console/libs/templates/skel/webroot/css.php	(revision 0)
+++ cake/console/libs/templates/skel/webroot/css.php	(revision 0)
@@ -0,0 +1,102 @@
+<?php
+/* SVN FILE: $Id: css.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+	header('HTTP/1.1 404 Not Found');
+	exit('File Not Found');
+}
+/**
+ * Enter description here...
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $path
+ * @param unknown_type $name
+ * @return unknown
+ */
+	function make_clean_css($path, $name) {
+		App::import('Vendor', 'csspp' . DS . 'csspp');
+		$data = file_get_contents($path);
+		$csspp = new csspp();
+		$output = $csspp->compress($data);
+		$ratio = 100 - (round(strlen($output) / strlen($data), 3) * 100);
+		$output = " /* file: $name, ratio: $ratio% */ " . $output;
+		return $output;
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $path
+ * @param unknown_type $content
+ * @return unknown
+ */
+	function write_css_cache($path, $content) {
+		if (!is_dir(dirname($path))) {
+			mkdir(dirname($path));
+		}
+		$cache = new File($path);
+		return $cache->write($content);
+	}
+
+	if (preg_match('|\.\.|', $url) || !preg_match('|^ccss/(.+)$|i', $url, $regs)) {
+		die('Wrong file name.');
+	}
+
+	$filename = 'css/' . $regs[1];
+	$filepath = CSS . $regs[1];
+	$cachepath = CACHE . 'css' . DS . str_replace(array('/','\\'), '-', $regs[1]);
+
+	if (!file_exists($filepath)) {
+		die('Wrong file name.');
+	}
+
+	if (file_exists($cachepath)) {
+		$templateModified = filemtime($filepath);
+		$cacheModified = filemtime($cachepath);
+
+		if ($templateModified > $cacheModified) {
+			$output = make_clean_css($filepath, $filename);
+			write_css_cache($cachepath, $output);
+		} else {
+			$output = file_get_contents($cachepath);
+		}
+	} else {
+		$output = make_clean_css($filepath, $filename);
+		write_css_cache($cachepath, $output);
+		$templateModified = time();
+	}
+
+	header("Date: " . date("D, j M Y G:i:s ", $templateModified) . 'GMT');
+	header("Content-Type: text/css");
+	header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
+	header("Cache-Control: max-age=86400, must-revalidate"); // HTTP/1.1
+	header("Pragma: cache");        // HTTP/1.0
+	print $output;
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/webroot/favicon.ico
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: cake/console/libs/templates/skel/webroot/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: cake/console/libs/templates/skel/models/behaviors/empty
===================================================================
Index: cake/console/libs/templates/skel/models/datasources/empty
===================================================================
Index: cake/console/libs/templates/skel/app_model.php
===================================================================
--- cake/console/libs/templates/skel/app_model.php	(revision 0)
+++ cake/console/libs/templates/skel/app_model.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
+
+/**
+ * Application model for Cake.
+ *
+ * This file is application-wide model file. You can put all
+ * application-wide model-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+/**
+ * Application model for Cake.
+ *
+ * Add your application-wide methods in the class below, your models
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.app
+ */
+class AppModel extends Model {
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/index.php
===================================================================
--- cake/console/libs/templates/skel/index.php	(revision 0)
+++ cake/console/libs/templates/skel/index.php	(revision 0)
@@ -0,0 +1,24 @@
+<?php
+/* SVN FILE: $Id: index.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/pages/empty
===================================================================
Index: cake/console/libs/templates/skel/views/layouts/xml/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/xml/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/xml/default.ctp	(revision 0)
@@ -0,0 +1,2 @@
+<?php e($xml->header()); ?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/layouts/flash.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/flash.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/flash.ctp	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: flash.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<?php echo $html->charset(); ?>
+<title><?php echo $page_title; ?></title>
+
+
+<?php if (Configure::read() == 0) { ?>
+<meta http-equiv="Refresh" content="<?php echo $pause?>;url=<?php echo $url?>"/>
+<?php } ?>
+<style><!--
+P { text-align:center; font:bold 1.1em sans-serif }
+A { color:#444; text-decoration:none }
+A:HOVER { text-decoration: underline; color:#44E }
+--></style>
+</head>
+<body>
+<p><a href="<?php echo $url?>"><?php echo $message?></a></p>
+</body>
+</html>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/layouts/email/html/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/email/html/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/email/html/default.ctp	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+
+<html>
+<head>
+	<title><?php echo $title_for_layout;?></title>
+</head>
+
+<body>
+	<?php echo $content_for_layout;?>
+
+	<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>
+</body>
+</html>
Index: cake/console/libs/templates/skel/views/layouts/email/text/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/email/text/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/email/text/default.ctp	(revision 0)
@@ -0,0 +1,29 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+
+<?php echo $content_for_layout;?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.
+
Index: cake/console/libs/templates/skel/views/layouts/rss/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/rss/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/rss/default.ctp	(revision 0)
@@ -0,0 +1,17 @@
+<?php
+echo $rss->header();
+
+if (!isset($channel)) {
+	$channel = array();
+}
+if (!isset($channel['title'])) {
+	$channel['title'] = $title_for_layout;
+}
+
+echo $rss->document(
+	$rss->channel(
+		array(), $channel, $content_for_layout
+	)
+);
+
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/layouts/ajax.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/ajax.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/ajax.ctp	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+/* SVN FILE: $Id: ajax.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/layouts/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/default.ctp	(revision 0)
@@ -0,0 +1,64 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.skel.views.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<?php echo $html->charset(); ?>
+	<title>
+		<?php __('CakePHP: the rapid development php framework:'); ?>
+		<?php echo $title_for_layout; ?>
+	</title>
+	<?php
+		echo $html->meta('icon');
+
+		echo $html->css('cake.generic');
+
+		echo $scripts_for_layout;
+	?>
+</head>
+<body>
+	<div id="container">
+		<div id="header">
+			<h1><?php echo $html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
+		</div>
+		<div id="content">
+
+			<?php $session->flash(); ?>
+
+			<?php echo $content_for_layout; ?>
+
+		</div>
+		<div id="footer">
+			<?php echo $html->link(
+					$html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true), 'border'=>"0")),
+					'http://www.cakephp.org/',
+					array('target'=>'_blank'), null, false
+				);
+			?>
+		</div>
+	</div>
+	<?php echo $cakeDebug; ?>
+</body>
+</html>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/layouts/js/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/layouts/js/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/layouts/js/default.ctp	(revision 0)
@@ -0,0 +1,2 @@
+<?php echo $scripts_for_layout; ?>
+<script type="text/javascript"><?php echo $content_for_layout; ?></script>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/helpers/empty
===================================================================
Index: cake/console/libs/templates/skel/views/scaffolds/empty
===================================================================
Index: cake/console/libs/templates/skel/views/elements/email/html/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/elements/email/html/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/elements/email/html/default.ctp	(revision 0)
@@ -0,0 +1,31 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php
+$content = explode("\n", $content);
+
+foreach ($content as $line):
+	echo '<p> ' . $line . '</p>';
+endforeach;
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/elements/email/text/default.ctp
===================================================================
--- cake/console/libs/templates/skel/views/elements/email/text/default.ctp	(revision 0)
+++ cake/console/libs/templates/skel/views/elements/email/text/default.ctp	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php echo $content; ?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/views/elements/empty
===================================================================
Index: cake/console/libs/templates/skel/views/errors/empty
===================================================================
Index: cake/console/libs/templates/skel/locale/eng/LC_MESSAGES/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/tests/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/cache/persistent/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/cache/models/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/cache/views/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/logs/empty
===================================================================
Index: cake/console/libs/templates/skel/tmp/sessions/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/groups/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/fixtures/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/cases/behaviors/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/cases/helpers/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/cases/models/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/cases/components/empty
===================================================================
Index: cake/console/libs/templates/skel/tests/cases/controllers/empty
===================================================================
Index: cake/console/libs/templates/skel/app_helper.php
===================================================================
--- cake/console/libs/templates/skel/app_helper.php	(revision 0)
+++ cake/console/libs/templates/skel/app_helper.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+/* SVN FILE: $Id: app_helper.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file is application-wide helper file. You can put all
+ * application-wide helper-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'Helper');
+/**
+ * This is a placeholder class.
+ * Create the same file in app/app_helper.php
+ *
+ * Add your application-wide methods in the class below, your helpers
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.cake
+ */
+class AppHelper extends Helper {
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/db_acl.sql
===================================================================
--- cake/console/libs/templates/skel/config/sql/db_acl.sql	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/db_acl.sql	(revision 0)
@@ -0,0 +1,40 @@
+# $Id: db_acl.sql 7945 2008-12-19 02:16:01Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE acos (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  parent_id INTEGER(10) DEFAULT NULL,
+  model VARCHAR(255) DEFAULT '',
+  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
+  alias VARCHAR(255) DEFAULT '',
+  lft INTEGER(10) DEFAULT NULL,
+  rght INTEGER(10) DEFAULT NULL,
+  PRIMARY KEY  (id)
+);
+
+CREATE TABLE aros_acos (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  aro_id INTEGER(10) UNSIGNED NOT NULL,
+  aco_id INTEGER(10) UNSIGNED NOT NULL,
+  _create CHAR(2) NOT NULL DEFAULT 0,
+  _read CHAR(2) NOT NULL DEFAULT 0,
+  _update CHAR(2) NOT NULL DEFAULT 0,
+  _delete CHAR(2) NOT NULL DEFAULT 0,
+  PRIMARY KEY(id)
+);
+
+CREATE TABLE aros (
+  id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  parent_id INTEGER(10) DEFAULT NULL,
+  model VARCHAR(255) DEFAULT '',
+  foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
+  alias VARCHAR(255) DEFAULT '',
+  lft INTEGER(10) DEFAULT NULL,
+  rght INTEGER(10) DEFAULT NULL,
+  PRIMARY KEY  (id)
+);
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/sessions.sql
===================================================================
--- cake/console/libs/templates/skel/config/sql/sessions.sql	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/sessions.sql	(revision 0)
@@ -0,0 +1,16 @@
+# $Id: sessions.sql 7118 2008-06-04 20:49:29Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#								1785 E. Sahara Avenue, Suite 490-204
+#								Las Vegas, Nevada 89104
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE cake_sessions (
+  id varchar(255) NOT NULL default '',
+  data text,
+  expires int(11) default NULL,
+  PRIMARY KEY  (id)
+);
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/i18n.php
===================================================================
--- cake/console/libs/templates/skel/config/sql/i18n.php	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/i18n.php	(revision 0)
@@ -0,0 +1,56 @@
+<?php
+/* SVN FILE: $Id: i18n.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*i18n schema generated on: 2007-11-25 07:11:25 : 1196004805*/
+/**
+ * This is i18n Schema file
+ *
+ * Use it to configure database for i18n
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create i18n
+ *
+ */
+class i18nSchema extends CakeSchema {
+
+	var $name = 'i18n';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $i18n = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'locale' => array('type'=>'string', 'null' => false, 'length' => 6, 'key' => 'index'),
+			'model' => array('type'=>'string', 'null' => false, 'key' => 'index'),
+			'foreign_key' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'),
+			'field' => array('type'=>'string', 'null' => false, 'key' => 'index'),
+			'content' => array('type'=>'text', 'null' => true, 'default' => NULL),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'locale' => array('column' => 'locale', 'unique' => 0), 'model' => array('column' => 'model', 'unique' => 0), 'row_id' => array('column' => 'foreign_key', 'unique' => 0), 'field' => array('column' => 'field', 'unique' => 0))
+		);
+
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/db_acl.php
===================================================================
--- cake/console/libs/templates/skel/config/sql/db_acl.php	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/db_acl.php	(revision 0)
@@ -0,0 +1,79 @@
+<?php
+/* SVN FILE: $Id: db_acl.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*DbAcl schema generated on: 2007-11-24 15:11:13 : 1195945453*/
+/**
+ * This is Acl Schema file
+ *
+ * Use it to configure database for ACL
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create DbAcl
+ *
+ */
+class DbAclSchema extends CakeSchema {
+
+	var $name = 'DbAcl';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $acos = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'model' => array('type'=>'string', 'null' => true),
+			'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'alias' => array('type'=>'string', 'null' => true),
+			'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+	var $aros = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'model' => array('type'=>'string', 'null' => true),
+			'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'alias' => array('type'=>'string', 'null' => true),
+			'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+	var $aros_acos = array(
+			'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
+			'aro_id' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'),
+			'aco_id' => array('type'=>'integer', 'null' => false, 'length' => 10),
+			'_create' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_read' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_update' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'_delete' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'ARO_ACO_KEY' => array('column' => array('aro_id', 'aco_id'), 'unique' => 1))
+		);
+
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/sessions.php
===================================================================
--- cake/console/libs/templates/skel/config/sql/sessions.php	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/sessions.php	(revision 0)
@@ -0,0 +1,53 @@
+<?php
+/* SVN FILE: $Id: sessions.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/*Sessions schema generated on: 2007-11-25 07:11:54 : 1196004714*/
+/**
+ * This is Sessions Schema file
+ *
+ * Use it to configure database for Sessions
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/*
+ *
+ * Using the Schema command line utility
+ * cake schema run create Sessions
+ *
+ */
+class SessionsSchema extends CakeSchema {
+
+	var $name = 'Sessions';
+
+	function before($event = array()) {
+		return true;
+	}
+
+	function after($event = array()) {
+	}
+
+	var $cake_sessions = array(
+			'id' => array('type'=>'string', 'null' => false, 'key' => 'primary'),
+			'data' => array('type'=>'text', 'null' => true, 'default' => NULL),
+			'expires' => array('type'=>'integer', 'null' => true, 'default' => NULL),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+		);
+
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/sql/i18n.sql
===================================================================
--- cake/console/libs/templates/skel/config/sql/i18n.sql	(revision 0)
+++ cake/console/libs/templates/skel/config/sql/i18n.sql	(revision 0)
@@ -0,0 +1,26 @@
+# $Id: i18n.sql 7945 2008-12-19 02:16:01Z gwoo $
+#
+# Copyright 2005-2008,	Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# http://www.opensource.org/licenses/mit-license.php The MIT License
+
+CREATE TABLE i18n (
+	id int(10) NOT NULL auto_increment,
+	locale varchar(6) NOT NULL,
+	model varchar(255) NOT NULL,
+	foreign_key int(10) NOT NULL,
+	field varchar(255) NOT NULL,
+	content mediumtext,
+	PRIMARY KEY	(id),
+#	UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
+#	INDEX I18N_LOCALE_ROW(locale, model, foreign_key),
+#	INDEX I18N_LOCALE_MODEL(locale, model),
+#	INDEX I18N_FIELD(model, foreign_key, field),
+#	INDEX I18N_ROW(model, foreign_key),
+	INDEX locale (locale),
+	INDEX model (model),
+	INDEX row_id (foreign_key),
+	INDEX field (field)
+);
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/acl.ini.php
===================================================================
--- cake/console/libs/templates/skel/config/acl.ini.php	(revision 0)
+++ cake/console/libs/templates/skel/config/acl.ini.php	(revision 0)
@@ -0,0 +1,74 @@
+;<?php die() ?>
+; SVN FILE: $Id: acl.ini.php 7945 2008-12-19 02:16:01Z gwoo $
+;/**
+; * Short description for file.
+; *
+; *
+; * PHP versions 4 and 5
+; *
+; * CakePHP(tm) :  Rapid Development Framework http://www.cakephp.org/
+; * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @filesource
+; * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+; * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+; * @package       cake
+; * @subpackage    cake.app.config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @version       $Revision: 7945 $
+; * @modifiedby    $LastChangedBy: gwoo $
+; * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+; * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+; */
+
+; acl.ini.php - Cake ACL Configuration
+; ---------------------------------------------------------------------
+; Use this file to specify user permissions.
+; aco = access control object (something in your application)
+; aro = access request object (something requesting access)
+;
+; User records are added as follows:
+;
+; [uid]
+; groups = group1, group2, group3
+; allow = aco1, aco2, aco3
+; deny = aco4, aco5, aco6
+;
+; Group records are added in a similar manner:
+;
+; [gid]
+; allow = aco1, aco2, aco3
+; deny = aco4, aco5, aco6
+;
+; The allow, deny, and groups sections are all optional.
+; NOTE: groups names *cannot* ever be the same as usernames!
+;
+; ACL permissions are checked in the following order:
+; 1. Check for user denies (and DENY if specified)
+; 2. Check for user allows (and ALLOW if specified)
+; 3. Gather user's groups
+; 4. Check group denies (and DENY if specified)
+; 5. Check group allows (and ALLOW if specified)
+; 6. If no aro, aco, or group information is found, DENY
+;
+; ---------------------------------------------------------------------
+
+;-------------------------------------
+;Users
+;-------------------------------------
+
+[username-goes-here]
+groups = group1, group2
+deny = aco1, aco2
+allow = aco3, aco4
+
+;-------------------------------------
+;Groups
+;-------------------------------------
+
+[groupname-goes-here]
+deny = aco5, aco6
+allow = aco7, aco8
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/bootstrap.php
===================================================================
--- cake/console/libs/templates/skel/config/bootstrap.php	(revision 0)
+++ cake/console/libs/templates/skel/config/bootstrap.php	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: bootstrap.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.10.8.2117
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ *
+ * This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded
+ * This is an application wide file to load any function that is not used within a class define.
+ * You can also use this to include or require any files in your application.
+ *
+ */
+/**
+ * The settings below can be used to set additional paths to models, views and controllers.
+ * This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
+ *
+ * $modelPaths = array('full path to models', 'second full path to models', 'etc...');
+ * $viewPaths = array('this path to views', 'second full path to views', 'etc...');
+ * $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...');
+ *
+ */
+//EOF
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/inflections.php
===================================================================
--- cake/console/libs/templates/skel/config/inflections.php	(revision 0)
+++ cake/console/libs/templates/skel/config/inflections.php	(revision 0)
@@ -0,0 +1,70 @@
+<?php
+/* SVN FILE: $Id: inflections.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Custom Inflected Words.
+ *
+ * This file is used to hold words that are not matched in the normail Inflector::pluralize() and
+ * Inflector::singularize()
+ *
+ * PHP versions 4 and %
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 1.0.0.2312
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * This is a key => value array of regex used to match words.
+ * If key matches then the value is returned.
+ *
+ *  $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice');
+ */
+	$pluralRules = array();
+/**
+ * This is a key only array of plural words that should not be inflected.
+ * Notice the last comma
+ *
+ * $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
+ */
+	$uninflectedPlural = array();
+/**
+ * This is a key => value array of plural irregular words.
+ * If key matches then the value is returned.
+ *
+ *  $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers')
+ */
+	$irregularPlural = array();
+/**
+ * This is a key => value array of regex used to match words.
+ * If key matches then the value is returned.
+ *
+ *  $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i')
+ */
+	$singularRules = array();
+/**
+ * This is a key only array of singular words that should not be inflected.
+ * You should not have to change this value below if you do change it use same format
+ * as the $uninflectedPlural above.
+ */
+	$uninflectedSingular = $uninflectedPlural;
+/**
+ * This is a key => value array of singular irregular words.
+ * Most of the time this will be a reverse of the above $irregularPlural array
+ * You should not have to change this value below if you do change it use same format
+ *
+ * $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother')
+ */
+	$irregularSingular = array_flip($irregularPlural);
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/routes.php
===================================================================
--- cake/console/libs/templates/skel/config/routes.php	(revision 0)
+++ cake/console/libs/templates/skel/config/routes.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+/* SVN FILE: $Id: routes.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * In this file, you set up routes to your controllers and their actions.
+ * Routes are very important mechanism that allows you to freely connect
+ * different urls to chosen controllers and their actions (functions).
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Here, we are connecting '/' (base path) to controller called 'Pages',
+ * its action called 'display', and we pass a param to select the view file
+ * to use (in this case, /app/views/pages/home.ctp)...
+ */
+	Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+/**
+ * ...and connect the rest of 'Pages' controller's urls.
+ */
+	Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/database.php.default
===================================================================
--- cake/console/libs/templates/skel/config/database.php.default	(revision 0)
+++ cake/console/libs/templates/skel/config/database.php.default	(revision 0)
@@ -0,0 +1,101 @@
+<?php
+/* SVN FILE: $Id: database.php.default 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * In this file you set up your database connection details.
+ *
+ * @package       cake
+ * @subpackage    cake.config
+ */
+/**
+ * Database configuration class.
+ * You can specify multiple configurations for production, development and testing.
+ *
+ * driver => The name of a supported driver; valid options are as follows:
+ *		mysql 		- MySQL 4 & 5,
+ *		mysqli 		- MySQL 4 & 5 Improved Interface (PHP5 only),
+ *		sqlite		- SQLite (PHP5 only),
+ *		postgres	- PostgreSQL 7 and higher,
+ *		mssql		- Microsoft SQL Server 2000 and higher,
+ *		db2			- IBM DB2, Cloudscape, and Apache Derby (http://php.net/ibm-db2)
+ *		oracle		- Oracle 8 and higher
+ *		firebird	- Firebird/Interbase
+ *		sybase		- Sybase ASE
+ *		adodb-[drivername]	- ADOdb interface wrapper (see below),
+ *		odbc		- ODBC DBO driver
+ *
+ * You can add custom database drivers (or override existing drivers) by adding the
+ * appropriate file to app/models/datasources/dbo.  Drivers should be named 'dbo_x.php',
+ * where 'x' is the name of the database.
+ *
+ * persistent => true / false
+ * Determines whether or not the database should use a persistent connection
+ *
+ * connect =>
+ * ADOdb set the connect to one of these
+ *	(http://phplens.com/adodb/supported.databases.html) and
+ *	append it '|p' for persistent connection. (mssql|p for example, or just mssql for not persistent)
+ * For all other databases, this setting is deprecated.
+ *
+ * host =>
+ * the host you connect to the database.  To add a socket or port number, use 'port' => #
+ *
+ * prefix =>
+ * Uses the given prefix for all the tables in this database.  This setting can be overridden
+ * on a per-table basis with the Model::$tablePrefix property.
+ *
+ * schema =>
+ * For Postgres and DB2, specifies which schema you would like to use the tables in. Postgres defaults to
+ * 'public', DB2 defaults to empty.
+ *
+ * encoding =>
+ * For MySQL, MySQLi, Postgres and DB2, specifies the character encoding to use when connecting to the
+ * database.  Uses database default.
+ *
+ */
+class DATABASE_CONFIG {
+
+	var $default = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'database_name',
+		'prefix' => '',
+	);
+
+	var $test = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'test_database_name',
+		'prefix' => '',
+	);
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/config/core.php
===================================================================
--- cake/console/libs/templates/skel/config/core.php	(revision 0)
+++ cake/console/libs/templates/skel/config/core.php	(revision 0)
@@ -0,0 +1,227 @@
+<?php
+/* SVN FILE: $Id: core.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behavior of Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * CakePHP Debug Level:
+ *
+ * Production Mode:
+ * 	0: No error messages, errors, or warnings shown. Flash messages redirect.
+ *
+ * Development Mode:
+ * 	1: Errors and warnings shown, model caches refreshed, flash messages halted.
+ * 	2: As in 1, but also with full debug messages and SQL output.
+ * 	3: As in 2, but also with full controller dump.
+ *
+ * In production mode, flash messages redirect after a time interval.
+ * In development mode, you need to click the flash message to continue.
+ */
+	Configure::write('debug', 2);
+/**
+ * Application wide charset encoding
+ */
+	Configure::write('App.encoding', 'UTF-8');
+/**
+ * To configure CakePHP *not* to use mod_rewrite and to
+ * use CakePHP pretty URLs, remove these .htaccess
+ * files:
+ *
+ * /.htaccess
+ * /app/.htaccess
+ * /app/webroot/.htaccess
+ *
+ * And uncomment the App.baseUrl below:
+ */
+	//Configure::write('App.baseUrl', env('SCRIPT_NAME'));
+/**
+ * Uncomment the define below to use CakePHP admin routes.
+ *
+ * The value of the define determines the name of the route
+ * and its associated controller actions:
+ *
+ * 'admin' 		-> admin_index() and /admin/controller/index
+ * 'superuser' -> superuser_index() and /superuser/controller/index
+ */
+	//Configure::write('Routing.admin', 'admin');
+
+/**
+ * Turn off all caching application-wide.
+ *
+ */
+	//Configure::write('Cache.disable', true);
+/**
+ * Enable cache checking.
+ *
+ * If set to true, for view caching you must still use the controller
+ * var $cacheAction inside your controllers to define caching settings.
+ * You can either set it controller-wide by setting var $cacheAction = true,
+ * or in each action using $this->cacheAction = true.
+ *
+ */
+	//Configure::write('Cache.check', true);
+/**
+ * Defines the default error type when using the log() function. Used for
+ * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
+ */
+	define('LOG_ERROR', 2);
+/**
+ * The preferred session handling method. Valid values:
+ *
+ * 'php'	 		Uses settings defined in your php.ini.
+ * 'cake'		Saves session files in CakePHP's /tmp directory.
+ * 'database'	Uses CakePHP's database sessions.
+ *
+ * To define a custom session handler, save it at /app/config/<name>.php.
+ * Set the value of 'Session.save' to <name> to utilize it in CakePHP.
+ *
+ * To use database sessions, execute the SQL file found at /app/config/sql/sessions.sql.
+ *
+ */
+	Configure::write('Session.save', 'php');
+/**
+ * The name of the table used to store CakePHP database sessions.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ *
+ * The table name set here should *not* include any table prefix defined elsewhere.
+ */
+	//Configure::write('Session.table', 'cake_sessions');
+/**
+ * The DATABASE_CONFIG::$var to use for database session handling.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ */
+	//Configure::write('Session.database', 'default');
+/**
+ * The name of CakePHP's session cookie.
+ */
+	Configure::write('Session.cookie', 'CAKEPHP');
+/**
+ * Session time out time (in seconds).
+ * Actual value depends on 'Security.level' setting.
+ */
+	Configure::write('Session.timeout', '120');
+/**
+ * If set to false, sessions are not automatically started.
+ */
+	Configure::write('Session.start', true);
+/**
+ * When set to false, HTTP_USER_AGENT will not be checked
+ * in the session
+ */
+	Configure::write('Session.checkAgent', true);
+/**
+ * The level of CakePHP security. The session timeout time defined
+ * in 'Session.timeout' is multiplied according to the settings here.
+ * Valid values:
+ *
+ * 'high'	Session timeout in 'Session.timeout' x 10
+ * 'medium'	Session timeout in 'Session.timeout' x 100
+ * 'low'		Session timeout in 'Session.timeout' x 300
+ *
+ * CakePHP session IDs are also regenerated between requests if
+ * 'Security.level' is set to 'high'.
+ */
+	Configure::write('Security.level', 'high');
+/**
+ * A random string used in security hashing methods.
+ */
+	Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
+/**
+ * Compress CSS output by removing comments, whitespace, repeating tags, etc.
+ * This requires a/var/cache directory to be writable by the web server for caching.
+ * and /vendors/csspp/csspp.php
+ *
+ * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css().
+ */
+	//Configure::write('Asset.filter.css', 'css.php');
+/**
+ * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the
+ * output, and setting the config below to the name of the script.
+ *
+ * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link().
+ */
+	//Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php');
+/**
+ * The classname and database used in CakePHP's
+ * access control lists.
+ */
+	Configure::write('Acl.classname', 'DbAcl');
+	Configure::write('Acl.database', 'default');
+/**
+ *
+ * Cache Engine Configuration
+ * Default settings provided below
+ *
+ * File storage engine.
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'File', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path
+ * 		'prefix' => 'cake_', //[optional]  prefix every cache file with this string
+ * 		'lock' => false, //[optional]  use file locking
+ * 		'serialize' => true, [optional]
+ *	));
+ *
+ *
+ * APC (http://pecl.php.net/package/APC)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Apc', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional]  prefix every cache file with this string
+ *	));
+ *
+ * Xcache (http://xcache.lighttpd.net/)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Xcache', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
+ *		'user' => 'user', //user from xcache.admin.user settings
+ *      'password' => 'password', //plaintext password (xcache.admin.pass)
+ *	));
+ *
+ *
+ * Memcache (http://www.danga.com/memcached/)
+ *
+ * 	 Cache::config('default', array(
+ *		'engine' => 'Memcache', //[required]
+ *		'duration'=> 3600, //[optional]
+ *		'probability'=> 100, //[optional]
+ * 		'prefix' => Inflector::slug(APP_DIR) . '_', //[optional]  prefix every cache file with this string
+ * 		'servers' => array(
+ * 			'127.0.0.1:11211' // localhost, default port 11211
+ * 		), //[optional]
+ * 		'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory)
+ *	));
+ *
+ */
+	Cache::config('default', array('engine' => 'File'));
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/controllers/components/empty
===================================================================
Index: cake/console/libs/templates/skel/controllers/pages_controller.php
===================================================================
--- cake/console/libs/templates/skel/controllers/pages_controller.php	(revision 0)
+++ cake/console/libs/templates/skel/controllers/pages_controller.php	(revision 0)
@@ -0,0 +1,86 @@
+<?php
+/* SVN FILE: $Id: pages_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Static content controller.
+ *
+ * This file will render views from views/pages/
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Static content controller
+ *
+ * Override this controller by placing a copy in controllers directory of an application
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ */
+class PagesController extends AppController {
+/**
+ * Controller name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Pages';
+/**
+ * Default helper
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html');
+/**
+ * This controller does not use a model
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+/**
+ * Displays a view
+ *
+ * @param mixed What page to display
+ * @access public
+ */
+	function display() {
+		$path = func_get_args();
+
+		$count = count($path);
+		if (!$count) {
+			$this->redirect('/');
+		}
+		$page = $subpage = $title = null;
+
+		if (!empty($path[0])) {
+			$page = $path[0];
+		}
+		if (!empty($path[1])) {
+			$subpage = $path[1];
+		}
+		if (!empty($path[$count - 1])) {
+			$title = Inflector::humanize($path[$count - 1]);
+		}
+		$this->set(compact('page', 'subpage', 'title'));
+		$this->render(join('/', $path));
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/app_controller.php
===================================================================
--- cake/console/libs/templates/skel/app_controller.php	(revision 0)
+++ cake/console/libs/templates/skel/app_controller.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file is application-wide controller file. You can put all
+ * application-wide controller-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for class.
+ *
+ * Add your application-wide methods in the class below, your controllers
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.app
+ */
+class AppController extends Controller {
+}
+?>
\ No newline at end of file
Index: cake/console/libs/templates/skel/.htaccess
===================================================================
--- cake/console/libs/templates/skel/.htaccess	(revision 0)
+++ cake/console/libs/templates/skel/.htaccess	(revision 0)
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine on
+    RewriteRule    ^$    webroot/    [L]
+    RewriteRule    (.*) webroot/$1    [L]
+ </IfModule>
\ No newline at end of file
Index: cake/tests/lib/cli_reporter.php
===================================================================
--- cake/tests/lib/cli_reporter.php	(revision 0)
+++ cake/tests/lib/cli_reporter.php	(revision 0)
@@ -0,0 +1,103 @@
+<?php
+/* SVN FILE: $Id: cli_reporter.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+	if (! defined('ST_FAILDETAIL_SEPARATOR')) {
+		define('ST_FAILDETAIL_SEPARATOR', "->");
+	}
+
+	if (version_compare(PHP_VERSION, '4.4.4', '<=') ||
+		PHP_SAPI == 'cgi') {
+		define('STDOUT', fopen('php://stdout', 'w'));
+		define('STDERR', fopen('php://stderr', 'w'));
+		register_shutdown_function(create_function('', 'fclose(STDOUT); fclose(STDERR); return true;'));
+	}
+/**
+ * Minimal command line test displayer. Writes fail details to STDERR. Returns 0
+ * to the shell if all tests pass, ST_FAILS_RETURN_CODE if any test fails.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ */
+class CLIReporter extends TextReporter {
+	var $faildetail_separator = ST_FAILDETAIL_SEPARATOR;
+
+	function CLIReporter($faildetail_separator = NULL) {
+		$this->SimpleReporter();
+		if (! is_null($faildetail_separator)) {
+			$this->setFailDetailSeparator($faildetail_separator);
+		}
+	}
+
+	function setFailDetailSeparator($separator) {
+		$this->faildetail_separator = $separator;
+	}
+/**
+ * Return a formatted faildetail for printing.
+ */
+	function &_paintTestFailDetail(&$message) {
+		$buffer = '';
+		$faildetail = $this->getTestList();
+		array_shift($faildetail);
+		$buffer .= implode($this->faildetail_separator, $faildetail);
+		$buffer .= $this->faildetail_separator . "$message\n";
+		return $buffer;
+	}
+/**
+ * Paint fail faildetail to STDERR.
+ */
+	function paintFail($message) {
+		parent::paintFail($message);
+		fwrite(STDERR, 'FAIL' . $this->faildetail_separator . $this->_paintTestFailDetail($message));
+	}
+/**
+ * Paint exception faildetail to STDERR.
+ */
+	function paintException($message) {
+		parent::paintException($message);
+		fwrite(STDERR, 'EXCEPTION' . $this->faildetail_separator . $this->_paintTestFailDetail($message));
+	}
+/**
+ * Paint a footer with test case name, timestamp, counts of fails and exceptions.
+ */
+	function paintFooter($test_name) {
+		$buffer = $this->getTestCaseProgress() . '/' . $this->getTestCaseCount() . ' test cases complete: ';
+
+		if (0 < ($this->getFailCount() + $this->getExceptionCount())) {
+			$buffer .= $this->getPassCount() . " passes";
+			if (0 < $this->getFailCount()) {
+				$buffer .= ", " . $this->getFailCount() . " fails";
+			}
+			if (0 < $this->getExceptionCount()) {
+				$buffer .= ", " . $this->getExceptionCount() . " exceptions";
+			}
+			$buffer .= ".\n";
+			fwrite(STDOUT, $buffer);
+		} else {
+			fwrite(STDOUT, $buffer . $this->getPassCount() . " passes.\n");
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/simpletest.php
===================================================================
--- cake/tests/lib/simpletest.php	(revision 0)
+++ cake/tests/lib/simpletest.php	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: simpletest.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+<div id="content">
+	<h2>SimpleTest is not installed</h2>
+	<p>You must install SimpleTest to use the CakePHP(tm) Test Suite.</p>
+	<p>SimpleTest can be placed in one of the following directories.</p>
+	<ul>
+		<li><?php echo CAKE; ?>vendors </li>
+		<li><?php echo APP_DIR . DS; ?>vendors</li>
+	</ul>
+	<p><a href="http://simpletest.org/en/download.html" target="_blank">Download SimpleTest</a></p>
+</div>
\ No newline at end of file
Index: cake/tests/lib/test_manager.php
===================================================================
--- cake/tests/lib/test_manager.php	(revision 0)
+++ cake/tests/lib/test_manager.php	(revision 0)
@@ -0,0 +1,780 @@
+<?php
+/* SVN FILE: $Id: test_manager.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+define('CORE_TEST_CASES', TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'cases');
+define('CORE_TEST_GROUPS', TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'groups');
+define('APP_TEST_CASES', TESTS . 'cases');
+define('APP_TEST_GROUPS', TESTS . 'groups');
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class TestManager {
+	var $_testExtension = '.test.php';
+	var $_groupExtension = '.group.php';
+	var $appTest = false;
+	var $pluginTest = false;
+/**
+ * Constructor for the TestManager class
+ *
+ * @return void
+ * @access public
+ */
+	function TestManager() {
+		$this->_installSimpleTest();
+		if (isset($_GET['app'])) {
+			$this->appTest = true;
+		}
+		if (isset($_GET['plugin'])) {
+			$this->pluginTest = $_GET['plugin'];
+		}
+	}
+/**
+ * Includes the required simpletest files in order for the testsuite to run
+ *
+ * @return void
+ * @access public
+ */
+	function _installSimpleTest() {
+		App::import('Vendor', array(
+			'simpletest' . DS . 'unit_tester',
+			'simpletest' . DS . 'mock_objects',
+			'simpletest' . DS . 'web_tester'
+		));
+		require_once(CAKE_TESTS_LIB . 'cake_web_test_case.php');
+		require_once(CAKE_TESTS_LIB . 'cake_test_case.php');
+	}
+/**
+ * Runs all tests in the Application depending on the current appTest setting
+ *
+ * @param string $reporter
+ * @return void
+ * @access public
+ */
+	function runAllTests(&$reporter, $testing = false) {
+		$manager =& new TestManager();
+
+		$testCases =& $manager->_getTestFileList($manager->_getTestsPath());
+		if ($manager->appTest) {
+			$test =& new GroupTest('All App Tests');
+		} else if ($manager->pluginTest) {
+			$test =& new GroupTest('All ' . Inflector::humanize($manager->pluginTest) . ' Plugin Tests');
+		} else {
+			$test =& new GroupTest('All Core Tests');
+		}
+
+		if ($testing) {
+			return $testCases;
+		}
+
+		foreach ($testCases as $testCase) {
+			$test->addTestFile($testCase);
+		}
+
+		return $test->run($reporter);
+	}
+/**
+ * Runs a specific test case file
+ *
+ * @param string $testCaseFile
+ * @param string $reporter
+ * @return void
+ * @access public
+ */
+	function runTestCase($testCaseFile, &$reporter, $testing = false) {
+		$manager =& new TestManager();
+
+		$testCaseFileWithPath = $manager->_getTestsPath() . DS . $testCaseFile;
+
+		if (!file_exists($testCaseFileWithPath)) {
+			trigger_error("Test case {$testCaseFile} cannot be found", E_USER_ERROR);
+			return false;
+		}
+
+		if ($testing) {
+			return true;
+		}
+
+		$test =& new GroupTest("Individual test case: " . $testCaseFile);
+		$test->addTestFile($testCaseFileWithPath);
+		return $test->run($reporter);
+	}
+/**
+ * Runs a specific group test file
+ *
+ * @param string $groupTestName
+ * @param string $reporter
+ * @return void
+ * @access public
+ */
+	function runGroupTest($groupTestName, &$reporter) {
+		$manager =& new TestManager();
+		$filePath = $manager->_getTestsPath('groups') . DS . strtolower($groupTestName) . $manager->_groupExtension;
+
+		if (!file_exists($filePath)) {
+			trigger_error("Group test {$groupTestName} cannot be found at {$filePath}", E_USER_ERROR);
+		}
+
+		require_once $filePath;
+		$test =& new GroupTest($groupTestName . ' group test');
+		foreach ($manager->_getGroupTestClassNames($filePath) as $groupTest) {
+			$testCase = new $groupTest();
+			$test->addTestCase($testCase);
+			if (isset($testCase->label)) {
+				$test->_label = $testCase->label;
+			}
+		}
+		return $test->run($reporter);
+	}
+/**
+ * Adds all testcases in a given directory to a given GroupTest object
+ *
+ * @param string $groupTest
+ * @param string $directory
+ * @return void
+ * @access public
+ */
+	function addTestCasesFromDirectory(&$groupTest, $directory = '.') {
+		$manager =& new TestManager();
+		$testCases =& $manager->_getTestFileList($directory);
+		foreach ($testCases as $testCase) {
+			$groupTest->addTestFile($testCase);
+		}
+	}
+/**
+ * Adds a specific test file and thereby all of its test cases and group tests to a given group test file
+ *
+ * @param string $groupTest
+ * @param string $file
+ * @return void
+ * @access public
+ */
+	function addTestFile(&$groupTest, $file) {
+		$manager =& new TestManager();
+
+		if (file_exists($file.'.test.php')) {
+			$file .= '.test.php';
+		} elseif (file_exists($file.'.group.php')) {
+			$file .= '.group.php';
+		}
+		$groupTest->addTestFile($file);
+	}
+/**
+ * Returns a list of test cases found in the current valid test case path
+ *
+ * @access public
+ */
+	function &getTestCaseList() {
+		$manager =& new TestManager();
+		$return = $manager->_getTestCaseList($manager->_getTestsPath());
+		return $return;
+	}
+/**
+ * Builds the list of test cases from a given directory
+ *
+ * @access public
+ */
+	function &_getTestCaseList($directory = '.') {
+		$fileList =& $this->_getTestFileList($directory);
+		$testCases = array();
+		foreach ($fileList as $testCaseFile) {
+			$testCases[$testCaseFile] = str_replace($directory . DS, '', $testCaseFile);
+		}
+		return $testCases;
+	}
+/**
+ * Returns a list of test files from a given directory
+ *
+ * @access public
+ */
+	function &_getTestFileList($directory = '.') {
+		$return = $this->_getRecursiveFileList($directory, array(&$this, '_isTestCaseFile'));
+		return $return;
+	}
+/**
+ * Returns a list of group tests found in the current valid test case path
+ *
+ * @access public
+ */
+	function &getGroupTestList() {
+		$manager =& new TestManager();
+		$return = $manager->_getTestGroupList($manager->_getTestsPath('groups'));
+		return $return;
+	}
+/**
+ * Returns a list of group test files from a given directory
+ *
+ * @access public
+ */
+	function &_getTestGroupFileList($directory = '.') {
+		$return = $this->_getRecursiveFileList($directory, array(&$this, '_isTestGroupFile'));
+		return $return;
+	}
+/**
+ * Returns a list of group test files from a given directory
+ *
+ * @access public
+ */
+	function &_getTestGroupList($directory = '.') {
+		$fileList =& $this->_getTestGroupFileList($directory);
+		$groupTests = array();
+
+		foreach ($fileList as $groupTestFile) {
+			$groupTests[$groupTestFile] = str_replace($this->_groupExtension, '', basename($groupTestFile));
+		}
+		sort($groupTests);
+		return $groupTests;
+	}
+/**
+ * Returns a list of class names from a group test file
+ *
+ * @access public
+ */
+	function &_getGroupTestClassNames($groupTestFile) {
+		$file = implode("\n", file($groupTestFile));
+		preg_match("~lass\s+?(.*)\s+?extends GroupTest~", $file, $matches);
+		if (!empty($matches)) {
+			unset($matches[0]);
+			return $matches;
+		}
+		return array();
+	}
+/**
+ * Gets a recursive list of files from a given directory and matches then against
+ * a given fileTestFunction, like isTestCaseFile()
+ *
+ * @access public
+ */
+	function &_getRecursiveFileList($directory = '.', $fileTestFunction) {
+		$fileList = array();
+		if (!is_dir($directory)) {
+			return $fileList;
+		}
+
+		$files = glob($directory . DS . '*');
+		foreach ($files as $file) {
+			if (is_dir($file)) {
+				$fileList = array_merge($fileList, $this->_getRecursiveFileList($file, $fileTestFunction));
+			} elseif ($fileTestFunction[0]->$fileTestFunction[1]($file)) {
+				$fileList[] = $file;
+			}
+		}
+		return $fileList;
+	}
+/**
+ * Tests if a file has the correct test case extension
+ *
+ * @param string $file
+ * @return void
+ * @access public
+ */
+	function _isTestCaseFile($file) {
+		return $this->_hasExpectedExtension($file, $this->_testExtension);
+	}
+/**
+ * Tests if a file has the correct group test extension
+ *
+ * @param string $file
+ * @return void
+ * @access public
+ */
+	function _isTestGroupFile($file) {
+		return $this->_hasExpectedExtension($file, $this->_groupExtension);
+	}
+/**
+ * Check if a file has a specific extension
+ *
+ * @param string $file
+ * @param string $extension
+ * @return void
+ * @access public
+ */
+	function _hasExpectedExtension($file, $extension) {
+		return $extension == strtolower(substr($file, (0 - strlen($extension))));
+	}
+/**
+ * Returns the given path to the test files depending on a given type of tests (cases, group, ..)
+ *
+ * @param string $type
+ * @return void
+ * @access public
+ */
+	function _getTestsPath($type = 'cases') {
+		if (!empty($this->appTest)) {
+			if ($type == 'cases') {
+				$result = APP_TEST_CASES;
+			} else if ($type == 'groups') {
+				$result = APP_TEST_GROUPS;
+			}
+		} else if (!empty($this->pluginTest)) {
+			$_pluginBasePath = APP . 'plugins' . DS . $this->pluginTest . DS . 'tests';
+			$pluginPaths = Configure::read('pluginPaths');
+			foreach ($pluginPaths as $path) {
+				if (file_exists($path . $this->pluginTest . DS . 'tests')) {
+					$_pluginBasePath = $path . $this->pluginTest . DS . 'tests';
+					break;
+				}
+			}
+			$result = $_pluginBasePath . DS . $type;
+		} else {
+			if ($type == 'cases') {
+				$result = CORE_TEST_CASES;
+			} else if ($type == 'groups') {
+				$result = CORE_TEST_GROUPS;
+			}
+		}
+		return $result;
+	}
+/**
+ * undocumented function
+ *
+ * @param string $type
+ * @return void
+ * @access public
+ */
+	function getExtension($type = 'test') {
+		$manager =& new TestManager();
+		if ($type == 'test') {
+			return $manager->_testExtension;
+		}
+		return $manager->_groupExtension;
+	}
+}
+/**
+ * The CliTestManager ensures that the list of available files are printed in the correct cli format
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CliTestManager extends TestManager {
+/**
+ * Prints the list of group tests in a cli friendly format
+ *
+ * @access public
+ */
+	function &getGroupTestList() {
+		$manager =& new CliTestManager();
+		$groupTests =& $manager->_getTestGroupList($manager->_getTestsPath('groups'));
+		$buffer = "Available Group Test:\n";
+
+		foreach ($groupTests as $groupTest) {
+			$buffer .= "  " . $groupTest . "\n";
+		}
+		return $buffer . "\n";
+	}
+/**
+ * Prints the list of test cases in a cli friendly format
+ *
+ * @access public
+ */
+	function &getTestCaseList() {
+		$manager =& new CliTestManager();
+		$testCases =& $manager->_getTestCaseList($manager->_getTestsPath());
+		$buffer = "Available Test Cases:\n";
+
+		foreach ($testCases as $testCaseFile => $testCase) {
+			$buffer .= "  " . $testCaseFile . "\n";
+		}
+		return $buffer . "\n";
+	}
+}
+/**
+ * The TextTestManager ensures that the list of available tests is printed as a list of urls in a text-friendly format
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class TextTestManager extends TestManager {
+	var $_url;
+/**
+ * Constructor
+ *
+ * @return void
+ * @access public
+ */
+	function TextTestManager() {
+		parent::TestManager();
+		$this->_url = $_SERVER['PHP_SELF'];
+	}
+/**
+ * Returns the base url
+ *
+ * @return void
+ * @access public
+ */
+	function getBaseURL() {
+		return $this->_url;
+	}
+/**
+ * Returns a list of available group tests in a text-friendly format
+ *
+ * @access public
+ */
+	function &getGroupTestList() {
+		$manager =& new TextTestManager();
+		$groupTests =& $manager->_getTestGroupList($manager->_getTestsPath('groups'));
+
+		$buffer = "Core Test Groups:\n";
+		$urlExtra = '';
+		if ($manager->appTest) {
+			$buffer = "App Test Groups:\n";
+			$urlExtra = '&app=true';
+		} else if ($manager->pluginTest) {
+			$buffer = Inflector::humanize($manager->pluginTest) . " Test Groups:\n";
+			$urlExtra = '&plugin=' . $manager->pluginTest;
+		}
+
+		$buffer .= "All tests\n" . $_SERVER['SERVER_NAME'] . $manager->getBaseURL() . "?group=all&output=txt{$urlExtra}\n";
+
+		foreach ((array)$groupTests as $groupTest) {
+			$buffer .= $_SERVER['SERVER_NAME']. $manager->getBaseURL()."?group=" . $groupTest . "&output=txt{$urlExtra}"."\n";
+		}
+
+		return $buffer;
+	}
+/**
+ * Returns a list of available test cases in a text-friendly format
+ *
+ * @access public
+ */
+	function &getTestCaseList() {
+		$manager =& new TextTestManager();
+		$testCases =& $manager->_getTestCaseList($manager->_getTestsPath());
+
+		$buffer = "Core Test Cases:\n";
+		$urlExtra = '';
+		if ($manager->appTest) {
+			$buffer = "App Test Cases:\n";
+			$urlExtra = '&app=true';
+		} else if ($manager->pluginTest) {
+			$buffer = Inflector::humanize($manager->pluginTest) . " Test Cases:\n";
+			$urlExtra = '&plugin=' . $manager->pluginTest;
+		}
+
+		if (1 > count($testCases)) {
+			$buffer .= "EMPTY";
+			return $buffer;
+		}
+
+		foreach ($testCases as $testCaseFile => $testCase) {
+			$buffer .= $_SERVER['SERVER_NAME']. $manager->getBaseURL()."?case=" . $testCase . "&output=txt"."\n";
+		}
+
+		$buffer .= "\n";
+		return $buffer;
+	}
+}
+/**
+ * The HtmlTestManager provides the foundation for the web-based CakePHP testsuite.
+ * It prints the different lists of tests and provides the interface for CodeCoverage, etc.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class HtmlTestManager extends TestManager {
+	var $_url;
+/**
+ * Constructor
+ *
+ * @return void
+ * @access public
+ */
+	function HtmlTestManager() {
+		parent::TestManager();
+		$this->_url = $_SERVER['PHP_SELF'];
+	}
+/**
+ * Returns the current base url
+ *
+ * @return void
+ * @access public
+ */
+	function getBaseURL() {
+		return $this->_url;
+	}
+/**
+ * Prints the links to the available group tests
+ *
+ * @access public
+ */
+	function &getGroupTestList() {
+		$urlExtra = '';
+		$manager =& new HtmlTestManager();
+		$groupTests =& $manager->_getTestGroupList($manager->_getTestsPath('groups'));
+
+		$buffer = "<h3>Core Test Groups:</h3>\n<ul>";
+		$urlExtra = null;
+		if ($manager->appTest) {
+			$buffer = "<h3>App Test Groups:</h3>\n<ul>";
+			$urlExtra = '&app=true';
+		} else if ($manager->pluginTest) {
+			$buffer = "<h3>" . Inflector::humanize($manager->pluginTest) . " Test Groups:</h3>\n<ul>";
+			$urlExtra = '&plugin=' . $manager->pluginTest;
+		}
+
+		$buffer .= "<li><a href='" . $manager->getBaseURL() . "?group=all$urlExtra'>All tests</a></li>\n";
+
+		foreach ($groupTests as $groupTest) {
+			$buffer .= "<li><a href='" . $manager->getBaseURL() . "?group={$groupTest}" . "{$urlExtra}'>" . $groupTest . "</a></li>\n";
+		}
+		$buffer .= "</ul>\n";
+		return $buffer;
+	}
+/**
+ * Prints the links to the available test cases
+ *
+ * @access public
+ */
+	function &getTestCaseList() {
+		$urlExtra = '';
+		$manager =& new HtmlTestManager();
+		$testCases =& $manager->_getTestCaseList($manager->_getTestsPath());
+
+		$buffer = "<h3>Core Test Cases:</h3>\n<ul>";
+		$urlExtra = null;
+		if ($manager->appTest) {
+			$buffer = "<h3>App Test Cases:</h3>\n<ul>";
+			$urlExtra = '&app=true';
+		} else if ($manager->pluginTest) {
+			$buffer = "<h3>" . Inflector::humanize($manager->pluginTest) . " Test Cases:</h3>\n<ul>";
+			$urlExtra = '&plugin=' . $manager->pluginTest;
+		}
+
+		if (1 > count($testCases)) {
+			$buffer .= "<strong>EMPTY</strong>";
+			return $buffer;
+		}
+
+		foreach ($testCases as $testCaseFile => $testCase) {
+			$title = explode(strpos($testCase, '\\') ? '\\' : '/', str_replace('.test.php', '', $testCase));
+			$title[count($title) - 1] = Inflector::camelize($title[count($title) - 1]);
+			$title = join(' / ', $title);
+
+				$buffer .= "<li><a href='" . $manager->getBaseURL() . "?case=" . urlencode($testCase) . $urlExtra ."'>" . $title . "</a></li>\n";
+		}
+		$buffer .= "</ul>\n";
+		return $buffer;
+	}
+}
+if (function_exists('caketestsgetreporter')) {
+	echo "You need a new test.php. \n";
+	echo "Try this one: " . CONSOLE_LIBS . "templates" . DS . "skel" . DS . "webroot" . DS . "test.php";
+	exit();
+} else {
+/**
+ * Returns an object of the currently needed reporter
+ *
+ * @access public
+ */
+	function &CakeTestsGetReporter() {
+		static $Reporter = NULL;
+		if (!$Reporter) {
+			switch (CAKE_TEST_OUTPUT) {
+				case CAKE_TEST_OUTPUT_HTML:
+					require_once CAKE_TESTS_LIB . 'cake_reporter.php';
+					$Reporter =& new CakeHtmlReporter();
+					break;
+				default:
+					$Reporter =& new TextReporter();
+					break;
+			}
+		}
+		return $Reporter;
+	}
+/**
+ * Provides the "Run More" links in the testsuite interface
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestRunMore() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				if (isset($_GET['group'])) {
+					if (isset($_GET['app'])) {
+						$show = '?show=groups&amp;app=true';
+					} else if (isset($_GET['plugin'])) {
+						$show = '?show=groups&amp;plugin=' . $_GET['plugin'];
+					} else {
+						$show = '?show=groups';
+					}
+					$query = '?group='.$_GET['group'];
+					if (isset($_GET['app'])) {
+						$query .= '&amp;app=true';
+					} elseif (isset($_GET['plugin'])) {
+						$query .= '&amp;plugin=' . $_GET['plugin'];
+					}
+				}
+				if (isset($_GET['case'])) {
+					if (isset($_GET['app'])) {
+						$show = '?show=cases&amp;app=true';
+					} else if (isset($_GET['plugin'])) {
+						$show = '?show=cases&amp;plugin=' . $_GET['plugin'];
+					} else {
+						$show = '?show=cases';
+					}
+					$query = '?case='.$_GET['case'];
+					if (isset($_GET['app'])) {
+						$query .= '&amp;app=true';
+					} elseif (isset($_GET['plugin'])) {
+						$query .= '&amp;plugin=' . $_GET['plugin'];
+					}
+				}
+				ob_start();
+				echo "<p><a href='" . RUN_TEST_LINK . $show . "'>Run more tests</a> | <a href='" . RUN_TEST_LINK . $query . "&show_passes=1'>Show Passes</a> | \n";
+
+				break;
+		}
+	}
+/**
+ * Provides the links to analyzing code coverage
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestAnalyzeCodeCoverage() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				if (isset($_GET['case'])) {
+					$query = '?case='.$_GET['case'];
+					if (isset($_GET['app'])) {
+						$query .= '&amp;app=true';
+					} elseif (isset($_GET['plugin'])) {
+						$query .= '&amp;plugin=' . $_GET['plugin'];
+					}
+				} else {
+					$query = '?group='.$_GET['group'];
+					if (isset($_GET['app'])) {
+						$query .= '&amp;app=true';
+					} elseif (isset($_GET['plugin'])) {
+						$query .= '&amp;plugin=' . $_GET['plugin'];
+					}
+				}
+				$query .= '&amp;code_coverage=true';
+				ob_start();
+				echo " <a href='" . RUN_TEST_LINK . $query . "'>Analyze Code Coverage</a></p>\n";
+
+				break;
+		}
+	}
+/**
+ * Prints a list of test cases
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestCaseList() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				ob_start();
+				echo HtmlTestManager::getTestCaseList();
+				break;
+			case CAKE_TEST_OUTPUT_TEXT:
+			default:
+				echo TextTestManager::getTestCaseList();
+				break;
+		}
+	}
+/**
+ * Prints a list of group tests
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestGroupTestList() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				echo HtmlTestManager::getGroupTestList();
+				break;
+			case CAKE_TEST_OUTPUT_TEXT:
+			default:
+				echo TextTestManager::getGroupTestList();
+				break;
+		}
+	}
+/**
+ * Includes the Testsuite Header
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestHeader() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				ob_start();
+				if (!class_exists('dispatcher')) {
+					require CAKE . 'dispatcher.php';
+				}
+				$dispatch =& new Dispatcher();
+				$dispatch->baseUrl();
+				define('BASE', $dispatch->webroot);
+				$baseUrl = BASE;
+				$characterSet = 'charset=utf-8';
+				include CAKE_TESTS_LIB . 'header.php';
+
+				break;
+			case CAKE_TEST_OUTPUT_TEXT:
+			default:
+				header('content-type: text/plain');
+				break;
+		}
+	}
+/**
+ * Provides the left hand navigation for the testsuite
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestSuiteHeader() {
+		switch (CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				ob_start();
+				$groups = $_SERVER['PHP_SELF'].'?show=groups';
+				$cases = $_SERVER['PHP_SELF'].'?show=cases';
+				$plugins = Configure::listObjects('plugin');
+				include CAKE_TESTS_LIB . 'content.php';
+				break;
+		}
+	}
+/**
+ * Provides the testsuite footer text
+ *
+ * @return void
+ * @access public
+ */
+	function CakePHPTestSuiteFooter() {
+		switch ( CAKE_TEST_OUTPUT) {
+			case CAKE_TEST_OUTPUT_HTML:
+				ob_start();
+				$baseUrl = BASE;
+				include CAKE_TESTS_LIB . 'footer.php';
+				break;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/cake_web_test_case.php
===================================================================
--- cake/tests/lib/cake_web_test_case.php	(revision 0)
+++ cake/tests/lib/cake_web_test_case.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+/* SVN FILE: $Id: cake_web_test_case.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+/**
+ * Short description
+ */
+	SimpleTestOptions::ignore('CakeWebTestCase');
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeWebTestCase extends WebTestCase {
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/content.php
===================================================================
--- cake/tests/lib/content.php	(revision 0)
+++ cake/tests/lib/content.php	(revision 0)
@@ -0,0 +1,65 @@
+<?php
+/* SVN FILE: $Id: content.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+<div class="test-menu">
+<ul>
+	<li>
+		<span  style="font-size: 18px">App</span>
+		<ul>
+			<li><a href='<?php echo $groups;?>&amp;app=true'>Test Groups</a></li>
+			<li><a href='<?php echo $cases;?>&amp;app=true'>Test Cases</a></li>
+		</ul>
+	</li>
+<?php
+if (!empty($plugins)):
+?>
+	<li style="padding-top: 10px">
+		<span  style="font-size: 18px">Plugins</span>
+	<?php foreach($plugins as $plugin):
+			$pluginPath = Inflector::underscore($plugin);
+	?>
+			<ul>
+				<li style="padding-top: 10px">
+					<span  style="font-size: 18px"><?php echo $plugin;?></span>
+					<ul>
+						<li><a href='<?php echo $groups;?>&amp;plugin=<?php echo $pluginPath; ?>'>Test Groups</a></li>
+						<li><a href='<?php echo $cases;?>&amp;plugin=<?php echo $pluginPath; ?>'>Test Cases</a></li>
+					</ul>
+				</li>
+			</ul>
+	<?php endforeach; ?>
+<?php endif;?>
+	<li style="padding-top: 10px">
+		<span style="font-size: 18px">Core</span>
+		<ul>
+			<li><a href='<?php echo $groups;?>'>Test Groups</a></li>
+			<li><a href='<?php echo $cases;?>'>Test Cases</a></li>
+		</ul>
+	</li>
+</ul>
+</div>
+<div  class="test-results">
Index: cake/tests/lib/cake_test_case.php
===================================================================
--- cake/tests/lib/cake_test_case.php	(revision 0)
+++ cake/tests/lib/cake_test_case.php	(revision 0)
@@ -0,0 +1,741 @@
+<?php
+/* SVN FILE: $Id: cake_test_case.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('dispatcher')) {
+	require CAKE . 'dispatcher.php';
+}
+require_once CAKE_TESTS_LIB . 'cake_test_model.php';
+require_once CAKE_TESTS_LIB . 'cake_test_fixture.php';
+App::import('Vendor', 'simpletest' . DS . 'unit_tester');
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestDispatcher extends Dispatcher {
+	var $controller;
+	var $testCase;
+
+	function testCase(&$testCase) {
+		$this->testCase =& $testCase;
+	}
+
+	function _invoke (&$controller, $params, $missingAction = false) {
+		$this->controller =& $controller;
+
+		if (isset($this->testCase) && method_exists($this->testCase, 'startController')) {
+			$this->testCase->startController($this->controller, $params);
+		}
+
+		$result = parent::_invoke($this->controller, $params, $missingAction);
+
+		if (isset($this->testCase) && method_exists($this->testCase, 'endController')) {
+			$this->testCase->endController($this->controller, $params);
+		}
+
+		return $result;
+	}
+}
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestCase extends UnitTestCase {
+/**
+ * Methods used internally.
+ *
+ * @var array
+ * @access private
+ */
+	var $methods = array('start', 'end', 'startcase', 'endcase', 'starttest', 'endtest');
+	var $__truncated = true;
+	var $__savedGetData = array();
+/**
+ * By default, all fixtures attached to this class will be truncated and reloaded after each test.
+ * Set this to false to handle manually
+ *
+ * @var array
+ * @access public
+ */
+	var $autoFixtures = true;
+/**
+ * Set this to false to avoid tables to be dropped if they already exist
+ *
+ * @var boolean
+ * @access public
+ */
+	var $dropTables = true;
+/**
+ * Maps fixture class names to fixture identifiers as included in CakeTestCase::$fixtures
+ *
+ * @var array
+ * @access protected
+ */
+	var $_fixtureClassMap = array();
+/**
+ * Called when a test case (group of methods) is about to start (to be overriden when needed.)
+ *
+ * @param string $method	Test method about to get executed.
+ *
+ * @access protected
+ */
+	function startCase() {
+	}
+/**
+ * Called when a test case (group of methods) has been executed (to be overriden when needed.)
+ *
+ * @param string $method	Test method about that was executed.
+ *
+ * @access protected
+ */
+	function endCase() {
+	}
+/**
+ * Called when a test case method is about to start (to be overriden when needed.)
+ *
+ * @param string $method	Test method about to get executed.
+ *
+ * @access protected
+ */
+	function startTest($method) {
+	}
+/**
+ * Called when a test case method has been executed (to be overriden when needed.)
+ *
+ * @param string $method	Test method about that was executed.
+ *
+ * @access protected
+ */
+	function endTest($method) {
+	}
+/**
+ * Overrides SimpleTestCase::assert to enable calling of skipIf() from within tests
+ */
+	function assert(&$expectation, $compare, $message = '%s') {
+		if ($this->_should_skip) {
+			return;
+		}
+		return parent::assert($expectation, $compare, $message);
+	}
+/**
+ * Overrides SimpleTestCase::skipIf to provide a boolean return value
+ */
+	function skipIf($shouldSkip, $message = '%s') {
+		parent::skipIf($shouldSkip, $message);
+		return $shouldSkip;
+	}
+/**
+ * Callback issued when a controller's action is about to be invoked through testAction().
+ *
+ * @param Controller $controller	Controller that's about to be invoked.
+ * @param array $params	Additional parameters as sent by testAction().
+ */
+	function startController(&$controller, $params = array()) {
+		if (isset($params['fixturize']) && ((is_array($params['fixturize']) && !empty($params['fixturize'])) || $params['fixturize'] === true)) {
+			if (!isset($this->db)) {
+				$this->_initDb();
+			}
+
+			if ($controller->uses === false) {
+				$list = array($controller->modelClass);
+			} else {
+				$list = is_array($controller->uses) ? $controller->uses : array($controller->uses);
+			}
+
+			$models = array();
+			ClassRegistry::config(array('ds' => $params['connection']));
+
+			foreach ($list as $name) {
+				if ((is_array($params['fixturize']) && in_array($name, $params['fixturize'])) || $params['fixturize'] === true) {
+					if (class_exists($name) || App::import('Model', $name)) {
+						$object =& ClassRegistry::init($name);
+						//switch back to specified datasource.
+						$object->setDataSource($params['connection']);
+						$db =& ConnectionManager::getDataSource($object->useDbConfig);
+						$db->cacheSources = false;
+
+						$models[$object->alias] = array(
+							'table' => $object->table,
+							'model' => $object->alias,
+							'key' => strtolower($name),
+						);
+					}
+				}
+			}
+			ClassRegistry::config(array('ds' => 'test_suite'));
+
+			if (!empty($models) && isset($this->db)) {
+				$this->_actionFixtures = array();
+
+				foreach ($models as $model) {
+					$fixture =& new CakeTestFixture($this->db);
+
+					$fixture->name = $model['model'] . 'Test';
+					$fixture->table = $model['table'];
+					$fixture->import = array('model' => $model['model'], 'records' => true);
+					$fixture->init();
+
+					$fixture->create($this->db);
+					$fixture->insert($this->db);
+					$this->_actionFixtures[] =& $fixture;
+				}
+
+				foreach ($models as $model) {
+					$object =& ClassRegistry::getObject($model['key']);
+					if ($object !== false) {
+						$object->setDataSource('test_suite');
+						$object->cacheSources = false;
+					}
+				}
+			}
+		}
+	}
+/**
+ * Callback issued when a controller's action has been invoked through testAction().
+ *
+ * @param Controller $controller	Controller that has been invoked.
+ * * @param array $params	Additional parameters as sent by testAction().
+ */
+	function endController(&$controller, $params = array()) {
+		if (isset($this->db) && isset($this->_actionFixtures) && !empty($this->_actionFixtures)) {
+			foreach ($this->_actionFixtures as $fixture) {
+				$fixture->drop($this->db);
+			}
+		}
+	}
+/**
+ * Executes a Cake URL, and can get (depending on the $params['return'] value):
+ *
+ * 1. 'result': Whatever the action returns (and also specifies $this->params['requested'] for controller)
+ * 2. 'view': The rendered view, without the layout
+ * 3. 'contents': The rendered view, within the layout.
+ * 4. 'vars': the view vars
+ *
+ * @param string $url	Cake URL to execute (e.g: /articles/view/455)
+ * @param array $params	Parameters, or simply a string of what to return
+ * @return mixed Whatever is returned depending of requested result
+ * @access public
+ */
+	function testAction($url, $params = array()) {
+		$default = array(
+			'return' => 'result',
+			'fixturize' => false,
+			'data' => array(),
+			'method' => 'post',
+			'connection' => 'default'
+		);
+
+		if (is_string($params)) {
+			$params = array('return' => $params);
+		}
+
+		$params = array_merge($default, $params);
+
+		$toSave = array(
+			'case' => null,
+			'group' => null,
+			'app' => null,
+			'output' => null,
+			'show' => null,
+			'plugin' => null
+		);
+		$this->__savedGetData = (empty($this->__savedGetData))
+				? array_intersect_key($_GET, $toSave)
+				: $this->__savedGetData;
+
+		$data = (!empty($params['data'])) ? $params['data'] : array();
+
+		if (strtolower($params['method']) == 'get') {
+			$_GET = array_merge($this->__savedGetData, $data);
+			$_POST = array();
+		} else {
+			$_POST = array('data' => $data);
+			$_GET = $this->__savedGetData;
+		}
+
+		$return = $params['return'];
+		$params = array_diff_key($params, array('data' => null, 'method' => null, 'return' => null));
+
+		$dispatcher =& new CakeTestDispatcher();
+		$dispatcher->testCase($this);
+
+		if ($return != 'result') {
+			if ($return != 'contents') {
+				$params['layout'] = false;
+			}
+
+			ob_start();
+			@$dispatcher->dispatch($url, $params);
+			$result = ob_get_clean();
+
+			if ($return == 'vars') {
+				$view =& ClassRegistry::getObject('view');
+				$viewVars = $view->getVars();
+
+				$result = array();
+
+				foreach ($viewVars as $var) {
+					$result[$var] = $view->getVar($var);
+				}
+
+				if (!empty($view->pageTitle)) {
+					$result = array_merge($result, array('title' => $view->pageTitle));
+				}
+			}
+		} else {
+			$params['return'] = 1;
+			$params['bare'] = 1;
+			$params['requested'] = 1;
+
+			$result = @$dispatcher->dispatch($url, $params);
+		}
+
+		if (isset($this->_actionFixtures)) {
+			unset($this->_actionFixtures);
+		}
+		ClassRegistry::flush();
+
+		return $result;
+	}
+/**
+ * Announces the start of a test.
+ *
+ * @param string $method Test method just started.
+ *
+ * @access public
+ */
+	function before($method) {
+		parent::before($method);
+
+		if (isset($this->fixtures) && (!is_array($this->fixtures) || empty($this->fixtures))) {
+			unset($this->fixtures);
+		}
+
+		// Set up DB connection
+		if (isset($this->fixtures) && strtolower($method) == 'start') {
+			$this->_initDb();
+			$this->_loadFixtures();
+		}
+
+		// Create records
+		if (isset($this->_fixtures) && isset($this->db) && !in_array(strtolower($method), array('start', 'end')) && $this->__truncated && $this->autoFixtures == true) {
+			foreach ($this->_fixtures as $fixture) {
+				$inserts = $fixture->insert($this->db);
+			}
+		}
+
+		if (!in_array(strtolower($method), $this->methods)) {
+			$this->startTest($method);
+		}
+	}
+/**
+ * Runs as first test to create tables.
+ *
+ * @access public
+ */
+	function start() {
+		if (isset($this->_fixtures) && isset($this->db)) {
+			Configure::write('Cache.disable', true);
+			$cacheSources = $this->db->cacheSources;
+			$this->db->cacheSources = false;
+			$sources = $this->db->listSources();
+			$this->db->cacheSources = $cacheSources;
+
+			if (!$this->dropTables) {
+				return;
+			}
+			foreach ($this->_fixtures as $fixture) {
+				if (in_array($fixture->table, $sources)) {
+					$fixture->drop($this->db);
+					$fixture->create($this->db);
+				} elseif (!in_array($fixture->table, $sources)) {
+					$fixture->create($this->db);
+				}
+			}
+		}
+	}
+/**
+ * Runs as last test to drop tables.
+ *
+ * @access public
+ */
+	function end() {
+		if (isset($this->_fixtures) && isset($this->db)) {
+			if ($this->dropTables) {
+				foreach (array_reverse($this->_fixtures) as $fixture) {
+					$fixture->drop($this->db);
+				}
+			}
+			$this->db->sources(true);
+			Configure::write('Cache.disable', false);
+		}
+
+		if (class_exists('ClassRegistry')) {
+			ClassRegistry::flush();
+		}
+	}
+/**
+ * Announces the end of a test.
+ *
+ * @param string $method Test method just finished.
+ *
+ * @access public
+ */
+	function after($method) {
+		if (isset($this->_fixtures) && isset($this->db) && !in_array(strtolower($method), array('start', 'end'))) {
+			foreach ($this->_fixtures as $fixture) {
+				$fixture->truncate($this->db);
+			}
+			$this->__truncated = true;
+		} else {
+			$this->__truncated = false;
+		}
+
+		if (!in_array(strtolower($method), $this->methods)) {
+			$this->endTest($method);
+		}
+		$this->_should_skip = false;
+
+		parent::after($method);
+	}
+/**
+ * Gets a list of test names. Normally that will be all internal methods that start with the
+ * name "test". This method should be overridden if you want a different rule.
+ *
+ * @return array	List of test names.
+ *
+ * @access public
+ */
+	function getTests() {
+		$methods = array_diff(parent::getTests(), array('testAction', 'testaction'));
+		$methods = array_merge(array_merge(array('start', 'startCase'), $methods), array('endCase', 'end'));
+		return $methods;
+	}
+/**
+ * Chooses which fixtures to load for a given test
+ *
+ * @param string $fixture Each parameter is a model name that corresponds to a fixture, i.e. 'Post', 'Author', etc.
+ * @access public
+ * @see CakeTestCase::$autoFixtures
+ */
+	function loadFixtures() {
+		$args = func_get_args();
+		foreach ($args as $class) {
+			if (isset($this->_fixtureClassMap[$class])) {
+				$fixture = $this->_fixtures[$this->_fixtureClassMap[$class]];
+
+				$fixture->truncate($this->db);
+				$fixture->insert($this->db);
+			} else {
+				trigger_error("Non-existent fixture class {$class} referenced in test", E_USER_WARNING);
+			}
+		}
+	}
+/**
+ * Takes an array $expected and generates a regex from it to match the provided $string. Samples for $expected:
+ *
+ * Checks for an input tag with a name attribute (contains any non-empty value) and an id attribute that contains 'my-input':
+ * 	array('input' => array('name', 'id' => 'my-input'))
+ *
+ * Checks for two p elements with some text in them:
+ * 	array(
+ * 		array('p' => true),
+ * 		'textA',
+ * 		'/p',
+ * 		array('p' => true),
+ * 		'textB',
+ * 		'/p'
+ *	)
+ *
+ * You can also specify a pattern expression as part of the attribute values, or the tag being defined,
+ * if you prepend the value with preg: and enclose it with slashes, like so:
+ *	array(
+ *  	array('input' => array('name', 'id' => 'preg:/FieldName\d+/')),
+ *  	'preg:/My\s+field/'
+ *	)
+ *
+ * Important: This function is very forgiving about whitespace and also accepts any permutation of attribute order.
+ * It will also allow whitespaces between specified tags.
+ *
+ * @param string $string An HTML/XHTML/XML string
+ * @param array $expected An array, see above
+ * @param string $message SimpleTest failure output string
+ * @access public
+ */
+	function assertTags($string, $expected, $fullDebug = false) {
+		$regex = array();
+		$normalized = array();
+		foreach ((array) $expected as $key => $val) {
+			if (!is_numeric($key)) {
+				$normalized[] = array($key => $val);
+			} else {
+				$normalized[] = $val;
+			}
+		}
+		$i = 0;
+		foreach ($normalized as $tags) {
+			$i++;
+			if (is_string($tags) && $tags{0} == '<') {
+				$tags = array(substr($tags, 1) => array());
+			} elseif (is_string($tags)) {
+				if (preg_match('/^\*?\//', $tags, $match)) {
+					$prefix = array(null, null);
+					if ($match[0] == '*/') {
+						$prefix = array('Anything, ', '.*?');
+					}
+					$regex[] = array(
+						sprintf('%sClose %s tag', $prefix[0], substr($tags, strlen($match[0]))),
+						sprintf('%s<[\s]*\/[\s]*%s[\s]*>[\n\r]*', $prefix[1], substr($tags,  strlen($match[0]))),
+						$i,
+					);
+					continue;
+				}
+				if (!empty($tags) && preg_match('/^preg\:\/(.+)\/$/i', $tags, $matches)) {
+					$tags = $matches[1];
+					$type = 'Regex matches';
+				} else {
+					$tags = preg_quote($tags, '/');
+					$type = 'Text equals';
+				}
+				$regex[] = array(
+					sprintf('%s "%s"', $type, $tags),
+					$tags,
+					$i,
+				);
+				continue;
+			}
+			foreach ($tags as $tag => $attributes) {
+				$regex[] = array(
+					sprintf('Open %s tag', $tag),
+					sprintf('[\s]*<%s', preg_quote($tag, '/')),
+					$i,
+				);
+				if ($attributes === true) {
+					$attributes = array();
+				}
+				$attrs = array();
+				$explanations = array();
+				foreach ($attributes as $attr => $val) {
+					if (is_numeric($attr) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
+						$attrs[] = $matches[1];
+						$explanations[] = sprintf('Regex "%s" matches', $matches[1]);
+						continue;
+					} else {
+						$quotes = '"';
+						if (is_numeric($attr)) {
+							$attr = $val;
+							$val = '.+?';
+							$explanations[] = sprintf('Attribute "%s" present', $attr);
+						} else if (!empty($val) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
+							$quotes = '"?';
+							$val = $matches[1];
+							$explanations[] = sprintf('Attribute "%s" matches "%s"', $attr, $val);
+						} else {
+							$explanations[] = sprintf('Attribute "%s" == "%s"', $attr, $val);
+							$val = preg_quote($val, '/');
+						}
+						$attrs[] = '[\s]+'.preg_quote($attr, '/').'='.$quotes.$val.$quotes;
+					}
+				}
+				if ($attrs) {
+					$permutations = $this->__array_permute($attrs);
+					$permutationTokens = array();
+					foreach ($permutations as $permutation) {
+						$permutationTokens[] = join('', $permutation);
+					}
+					$regex[] = array(
+						sprintf('%s', join(', ', $explanations)),
+						$permutationTokens,
+						$i,
+					);
+				}
+				$regex[] = array(
+					sprintf('End %s tag', $tag),
+					'[\s]*\/?[\s]*>[\n\r]*',
+					$i,
+				);
+			}
+		}
+		foreach ($regex as $i => $assertation) {
+			list($description, $expressions, $itemNum) = $assertation;
+			$matches = false;
+			foreach ((array)$expressions as $expression) {
+				if (preg_match(sprintf('/^%s/s', $expression), $string, $match)) {
+					$matches = true;
+					$string = substr($string, strlen($match[0]));
+					break;
+				}
+			}
+			if (!$matches) {
+				$this->assert(new TrueExpectation(), false, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
+				if ($fullDebug) {
+					debug($string, true);
+					debug($regex, true);
+				}
+				return false;
+			}
+		}
+		return $this->assert(new TrueExpectation(), true, '%s');
+	}
+/**
+ * Generates all permutation of an array $items and returns them in a new array.
+ *
+ * @param array $items An array of items
+ * @return array
+ * @access public
+ */
+	function __array_permute($items, $perms = array()) {
+		static $permuted;
+		if (empty($perms)) {
+			$permuted = array();
+		}
+
+		if (empty($items)) {
+			$permuted[] = $perms;
+		} else {
+			$numItems = count($items) - 1;
+			for ($i = $numItems; $i >= 0; --$i) {
+				$newItems = $items;
+				$newPerms = $perms;
+				list($tmp) = array_splice($newItems, $i, 1);
+				array_unshift($newPerms, $tmp);
+				$this->__array_permute($newItems, $newPerms);
+			}
+			return $permuted;
+		}
+	}
+/**
+ * Initialize DB connection.
+ *
+ * @access protected
+ */
+	function _initDb() {
+		$testDbAvailable = in_array('test', array_keys(ConnectionManager::enumConnectionObjects()));
+
+		$_prefix = null;
+
+		if ($testDbAvailable) {
+			// Try for test DB
+			restore_error_handler();
+			@$db =& ConnectionManager::getDataSource('test');
+			set_error_handler('simpleTestErrorHandler');
+			$testDbAvailable = $db->isConnected();
+		}
+
+		// Try for default DB
+		if (!$testDbAvailable) {
+			$db =& ConnectionManager::getDataSource('default');
+			$_prefix = $db->config['prefix'];
+			$db->config['prefix'] = 'test_suite_';
+		}
+
+		ConnectionManager::create('test_suite', $db->config);
+		$db->config['prefix'] = $_prefix;
+
+		// Get db connection
+		$this->db =& ConnectionManager::getDataSource('test_suite');
+		$this->db->cacheSources  = false;
+
+		ClassRegistry::config(array('ds' => 'test_suite'));
+	}
+/**
+ * Load fixtures specified in var $fixtures.
+ *
+ * @access private
+ */
+	function _loadFixtures() {
+		if (!isset($this->fixtures) || empty($this->fixtures)) {
+			return;
+		}
+
+		if (!is_array($this->fixtures)) {
+			$this->fixtures = array_map('trim', explode(',', $this->fixtures));
+		}
+
+		$this->_fixtures = array();
+
+		foreach ($this->fixtures as $index => $fixture) {
+			$fixtureFile = null;
+
+			if (strpos($fixture, 'core.') === 0) {
+				$fixture = substr($fixture, strlen('core.'));
+				foreach (Configure::corePaths('cake') as $key => $path) {
+					$fixturePaths[] = $path . 'tests' . DS . 'fixtures';
+				}
+			} elseif (strpos($fixture, 'app.') === 0) {
+				$fixture = substr($fixture, strlen('app.'));
+				$fixturePaths = array(
+					TESTS . 'fixtures',
+					VENDORS . 'tests' . DS . 'fixtures'
+				);
+			} elseif (strpos($fixture, 'plugin.') === 0) {
+				$parts = explode('.', $fixture, 3);
+				$pluginName = $parts[1];
+				$fixture = $parts[2];
+				$fixturePaths = array(
+					APP . 'plugins' . DS . $pluginName . DS . 'tests' . DS . 'fixtures',
+					TESTS . 'fixtures',
+					VENDORS . 'tests' . DS . 'fixtures'
+				);
+				$pluginPaths = Configure::read('pluginPaths');
+				foreach ($pluginPaths as $path) {
+					if (file_exists($path . $pluginName . DS . 'tests' . DS. 'fixtures')) {
+						$fixturePaths[0] = $path . $pluginName . DS . 'tests' . DS. 'fixtures';
+						break;
+					}
+				}
+			} else {
+				$fixturePaths = array(
+					TESTS . 'fixtures',
+					VENDORS . 'tests' . DS . 'fixtures',
+					TEST_CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'tests' . DS . 'fixtures'
+				);
+			}
+
+			foreach ($fixturePaths as $path) {
+				if (is_readable($path . DS . $fixture . '_fixture.php')) {
+					$fixtureFile = $path . DS . $fixture . '_fixture.php';
+					break;
+				}
+			}
+
+			if (isset($fixtureFile)) {
+				require_once($fixtureFile);
+				$fixtureClass = Inflector::camelize($fixture) . 'Fixture';
+				$this->_fixtures[$this->fixtures[$index]] =& new $fixtureClass($this->db);
+				$this->_fixtureClassMap[Inflector::camelize($fixture)] = $this->fixtures[$index];
+			}
+		}
+
+		if (empty($this->_fixtures)) {
+			unset($this->_fixtures);
+		}
+	}
+}
+?>
Index: cake/tests/lib/footer.php
===================================================================
--- cake/tests/lib/footer.php	(revision 0)
+++ cake/tests/lib/footer.php	(revision 0)
@@ -0,0 +1,36 @@
+<?php
+/* SVN FILE: $Id: footer.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>	</div>
+		</div>
+ 		<div id="footer">
+ 			<!--PLEASE USE ONE OF THE POWERED BY CAKEPHP LOGO-->
+ 			<a href="http://www.cakephp.org/" target="_blank">
+ 				<img src="<?php echo $baseUrl; ?>img/cake.power.gif" alt="CakePHP(tm) :: Rapid Development Framework" /></a></p>
+ 		</div>
+	</div>
+</body>
+</html>
\ No newline at end of file
Index: cake/tests/lib/xdebug.php
===================================================================
--- cake/tests/lib/xdebug.php	(revision 0)
+++ cake/tests/lib/xdebug.php	(revision 0)
@@ -0,0 +1,31 @@
+<?php
+/* SVN FILE: $Id: xdebug.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+	<div id="content">
+		<h2>Xdebug is not installed</h2>
+		<p>You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation.</p>
+		<p><a href="http://www.xdebug.org/docs/install" target="_blank">Learn How To Install Xdebug</a></p>
\ No newline at end of file
Index: cake/tests/lib/code_coverage_manager.php
===================================================================
--- cake/tests/lib/code_coverage_manager.php	(revision 0)
+++ cake/tests/lib/code_coverage_manager.php	(revision 0)
@@ -0,0 +1,764 @@
+<?php
+/* SVN FILE: $Id: code_coverage_manager.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * A class to manage all aspects for Code Coverage Analysis
+ *
+ * This class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Folder');
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CodeCoverageManager {
+/**
+ * Is this an app test case?
+ *
+ * @var string
+ */
+	var $appTest = false;
+/**
+ * Is this an app test case?
+ *
+ * @var string
+ */
+	var $pluginTest = false;
+/**
+ * Is this a grouptest?
+ *
+ * @var string
+ * @access public
+ */
+	var $groupTest = false;
+/**
+ * The test case file to analyze
+ *
+ * @var string
+ */
+	var $testCaseFile = '';
+/**
+ * The currently used CakeTestReporter
+ *
+ * @var string
+ */
+	var $reporter = '';
+/**
+ * undocumented variable
+ *
+ * @var string
+ */
+	var $numDiffContextLines = 7;
+/**
+ * Returns a singleton instance
+ *
+ * @return object
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new CodeCoverageManager();
+		}
+		return $instance[0];
+	}
+/**
+ * Starts a new Coverage Analyzation for a given test case file
+ * @TODO: Works with $_GET now within the function body, which will make it hard when we do code coverage reports for CLI
+ *
+ * @param string $testCaseFile
+ * @param string $reporter
+ * @return void
+ */
+	function start($testCaseFile, &$reporter) {
+		$manager =& CodeCoverageManager::getInstance();
+		$manager->reporter = $reporter;
+		$testCaseFile = str_replace(DS . DS, DS, $testCaseFile);
+		$thisFile = str_replace('.php', '.test.php', basename(__FILE__));
+
+		if (strpos($testCaseFile, $thisFile) !== false) {
+			trigger_error('Xdebug supports no parallel coverage analysis - so this is not possible.', E_USER_ERROR);
+		}
+
+		if (isset($_GET['app'])) {
+			$manager->appTest = true;
+		}
+
+		if (isset($_GET['group'])) {
+			$manager->groupTest = true;
+		}
+
+		if (isset($_GET['plugin'])) {
+			$manager->pluginTest = Inflector::underscore($_GET['plugin']);
+		}
+		$manager->testCaseFile = $testCaseFile;
+		xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
+	}
+/**
+ * Stops the current code coverage analyzation and dumps a nice report depending on the reporter that was passed to start()
+ *
+ * @return void
+ */
+	function report($output = true) {
+		$manager =& CodeCoverageManager::getInstance();
+
+		if (!$manager->groupTest) {
+			$testObjectFile = $manager->__testObjectFileFromCaseFile($manager->testCaseFile, $manager->appTest);
+
+			if (!file_exists($testObjectFile)) {
+				trigger_error('This test object file is invalid: ' . $testObjectFile);
+				return ;
+			}
+			$dump = xdebug_get_code_coverage();
+			xdebug_stop_code_coverage();
+			$coverageData = array();
+
+			foreach ($dump as $file => $data) {
+				if ($file == $testObjectFile) {
+					$coverageData = $data;
+					break;
+				}
+			}
+
+			if (empty($coverageData) && $output) {
+				echo 'The test object file is never loaded.';
+			}
+			$execCodeLines = $manager->__getExecutableLines(file_get_contents($testObjectFile));
+			$result = '';
+
+			switch (get_class($manager->reporter)) {
+				case 'CakeHtmlReporter':
+					$result = $manager->reportCaseHtmlDiff(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+				case 'CLIReporter':
+					$result = $manager->reportCaseCli(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+				default:
+					trigger_error('Currently only HTML and CLI reporting is supported for code coverage analysis.');
+					break;
+			}
+		} else {
+			$testObjectFiles = $manager->__testObjectFilesFromGroupFile($manager->testCaseFile, $manager->appTest);
+
+			foreach ($testObjectFiles as $file) {
+				if (!file_exists($file)) {
+					trigger_error('This test object file is invalid: ' . $file);
+					return ;
+				}
+			}
+			$dump = xdebug_get_code_coverage();
+			xdebug_stop_code_coverage();
+			$coverageData = array();
+			foreach ($dump as $file => $data) {
+				if (in_array($file, $testObjectFiles)) {
+					$coverageData[$file] = $data;
+				}
+			}
+
+			if (empty($coverageData) && $output) {
+				echo 'The test object files are never loaded.';
+			}
+			$execCodeLines = $manager->__getExecutableLines($testObjectFiles);
+			$result = '';
+
+			switch (get_class($manager->reporter)) {
+				case 'CakeHtmlReporter':
+					$result = $manager->reportGroupHtml($testObjectFiles, $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+				case 'CLIReporter':
+					$result = $manager->reportGroupCli($testObjectFiles, $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+				default:
+					trigger_error('Currently only HTML and CLI reporting is supported for code coverage analysis.');
+					break;
+			}
+		}
+
+		if ($output) {
+			echo $result;
+		}
+	}
+/**
+ * Html reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ */
+	function reportCaseHtml($testObjectFile, $coverageData, $execCodeLines) {
+		$manager = CodeCoverageManager::getInstance();
+		$lineCount = $coveredCount = 0;
+		$report = '';
+
+		foreach ($testObjectFile as $num => $line) {
+			$num++;
+			$foundByManualFinder = isset($execCodeLines[$num]) && trim($execCodeLines[$num]) != '';
+			$foundByXdebug = isset($coverageData[$num]) && $coverageData[$num] !== -2;
+
+			// xdebug does not find all executable lines (zend engine fault)
+			if ($foundByManualFinder && $foundByXdebug) {
+				$class = 'uncovered';
+				$lineCount++;
+
+				if ($coverageData[$num] > 0) {
+					$class = 'covered';
+					$coveredCount++;
+				}
+			} else {
+				$class = 'ignored';
+			}
+			$report .= $manager->__paintCodeline($class, $num, $line);;
+		}
+		return $manager->__paintHeader($lineCount, $coveredCount, $report);
+	}
+/**
+ * Diff reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ */
+	function reportCaseHtmlDiff($testObjectFile, $coverageData, $execCodeLines, $numContextLines) {
+		$manager = CodeCoverageManager::getInstance();
+		$total = count($testObjectFile);
+		$lines = array();
+
+		for ($i = 1; $i < $total + 1; $i++) {
+			$foundByManualFinder = isset($execCodeLines[$i]) && trim($execCodeLines[$i]) != '';
+			$foundByXdebug = isset($coverageData[$i]);
+
+			if (!$foundByManualFinder || !$foundByXdebug || $coverageData[$i] === -2) {
+				if (isset($lines[$i])) {
+					$lines[$i] = 'ignored ' . $lines[$i];
+				} else {
+					$lines[$i] = 'ignored';
+				}
+				continue;
+			}
+
+			if ($coverageData[$i] !== -1) {
+				if (isset($lines[$i])) {
+					$lines[$i] = 'covered ' . $lines[$i];
+				} else {
+					$lines[$i] = 'covered';
+				}
+				continue;
+			}
+			$lines[$i] = 'uncovered show';
+			$foundEndBlockInContextSearch = false;
+
+			for ($j = 1; $j <= $numContextLines; $j++) {
+				$key = $i - $j;
+
+				if ($key > 0 && isset($lines[$key])) {
+					if (strpos($lines[$key], 'end') !== false) {
+						$foundEndBlockInContextSearch = true;
+						if ($j < $numContextLines) {
+							$lines[$key] = str_replace('end', '', $lines[$key-1]);
+						}
+					}
+
+					if (strpos($lines[$key], 'uncovered') === false) {
+						if (strpos($lines[$key], 'covered') !== false) {
+							$lines[$key] .= ' show';
+						} else {
+							$lines[$key] = 'ignored show';
+						}
+					}
+
+					if ($j == $numContextLines) {
+						$lineBeforeIsEndBlock = strpos($lines[$key-1], 'end') !== false;
+						$lineBeforeIsShown = strpos($lines[$key-1], 'show') !== false;
+						$lineBeforeIsUncovered = strpos($lines[$key-1], 'uncovered') !== false;
+
+						if (!$foundEndBlockInContextSearch && !$lineBeforeIsUncovered && ($lineBeforeIsEndBlock)) {
+							$lines[$key-1] = str_replace('end', '', $lines[$key-1]);
+						}
+
+						if (!$lineBeforeIsShown && !$lineBeforeIsUncovered) {
+							$lines[$key] .= ' start';
+						}
+					}
+				}
+				$key = $i + $j;
+
+				if ($key < $total) {
+					$lines[$key] = 'show';
+
+					if ($j == $numContextLines) {
+						$lines[$key] .= ' end';
+					}
+				}
+			}
+		}
+
+		// find the last "uncovered" or "show"n line and "end" its block
+		$lastShownLine = $manager->__array_strpos($lines, 'show', true);
+		if (isset($lines[$lastShownLine])) {
+			$lines[$lastShownLine] .= ' end';
+		}
+
+		// give the first start line another class so we can control the top padding of the entire results
+		$firstShownLine = $manager->__array_strpos($lines, 'show');
+		if (isset($lines[$firstShownLine])) {
+			$lines[$firstShownLine] .= ' realstart';
+		}
+
+		// get the output
+		$lineCount = $coveredCount = 0;
+		$report = '';
+		foreach ($testObjectFile as $num => $line) {
+			// start line count at 1
+			$num++;
+			$class = $lines[$num];
+
+			if (strpos($class, 'ignored') === false) {
+				$lineCount++;
+
+				if (strpos($class, 'covered') !== false && strpos($class, 'uncovered') === false) {
+					$coveredCount++;
+				}
+			}
+
+			if (strpos($class, 'show') !== false) {
+				$report .= $manager->__paintCodeline($class, $num, $line);
+			}
+		}
+		return $manager->__paintHeader($lineCount, $coveredCount, $report);
+	}
+/**
+ * CLI reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ */
+	function reportCaseCli($testObjectFile, $coverageData, $execCodeLines) {
+		$manager = CodeCoverageManager::getInstance();
+		$lineCount = $coveredCount = 0;
+		$report = '';
+
+		foreach ($testObjectFile as $num => $line) {
+			$num++;
+			$foundByManualFinder = isset($execCodeLines[$num]) && trim($execCodeLines[$num]) != '';
+			$foundByXdebug = isset($coverageData[$num]) && $coverageData[$num] !== -2;
+
+			if ($foundByManualFinder && $foundByXdebug) {
+				$lineCount++;
+
+				if ($coverageData[$num] > 0) {
+					$coveredCount++;
+				}
+			}
+		}
+		return $manager->__paintHeaderCli($lineCount, $coveredCount, $report);
+	}
+/**
+ * Diff reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ */
+	function reportGroupHtml($testObjectFiles, $coverageData, $execCodeLines, $numContextLines) {
+		$manager = CodeCoverageManager::getInstance();
+		$report = '';
+
+		foreach ($testObjectFiles as $testObjectFile) {
+			$lineCount = $coveredCount = 0;
+			$objFilename = $testObjectFile;
+			$testObjectFile = file($testObjectFile);
+
+			foreach ($testObjectFile as $num => $line) {
+				$num++;
+				$foundByManualFinder = isset($execCodeLines[$objFilename][$num]) && trim($execCodeLines[$objFilename][$num]) != '';
+				$foundByXdebug = isset($coverageData[$objFilename][$num]) && $coverageData[$objFilename][$num] !== -2;
+
+				if ($foundByManualFinder && $foundByXdebug) {
+					$class = 'uncovered';
+					$lineCount++;
+
+					if ($coverageData[$objFilename][$num] > 0) {
+						$class = 'covered';
+						$coveredCount++;
+					}
+				} else {
+					$class = 'ignored';
+				}
+			}
+			$report .= $manager->__paintGroupResultLine($objFilename, $lineCount, $coveredCount);
+		}
+		return $manager->__paintGroupResultHeader($report);
+	}
+/**
+ * CLI reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ */
+	function reportGroupCli($testObjectFiles, $coverageData, $execCodeLines) {
+		$manager = CodeCoverageManager::getInstance();
+		$report = '';
+
+		foreach ($testObjectFiles as $testObjectFile) {
+			$lineCount = $coveredCount = 0;
+			$objFilename = $testObjectFile;
+			$testObjectFile = file($testObjectFile);
+
+			foreach ($testObjectFile as $num => $line) {
+				$num++;
+				$foundByManualFinder = isset($execCodeLines[$objFilename][$num]) && trim($execCodeLines[$objFilename][$num]) != '';
+				$foundByXdebug = isset($coverageData[$objFilename][$num]) && $coverageData[$objFilename][$num] !== -2;
+
+				if ($foundByManualFinder && $foundByXdebug) {
+					$lineCount++;
+
+					if ($coverageData[$objFilename][$num] > 0) {
+						$coveredCount++;
+					}
+				}
+			}
+			$report .= $manager->__paintGroupResultLineCli($objFilename, $lineCount, $coveredCount);
+		}
+		return $report;
+	}
+/**
+ * Returns the name of the test object file based on a given test case file name
+ *
+ * @param string $file
+ * @param string $isApp
+ * @return string name of the test object file
+ * @access private
+ */
+	function __testObjectFileFromCaseFile($file, $isApp = true) {
+		$manager = CodeCoverageManager::getInstance();
+		$path = $manager->__getTestFilesPath($isApp);
+		$folderPrefixMap = array(
+			'behaviors' => 'models',
+			'components' => 'controllers',
+			'helpers' => 'views'
+		);
+
+		foreach ($folderPrefixMap as $dir => $prefix) {
+			if (strpos($file, $dir) === 0) {
+				$path .= $prefix . DS;
+				break;
+			}
+		}
+		$testManager =& new TestManager();
+		$testFile = str_replace(array('/', $testManager->_testExtension), array(DS, '.php'), $file);
+
+		$folder =& new Folder();
+		$folder->cd(ROOT . DS . CAKE_TESTS_LIB);
+		$contents = $folder->ls();
+
+		if (in_array(basename($testFile), $contents[1])) {
+			$testFile = basename($testFile);
+			$path = ROOT . DS . CAKE_TESTS_LIB;
+		}
+		$path .= $testFile;
+		$realpath = realpath($path);
+
+		if ($realpath) {
+			return $realpath;
+		}
+		return $path;
+	}
+/**
+ * Returns an array of names of the test object files based on a given test group file name
+ *
+ * @param array $files
+ * @param string $isApp
+ * @return array names of the test object files
+ * @access private
+ */
+	function __testObjectFilesFromGroupFile($groupFile, $isApp = true) {
+		$manager = CodeCoverageManager::getInstance();
+		$testManager =& new TestManager();
+
+		$path = TESTS . 'groups';
+
+		if (!$isApp) {
+			$path = ROOT . DS . 'cake' . DS . 'tests' . DS . 'groups';
+		}
+
+		if (!!$manager->pluginTest) {
+			$path = APP . 'plugins' . DS . $manager->pluginTest . DS . 'tests' . DS . 'groups';
+
+			$pluginPaths = Configure::read('pluginPaths');
+			foreach ($pluginPaths as $pluginPath) {
+				$tmpPath = $pluginPath . $manager->pluginTest . DS . 'tests' . DS. 'groups';
+				if (file_exists($tmpPath)) {
+					$path = $tmpPath;
+					break;
+				}
+			}
+		}
+		$path .= DS . $groupFile . $testManager->_groupExtension;
+
+		if (!file_exists($path)) {
+			trigger_error('This group file does not exist!');
+			return array();
+		}
+
+		$result = array();
+		$groupContent = file_get_contents($path);
+		$ds = '\s*\.\s*DS\s*\.\s*';
+		$pluginTest = 'APP\.\'plugins\'' . $ds . '\'' . $manager->pluginTest . '\'' . $ds . '\'tests\'' . $ds . '\'cases\'';
+		$pattern = '/\s*TestManager::addTestFile\(\s*\$this,\s*(' . $pluginTest . '|APP_TEST_CASES|CORE_TEST_CASES)' . $ds . '(.*?)\)/i';
+		preg_match_all($pattern, $groupContent, $matches);
+
+		foreach ($matches[2] as $file) {
+			$patterns = array(
+				'/\s*\.\s*DS\s*\.\s*/',
+				'/\s*APP_TEST_CASES\s*/',
+				'/\s*CORE_TEST_CASES\s*/',
+			);
+
+			$replacements = array(DS, '', '');
+			$file = preg_replace($patterns, $replacements, $file);
+			$file = str_replace("'", '', $file);
+			$result[] = $manager->__testObjectFileFromCaseFile($file, $isApp) . '.php';
+		}
+		return $result;
+	}
+/**
+ * Parses a given code string into an array of lines and replaces some non-executable code lines with the needed
+ * amount of new lines in order for the code line numbers to stay in sync
+ *
+ * @param string $content
+ * @return array array of lines
+ * @access private
+ */
+	function __getExecutableLines($content) {
+		if (is_array($content)) {
+			$manager = CodeCoverageManager::getInstance();
+			$result = array();
+			foreach ($content as $file) {
+				$result[$file] = $manager->__getExecutableLines(file_get_contents($file));
+			}
+			return $result;
+		}
+		$content = h($content);
+		// arrays are 0-indexed, but we want 1-indexed stuff now as we are talking code lines mind you (**)
+		$content = "\n" . $content;
+		// // strip unwanted lines
+		$content = preg_replace_callback("/(@codeCoverageIgnoreStart.*?@codeCoverageIgnoreEnd)/is", array('CodeCoverageManager', '__replaceWithNewlines'), $content);
+		// strip php | ?\> tag only lines
+		$content = preg_replace('/[ |\t]*[&lt;\?php|\?&gt;]+[ |\t]*/', '', $content);
+
+		// strip lines that contain only braces and parenthesis
+		$content = preg_replace('/[ |\t]*[{|}|\(|\)]+[ |\t]*/', '', $content);
+		$result = explode("\n", $content);
+		// unset the zero line again to get the original line numbers, but starting at 1, see (**)
+		unset($result[0]);
+		return $result;
+	}
+/**
+ * Replaces a given arg with the number of newlines in it
+ *
+ * @return string the number of newlines in a given arg
+ * @access private
+ */
+	function __replaceWithNewlines() {
+		$args = func_get_args();
+		$numLineBreaks = count(explode("\n", $args[0][0]));
+		return str_pad('', $numLineBreaks - 1, "\n");
+	}
+/**
+ * Paints the headline for code coverage analysis
+ *
+ * @param string $codeCoverage
+ * @param string $report
+ * @return void
+ * @access private
+ */
+	function __paintHeader($lineCount, $coveredCount, $report) {
+		$manager =& CodeCoverageManager::getInstance();
+		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
+		return $report = '<h2>Code Coverage: ' . $codeCoverage . '%</h2>
+						<div class="code-coverage-results"><pre>' . $report . '</pre></div>';
+	}
+/**
+ * Displays a notification concerning group test results
+ *
+ * @return void
+ * @access public
+ */
+	function __paintGroupResultHeader($report) {
+		return '<div class="code-coverage-results"><p class="note">Please keep in mind that the coverage can vary a little bit depending on how much the different tests in the group interfere. If for example, TEST A calls a line from TEST OBJECT B, the coverage for TEST OBJECT B will be a little greater than if you were running the corresponding test case for TEST OBJECT B alone.</p><pre>' . $report . '</pre></div>';
+	}
+/**
+ * Paints the headline for code coverage analysis
+ *
+ * @param string $codeCoverage
+ * @param string $report
+ * @return void
+ * @access private
+ */
+	function __paintGroupResultLine($file, $lineCount, $coveredCount) {
+		$manager =& CodeCoverageManager::getInstance();
+		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
+		$class = 'result-bad';
+
+		if ($codeCoverage > 50) {
+			$class = 'result-ok';
+		}
+		if ($codeCoverage > 80) {
+			$class = 'result-good';
+		}
+		return '<p>Code Coverage for ' . $file . ': <span class="' . $class . '">' . $codeCoverage . '%</span></p>';
+	}
+/**
+ * Paints the headline for code coverage analysis
+ *
+ * @param string $codeCoverage
+ * @param string $report
+ * @return void
+ * @access private
+ */
+	function __paintGroupResultLineCli($file, $lineCount, $coveredCount) {
+		$manager =& CodeCoverageManager::getInstance();
+		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
+		$class = 'bad';
+
+		if ($codeCoverage > 50) {
+			$class = 'ok';
+		}
+		if ($codeCoverage > 80) {
+			$class = 'good';
+		}
+		return "\n" . 'Code Coverage for ' . $file . ': ' . $codeCoverage . '% (' . $class . ')' . "\n";
+	}
+/**
+ * Paints the headline for code coverage analysis in the CLI
+ *
+ * @param string $codeCoverage
+ * @param string $report
+ * @return void
+ * @access private
+ */
+	function __paintHeaderCli($lineCount, $coveredCount, $report) {
+		$manager =& CodeCoverageManager::getInstance();
+		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
+		return $report = 'Code Coverage: ' . $codeCoverage . '%';
+	}
+/**
+ * Paints a code line for html output
+ *
+ * @package       default
+ * @access private
+ */
+	function __paintCodeline($class, $num, $line) {
+		$line = h($line);
+
+		if (trim($line) == '') {
+			$line = '&nbsp;'; // Win IE fix
+		}
+		return '<div class="code-line ' . trim($class) . '"><span class="line-num">' . $num . '</span><span class="content">' . $line . '</span></div>';
+	}
+/**
+ * Calculates the coverage percentage based on a line count and a covered line count
+ *
+ * @param string $lineCount
+ * @param string $coveredCount
+ * @return void
+ * @access private
+ */
+	function __calcCoverage($lineCount, $coveredCount) {
+		if ($coveredCount > $lineCount) {
+			trigger_error('Sorry, you cannot have more covered lines than total lines!');
+		}
+		return ($lineCount != 0)
+				? round(100 * $coveredCount / $lineCount, 2)
+				: '0.00';
+	}
+/**
+ * Gets us the base path to look for the test files
+ *
+ * @param string $isApp
+ * @return void
+ * @access public
+ */
+	function __getTestFilesPath($isApp = true) {
+		$manager = CodeCoverageManager::getInstance();
+		$path = ROOT . DS;
+
+		if ($isApp) {
+			$path .= APP_DIR . DS;
+		} elseif (!!$manager->pluginTest) {
+			$pluginPath = APP . 'plugins' . DS . $manager->pluginTest . DS;
+
+			$pluginPaths = Configure::read('pluginPaths');
+			foreach ($pluginPaths as $tmpPath) {
+				$tmpPath = $tmpPath . $manager->pluginTest . DS;
+				if (file_exists($tmpPath)) {
+					$pluginPath = $tmpPath;
+					break;
+				}
+			}
+
+			$path = $pluginPath;
+		} else {
+			$path = TEST_CAKE_CORE_INCLUDE_PATH;
+		}
+
+		return $path;
+	}
+/**
+ * Finds the last element of an array that contains $needle in a strpos computation
+ *
+ * @param array $arr
+ * @param string $needle
+ * @return void
+ * @access private
+ */
+	function __array_strpos($arr, $needle, $reverse = false) {
+		if (!is_array($arr) || empty($arr)) {
+			return false;
+		}
+
+		if ($reverse) {
+			$arr = array_reverse($arr, true);
+		}
+
+		foreach ($arr as $key => $val) {
+			if (strpos($val, $needle) !== false) {
+				return $key;
+			}
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/cake_test_model.php
===================================================================
--- cake/tests/lib/cake_test_model.php	(revision 0)
+++ cake/tests/lib/cake_test_model.php	(revision 0)
@@ -0,0 +1,38 @@
+<?php
+/* SVN FILE: $Id: cake_test_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once LIBS.'model'.DS.'model.php';
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestModel extends Model {
+	var $useDbConfig = 'test_suite';
+	var $cacheSources = false;
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/cake_reporter.php
===================================================================
--- cake/tests/lib/cake_reporter.php	(revision 0)
+++ cake/tests/lib/cake_reporter.php	(revision 0)
@@ -0,0 +1,199 @@
+<?php
+/* SVN FILE: $Id: cake_reporter.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeHtmlReporter extends SimpleReporter {
+	var $_character_set;
+	var $_show_passes = false;
+/**
+ *    Does nothing yet. The first output will
+ *    be sent on the first test start. For use
+ *    by a web browser.
+ *    @access public
+ */
+	function CakeHtmlReporter($character_set = 'ISO-8859-1') {
+		if (isset($_GET['show_passes']) && $_GET['show_passes']) {
+			$this->_show_passes = true;
+		}
+		$this->SimpleReporter();
+		$this->_character_set = $character_set;
+	}
+/**
+ * Paints the top of the web page setting the
+ * title to the name of the starting test.
+ * @param string $test_name      Name class of test.
+ * @access public
+ */
+	function paintHeader($testName) {
+		$this->sendNoCacheHeaders();
+		echo "<h2>$testName</h2>\n";
+		echo "<ul class='tests'>\n";
+	}
+/**
+ * Send the headers necessary to ensure the page is
+ * reloaded on every request. Otherwise you could be
+ * scratching your head over out of date test data.
+ * @access public
+ * @static
+ */
+	function sendNoCacheHeaders() {
+		if (!headers_sent()) {
+			header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+			header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+			header("Cache-Control: no-store, no-cache, must-revalidate");
+			header("Cache-Control: post-check=0, pre-check=0", false);
+			header("Pragma: no-cache");
+		}
+	}
+/**
+ * Paints the end of the test with a summary of
+ * the passes and failures.
+ * @param string $test_name        Name class of test.
+ * @access public
+ */
+	function paintFooter($test_name) {
+		$colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green");
+		echo "</ul>\n";
+		echo "<div style=\"";
+		echo "padding: 8px; margin: 1em 0; background-color: $colour; color: white;";
+		echo "\">";
+		echo $this->getTestCaseProgress() . "/" . $this->getTestCaseCount();
+		echo " test cases complete:\n";
+		echo "<strong>" . $this->getPassCount() . "</strong> passes, ";
+		echo "<strong>" . $this->getFailCount() . "</strong> fails and ";
+		echo "<strong>" . $this->getExceptionCount() . "</strong> exceptions.";
+		echo "</div>\n";
+		echo "</body>\n</html>\n";
+	}
+/**
+ * Paints the test failure with a breadcrumbs
+ * trail of the nesting test suites below the
+ * top level test.
+ * @param string $message Failure message displayed in
+ *                       the context of the other tests.
+ * @access public
+ */
+	function paintFail($message) {
+		parent::paintFail($message);
+		echo "<li class='fail'>\n";
+		echo "<span>Failed</span>";
+		echo "<div class='msg'>" . $this->_htmlEntities($message) . "</div>\n";
+		$breadcrumb = Set::filter($this->getTestList());
+		array_shift($breadcrumb);
+		echo "<div>" . implode(" -&gt; ", $breadcrumb) . "</div>\n";
+		echo "</li>\n";
+	}
+/**
+ * Paints the test pass with a breadcrumbs
+ * trail of the nesting test suites below the
+ * top level test.
+ * @param string $message Pass message displayed in
+ *                        the context of the other tests.
+ * @access public
+ */
+	function paintPass($message) {
+		parent::paintPass($message);
+
+		if ($this->_show_passes) {
+			echo "<li class='pass'>\n";
+			echo "<span>Passed</span> ";
+			$breadcrumb = Set::filter($this->getTestList());
+			array_shift($breadcrumb);
+			echo implode(" -&gt; ", $breadcrumb);
+			echo "<br />" . $this->_htmlEntities($message) . "\n";
+			echo "</li>\n";
+		}
+	}
+/**
+ * Paints a PHP error.
+ * @param string $message Message is ignored.
+ * @access public
+ */
+	function paintError($message) {
+		parent::paintError($message);
+		echo "<li class='error'>\n";
+		echo "<span>Error</span>";
+		echo "<div class='msg'>" . $this->_htmlEntities($message) . "</div>\n";
+		$breadcrumb = Set::filter($this->getTestList());
+		array_shift($breadcrumb);
+		echo "<div>" . implode(" -&gt; ", $breadcrumb) . "</div>\n";
+		echo "</li>\n";
+	}
+/**
+ * Paints a PHP exception.
+ * @param Exception $exception Exception to display.
+ * @access public
+ */
+	function paintException($exception) {
+		parent::paintException($exception);
+		echo "<li class='fail'>\n";
+		echo "<span>Exception</span>";
+		$message = 'Unexpected exception of type [' . get_class($exception) .
+			'] with message ['. $exception->getMessage() .
+			'] in ['. $exception->getFile() .
+			' line ' . $exception->getLine() . ']';
+		echo "<div class='msg'>" . $this->_htmlEntities($message) . "</div>\n";
+		$breadcrumb = Set::filter($this->getTestList());
+		array_shift($breadcrumb);
+		echo "<div>" . implode(" -&gt; ", $breadcrumb) . "</div>\n";
+		echo "</li>\n";
+	}
+/**
+ * Prints the message for skipping tests.
+ * @param string $message    Text of skip condition.
+ * @access public
+ */
+	function paintSkip($message) {
+		parent::paintSkip($message);
+		echo "<li class='skipped'>\n";
+		echo "<span>Skipped</span> ";
+		echo $this->_htmlEntities($message);
+		echo "</li>\n";
+	}
+/**
+ * Paints formatted text such as dumped variables.
+ * @param string $message Text to show.
+ * @access public
+ */
+	function paintFormattedMessage($message) {
+		echo '<pre>' . $this->_htmlEntities($message) . '</pre>';
+	}
+/**
+ * Character set adjusted entity conversion.
+ * @param string $message Plain text or Unicode message.
+ * @return string Browser readable message.
+ * @access protected
+ */
+	function _htmlEntities($message) {
+		return htmlentities($message, ENT_COMPAT, $this->_character_set);
+	}
+}
+?>
\ No newline at end of file
Index: cake/tests/lib/cake_test_fixture.php
===================================================================
--- cake/tests/lib/cake_test_fixture.php	(revision 0)
+++ cake/tests/lib/cake_test_fixture.php	(revision 0)
@@ -0,0 +1,188 @@
+<?php
+/* SVN FILE: $Id: cake_test_fixture.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestFixture extends Object {
+/**
+ * Cake's DBO driver (e.g: DboMysql).
+ *
+ * @access public
+ */
+	var $db = null;
+/**
+ * Full Table Name
+ *
+ * @access public
+ */
+	var $table = null;
+
+/**
+ * Instantiate the fixture.
+ *
+ * @access public
+ */
+	function __construct() {
+		App::import('Model', 'Schema');
+		$this->Schema = new CakeSchema(array('name' => 'TestSuite', 'connection' => 'test_suite'));
+
+		$this->init();
+	}
+/**
+ * Initialize the fixture.
+ *
+ * @param object	Cake's DBO driver (e.g: DboMysql).
+ * @access public
+ *
+ */
+	function init() {
+		if (isset($this->import) && (is_string($this->import) || is_array($this->import))) {
+			$import = array_merge(array('connection' => 'default', 'records' => false), is_array($this->import) ? $this->import : array('model' => $this->import));
+
+			if (isset($import['model']) && App::import('Model', $import['model'])) {
+				ClassRegistry::config(array('ds' => $import['connection']));
+				$model =& ClassRegistry::init($import['model']);
+				$db =& ConnectionManager::getDataSource($model->useDbConfig);
+				$db->cacheSources = false;
+				$this->fields = $model->schema(true);
+				$this->fields[$model->primaryKey]['key'] = 'primary';
+				ClassRegistry::config(array('ds' => 'test_suite'));
+				ClassRegistry::flush();
+			} elseif (isset($import['table'])) {
+				$model =& new Model(null, $import['table'], $import['connection']);
+				$db =& ConnectionManager::getDataSource($import['connection']);
+				$db->cacheSources = false;
+				$model->useDbConfig = $import['connection'];
+				$model->name = Inflector::camelize(Inflector::singularize($import['table']));
+				$model->table = $import['table'];
+				$model->tablePrefix = $db->config['prefix'];
+				$this->fields = $model->schema(true);
+			}
+
+			if (isset($import['records']) && $import['records'] !== false && isset($model) && isset($db)) {
+				$this->records = array();
+				$query = array(
+					'fields' => array_keys($this->fields),
+					'table' => $db->fullTableName($model->table),
+					'alias' => $model->alias,
+					'conditions' => array(),
+					'order' => null,
+					'limit' => null,
+					'group' => null
+				);
+
+				foreach ($query['fields'] as $index => $field) {
+					$query['fields'][$index] = $db->name($query['alias']) . '.' . $db->name($field);
+				}
+				$records = $db->fetchAll($db->buildStatement($query, $model), false, $model->alias);
+
+				if ($records !== false && !empty($records)) {
+					$this->records = Set::extract($records, '{n}.' . $model->alias);
+				}
+			}
+		}
+
+		if (!isset($this->table)) {
+			$this->table = Inflector::underscore(Inflector::pluralize($this->name));
+		}
+
+		if (!isset($this->primaryKey) && isset($this->fields['id'])) {
+			$this->primaryKey = 'id';
+		}
+	}
+/**
+ * Run before all tests execute, should return SQL statement to create table for this fixture could be executed successfully.
+ *
+ * @param object	$db	An instance of the database object used to create the fixture table
+ * @return boolean True on success, false on failure
+ * @access public
+ */
+	function create(&$db) {
+		if (!isset($this->fields) || empty($this->fields)) {
+			return false;
+		}
+
+		$this->Schema->_build(array($this->table => $this->fields));
+		return (
+			$db->execute($db->createSchema($this->Schema), array('log' => false)) !== false
+		);
+	}
+/**
+ * Run after all tests executed, should return SQL statement to drop table for this fixture.
+ *
+ * @param object	$db	An instance of the database object used to create the fixture table
+ * @return boolean True on success, false on failure
+ * @access public
+ */
+	function drop(&$db) {
+		$this->Schema->_build(array($this->table => $this->fields));
+		return (
+			$db->execute($db->dropSchema($this->Schema), array('log' => false)) !== false
+		);
+	}
+/**
+ * Run before each tests is executed, should return a set of SQL statements to insert records for the table
+ * of this fixture could be executed successfully.
+ *
+ * @param object $db An instance of the database into which the records will be inserted
+ * @return boolean on success or if there are no records to insert, or false on failure
+ * @access public
+ */
+	function insert(&$db) {
+		if (!isset($this->_insert)) {
+			$values = array();
+
+			if (isset($this->records) && !empty($this->records)) {
+				foreach ($this->records as $record) {
+					$fields = array_keys($record);
+					$values[] = '(' . implode(', ', array_map(array(&$db, 'value'), array_values($record))) . ')';
+				}
+				return $db->insertMulti($this->table, $fields, $values);
+			}
+			return true;
+		}
+	}
+/**
+ * Truncates the current fixture. Can be overwritten by classes extending CakeFixture to trigger other events before / after
+ * truncate.
+ *
+ * @param object $db A reference to a db instance
+ * @return boolean
+ * @access public
+ */
+	function truncate(&$db) {
+		$fullDebug = $db->fullDebug;
+		$db->fullDebug = false;
+		$return = $db->truncate($this->table);
+		$db->fullDebug = $fullDebug;
+		return $return;
+	}
+}
+?>
Index: cake/tests/lib/header.php
===================================================================
--- cake/tests/lib/header.php	(revision 0)
+++ cake/tests/lib/header.php	(revision 0)
@@ -0,0 +1,132 @@
+<?php
+/* SVN FILE: $Id: header.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+	<head>
+		<meta http-equiv='content-Type' content='text/html; <?php echo $characterSet; ?>' />
+		<title>CakePHP Test Suite v 1.2.0.0</title>
+		<style type="text/css">
+			h3 {font-size: 170%; padding-top: 1em}
+			a {font-size: 120%}
+			li {line-height: 140%}
+			.test-menu {float:left; margin-right: 24px;}
+			.test-results {float:left; width: 67%;}
+			ul.tests {margin: 0; font-size:12px;}
+			ul.tests li {
+				list-style: none; 
+				margin: 14px 0; 
+				padding-left: 20px;
+			}
+			ul.tests li span { 
+				font-size:14px; 
+				text-transform: uppercase; 
+				font-weight: bold; 
+			}
+			ul.tests li.pass span, ul.tests li.skipped span { display:inline;}
+			ul.tests li.fail span { color: red; }
+			ul.tests li.pass span { color: green; }
+			ul.tests li.skipped span { color: navy; }
+			ul.tests li.error span { color : #d15d00; }
+			
+			ul.tests li.pass,
+			ul.tests li.error,
+			ul.tests li.skipped,
+			ul.tests li.fail {
+				background: #fff2f2 url(http://cakephp.org/img/test-fail-icon.png) 5px 5px no-repeat;
+				border-top: 1px dotted red;
+				border-bottom: 1px dotted red;
+				padding:5px 10px 2px 25px;
+			}
+			ul.tests li.pass {
+				background-color: #f2fff2;
+				background-image: url(http://cakephp.org/img/test-pass-icon.png);
+				border-color:green;
+			}
+			ul.tests li.skipped {
+				background-color: #edf1ff;
+				background-image: url(http://cakephp.org/img/test-skip-icon.png);
+				border-color:navy;
+			}
+			ul.tests li.error {
+				background-color: #ffffe5;
+				background-image: url(http://cakephp.org/img/test-error-icon.png);
+				border-color: #DF6300;
+			}
+			ul.tests li div { margin: 5px 0 8px 0; }
+			ul.tests li div.msg { font-weight: bold; }
+			table caption { color:#fff; }
+			
+			div.code-coverage-results div.code-line {
+				padding-left:5px;
+				display:block;
+				margin-left:10px;
+			}
+			div.code-coverage-results div.uncovered span.content { background:#ecc; }
+			div.code-coverage-results div.covered span.content { background:#cec; }
+			div.code-coverage-results div.ignored span.content { color:#aaa; }
+			div.code-coverage-results span.line-num {
+				color:#666;
+				display:block;
+				float:left;
+				width:20px;
+				text-align:right;
+				margin-right:5px;
+			}
+			div.code-coverage-results span.line-num strong { color:#666; }
+			div.code-coverage-results div.start {
+				border:1px solid #aaa;
+				border-width:1px 1px 0px 1px;
+				margin-top:30px;
+				padding-top:5px;
+			}
+			div.code-coverage-results div.end {
+				border:1px solid #aaa;
+				border-width:0px 1px 1px 1px;
+				margin-bottom:30px;
+				padding-bottom:5px;
+			}
+			div.code-coverage-results div.realstart { margin-top:0px; }
+			div.code-coverage-results p.note {
+				color:#bbb;
+				padding:5px;
+				margin:5px 0 10px;
+				font-size:10px;
+			}
+			div.code-coverage-results span.result-bad { color: #a00; }
+			div.code-coverage-results span.result-ok { color: #fa0; }
+			div.code-coverage-results span.result-good { color: #0a0; }
+		</style>
+		<link rel="stylesheet" type="text/css" href="<?php echo $baseUrl; ?>css/cake.generic.css" />
+	</head>
+	<body>
+		<div id="container">
+			<div id="header">
+				<h1>CakePHP: the rapid development php framework</h1>
+			</div>
+			<div id="content">
+			<h2>CakePHP Test Suite v 1.2.0.0</h2>
Index: cake/bootstrap.php
===================================================================
--- cake/bootstrap.php	(revision 0)
+++ cake/bootstrap.php	(revision 0)
@@ -0,0 +1,48 @@
+<?php
+/* SVN FILE: $Id: bootstrap.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Basic Cake functionality.
+ *
+ * Core functions for including other source files, loading models and so forth.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!defined('PHP5')) {
+	define('PHP5', (PHP_VERSION >= 5));
+}
+/**
+ * Configuration, directory layout and standard libraries
+ */
+	if (!isset($bootstrap)) {
+		require CORE_PATH . 'cake' . DS . 'basics.php';
+		$TIME_START = getMicrotime();
+		require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php';
+		require LIBS . 'object.php';
+		require LIBS . 'inflector.php';
+		require LIBS . 'configure.php';
+	}
+	require LIBS . 'cache.php';
+
+	Configure::getInstance();
+
+	$url = null;
+
+	App::import('Core', array('Dispatcher'));
+?>
\ No newline at end of file
Index: cake/VERSION.txt
===================================================================
--- cake/VERSION.txt	(revision 0)
+++ cake/VERSION.txt	(revision 0)
@@ -0,0 +1,9 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+// +---------------------------------------------------------------------------------------------------+ //
+// + $Id: VERSION.txt 8004 2009-01-16 20:15:21Z gwoo $
+// + Last Modified: $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+// + Modified By: $LastChangedBy: gwoo $
+// +---------------------------------------------------------------------------------------------------+ //
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+1.2.1.8004
\ No newline at end of file
Index: cake/config/paths.php
===================================================================
--- cake/config/paths.php	(revision 0)
+++ cake/config/paths.php	(revision 0)
@@ -0,0 +1,210 @@
+<?php
+/* SVN FILE: $Id: paths.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * In this file you set paths to different directories used by Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * If the index.php file is used instead of an .htaccess file
+ * or if the user can not set the web root to use the public
+ * directory we will define ROOT there, otherwise we set it
+ * here.
+ */
+	if (!defined('ROOT')) {
+		define('ROOT', '../');
+	}
+	if (!defined('WEBROOT_DIR')) {
+		define('WEBROOT_DIR', 'webroot');
+	}
+/**
+ * Path to the cake directory.
+ */
+	define('CAKE', CORE_PATH.'cake'.DS);
+/**
+ * Path to the application's directory.
+ */
+if (!defined('APP')) {
+	define('APP', ROOT.DS.APP_DIR.DS);
+}
+/**
+ * Path to the application's models directory.
+ */
+	define('MODELS', APP.'models'.DS);
+/**
+ * Path to model behaviors directory.
+ */
+	define('BEHAVIORS', MODELS.'behaviors'.DS);
+/**
+ * Path to the application's controllers directory.
+ */
+	define('CONTROLLERS', APP.'controllers'.DS);
+/**
+ * Path to the application's components directory.
+ */
+	define('COMPONENTS', CONTROLLERS.'components'.DS);
+/**
+ * Path to the application's views directory.
+ */
+	define('VIEWS', APP.'views'.DS);
+/**
+ * Path to the application's helpers directory.
+ */
+	define('HELPERS', VIEWS.'helpers'.DS);
+/**
+ * Path to the application's view's layouts directory.
+ */
+	define('LAYOUTS', VIEWS.'layouts'.DS);
+/**
+ * Path to the application's view's elements directory.
+ * It's supposed to hold pieces of PHP/HTML that are used on multiple pages
+ * and are not linked to a particular layout (like polls, footers and so on).
+ */
+	define('ELEMENTS', VIEWS.'elements'.DS);
+/**
+ * Path to the configuration files directory.
+ */
+if (!defined('CONFIGS')) {
+	define('CONFIGS', APP.'config'.DS);
+}
+/**
+ * Path to the libs directory.
+ */
+	define('INFLECTIONS', CAKE.'config'.DS.'inflections'.DS);
+/**
+ * Path to the libs directory.
+ */
+	define('LIBS', CAKE.'libs'.DS);
+/**
+ * Path to the public CSS directory.
+ */
+	define('CSS', WWW_ROOT.'css'.DS);
+/**
+ * Path to the public JavaScript directory.
+ */
+	define('JS', WWW_ROOT.'js'.DS);
+/**
+ * Path to the public images directory.
+ */
+	define('IMAGES', WWW_ROOT.'img'.DS);
+/**
+ * Path to the console libs direcotry.
+ */
+	define('CONSOLE_LIBS', CAKE.'console'.DS.'libs'.DS);
+/**
+ * Path to the tests directory.
+ */
+if (!defined('TESTS')) {
+	define('TESTS', APP.'tests'.DS);
+}
+/**
+ * Path to the core tests directory.
+ */
+if (!defined('CAKE_TESTS')) {
+	define('CAKE_TESTS', CAKE.'tests'.DS);
+}
+/**
+ * Path to the test suite.
+ */
+	define('CAKE_TESTS_LIB', CAKE_TESTS.'lib'.DS);
+
+/**
+ * Path to the controller test directory.
+ */
+	define('CONTROLLER_TESTS', TESTS.'cases'.DS.'controllers'.DS);
+/**
+ * Path to the components test directory.
+ */
+	define('COMPONENT_TESTS', TESTS.'cases'.DS.'components'.DS);
+/**
+ * Path to the helpers test directory.
+ */
+	define('HELPER_TESTS', TESTS.'cases'.DS.'views'.DS.'helpers'.DS);
+/**
+ * Path to the models' test directory.
+ */
+	define('MODEL_TESTS', TESTS.'cases'.DS.'models'.DS);
+/**
+ * Path to the lib test directory.
+ */
+	define('LIB_TESTS', CAKE_TESTS.'cases'.DS.'lib'.DS);
+/**
+ * Path to the temporary files directory.
+ */
+if (!defined('TMP')) {
+	define('TMP', APP.'tmp'.DS);
+}
+/**
+ * Path to the logs directory.
+ */
+	define('LOGS', TMP.'logs'.DS);
+/**
+ * Path to the cache files directory. It can be shared between hosts in a multi-server setup.
+ */
+	define('CACHE', TMP.'cache'.DS);
+/**
+ * Path to the vendors directory.
+ */
+if (!defined('VENDORS')) {
+	define('VENDORS', CAKE_CORE_INCLUDE_PATH.DS.'vendors'.DS);
+}
+/**
+ * Path to the Pear directory
+ * The purporse is to make it easy porting Pear libs into Cake
+ * without setting the include_path PHP variable.
+ */
+	define('PEAR', VENDORS.'Pear'.DS);
+/**
+ *  Full url prefix
+ */
+if (!defined('FULL_BASE_URL')) {
+	$s = null;
+	if (env('HTTPS')) {
+		$s ='s';
+	}
+
+	$httpHost = env('HTTP_HOST');
+
+	if (isset($httpHost)) {
+		define('FULL_BASE_URL', 'http'.$s.'://'.$httpHost);
+	}
+	unset($httpHost, $s);
+}
+/**
+ * Web path to the public images directory.
+ */
+if (!defined('IMAGES_URL')) {
+	define('IMAGES_URL', 'img/');
+}
+/**
+ * Web path to the CSS files directory.
+ */
+if (!defined('CSS_URL')) {
+	define('CSS_URL', 'css/');
+}
+/**
+ * Web path to the js files directory.
+ */
+if (!defined('JS_URL')) {
+	define('JS_URL', 'js/');
+}
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0530_058f.php
===================================================================
--- cake/config/unicode/casefolding/0530_058f.php	(revision 0)
+++ cake/config/unicode/casefolding/0530_058f.php	(revision 0)
@@ -0,0 +1,84 @@
+<?php
+/* SVN FILE: $Id: 0530_058f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0530 through U+058F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0530_058f'][] = array('upper' => 1329, 'status' => 'C', 'lower' => array(1377)); /* ARMENIAN CAPITAL LETTER AYB */
+$config['0530_058f'][] = array('upper' => 1330, 'status' => 'C', 'lower' => array(1378)); /* ARMENIAN CAPITAL LETTER BEN */
+$config['0530_058f'][] = array('upper' => 1331, 'status' => 'C', 'lower' => array(1379)); /* ARMENIAN CAPITAL LETTER GIM */
+$config['0530_058f'][] = array('upper' => 1332, 'status' => 'C', 'lower' => array(1380)); /* ARMENIAN CAPITAL LETTER DA */
+$config['0530_058f'][] = array('upper' => 1333, 'status' => 'C', 'lower' => array(1381)); /* ARMENIAN CAPITAL LETTER ECH */
+$config['0530_058f'][] = array('upper' => 1334, 'status' => 'C', 'lower' => array(1382)); /* ARMENIAN CAPITAL LETTER ZA */
+$config['0530_058f'][] = array('upper' => 1335, 'status' => 'C', 'lower' => array(1383)); /* ARMENIAN CAPITAL LETTER EH */
+$config['0530_058f'][] = array('upper' => 1336, 'status' => 'C', 'lower' => array(1384)); /* ARMENIAN CAPITAL LETTER ET */
+$config['0530_058f'][] = array('upper' => 1337, 'status' => 'C', 'lower' => array(1385)); /* ARMENIAN CAPITAL LETTER TO */
+$config['0530_058f'][] = array('upper' => 1338, 'status' => 'C', 'lower' => array(1386)); /* ARMENIAN CAPITAL LETTER ZHE */
+$config['0530_058f'][] = array('upper' => 1339, 'status' => 'C', 'lower' => array(1387)); /* ARMENIAN CAPITAL LETTER INI */
+$config['0530_058f'][] = array('upper' => 1340, 'status' => 'C', 'lower' => array(1388)); /* ARMENIAN CAPITAL LETTER LIWN */
+$config['0530_058f'][] = array('upper' => 1341, 'status' => 'C', 'lower' => array(1389)); /* ARMENIAN CAPITAL LETTER XEH */
+$config['0530_058f'][] = array('upper' => 1342, 'status' => 'C', 'lower' => array(1390)); /* ARMENIAN CAPITAL LETTER CA */
+$config['0530_058f'][] = array('upper' => 1343, 'status' => 'C', 'lower' => array(1391)); /* ARMENIAN CAPITAL LETTER KEN */
+$config['0530_058f'][] = array('upper' => 1344, 'status' => 'C', 'lower' => array(1392)); /* ARMENIAN CAPITAL LETTER HO */
+$config['0530_058f'][] = array('upper' => 1345, 'status' => 'C', 'lower' => array(1393)); /* ARMENIAN CAPITAL LETTER JA */
+$config['0530_058f'][] = array('upper' => 1346, 'status' => 'C', 'lower' => array(1394)); /* ARMENIAN CAPITAL LETTER GHAD */
+$config['0530_058f'][] = array('upper' => 1347, 'status' => 'C', 'lower' => array(1395)); /* ARMENIAN CAPITAL LETTER CHEH */
+$config['0530_058f'][] = array('upper' => 1348, 'status' => 'C', 'lower' => array(1396)); /* ARMENIAN CAPITAL LETTER MEN */
+$config['0530_058f'][] = array('upper' => 1349, 'status' => 'C', 'lower' => array(1397)); /* ARMENIAN CAPITAL LETTER YI */
+$config['0530_058f'][] = array('upper' => 1350, 'status' => 'C', 'lower' => array(1398)); /* ARMENIAN CAPITAL LETTER NOW */
+$config['0530_058f'][] = array('upper' => 1351, 'status' => 'C', 'lower' => array(1399)); /* ARMENIAN CAPITAL LETTER SHA */
+$config['0530_058f'][] = array('upper' => 1352, 'status' => 'C', 'lower' => array(1400)); /* ARMENIAN CAPITAL LETTER VO */
+$config['0530_058f'][] = array('upper' => 1353, 'status' => 'C', 'lower' => array(1401)); /* ARMENIAN CAPITAL LETTER CHA */
+$config['0530_058f'][] = array('upper' => 1354, 'status' => 'C', 'lower' => array(1402)); /* ARMENIAN CAPITAL LETTER PEH */
+$config['0530_058f'][] = array('upper' => 1355, 'status' => 'C', 'lower' => array(1403)); /* ARMENIAN CAPITAL LETTER JHEH */
+$config['0530_058f'][] = array('upper' => 1356, 'status' => 'C', 'lower' => array(1404)); /* ARMENIAN CAPITAL LETTER RA */
+$config['0530_058f'][] = array('upper' => 1357, 'status' => 'C', 'lower' => array(1405)); /* ARMENIAN CAPITAL LETTER SEH */
+$config['0530_058f'][] = array('upper' => 1358, 'status' => 'C', 'lower' => array(1406)); /* ARMENIAN CAPITAL LETTER VEW */
+$config['0530_058f'][] = array('upper' => 1359, 'status' => 'C', 'lower' => array(1407)); /* ARMENIAN CAPITAL LETTER TIWN */
+$config['0530_058f'][] = array('upper' => 1360, 'status' => 'C', 'lower' => array(1408)); /* ARMENIAN CAPITAL LETTER REH */
+$config['0530_058f'][] = array('upper' => 1361, 'status' => 'C', 'lower' => array(1409)); /* ARMENIAN CAPITAL LETTER CO */
+$config['0530_058f'][] = array('upper' => 1362, 'status' => 'C', 'lower' => array(1410)); /* ARMENIAN CAPITAL LETTER YIWN */
+$config['0530_058f'][] = array('upper' => 1363, 'status' => 'C', 'lower' => array(1411)); /* ARMENIAN CAPITAL LETTER PIWR */
+$config['0530_058f'][] = array('upper' => 1364, 'status' => 'C', 'lower' => array(1412)); /* ARMENIAN CAPITAL LETTER KEH */
+$config['0530_058f'][] = array('upper' => 1365, 'status' => 'C', 'lower' => array(1413)); /* ARMENIAN CAPITAL LETTER OH */
+$config['0530_058f'][] = array('upper' => 1366, 'status' => 'C', 'lower' => array(1414)); /* ARMENIAN CAPITAL LETTER FEH */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2c60_2c7f.php
===================================================================
--- cake/config/unicode/casefolding/2c60_2c7f.php	(revision 0)
+++ cake/config/unicode/casefolding/2c60_2c7f.php	(revision 0)
@@ -0,0 +1,54 @@
+<?php
+/* SVN FILE: $Id: 2c60_2c7f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2C60 through U+2C7F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2c60_2c7f'][] = array('upper' => 11360, 'status' => 'C', 'lower' => array(11361)); /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */
+$config['2c60_2c7f'][] = array('upper' => 11362, 'status' => 'C', 'lower' => array(619)); /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */
+$config['2c60_2c7f'][] = array('upper' => 11363, 'status' => 'C', 'lower' => array(7549)); /* LATIN CAPITAL LETTER P WITH STROKE */
+$config['2c60_2c7f'][] = array('upper' => 11364, 'status' => 'C', 'lower' => array(637)); /* LATIN CAPITAL LETTER R WITH TAIL */
+$config['2c60_2c7f'][] = array('upper' => 11367, 'status' => 'C', 'lower' => array(11368)); /* LATIN CAPITAL LETTER H WITH DESCENDER */
+$config['2c60_2c7f'][] = array('upper' => 11369, 'status' => 'C', 'lower' => array(11370)); /* LATIN CAPITAL LETTER K WITH DESCENDER */
+$config['2c60_2c7f'][] = array('upper' => 11371, 'status' => 'C', 'lower' => array(11372)); /* LATIN CAPITAL LETTER Z WITH DESCENDER */
+$config['2c60_2c7f'][] = array('upper' => 11381, 'status' => 'C', 'lower' => array(11382)); /* LATIN CAPITAL LETTER HALF H */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0250_02af.php
===================================================================
--- cake/config/unicode/casefolding/0250_02af.php	(revision 0)
+++ cake/config/unicode/casefolding/0250_02af.php	(revision 0)
@@ -0,0 +1,47 @@
+<?php
+/* SVN FILE: $Id: 0250_02af.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0080 through U+00FF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.6833
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0250_02af'][] = array('upper' => 422, 'status' => 'C', 'lower' => array(640));
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0080_00ff.php
===================================================================
--- cake/config/unicode/casefolding/0080_00ff.php	(revision 0)
+++ cake/config/unicode/casefolding/0080_00ff.php	(revision 0)
@@ -0,0 +1,79 @@
+<?php
+/* SVN FILE: $Id: 0080_00ff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0080 through U+00FF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0080_00ff'][] = array('upper' => 181, 'status' => 'C', 'lower' => array(956));
+$config['0080_00ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(181));
+$config['0080_00ff'][] = array('upper' => 192, 'status' => 'C', 'lower' => array(224)); /* LATIN CAPITAL LETTER A WITH GRAVE */
+$config['0080_00ff'][] = array('upper' => 193, 'status' => 'C', 'lower' => array(225)); /* LATIN CAPITAL LETTER A WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 194, 'status' => 'C', 'lower' => array(226)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+$config['0080_00ff'][] = array('upper' => 195, 'status' => 'C', 'lower' => array(227)); /* LATIN CAPITAL LETTER A WITH TILDE */
+$config['0080_00ff'][] = array('upper' => 196, 'status' => 'C', 'lower' => array(228)); /* LATIN CAPITAL LETTER A WITH DIAERESIS */
+$config['0080_00ff'][] = array('upper' => 197, 'status' => 'C', 'lower' => array(229)); /* LATIN CAPITAL LETTER A WITH RING ABOVE */
+$config['0080_00ff'][] = array('upper' => 198, 'status' => 'C', 'lower' => array(230)); /* LATIN CAPITAL LETTER AE */
+$config['0080_00ff'][] = array('upper' => 199, 'status' => 'C', 'lower' => array(231)); /* LATIN CAPITAL LETTER C WITH CEDILLA */
+$config['0080_00ff'][] = array('upper' => 200, 'status' => 'C', 'lower' => array(232)); /* LATIN CAPITAL LETTER E WITH GRAVE */
+$config['0080_00ff'][] = array('upper' => 201, 'status' => 'C', 'lower' => array(233)); /* LATIN CAPITAL LETTER E WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 202, 'status' => 'C', 'lower' => array(234)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
+$config['0080_00ff'][] = array('upper' => 203, 'status' => 'C', 'lower' => array(235)); /* LATIN CAPITAL LETTER E WITH DIAERESIS */
+$config['0080_00ff'][] = array('upper' => 204, 'status' => 'C', 'lower' => array(236)); /* LATIN CAPITAL LETTER I WITH GRAVE */
+$config['0080_00ff'][] = array('upper' => 205, 'status' => 'C', 'lower' => array(237)); /* LATIN CAPITAL LETTER I WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 206, 'status' => 'C', 'lower' => array(238)); /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+$config['0080_00ff'][] = array('upper' => 207, 'status' => 'C', 'lower' => array(239)); /* LATIN CAPITAL LETTER I WITH DIAERESIS */
+$config['0080_00ff'][] = array('upper' => 208, 'status' => 'C', 'lower' => array(240)); /* LATIN CAPITAL LETTER ETH */
+$config['0080_00ff'][] = array('upper' => 209, 'status' => 'C', 'lower' => array(241)); /* LATIN CAPITAL LETTER N WITH TILDE */
+$config['0080_00ff'][] = array('upper' => 210, 'status' => 'C', 'lower' => array(242)); /* LATIN CAPITAL LETTER O WITH GRAVE */
+$config['0080_00ff'][] = array('upper' => 211, 'status' => 'C', 'lower' => array(243)); /* LATIN CAPITAL LETTER O WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 212, 'status' => 'C', 'lower' => array(244)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+$config['0080_00ff'][] = array('upper' => 213, 'status' => 'C', 'lower' => array(245)); /* LATIN CAPITAL LETTER O WITH TILDE */
+$config['0080_00ff'][] = array('upper' => 214, 'status' => 'C', 'lower' => array(246)); /* LATIN CAPITAL LETTER O WITH DIAERESIS */
+$config['0080_00ff'][] = array('upper' => 216, 'status' => 'C', 'lower' => array(248)); /* LATIN CAPITAL LETTER O WITH STROKE */
+$config['0080_00ff'][] = array('upper' => 217, 'status' => 'C', 'lower' => array(249)); /* LATIN CAPITAL LETTER U WITH GRAVE */
+$config['0080_00ff'][] = array('upper' => 218, 'status' => 'C', 'lower' => array(250)); /* LATIN CAPITAL LETTER U WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 219, 'status' => 'C', 'lower' => array(251)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
+$config['0080_00ff'][] = array('upper' => 220, 'status' => 'C', 'lower' => array(252)); /* LATIN CAPITAL LETTER U WITH DIAERESIS */
+$config['0080_00ff'][] = array('upper' => 221, 'status' => 'C', 'lower' => array(253)); /* LATIN CAPITAL LETTER Y WITH ACUTE */
+$config['0080_00ff'][] = array('upper' => 222, 'status' => 'C', 'lower' => array(254)); /* LATIN CAPITAL LETTER THORN */
+$config['0080_00ff'][] = array('upper' => 223, 'status' => 'F', 'lower' => array(115, 115)); /* LATIN SMALL LETTER SHARP S */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0400_04ff.php
===================================================================
--- cake/config/unicode/casefolding/0400_04ff.php	(revision 0)
+++ cake/config/unicode/casefolding/0400_04ff.php	(revision 0)
@@ -0,0 +1,170 @@
+<?php
+/* SVN FILE: $Id: 0400_04ff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0400 through U+04FF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0400_04ff'][] = array('upper' => 1024, 'status' => 'C', 'lower' => array(1104)); /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */
+$config['0400_04ff'][] = array('upper' => 1025, 'status' => 'C', 'lower' => array(1105)); /* CYRILLIC CAPITAL LETTER IO */
+$config['0400_04ff'][] = array('upper' => 1026, 'status' => 'C', 'lower' => array(1106)); /* CYRILLIC CAPITAL LETTER DJE */
+$config['0400_04ff'][] = array('upper' => 1027, 'status' => 'C', 'lower' => array(1107)); /* CYRILLIC CAPITAL LETTER GJE */
+$config['0400_04ff'][] = array('upper' => 1028, 'status' => 'C', 'lower' => array(1108)); /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+$config['0400_04ff'][] = array('upper' => 1029, 'status' => 'C', 'lower' => array(1109)); /* CYRILLIC CAPITAL LETTER DZE */
+$config['0400_04ff'][] = array('upper' => 1030, 'status' => 'C', 'lower' => array(1110)); /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+$config['0400_04ff'][] = array('upper' => 1031, 'status' => 'C', 'lower' => array(1111)); /* CYRILLIC CAPITAL LETTER YI */
+$config['0400_04ff'][] = array('upper' => 1032, 'status' => 'C', 'lower' => array(1112)); /* CYRILLIC CAPITAL LETTER JE */
+$config['0400_04ff'][] = array('upper' => 1033, 'status' => 'C', 'lower' => array(1113)); /* CYRILLIC CAPITAL LETTER LJE */
+$config['0400_04ff'][] = array('upper' => 1034, 'status' => 'C', 'lower' => array(1114)); /* CYRILLIC CAPITAL LETTER NJE */
+$config['0400_04ff'][] = array('upper' => 1035, 'status' => 'C', 'lower' => array(1115)); /* CYRILLIC CAPITAL LETTER TSHE */
+$config['0400_04ff'][] = array('upper' => 1036, 'status' => 'C', 'lower' => array(1116)); /* CYRILLIC CAPITAL LETTER KJE */
+$config['0400_04ff'][] = array('upper' => 1037, 'status' => 'C', 'lower' => array(1117)); /* CYRILLIC CAPITAL LETTER I WITH GRAVE */
+$config['0400_04ff'][] = array('upper' => 1038, 'status' => 'C', 'lower' => array(1118)); /* CYRILLIC CAPITAL LETTER SHORT U */
+$config['0400_04ff'][] = array('upper' => 1039, 'status' => 'C', 'lower' => array(1119)); /* CYRILLIC CAPITAL LETTER DZHE */
+$config['0400_04ff'][] = array('upper' => 1040, 'status' => 'C', 'lower' => array(1072)); /* CYRILLIC CAPITAL LETTER A */
+$config['0400_04ff'][] = array('upper' => 1041, 'status' => 'C', 'lower' => array(1073)); /* CYRILLIC CAPITAL LETTER BE */
+$config['0400_04ff'][] = array('upper' => 1042, 'status' => 'C', 'lower' => array(1074)); /* CYRILLIC CAPITAL LETTER VE */
+$config['0400_04ff'][] = array('upper' => 1043, 'status' => 'C', 'lower' => array(1075)); /* CYRILLIC CAPITAL LETTER GHE */
+$config['0400_04ff'][] = array('upper' => 1044, 'status' => 'C', 'lower' => array(1076)); /* CYRILLIC CAPITAL LETTER DE */
+$config['0400_04ff'][] = array('upper' => 1045, 'status' => 'C', 'lower' => array(1077)); /* CYRILLIC CAPITAL LETTER IE */
+$config['0400_04ff'][] = array('upper' => 1046, 'status' => 'C', 'lower' => array(1078)); /* CYRILLIC CAPITAL LETTER ZHE */
+$config['0400_04ff'][] = array('upper' => 1047, 'status' => 'C', 'lower' => array(1079)); /* CYRILLIC CAPITAL LETTER ZE */
+$config['0400_04ff'][] = array('upper' => 1048, 'status' => 'C', 'lower' => array(1080)); /* CYRILLIC CAPITAL LETTER I */
+$config['0400_04ff'][] = array('upper' => 1049, 'status' => 'C', 'lower' => array(1081)); /* CYRILLIC CAPITAL LETTER SHORT I */
+$config['0400_04ff'][] = array('upper' => 1050, 'status' => 'C', 'lower' => array(1082)); /* CYRILLIC CAPITAL LETTER KA */
+$config['0400_04ff'][] = array('upper' => 1051, 'status' => 'C', 'lower' => array(1083)); /* CYRILLIC CAPITAL LETTER EL */
+$config['0400_04ff'][] = array('upper' => 1052, 'status' => 'C', 'lower' => array(1084)); /* CYRILLIC CAPITAL LETTER EM */
+$config['0400_04ff'][] = array('upper' => 1053, 'status' => 'C', 'lower' => array(1085)); /* CYRILLIC CAPITAL LETTER EN */
+$config['0400_04ff'][] = array('upper' => 1054, 'status' => 'C', 'lower' => array(1086)); /* CYRILLIC CAPITAL LETTER O */
+$config['0400_04ff'][] = array('upper' => 1055, 'status' => 'C', 'lower' => array(1087)); /* CYRILLIC CAPITAL LETTER PE */
+$config['0400_04ff'][] = array('upper' => 1056, 'status' => 'C', 'lower' => array(1088)); /* CYRILLIC CAPITAL LETTER ER */
+$config['0400_04ff'][] = array('upper' => 1057, 'status' => 'C', 'lower' => array(1089)); /* CYRILLIC CAPITAL LETTER ES */
+$config['0400_04ff'][] = array('upper' => 1058, 'status' => 'C', 'lower' => array(1090)); /* CYRILLIC CAPITAL LETTER TE */
+$config['0400_04ff'][] = array('upper' => 1059, 'status' => 'C', 'lower' => array(1091)); /* CYRILLIC CAPITAL LETTER U */
+$config['0400_04ff'][] = array('upper' => 1060, 'status' => 'C', 'lower' => array(1092)); /* CYRILLIC CAPITAL LETTER EF */
+$config['0400_04ff'][] = array('upper' => 1061, 'status' => 'C', 'lower' => array(1093)); /* CYRILLIC CAPITAL LETTER HA */
+$config['0400_04ff'][] = array('upper' => 1062, 'status' => 'C', 'lower' => array(1094)); /* CYRILLIC CAPITAL LETTER TSE */
+$config['0400_04ff'][] = array('upper' => 1063, 'status' => 'C', 'lower' => array(1095)); /* CYRILLIC CAPITAL LETTER CHE */
+$config['0400_04ff'][] = array('upper' => 1064, 'status' => 'C', 'lower' => array(1096)); /* CYRILLIC CAPITAL LETTER SHA */
+$config['0400_04ff'][] = array('upper' => 1065, 'status' => 'C', 'lower' => array(1097)); /* CYRILLIC CAPITAL LETTER SHCHA */
+$config['0400_04ff'][] = array('upper' => 1066, 'status' => 'C', 'lower' => array(1098)); /* CYRILLIC CAPITAL LETTER HARD SIGN */
+$config['0400_04ff'][] = array('upper' => 1067, 'status' => 'C', 'lower' => array(1099)); /* CYRILLIC CAPITAL LETTER YERU */
+$config['0400_04ff'][] = array('upper' => 1068, 'status' => 'C', 'lower' => array(1100)); /* CYRILLIC CAPITAL LETTER SOFT SIGN */
+$config['0400_04ff'][] = array('upper' => 1069, 'status' => 'C', 'lower' => array(1101)); /* CYRILLIC CAPITAL LETTER E */
+$config['0400_04ff'][] = array('upper' => 1070, 'status' => 'C', 'lower' => array(1102)); /* CYRILLIC CAPITAL LETTER YU */
+$config['0400_04ff'][] = array('upper' => 1071, 'status' => 'C', 'lower' => array(1103)); /* CYRILLIC CAPITAL LETTER YA */
+$config['0400_04ff'][] = array('upper' => 1120, 'status' => 'C', 'lower' => array(1121)); /* CYRILLIC CAPITAL LETTER OMEGA */
+$config['0400_04ff'][] = array('upper' => 1122, 'status' => 'C', 'lower' => array(1123)); /* CYRILLIC CAPITAL LETTER YAT */
+$config['0400_04ff'][] = array('upper' => 1124, 'status' => 'C', 'lower' => array(1125)); /* CYRILLIC CAPITAL LETTER IOTIFIED E */
+$config['0400_04ff'][] = array('upper' => 1126, 'status' => 'C', 'lower' => array(1127)); /* CYRILLIC CAPITAL LETTER LITTLE YUS */
+$config['0400_04ff'][] = array('upper' => 1128, 'status' => 'C', 'lower' => array(1129)); /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */
+$config['0400_04ff'][] = array('upper' => 1130, 'status' => 'C', 'lower' => array(1131)); /* CYRILLIC CAPITAL LETTER BIG YUS */
+$config['0400_04ff'][] = array('upper' => 1132, 'status' => 'C', 'lower' => array(1133)); /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */
+$config['0400_04ff'][] = array('upper' => 1134, 'status' => 'C', 'lower' => array(1135)); /* CYRILLIC CAPITAL LETTER KSI */
+$config['0400_04ff'][] = array('upper' => 1136, 'status' => 'C', 'lower' => array(1137)); /* CYRILLIC CAPITAL LETTER PSI */
+$config['0400_04ff'][] = array('upper' => 1138, 'status' => 'C', 'lower' => array(1139)); /* CYRILLIC CAPITAL LETTER FITA */
+$config['0400_04ff'][] = array('upper' => 1140, 'status' => 'C', 'lower' => array(1141)); /* CYRILLIC CAPITAL LETTER IZHITSA */
+$config['0400_04ff'][] = array('upper' => 1142, 'status' => 'C', 'lower' => array(1143)); /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
+$config['0400_04ff'][] = array('upper' => 1144, 'status' => 'C', 'lower' => array(1145)); /* CYRILLIC CAPITAL LETTER UK */
+$config['0400_04ff'][] = array('upper' => 1146, 'status' => 'C', 'lower' => array(1147)); /* CYRILLIC CAPITAL LETTER ROUND OMEGA */
+$config['0400_04ff'][] = array('upper' => 1148, 'status' => 'C', 'lower' => array(1149)); /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */
+$config['0400_04ff'][] = array('upper' => 1150, 'status' => 'C', 'lower' => array(1151)); /* CYRILLIC CAPITAL LETTER OT */
+$config['0400_04ff'][] = array('upper' => 1152, 'status' => 'C', 'lower' => array(1153)); /* CYRILLIC CAPITAL LETTER KOPPA */
+$config['0400_04ff'][] = array('upper' => 1162, 'status' => 'C', 'lower' => array(1163)); /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */
+$config['0400_04ff'][] = array('upper' => 1164, 'status' => 'C', 'lower' => array(1165)); /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */
+$config['0400_04ff'][] = array('upper' => 1166, 'status' => 'C', 'lower' => array(1167)); /* CYRILLIC CAPITAL LETTER ER WITH TICK */
+$config['0400_04ff'][] = array('upper' => 1168, 'status' => 'C', 'lower' => array(1169)); /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */
+$config['0400_04ff'][] = array('upper' => 1170, 'status' => 'C', 'lower' => array(1171)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */
+$config['0400_04ff'][] = array('upper' => 1172, 'status' => 'C', 'lower' => array(1173)); /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */
+$config['0400_04ff'][] = array('upper' => 1174, 'status' => 'C', 'lower' => array(1175)); /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1176, 'status' => 'C', 'lower' => array(1177)); /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1178, 'status' => 'C', 'lower' => array(1179)); /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1180, 'status' => 'C', 'lower' => array(1181)); /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */
+$config['0400_04ff'][] = array('upper' => 1182, 'status' => 'C', 'lower' => array(1183)); /* CYRILLIC CAPITAL LETTER KA WITH STROKE */
+$config['0400_04ff'][] = array('upper' => 1184, 'status' => 'C', 'lower' => array(1185)); /* CYRILLIC CAPITAL LETTER BASHKIR KA */
+$config['0400_04ff'][] = array('upper' => 1186, 'status' => 'C', 'lower' => array(1187)); /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1188, 'status' => 'C', 'lower' => array(1189)); /* CYRILLIC CAPITAL LIGATURE EN GHE */
+$config['0400_04ff'][] = array('upper' => 1190, 'status' => 'C', 'lower' => array(1191)); /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */
+$config['0400_04ff'][] = array('upper' => 1192, 'status' => 'C', 'lower' => array(1193)); /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */
+$config['0400_04ff'][] = array('upper' => 1194, 'status' => 'C', 'lower' => array(1195)); /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1196, 'status' => 'C', 'lower' => array(1197)); /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1198, 'status' => 'C', 'lower' => array(1199)); /* CYRILLIC CAPITAL LETTER STRAIGHT U */
+$config['0400_04ff'][] = array('upper' => 1200, 'status' => 'C', 'lower' => array(1201)); /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */
+$config['0400_04ff'][] = array('upper' => 1202, 'status' => 'C', 'lower' => array(1203)); /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1204, 'status' => 'C', 'lower' => array(1205)); /* CYRILLIC CAPITAL LIGATURE TE TSE */
+$config['0400_04ff'][] = array('upper' => 1206, 'status' => 'C', 'lower' => array(1207)); /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1208, 'status' => 'C', 'lower' => array(1209)); /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */
+$config['0400_04ff'][] = array('upper' => 1210, 'status' => 'C', 'lower' => array(1211)); /* CYRILLIC CAPITAL LETTER SHHA */
+$config['0400_04ff'][] = array('upper' => 1212, 'status' => 'C', 'lower' => array(1213)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */
+$config['0400_04ff'][] = array('upper' => 1214, 'status' => 'C', 'lower' => array(1215)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1216, 'status' => 'C', 'lower' => array(1231)); /* CYRILLIC LETTER PALOCHKA */
+$config['0400_04ff'][] = array('upper' => 1217, 'status' => 'C', 'lower' => array(1218)); /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */
+$config['0400_04ff'][] = array('upper' => 1219, 'status' => 'C', 'lower' => array(1220)); /* CYRILLIC CAPITAL LETTER KA WITH HOOK */
+$config['0400_04ff'][] = array('upper' => 1221, 'status' => 'C', 'lower' => array(1222)); /* CYRILLIC CAPITAL LETTER EL WITH TAIL */
+$config['0400_04ff'][] = array('upper' => 1223, 'status' => 'C', 'lower' => array(1224)); /* CYRILLIC CAPITAL LETTER EN WITH HOOK */
+$config['0400_04ff'][] = array('upper' => 1225, 'status' => 'C', 'lower' => array(1226)); /* CYRILLIC CAPITAL LETTER EN WITH TAIL */
+$config['0400_04ff'][] = array('upper' => 1227, 'status' => 'C', 'lower' => array(1228)); /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */
+$config['0400_04ff'][] = array('upper' => 1229, 'status' => 'C', 'lower' => array(1230)); /* CYRILLIC CAPITAL LETTER EM WITH TAIL */
+$config['0400_04ff'][] = array('upper' => 1232, 'status' => 'C', 'lower' => array(1233)); /* CYRILLIC CAPITAL LETTER A WITH BREVE */
+$config['0400_04ff'][] = array('upper' => 1234, 'status' => 'C', 'lower' => array(1235)); /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1236, 'status' => 'C', 'lower' => array(1237)); /* CYRILLIC CAPITAL LIGATURE A IE */
+$config['0400_04ff'][] = array('upper' => 1238, 'status' => 'C', 'lower' => array(1239)); /* CYRILLIC CAPITAL LETTER IE WITH BREVE */
+$config['0400_04ff'][] = array('upper' => 1240, 'status' => 'C', 'lower' => array(1241)); /* CYRILLIC CAPITAL LETTER SCHWA */
+$config['0400_04ff'][] = array('upper' => 1242, 'status' => 'C', 'lower' => array(1243)); /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1244, 'status' => 'C', 'lower' => array(1245)); /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1246, 'status' => 'C', 'lower' => array(1247)); /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1248, 'status' => 'C', 'lower' => array(1249)); /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */
+$config['0400_04ff'][] = array('upper' => 1250, 'status' => 'C', 'lower' => array(1251)); /* CYRILLIC CAPITAL LETTER I WITH MACRON */
+$config['0400_04ff'][] = array('upper' => 1252, 'status' => 'C', 'lower' => array(1253)); /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1254, 'status' => 'C', 'lower' => array(1255)); /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1256, 'status' => 'C', 'lower' => array(1257)); /* CYRILLIC CAPITAL LETTER BARRED O */
+$config['0400_04ff'][] = array('upper' => 1258, 'status' => 'C', 'lower' => array(1259)); /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1260, 'status' => 'C', 'lower' => array(1261)); /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1262, 'status' => 'C', 'lower' => array(1263)); /* CYRILLIC CAPITAL LETTER U WITH MACRON */
+$config['0400_04ff'][] = array('upper' => 1264, 'status' => 'C', 'lower' => array(1265)); /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1266, 'status' => 'C', 'lower' => array(1267)); /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */
+$config['0400_04ff'][] = array('upper' => 1268, 'status' => 'C', 'lower' => array(1269)); /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1270, 'status' => 'C', 'lower' => array(1271)); /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */
+$config['0400_04ff'][] = array('upper' => 1272, 'status' => 'C', 'lower' => array(1273)); /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */
+$config['0400_04ff'][] = array('upper' => 1274, 'status' => 'C', 'lower' => array(1275)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */
+$config['0400_04ff'][] = array('upper' => 1276, 'status' => 'C', 'lower' => array(1277)); /* CYRILLIC CAPITAL LETTER HA WITH HOOK */
+$config['0400_04ff'][] = array('upper' => 1278, 'status' => 'C', 'lower' => array(1279)); /* CYRILLIC CAPITAL LETTER HA WITH STROKE */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/1e00_1eff.php
===================================================================
--- cake/config/unicode/casefolding/1e00_1eff.php	(revision 0)
+++ cake/config/unicode/casefolding/1e00_1eff.php	(revision 0)
@@ -0,0 +1,174 @@
+<?php
+/* SVN FILE: $Id: 1e00_1eff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+1E00 through U+1EFF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['1e00_1eff'][] = array('upper' => 7680, 'status' => 'C', 'lower' => array(7681)); /* LATIN CAPITAL LETTER A WITH RING BELOW */
+$config['1e00_1eff'][] = array('upper' => 7682, 'status' => 'C', 'lower' => array(7683)); /* LATIN CAPITAL LETTER B WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7684, 'status' => 'C', 'lower' => array(7685)); /* LATIN CAPITAL LETTER B WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7686, 'status' => 'C', 'lower' => array(7687)); /* LATIN CAPITAL LETTER B WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7688, 'status' => 'C', 'lower' => array(7689)); /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7690, 'status' => 'C', 'lower' => array(7691)); /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7692, 'status' => 'C', 'lower' => array(7693)); /* LATIN CAPITAL LETTER D WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7694, 'status' => 'C', 'lower' => array(7695)); /* LATIN CAPITAL LETTER D WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7696, 'status' => 'C', 'lower' => array(7697)); /* LATIN CAPITAL LETTER D WITH CEDILLA */
+$config['1e00_1eff'][] = array('upper' => 7698, 'status' => 'C', 'lower' => array(7699)); /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7700, 'status' => 'C', 'lower' => array(7701)); /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7702, 'status' => 'C', 'lower' => array(7703)); /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7704, 'status' => 'C', 'lower' => array(7705)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7706, 'status' => 'C', 'lower' => array(7707)); /* LATIN CAPITAL LETTER E WITH TILDE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7708, 'status' => 'C', 'lower' => array(7709)); /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */
+$config['1e00_1eff'][] = array('upper' => 7710, 'status' => 'C', 'lower' => array(7711)); /* LATIN CAPITAL LETTER F WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7712, 'status' => 'C', 'lower' => array(7713)); /* LATIN CAPITAL LETTER G WITH MACRON */
+$config['1e00_1eff'][] = array('upper' => 7714, 'status' => 'C', 'lower' => array(7715)); /* LATIN CAPITAL LETTER H WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7716, 'status' => 'C', 'lower' => array(7717)); /* LATIN CAPITAL LETTER H WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7718, 'status' => 'C', 'lower' => array(7719)); /* LATIN CAPITAL LETTER H WITH DIAERESIS */
+$config['1e00_1eff'][] = array('upper' => 7720, 'status' => 'C', 'lower' => array(7721)); /* LATIN CAPITAL LETTER H WITH CEDILLA */
+$config['1e00_1eff'][] = array('upper' => 7722, 'status' => 'C', 'lower' => array(7723)); /* LATIN CAPITAL LETTER H WITH BREVE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7724, 'status' => 'C', 'lower' => array(7725)); /* LATIN CAPITAL LETTER I WITH TILDE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7726, 'status' => 'C', 'lower' => array(7727)); /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7728, 'status' => 'C', 'lower' => array(7729)); /* LATIN CAPITAL LETTER K WITH ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7730, 'status' => 'C', 'lower' => array(7731)); /* LATIN CAPITAL LETTER K WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7732, 'status' => 'C', 'lower' => array(7733)); /* LATIN CAPITAL LETTER K WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7734, 'status' => 'C', 'lower' => array(7735)); /* LATIN CAPITAL LETTER L WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7736, 'status' => 'C', 'lower' => array(7737)); /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */
+$config['1e00_1eff'][] = array('upper' => 7738, 'status' => 'C', 'lower' => array(7739)); /* LATIN CAPITAL LETTER L WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7740, 'status' => 'C', 'lower' => array(7741)); /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7742, 'status' => 'C', 'lower' => array(7743)); /* LATIN CAPITAL LETTER M WITH ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7744, 'status' => 'C', 'lower' => array(7745)); /* LATIN CAPITAL LETTER M WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7746, 'status' => 'C', 'lower' => array(7747)); /* LATIN CAPITAL LETTER M WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7748, 'status' => 'C', 'lower' => array(7749)); /* LATIN CAPITAL LETTER N WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7750, 'status' => 'C', 'lower' => array(7751)); /* LATIN CAPITAL LETTER N WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7752, 'status' => 'C', 'lower' => array(7753)); /* LATIN CAPITAL LETTER N WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7754, 'status' => 'C', 'lower' => array(7755)); /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7756, 'status' => 'C', 'lower' => array(7757)); /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7758, 'status' => 'C', 'lower' => array(7759)); /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */
+$config['1e00_1eff'][] = array('upper' => 7760, 'status' => 'C', 'lower' => array(7761)); /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7762, 'status' => 'C', 'lower' => array(7763)); /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7764, 'status' => 'C', 'lower' => array(7765)); /* LATIN CAPITAL LETTER P WITH ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7766, 'status' => 'C', 'lower' => array(7767)); /* LATIN CAPITAL LETTER P WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7768, 'status' => 'C', 'lower' => array(7769)); /* LATIN CAPITAL LETTER R WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7770, 'status' => 'C', 'lower' => array(7771)); /* LATIN CAPITAL LETTER R WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7772, 'status' => 'C', 'lower' => array(7773)); /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */
+$config['1e00_1eff'][] = array('upper' => 7774, 'status' => 'C', 'lower' => array(7775)); /* LATIN CAPITAL LETTER R WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7776, 'status' => 'C', 'lower' => array(7777)); /* LATIN CAPITAL LETTER S WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7778, 'status' => 'C', 'lower' => array(7779)); /* LATIN CAPITAL LETTER S WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7780, 'status' => 'C', 'lower' => array(7781)); /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7782, 'status' => 'C', 'lower' => array(7783)); /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7784, 'status' => 'C', 'lower' => array(7785)); /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7786, 'status' => 'C', 'lower' => array(7787)); /* LATIN CAPITAL LETTER T WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7788, 'status' => 'C', 'lower' => array(7789)); /* LATIN CAPITAL LETTER T WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7790, 'status' => 'C', 'lower' => array(7791)); /* LATIN CAPITAL LETTER T WITH LINE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7792, 'status' => 'C', 'lower' => array(7793)); /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7794, 'status' => 'C', 'lower' => array(7795)); /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */
+$config['1e00_1eff'][] = array('upper' => 7796, 'status' => 'C', 'lower' => array(7797)); /* LATIN CAPITAL LETTER U WITH TILDE BELOW */
+$config['1e00_1eff'][] = array('upper' => 7798, 'status' => 'C', 'lower' => array(7799)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */
+$config['1e00_1eff'][] = array('upper' => 7800, 'status' => 'C', 'lower' => array(7801)); /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7802, 'status' => 'C', 'lower' => array(7803)); /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */
+$config['1e00_1eff'][] = array('upper' => 7804, 'status' => 'C', 'lower' => array(7805)); /* LATIN CAPITAL LETTER V WITH TILDE */
+$config['1e00_1eff'][] = array('upper' => 7806, 'status' => 'C', 'lower' => array(7807)); /* LATIN CAPITAL LETTER V WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7808, 'status' => 'C', 'lower' => array(7809)); /* LATIN CAPITAL LETTER W WITH GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7810, 'status' => 'C', 'lower' => array(7811)); /* LATIN CAPITAL LETTER W WITH ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7812, 'status' => 'C', 'lower' => array(7813)); /* LATIN CAPITAL LETTER W WITH DIAERESIS */
+$config['1e00_1eff'][] = array('upper' => 7814, 'status' => 'C', 'lower' => array(7815)); /* LATIN CAPITAL LETTER W WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7816, 'status' => 'C', 'lower' => array(7817)); /* LATIN CAPITAL LETTER W WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7818, 'status' => 'C', 'lower' => array(7819)); /* LATIN CAPITAL LETTER X WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7820, 'status' => 'C', 'lower' => array(7821)); /* LATIN CAPITAL LETTER X WITH DIAERESIS */
+$config['1e00_1eff'][] = array('upper' => 7822, 'status' => 'C', 'lower' => array(7823)); /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7824, 'status' => 'C', 'lower' => array(7825)); /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */
+$config['1e00_1eff'][] = array('upper' => 7826, 'status' => 'C', 'lower' => array(7827)); /* LATIN CAPITAL LETTER Z WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7828, 'status' => 'C', 'lower' => array(7829)); /* LATIN CAPITAL LETTER Z WITH LINE BELOW */
+
+//$config['1e00_1eff'][] = array('upper' => 7830, 'status' => 'F', 'lower' => array(104, 817)); /* LATIN SMALL LETTER H WITH LINE BELOW */
+//$config['1e00_1eff'][] = array('upper' => 7831, 'status' => 'F', 'lower' => array(116, 776)); /* LATIN SMALL LETTER T WITH DIAERESIS */
+//$config['1e00_1eff'][] = array('upper' => 7832, 'status' => 'F', 'lower' => array(119, 778)); /* LATIN SMALL LETTER W WITH RING ABOVE */
+//$config['1e00_1eff'][] = array('upper' => 7833, 'status' => 'F', 'lower' => array(121, 778)); /* LATIN SMALL LETTER Y WITH RING ABOVE */
+//$config['1e00_1eff'][] = array('upper' => 7834, 'status' => 'F', 'lower' => array(97, 702)); /* LATIN SMALL LETTER A WITH RIGHT HALF RING */
+//$config['1e00_1eff'][] = array('upper' => 7835, 'status' => 'C', 'lower' => array(7777)); /* LATIN SMALL LETTER LONG S WITH DOT ABOVE */
+
+$config['1e00_1eff'][] = array('upper' => 7840, 'status' => 'C', 'lower' => array(7841)); /* LATIN CAPITAL LETTER A WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7842, 'status' => 'C', 'lower' => array(7843)); /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7844, 'status' => 'C', 'lower' => array(7845)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7846, 'status' => 'C', 'lower' => array(7847)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7848, 'status' => 'C', 'lower' => array(7849)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7850, 'status' => 'C', 'lower' => array(7851)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7852, 'status' => 'C', 'lower' => array(7853)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7854, 'status' => 'C', 'lower' => array(7855)); /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7856, 'status' => 'C', 'lower' => array(7857)); /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7858, 'status' => 'C', 'lower' => array(7859)); /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7860, 'status' => 'C', 'lower' => array(7861)); /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7862, 'status' => 'C', 'lower' => array(7863)); /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7864, 'status' => 'C', 'lower' => array(7865)); /* LATIN CAPITAL LETTER E WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7866, 'status' => 'C', 'lower' => array(7867)); /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7868, 'status' => 'C', 'lower' => array(7869)); /* LATIN CAPITAL LETTER E WITH TILDE */
+$config['1e00_1eff'][] = array('upper' => 7870, 'status' => 'C', 'lower' => array(7871)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7872, 'status' => 'C', 'lower' => array(7873)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7874, 'status' => 'C', 'lower' => array(7875)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7876, 'status' => 'C', 'lower' => array(7877)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7878, 'status' => 'C', 'lower' => array(7879)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7880, 'status' => 'C', 'lower' => array(7881)); /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7882, 'status' => 'C', 'lower' => array(7883)); /* LATIN CAPITAL LETTER I WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7884, 'status' => 'C', 'lower' => array(7885)); /* LATIN CAPITAL LETTER O WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7886, 'status' => 'C', 'lower' => array(7887)); /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7888, 'status' => 'C', 'lower' => array(7889)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7890, 'status' => 'C', 'lower' => array(7891)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7892, 'status' => 'C', 'lower' => array(7893)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7894, 'status' => 'C', 'lower' => array(7895)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7896, 'status' => 'C', 'lower' => array(7897)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7898, 'status' => 'C', 'lower' => array(7899)); /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7900, 'status' => 'C', 'lower' => array(7901)); /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7902, 'status' => 'C', 'lower' => array(7903)); /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7904, 'status' => 'C', 'lower' => array(7905)); /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7906, 'status' => 'C', 'lower' => array(7907)); /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7908, 'status' => 'C', 'lower' => array(7909)); /* LATIN CAPITAL LETTER U WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7910, 'status' => 'C', 'lower' => array(7911)); /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7912, 'status' => 'C', 'lower' => array(7913)); /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */
+$config['1e00_1eff'][] = array('upper' => 7914, 'status' => 'C', 'lower' => array(7915)); /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7916, 'status' => 'C', 'lower' => array(7917)); /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7918, 'status' => 'C', 'lower' => array(7919)); /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */
+$config['1e00_1eff'][] = array('upper' => 7920, 'status' => 'C', 'lower' => array(7921)); /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7922, 'status' => 'C', 'lower' => array(7923)); /* LATIN CAPITAL LETTER Y WITH GRAVE */
+$config['1e00_1eff'][] = array('upper' => 7924, 'status' => 'C', 'lower' => array(7925)); /* LATIN CAPITAL LETTER Y WITH DOT BELOW */
+$config['1e00_1eff'][] = array('upper' => 7926, 'status' => 'C', 'lower' => array(7927)); /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */
+$config['1e00_1eff'][] = array('upper' => 7928, 'status' => 'C', 'lower' => array(7929)); /* LATIN CAPITAL LETTER Y WITH TILDE */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0370_03ff.php
===================================================================
--- cake/config/unicode/casefolding/0370_03ff.php	(revision 0)
+++ cake/config/unicode/casefolding/0370_03ff.php	(revision 0)
@@ -0,0 +1,108 @@
+<?php
+/* SVN FILE: $Id: 0370_03ff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0370 through U+03FF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0370_03ff'][] = array('upper' => 902, 'status' => 'C', 'lower' => array(940)); /* GREEK CAPITAL LETTER ALPHA WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 904, 'status' => 'C', 'lower' => array(941)); /* GREEK CAPITAL LETTER EPSILON WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 905, 'status' => 'C', 'lower' => array(942)); /* GREEK CAPITAL LETTER ETA WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 906, 'status' => 'C', 'lower' => array(943)); /* GREEK CAPITAL LETTER IOTA WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 908, 'status' => 'C', 'lower' => array(972)); /* GREEK CAPITAL LETTER OMICRON WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 910, 'status' => 'C', 'lower' => array(973)); /* GREEK CAPITAL LETTER UPSILON WITH TONOS */
+$config['0370_03ff'][] = array('upper' => 911, 'status' => 'C', 'lower' => array(974)); /* GREEK CAPITAL LETTER OMEGA WITH TONOS */
+//$config['0370_03ff'][] = array('upper' => 912, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
+$config['0370_03ff'][] = array('upper' => 913, 'status' => 'C', 'lower' => array(945)); /* GREEK CAPITAL LETTER ALPHA */
+$config['0370_03ff'][] = array('upper' => 914, 'status' => 'C', 'lower' => array(946)); /* GREEK CAPITAL LETTER BETA */
+$config['0370_03ff'][] = array('upper' => 915, 'status' => 'C', 'lower' => array(947)); /* GREEK CAPITAL LETTER GAMMA */
+$config['0370_03ff'][] = array('upper' => 916, 'status' => 'C', 'lower' => array(948)); /* GREEK CAPITAL LETTER DELTA */
+$config['0370_03ff'][] = array('upper' => 917, 'status' => 'C', 'lower' => array(949)); /* GREEK CAPITAL LETTER EPSILON */
+$config['0370_03ff'][] = array('upper' => 918, 'status' => 'C', 'lower' => array(950)); /* GREEK CAPITAL LETTER ZETA */
+$config['0370_03ff'][] = array('upper' => 919, 'status' => 'C', 'lower' => array(951)); /* GREEK CAPITAL LETTER ETA */
+$config['0370_03ff'][] = array('upper' => 920, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL LETTER THETA */
+$config['0370_03ff'][] = array('upper' => 921, 'status' => 'C', 'lower' => array(953)); /* GREEK CAPITAL LETTER IOTA */
+$config['0370_03ff'][] = array('upper' => 922, 'status' => 'C', 'lower' => array(954)); /* GREEK CAPITAL LETTER KAPPA */
+$config['0370_03ff'][] = array('upper' => 923, 'status' => 'C', 'lower' => array(955)); /* GREEK CAPITAL LETTER LAMDA */
+$config['0370_03ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(956)); /* GREEK CAPITAL LETTER MU */
+$config['0370_03ff'][] = array('upper' => 925, 'status' => 'C', 'lower' => array(957)); /* GREEK CAPITAL LETTER NU */
+$config['0370_03ff'][] = array('upper' => 926, 'status' => 'C', 'lower' => array(958)); /* GREEK CAPITAL LETTER XI */
+$config['0370_03ff'][] = array('upper' => 927, 'status' => 'C', 'lower' => array(959)); /* GREEK CAPITAL LETTER OMICRON */
+$config['0370_03ff'][] = array('upper' => 928, 'status' => 'C', 'lower' => array(960)); /* GREEK CAPITAL LETTER PI */
+$config['0370_03ff'][] = array('upper' => 929, 'status' => 'C', 'lower' => array(961)); /* GREEK CAPITAL LETTER RHO */
+$config['0370_03ff'][] = array('upper' => 931, 'status' => 'C', 'lower' => array(963)); /* GREEK CAPITAL LETTER SIGMA */
+$config['0370_03ff'][] = array('upper' => 932, 'status' => 'C', 'lower' => array(964)); /* GREEK CAPITAL LETTER TAU */
+$config['0370_03ff'][] = array('upper' => 933, 'status' => 'C', 'lower' => array(965)); /* GREEK CAPITAL LETTER UPSILON */
+$config['0370_03ff'][] = array('upper' => 934, 'status' => 'C', 'lower' => array(966)); /* GREEK CAPITAL LETTER PHI */
+$config['0370_03ff'][] = array('upper' => 935, 'status' => 'C', 'lower' => array(967)); /* GREEK CAPITAL LETTER CHI */
+$config['0370_03ff'][] = array('upper' => 936, 'status' => 'C', 'lower' => array(968)); /* GREEK CAPITAL LETTER PSI */
+$config['0370_03ff'][] = array('upper' => 937, 'status' => 'C', 'lower' => array(969)); /* GREEK CAPITAL LETTER OMEGA */
+$config['0370_03ff'][] = array('upper' => 938, 'status' => 'C', 'lower' => array(970)); /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+$config['0370_03ff'][] = array('upper' => 939, 'status' => 'C', 'lower' => array(971)); /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+$config['0370_03ff'][] = array('upper' => 944, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
+$config['0370_03ff'][] = array('upper' => 962, 'status' => 'C', 'lower' => array(963)); /* GREEK SMALL LETTER FINAL SIGMA */
+$config['0370_03ff'][] = array('upper' => 976, 'status' => 'C', 'lower' => array(946)); /* GREEK BETA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 977, 'status' => 'C', 'lower' => array(952)); /* GREEK THETA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 981, 'status' => 'C', 'lower' => array(966)); /* GREEK PHI SYMBOL */
+$config['0370_03ff'][] = array('upper' => 982, 'status' => 'C', 'lower' => array(960)); /* GREEK PI SYMBOL */
+$config['0370_03ff'][] = array('upper' => 984, 'status' => 'C', 'lower' => array(985)); /* GREEK LETTER ARCHAIC KOPPA */
+$config['0370_03ff'][] = array('upper' => 986, 'status' => 'C', 'lower' => array(987)); /* GREEK LETTER STIGMA */
+$config['0370_03ff'][] = array('upper' => 988, 'status' => 'C', 'lower' => array(989)); /* GREEK LETTER DIGAMMA */
+$config['0370_03ff'][] = array('upper' => 990, 'status' => 'C', 'lower' => array(991)); /* GREEK LETTER KOPPA */
+$config['0370_03ff'][] = array('upper' => 992, 'status' => 'C', 'lower' => array(993)); /* GREEK LETTER SAMPI */
+$config['0370_03ff'][] = array('upper' => 994, 'status' => 'C', 'lower' => array(995)); /* COPTIC CAPITAL LETTER SHEI */
+$config['0370_03ff'][] = array('upper' => 996, 'status' => 'C', 'lower' => array(997)); /* COPTIC CAPITAL LETTER FEI */
+$config['0370_03ff'][] = array('upper' => 998, 'status' => 'C', 'lower' => array(999)); /* COPTIC CAPITAL LETTER KHEI */
+$config['0370_03ff'][] = array('upper' => 1000, 'status' => 'C', 'lower' => array(1001)); /* COPTIC CAPITAL LETTER HORI */
+$config['0370_03ff'][] = array('upper' => 1002, 'status' => 'C', 'lower' => array(1003)); /* COPTIC CAPITAL LETTER GANGIA */
+$config['0370_03ff'][] = array('upper' => 1004, 'status' => 'C', 'lower' => array(1005)); /* COPTIC CAPITAL LETTER SHIMA */
+$config['0370_03ff'][] = array('upper' => 1006, 'status' => 'C', 'lower' => array(1007)); /* COPTIC CAPITAL LETTER DEI */
+$config['0370_03ff'][] = array('upper' => 1008, 'status' => 'C', 'lower' => array(954)); /* GREEK KAPPA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1009, 'status' => 'C', 'lower' => array(961)); /* GREEK RHO SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1012, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL THETA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1013, 'status' => 'C', 'lower' => array(949)); /* GREEK LUNATE EPSILON SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1015, 'status' => 'C', 'lower' => array(1016)); /* GREEK CAPITAL LETTER SHO */
+$config['0370_03ff'][] = array('upper' => 1017, 'status' => 'C', 'lower' => array(1010)); /* GREEK CAPITAL LUNATE SIGMA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1018, 'status' => 'C', 'lower' => array(1019)); /* GREEK CAPITAL LETTER SAN */
+$config['0370_03ff'][] = array('upper' => 1021, 'status' => 'C', 'lower' => array(891)); /* GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1022, 'status' => 'C', 'lower' => array(892)); /* GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL */
+$config['0370_03ff'][] = array('upper' => 1023, 'status' => 'C', 'lower' => array(893)); /* GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/1f00_1fff.php
===================================================================
--- cake/config/unicode/casefolding/1f00_1fff.php	(revision 0)
+++ cake/config/unicode/casefolding/1f00_1fff.php	(revision 0)
@@ -0,0 +1,222 @@
+<?php
+/* SVN FILE: $Id: 1f00_1fff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+1F00 through U+1FFF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['1f00_1fff'][] = array('upper' => 7944, 'status' => 'C', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 7945, 'status' => 'C', 'lower' => array(7937)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 7946, 'status' => 'C', 'lower' => array(7938)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7947, 'status' => 'C', 'lower' => array(7939)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7948, 'status' => 'C', 'lower' => array(7940)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7949, 'status' => 'C', 'lower' => array(7941)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7950, 'status' => 'C', 'lower' => array(7942)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 7951, 'status' => 'C', 'lower' => array(7943)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 7960, 'status' => 'C', 'lower' => array(7952)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 7961, 'status' => 'C', 'lower' => array(7953)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 7962, 'status' => 'C', 'lower' => array(7954)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7963, 'status' => 'C', 'lower' => array(7955)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7964, 'status' => 'C', 'lower' => array(7956)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7965, 'status' => 'C', 'lower' => array(7957)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7976, 'status' => 'C', 'lower' => array(7968)); /* GREEK CAPITAL LETTER ETA WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 7977, 'status' => 'C', 'lower' => array(7969)); /* GREEK CAPITAL LETTER ETA WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 7978, 'status' => 'C', 'lower' => array(7970)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7979, 'status' => 'C', 'lower' => array(7971)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7980, 'status' => 'C', 'lower' => array(7972)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7981, 'status' => 'C', 'lower' => array(7973)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7982, 'status' => 'C', 'lower' => array(7974)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 7983, 'status' => 'C', 'lower' => array(7975)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 7992, 'status' => 'C', 'lower' => array(7984)); /* GREEK CAPITAL LETTER IOTA WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 7993, 'status' => 'C', 'lower' => array(7985)); /* GREEK CAPITAL LETTER IOTA WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 7994, 'status' => 'C', 'lower' => array(7986)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7995, 'status' => 'C', 'lower' => array(7987)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 7996, 'status' => 'C', 'lower' => array(7988)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7997, 'status' => 'C', 'lower' => array(7989)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 7998, 'status' => 'C', 'lower' => array(7990)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 7999, 'status' => 'C', 'lower' => array(7991)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8008, 'status' => 'C', 'lower' => array(8000)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 8009, 'status' => 'C', 'lower' => array(8001)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 8010, 'status' => 'C', 'lower' => array(8002)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8011, 'status' => 'C', 'lower' => array(8003)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8012, 'status' => 'C', 'lower' => array(8004)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8013, 'status' => 'C', 'lower' => array(8005)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8016, 'status' => 'F', 'lower' => array(965, 787)); /* GREEK SMALL LETTER UPSILON WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 8018, 'status' => 'F', 'lower' => array(965, 787, 768)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8020, 'status' => 'F', 'lower' => array(965, 787, 769)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8022, 'status' => 'F', 'lower' => array(965, 787, 834)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8025, 'status' => 'C', 'lower' => array(8017)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 8027, 'status' => 'C', 'lower' => array(8019)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8029, 'status' => 'C', 'lower' => array(8021)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8031, 'status' => 'C', 'lower' => array(8023)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8040, 'status' => 'C', 'lower' => array(8032)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 8041, 'status' => 'C', 'lower' => array(8033)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 8042, 'status' => 'C', 'lower' => array(8034)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8043, 'status' => 'C', 'lower' => array(8035)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8044, 'status' => 'C', 'lower' => array(8036)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8045, 'status' => 'C', 'lower' => array(8037)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8046, 'status' => 'C', 'lower' => array(8038)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8047, 'status' => 'C', 'lower' => array(8039)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8064, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8065, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8066, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8067, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8068, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8069, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8070, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8071, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'S', 'lower' => array(8064)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'S', 'lower' => array(8065)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'S', 'lower' => array(8066)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'S', 'lower' => array(8067)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'S', 'lower' => array(8068)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'S', 'lower' => array(8069)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'S', 'lower' => array(8070)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'S', 'lower' => array(8071)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8080, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8081, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8082, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8083, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8084, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8085, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8086, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8087, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'S', 'lower' => array(8080)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'S', 'lower' => array(8081)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'S', 'lower' => array(8082)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'S', 'lower' => array(8083)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'S', 'lower' => array(8084)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'S', 'lower' => array(8085)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'S', 'lower' => array(8086)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'S', 'lower' => array(8087)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8096, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8097, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8098, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8099, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8100, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8101, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8102, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8103, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'S', 'lower' => array(8096)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'S', 'lower' => array(8097)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'S', 'lower' => array(8098)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'S', 'lower' => array(8099)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'S', 'lower' => array(8100)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'S', 'lower' => array(8101)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'S', 'lower' => array(8102)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'S', 'lower' => array(8103)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8114, 'status' => 'F', 'lower' => array(8048, 953)); /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8115, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8116, 'status' => 'F', 'lower' => array(940, 953)); /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8118, 'status' => 'F', 'lower' => array(945, 834)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8119, 'status' => 'F', 'lower' => array(945, 834, 953)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8120, 'status' => 'C', 'lower' => array(8112)); /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */
+$config['1f00_1fff'][] = array('upper' => 8121, 'status' => 'C', 'lower' => array(8113)); /* GREEK CAPITAL LETTER ALPHA WITH MACRON */
+$config['1f00_1fff'][] = array('upper' => 8122, 'status' => 'C', 'lower' => array(8048)); /* GREEK CAPITAL LETTER ALPHA WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8123, 'status' => 'C', 'lower' => array(8049)); /* GREEK CAPITAL LETTER ALPHA WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'S', 'lower' => array(8115)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8126, 'status' => 'C', 'lower' => array(953)); /* GREEK PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8130, 'status' => 'F', 'lower' => array(8052, 953)); /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8131, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8132, 'status' => 'F', 'lower' => array(942, 953)); /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8134, 'status' => 'F', 'lower' => array(951, 834)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8135, 'status' => 'F', 'lower' => array(951, 834, 953)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8136, 'status' => 'C', 'lower' => array(8050)); /* GREEK CAPITAL LETTER EPSILON WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8137, 'status' => 'C', 'lower' => array(8051)); /* GREEK CAPITAL LETTER EPSILON WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8138, 'status' => 'C', 'lower' => array(8052)); /* GREEK CAPITAL LETTER ETA WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8139, 'status' => 'C', 'lower' => array(8053)); /* GREEK CAPITAL LETTER ETA WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'S', 'lower' => array(8131)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8146, 'status' => 'F', 'lower' => array(953, 776, 768)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8147, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8150, 'status' => 'F', 'lower' => array(953, 834)); /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8151, 'status' => 'F', 'lower' => array(953, 776, 834)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8152, 'status' => 'C', 'lower' => array(8144)); /* GREEK CAPITAL LETTER IOTA WITH VRACHY */
+$config['1f00_1fff'][] = array('upper' => 8153, 'status' => 'C', 'lower' => array(8145)); /* GREEK CAPITAL LETTER IOTA WITH MACRON */
+$config['1f00_1fff'][] = array('upper' => 8154, 'status' => 'C', 'lower' => array(8054)); /* GREEK CAPITAL LETTER IOTA WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8155, 'status' => 'C', 'lower' => array(8055)); /* GREEK CAPITAL LETTER IOTA WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8162, 'status' => 'F', 'lower' => array(965, 776, 768)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */
+$config['1f00_1fff'][] = array('upper' => 8163, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */
+$config['1f00_1fff'][] = array('upper' => 8164, 'status' => 'F', 'lower' => array(961, 787)); /* GREEK SMALL LETTER RHO WITH PSILI */
+$config['1f00_1fff'][] = array('upper' => 8166, 'status' => 'F', 'lower' => array(965, 834)); /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8167, 'status' => 'F', 'lower' => array(965, 776, 834)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8168, 'status' => 'C', 'lower' => array(8160)); /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */
+$config['1f00_1fff'][] = array('upper' => 8169, 'status' => 'C', 'lower' => array(8161)); /* GREEK CAPITAL LETTER UPSILON WITH MACRON */
+$config['1f00_1fff'][] = array('upper' => 8170, 'status' => 'C', 'lower' => array(8058)); /* GREEK CAPITAL LETTER UPSILON WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8171, 'status' => 'C', 'lower' => array(8059)); /* GREEK CAPITAL LETTER UPSILON WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8172, 'status' => 'C', 'lower' => array(8165)); /* GREEK CAPITAL LETTER RHO WITH DASIA */
+$config['1f00_1fff'][] = array('upper' => 8178, 'status' => 'F', 'lower' => array(8060, 953)); /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8179, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8180, 'status' => 'F', 'lower' => array(974, 953)); /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8182, 'status' => 'F', 'lower' => array(969, 834)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */
+$config['1f00_1fff'][] = array('upper' => 8183, 'status' => 'F', 'lower' => array(969, 834, 953)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8184, 'status' => 'C', 'lower' => array(8056)); /* GREEK CAPITAL LETTER OMICRON WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8185, 'status' => 'C', 'lower' => array(8057)); /* GREEK CAPITAL LETTER OMICRON WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8186, 'status' => 'C', 'lower' => array(8060)); /* GREEK CAPITAL LETTER OMEGA WITH VARIA */
+$config['1f00_1fff'][] = array('upper' => 8187, 'status' => 'C', 'lower' => array(8061)); /* GREEK CAPITAL LETTER OMEGA WITH OXIA */
+$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
+$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'S', 'lower' => array(8179)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2c80_2cff.php
===================================================================
--- cake/config/unicode/casefolding/2c80_2cff.php	(revision 0)
+++ cake/config/unicode/casefolding/2c80_2cff.php	(revision 0)
@@ -0,0 +1,96 @@
+<?php
+/* SVN FILE: $Id: 2c80_2cff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2C80 through U+2CFF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2c80_2cff'][] = array('upper' => 11392, 'status' => 'C', 'lower' => array(11393)); /* COPTIC CAPITAL LETTER ALFA */
+$config['2c80_2cff'][] = array('upper' => 11394, 'status' => 'C', 'lower' => array(11395)); /* COPTIC CAPITAL LETTER VIDA */
+$config['2c80_2cff'][] = array('upper' => 11396, 'status' => 'C', 'lower' => array(11397)); /* COPTIC CAPITAL LETTER GAMMA */
+$config['2c80_2cff'][] = array('upper' => 11398, 'status' => 'C', 'lower' => array(11399)); /* COPTIC CAPITAL LETTER DALDA */
+$config['2c80_2cff'][] = array('upper' => 11400, 'status' => 'C', 'lower' => array(11401)); /* COPTIC CAPITAL LETTER EIE */
+$config['2c80_2cff'][] = array('upper' => 11402, 'status' => 'C', 'lower' => array(11403)); /* COPTIC CAPITAL LETTER SOU */
+$config['2c80_2cff'][] = array('upper' => 11404, 'status' => 'C', 'lower' => array(11405)); /* COPTIC CAPITAL LETTER ZATA */
+$config['2c80_2cff'][] = array('upper' => 11406, 'status' => 'C', 'lower' => array(11407)); /* COPTIC CAPITAL LETTER HATE */
+$config['2c80_2cff'][] = array('upper' => 11408, 'status' => 'C', 'lower' => array(11409)); /* COPTIC CAPITAL LETTER THETHE */
+$config['2c80_2cff'][] = array('upper' => 11410, 'status' => 'C', 'lower' => array(11411)); /* COPTIC CAPITAL LETTER IAUDA */
+$config['2c80_2cff'][] = array('upper' => 11412, 'status' => 'C', 'lower' => array(11413)); /* COPTIC CAPITAL LETTER KAPA */
+$config['2c80_2cff'][] = array('upper' => 11414, 'status' => 'C', 'lower' => array(11415)); /* COPTIC CAPITAL LETTER LAULA */
+$config['2c80_2cff'][] = array('upper' => 11416, 'status' => 'C', 'lower' => array(11417)); /* COPTIC CAPITAL LETTER MI */
+$config['2c80_2cff'][] = array('upper' => 11418, 'status' => 'C', 'lower' => array(11419)); /* COPTIC CAPITAL LETTER NI */
+$config['2c80_2cff'][] = array('upper' => 11420, 'status' => 'C', 'lower' => array(11421)); /* COPTIC CAPITAL LETTER KSI */
+$config['2c80_2cff'][] = array('upper' => 11422, 'status' => 'C', 'lower' => array(11423)); /* COPTIC CAPITAL LETTER O */
+$config['2c80_2cff'][] = array('upper' => 11424, 'status' => 'C', 'lower' => array(11425)); /* COPTIC CAPITAL LETTER PI */
+$config['2c80_2cff'][] = array('upper' => 11426, 'status' => 'C', 'lower' => array(11427)); /* COPTIC CAPITAL LETTER RO */
+$config['2c80_2cff'][] = array('upper' => 11428, 'status' => 'C', 'lower' => array(11429)); /* COPTIC CAPITAL LETTER SIMA */
+$config['2c80_2cff'][] = array('upper' => 11430, 'status' => 'C', 'lower' => array(11431)); /* COPTIC CAPITAL LETTER TAU */
+$config['2c80_2cff'][] = array('upper' => 11432, 'status' => 'C', 'lower' => array(11433)); /* COPTIC CAPITAL LETTER UA */
+$config['2c80_2cff'][] = array('upper' => 11434, 'status' => 'C', 'lower' => array(11435)); /* COPTIC CAPITAL LETTER FI */
+$config['2c80_2cff'][] = array('upper' => 11436, 'status' => 'C', 'lower' => array(11437)); /* COPTIC CAPITAL LETTER KHI */
+$config['2c80_2cff'][] = array('upper' => 11438, 'status' => 'C', 'lower' => array(11439)); /* COPTIC CAPITAL LETTER PSI */
+$config['2c80_2cff'][] = array('upper' => 11440, 'status' => 'C', 'lower' => array(11441)); /* COPTIC CAPITAL LETTER OOU */
+$config['2c80_2cff'][] = array('upper' => 11442, 'status' => 'C', 'lower' => array(11443)); /* COPTIC CAPITAL LETTER DIALECT-P ALEF */
+$config['2c80_2cff'][] = array('upper' => 11444, 'status' => 'C', 'lower' => array(11445)); /* COPTIC CAPITAL LETTER OLD COPTIC AIN */
+$config['2c80_2cff'][] = array('upper' => 11446, 'status' => 'C', 'lower' => array(11447)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */
+$config['2c80_2cff'][] = array('upper' => 11448, 'status' => 'C', 'lower' => array(11449)); /* COPTIC CAPITAL LETTER DIALECT-P KAPA */
+$config['2c80_2cff'][] = array('upper' => 11450, 'status' => 'C', 'lower' => array(11451)); /* COPTIC CAPITAL LETTER DIALECT-P NI */
+$config['2c80_2cff'][] = array('upper' => 11452, 'status' => 'C', 'lower' => array(11453)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */
+$config['2c80_2cff'][] = array('upper' => 11454, 'status' => 'C', 'lower' => array(11455)); /* COPTIC CAPITAL LETTER OLD COPTIC OOU */
+$config['2c80_2cff'][] = array('upper' => 11456, 'status' => 'C', 'lower' => array(11457)); /* COPTIC CAPITAL LETTER SAMPI */
+$config['2c80_2cff'][] = array('upper' => 11458, 'status' => 'C', 'lower' => array(11459)); /* COPTIC CAPITAL LETTER CROSSED SHEI */
+$config['2c80_2cff'][] = array('upper' => 11460, 'status' => 'C', 'lower' => array(11461)); /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */
+$config['2c80_2cff'][] = array('upper' => 11462, 'status' => 'C', 'lower' => array(11463)); /* COPTIC CAPITAL LETTER OLD COPTIC ESH */
+$config['2c80_2cff'][] = array('upper' => 11464, 'status' => 'C', 'lower' => array(11465)); /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */
+$config['2c80_2cff'][] = array('upper' => 11466, 'status' => 'C', 'lower' => array(11467)); /* COPTIC CAPITAL LETTER DIALECT-P HORI */
+$config['2c80_2cff'][] = array('upper' => 11468, 'status' => 'C', 'lower' => array(11469)); /* COPTIC CAPITAL LETTER OLD COPTIC HORI */
+$config['2c80_2cff'][] = array('upper' => 11470, 'status' => 'C', 'lower' => array(11471)); /* COPTIC CAPITAL LETTER OLD COPTIC HA */
+$config['2c80_2cff'][] = array('upper' => 11472, 'status' => 'C', 'lower' => array(11473)); /* COPTIC CAPITAL LETTER L-SHAPED HA */
+$config['2c80_2cff'][] = array('upper' => 11474, 'status' => 'C', 'lower' => array(11475)); /* COPTIC CAPITAL LETTER OLD COPTIC HEI */
+$config['2c80_2cff'][] = array('upper' => 11476, 'status' => 'C', 'lower' => array(11477)); /* COPTIC CAPITAL LETTER OLD COPTIC HAT */
+$config['2c80_2cff'][] = array('upper' => 11478, 'status' => 'C', 'lower' => array(11479)); /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */
+$config['2c80_2cff'][] = array('upper' => 11480, 'status' => 'C', 'lower' => array(11481)); /* COPTIC CAPITAL LETTER OLD COPTIC DJA */
+$config['2c80_2cff'][] = array('upper' => 11482, 'status' => 'C', 'lower' => array(11483)); /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */
+$config['2c80_2cff'][] = array('upper' => 11484, 'status' => 'C', 'lower' => array(11485)); /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */
+$config['2c80_2cff'][] = array('upper' => 11486, 'status' => 'C', 'lower' => array(11487)); /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */
+$config['2c80_2cff'][] = array('upper' => 11488, 'status' => 'C', 'lower' => array(11489)); /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */
+$config['2c80_2cff'][] = array('upper' => 11490, 'status' => 'C', 'lower' => array(11491)); /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2460_24ff.php
===================================================================
--- cake/config/unicode/casefolding/2460_24ff.php	(revision 0)
+++ cake/config/unicode/casefolding/2460_24ff.php	(revision 0)
@@ -0,0 +1,72 @@
+<?php
+/* SVN FILE: $Id: 2460_24ff.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2460 through U+24FF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2460_24ff'][] = array('upper' => 9398, 'status' => 'C', 'lower' => array(9424)); /* CIRCLED LATIN CAPITAL LETTER A */
+$config['2460_24ff'][] = array('upper' => 9399, 'status' => 'C', 'lower' => array(9425)); /* CIRCLED LATIN CAPITAL LETTER B */
+$config['2460_24ff'][] = array('upper' => 9400, 'status' => 'C', 'lower' => array(9426)); /* CIRCLED LATIN CAPITAL LETTER C */
+$config['2460_24ff'][] = array('upper' => 9401, 'status' => 'C', 'lower' => array(9427)); /* CIRCLED LATIN CAPITAL LETTER D */
+$config['2460_24ff'][] = array('upper' => 9402, 'status' => 'C', 'lower' => array(9428)); /* CIRCLED LATIN CAPITAL LETTER E */
+$config['2460_24ff'][] = array('upper' => 9403, 'status' => 'C', 'lower' => array(9429)); /* CIRCLED LATIN CAPITAL LETTER F */
+$config['2460_24ff'][] = array('upper' => 9404, 'status' => 'C', 'lower' => array(9430)); /* CIRCLED LATIN CAPITAL LETTER G */
+$config['2460_24ff'][] = array('upper' => 9405, 'status' => 'C', 'lower' => array(9431)); /* CIRCLED LATIN CAPITAL LETTER H */
+$config['2460_24ff'][] = array('upper' => 9406, 'status' => 'C', 'lower' => array(9432)); /* CIRCLED LATIN CAPITAL LETTER I */
+$config['2460_24ff'][] = array('upper' => 9407, 'status' => 'C', 'lower' => array(9433)); /* CIRCLED LATIN CAPITAL LETTER J */
+$config['2460_24ff'][] = array('upper' => 9408, 'status' => 'C', 'lower' => array(9434)); /* CIRCLED LATIN CAPITAL LETTER K */
+$config['2460_24ff'][] = array('upper' => 9409, 'status' => 'C', 'lower' => array(9435)); /* CIRCLED LATIN CAPITAL LETTER L */
+$config['2460_24ff'][] = array('upper' => 9410, 'status' => 'C', 'lower' => array(9436)); /* CIRCLED LATIN CAPITAL LETTER M */
+$config['2460_24ff'][] = array('upper' => 9411, 'status' => 'C', 'lower' => array(9437)); /* CIRCLED LATIN CAPITAL LETTER N */
+$config['2460_24ff'][] = array('upper' => 9412, 'status' => 'C', 'lower' => array(9438)); /* CIRCLED LATIN CAPITAL LETTER O */
+$config['2460_24ff'][] = array('upper' => 9413, 'status' => 'C', 'lower' => array(9439)); /* CIRCLED LATIN CAPITAL LETTER P */
+$config['2460_24ff'][] = array('upper' => 9414, 'status' => 'C', 'lower' => array(9440)); /* CIRCLED LATIN CAPITAL LETTER Q */
+$config['2460_24ff'][] = array('upper' => 9415, 'status' => 'C', 'lower' => array(9441)); /* CIRCLED LATIN CAPITAL LETTER R */
+$config['2460_24ff'][] = array('upper' => 9416, 'status' => 'C', 'lower' => array(9442)); /* CIRCLED LATIN CAPITAL LETTER S */
+$config['2460_24ff'][] = array('upper' => 9417, 'status' => 'C', 'lower' => array(9443)); /* CIRCLED LATIN CAPITAL LETTER T */
+$config['2460_24ff'][] = array('upper' => 9418, 'status' => 'C', 'lower' => array(9444)); /* CIRCLED LATIN CAPITAL LETTER U */
+$config['2460_24ff'][] = array('upper' => 9419, 'status' => 'C', 'lower' => array(9445)); /* CIRCLED LATIN CAPITAL LETTER V */
+$config['2460_24ff'][] = array('upper' => 9420, 'status' => 'C', 'lower' => array(9446)); /* CIRCLED LATIN CAPITAL LETTER W */
+$config['2460_24ff'][] = array('upper' => 9421, 'status' => 'C', 'lower' => array(9447)); /* CIRCLED LATIN CAPITAL LETTER X */
+$config['2460_24ff'][] = array('upper' => 9422, 'status' => 'C', 'lower' => array(9448)); /* CIRCLED LATIN CAPITAL LETTER Y */
+$config['2460_24ff'][] = array('upper' => 9423, 'status' => 'C', 'lower' => array(9449)); /* CIRCLED LATIN CAPITAL LETTER Z */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0100_017f.php
===================================================================
--- cake/config/unicode/casefolding/0100_017f.php	(revision 0)
+++ cake/config/unicode/casefolding/0100_017f.php	(revision 0)
@@ -0,0 +1,112 @@
+<?php
+/* SVN FILE: $Id: 0100_017f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0100 through U+017F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0100_017f'][] = array('upper' => 256, 'status' => 'C', 'lower' => array(257)); /* LATIN CAPITAL LETTER A WITH MACRON */
+$config['0100_017f'][] = array('upper' => 258, 'status' => 'C', 'lower' => array(259)); /* LATIN CAPITAL LETTER A WITH BREVE */
+$config['0100_017f'][] = array('upper' => 260, 'status' => 'C', 'lower' => array(261)); /* LATIN CAPITAL LETTER A WITH OGONEK */
+$config['0100_017f'][] = array('upper' => 262, 'status' => 'C', 'lower' => array(263)); /* LATIN CAPITAL LETTER C WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 264, 'status' => 'C', 'lower' => array(265)); /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 266, 'status' => 'C', 'lower' => array(267)); /* LATIN CAPITAL LETTER C WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 268, 'status' => 'C', 'lower' => array(269)); /* LATIN CAPITAL LETTER C WITH CARON */
+$config['0100_017f'][] = array('upper' => 270, 'status' => 'C', 'lower' => array(271)); /* LATIN CAPITAL LETTER D WITH CARON */
+$config['0100_017f'][] = array('upper' => 272, 'status' => 'C', 'lower' => array(273)); /* LATIN CAPITAL LETTER D WITH STROKE */
+$config['0100_017f'][] = array('upper' => 274, 'status' => 'C', 'lower' => array(275)); /* LATIN CAPITAL LETTER E WITH MACRON */
+$config['0100_017f'][] = array('upper' => 276, 'status' => 'C', 'lower' => array(277)); /* LATIN CAPITAL LETTER E WITH BREVE */
+$config['0100_017f'][] = array('upper' => 278, 'status' => 'C', 'lower' => array(279)); /* LATIN CAPITAL LETTER E WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 280, 'status' => 'C', 'lower' => array(281)); /* LATIN CAPITAL LETTER E WITH OGONEK */
+$config['0100_017f'][] = array('upper' => 282, 'status' => 'C', 'lower' => array(283)); /* LATIN CAPITAL LETTER E WITH CARON */
+$config['0100_017f'][] = array('upper' => 284, 'status' => 'C', 'lower' => array(285)); /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 286, 'status' => 'C', 'lower' => array(287)); /* LATIN CAPITAL LETTER G WITH BREVE */
+$config['0100_017f'][] = array('upper' => 288, 'status' => 'C', 'lower' => array(289)); /* LATIN CAPITAL LETTER G WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 290, 'status' => 'C', 'lower' => array(291)); /* LATIN CAPITAL LETTER G WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 292, 'status' => 'C', 'lower' => array(293)); /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 294, 'status' => 'C', 'lower' => array(295)); /* LATIN CAPITAL LETTER H WITH STROKE */
+$config['0100_017f'][] = array('upper' => 296, 'status' => 'C', 'lower' => array(297)); /* LATIN CAPITAL LETTER I WITH TILDE */
+$config['0100_017f'][] = array('upper' => 298, 'status' => 'C', 'lower' => array(299)); /* LATIN CAPITAL LETTER I WITH MACRON */
+$config['0100_017f'][] = array('upper' => 300, 'status' => 'C', 'lower' => array(301)); /* LATIN CAPITAL LETTER I WITH BREVE */
+$config['0100_017f'][] = array('upper' => 302, 'status' => 'C', 'lower' => array(303)); /* LATIN CAPITAL LETTER I WITH OGONEK */
+$config['0100_017f'][] = array('upper' => 304, 'status' => 'F', 'lower' => array(105, 775)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 304, 'status' => 'T', 'lower' => array(105)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 306, 'status' => 'C', 'lower' => array(307)); /* LATIN CAPITAL LIGATURE IJ */
+$config['0100_017f'][] = array('upper' => 308, 'status' => 'C', 'lower' => array(309)); /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 310, 'status' => 'C', 'lower' => array(311)); /* LATIN CAPITAL LETTER K WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 313, 'status' => 'C', 'lower' => array(314)); /* LATIN CAPITAL LETTER L WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 315, 'status' => 'C', 'lower' => array(316)); /* LATIN CAPITAL LETTER L WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 317, 'status' => 'C', 'lower' => array(318)); /* LATIN CAPITAL LETTER L WITH CARON */
+$config['0100_017f'][] = array('upper' => 319, 'status' => 'C', 'lower' => array(320)); /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */
+$config['0100_017f'][] = array('upper' => 321, 'status' => 'C', 'lower' => array(322)); /* LATIN CAPITAL LETTER L WITH STROKE */
+$config['0100_017f'][] = array('upper' => 323, 'status' => 'C', 'lower' => array(324)); /* LATIN CAPITAL LETTER N WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 325, 'status' => 'C', 'lower' => array(326)); /* LATIN CAPITAL LETTER N WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 327, 'status' => 'C', 'lower' => array(328)); /* LATIN CAPITAL LETTER N WITH CARON */
+$config['0100_017f'][] = array('upper' => 329, 'status' => 'F', 'lower' => array(700, 110)); /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */
+$config['0100_017f'][] = array('upper' => 330, 'status' => 'C', 'lower' => array(331)); /* LATIN CAPITAL LETTER ENG */
+$config['0100_017f'][] = array('upper' => 332, 'status' => 'C', 'lower' => array(333)); /* LATIN CAPITAL LETTER O WITH MACRON */
+$config['0100_017f'][] = array('upper' => 334, 'status' => 'C', 'lower' => array(335)); /* LATIN CAPITAL LETTER O WITH BREVE */
+$config['0100_017f'][] = array('upper' => 336, 'status' => 'C', 'lower' => array(337)); /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+$config['0100_017f'][] = array('upper' => 338, 'status' => 'C', 'lower' => array(339)); /* LATIN CAPITAL LIGATURE OE */
+$config['0100_017f'][] = array('upper' => 340, 'status' => 'C', 'lower' => array(341)); /* LATIN CAPITAL LETTER R WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 342, 'status' => 'C', 'lower' => array(343)); /* LATIN CAPITAL LETTER R WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 344, 'status' => 'C', 'lower' => array(345)); /* LATIN CAPITAL LETTER R WITH CARON */
+$config['0100_017f'][] = array('upper' => 346, 'status' => 'C', 'lower' => array(347)); /* LATIN CAPITAL LETTER S WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 348, 'status' => 'C', 'lower' => array(349)); /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 350, 'status' => 'C', 'lower' => array(351)); /* LATIN CAPITAL LETTER S WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 352, 'status' => 'C', 'lower' => array(353)); /* LATIN CAPITAL LETTER S WITH CARON */
+$config['0100_017f'][] = array('upper' => 354, 'status' => 'C', 'lower' => array(355)); /* LATIN CAPITAL LETTER T WITH CEDILLA */
+$config['0100_017f'][] = array('upper' => 356, 'status' => 'C', 'lower' => array(357)); /* LATIN CAPITAL LETTER T WITH CARON */
+$config['0100_017f'][] = array('upper' => 358, 'status' => 'C', 'lower' => array(359)); /* LATIN CAPITAL LETTER T WITH STROKE */
+$config['0100_017f'][] = array('upper' => 360, 'status' => 'C', 'lower' => array(361)); /* LATIN CAPITAL LETTER U WITH TILDE */
+$config['0100_017f'][] = array('upper' => 362, 'status' => 'C', 'lower' => array(363)); /* LATIN CAPITAL LETTER U WITH MACRON */
+$config['0100_017f'][] = array('upper' => 364, 'status' => 'C', 'lower' => array(365)); /* LATIN CAPITAL LETTER U WITH BREVE */
+$config['0100_017f'][] = array('upper' => 366, 'status' => 'C', 'lower' => array(367)); /* LATIN CAPITAL LETTER U WITH RING ABOVE */
+$config['0100_017f'][] = array('upper' => 368, 'status' => 'C', 'lower' => array(369)); /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+$config['0100_017f'][] = array('upper' => 370, 'status' => 'C', 'lower' => array(371)); /* LATIN CAPITAL LETTER U WITH OGONEK */
+$config['0100_017f'][] = array('upper' => 372, 'status' => 'C', 'lower' => array(373)); /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 374, 'status' => 'C', 'lower' => array(375)); /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */
+$config['0100_017f'][] = array('upper' => 376, 'status' => 'C', 'lower' => array(255)); /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
+$config['0100_017f'][] = array('upper' => 377, 'status' => 'C', 'lower' => array(378)); /* LATIN CAPITAL LETTER Z WITH ACUTE */
+$config['0100_017f'][] = array('upper' => 379, 'status' => 'C', 'lower' => array(380)); /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+$config['0100_017f'][] = array('upper' => 381, 'status' => 'C', 'lower' => array(382)); /* LATIN CAPITAL LETTER Z WITH CARON */
+$config['0100_017f'][] = array('upper' => 383, 'status' => 'C', 'lower' => array(115)); /* LATIN SMALL LETTER LONG S */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2100_214f.php
===================================================================
--- cake/config/unicode/casefolding/2100_214f.php	(revision 0)
+++ cake/config/unicode/casefolding/2100_214f.php	(revision 0)
@@ -0,0 +1,50 @@
+<?php
+/* SVN FILE: $Id: 2100_214f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2100 through U+214F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2100_214f'][] = array('upper' => 8486, 'status' => 'C', 'lower' => array(969)); /* OHM SIGN */
+$config['2100_214f'][] = array('upper' => 8490, 'status' => 'C', 'lower' => array(107)); /* KELVIN SIGN */
+$config['2100_214f'][] = array('upper' => 8491, 'status' => 'C', 'lower' => array(229)); /* ANGSTROM SIGN */
+$config['2100_214f'][] = array('upper' => 8498, 'status' => 'C', 'lower' => array(8526)); /* TURNED CAPITAL F */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0500_052f.php
===================================================================
--- cake/config/unicode/casefolding/0500_052f.php	(revision 0)
+++ cake/config/unicode/casefolding/0500_052f.php	(revision 0)
@@ -0,0 +1,54 @@
+<?php
+/* SVN FILE: $Id: 0500_052f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0500 through U+052F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0500_052f'][] = array('upper' => 1280, 'status' => 'C', 'lower' => array(1281)); /* CYRILLIC CAPITAL LETTER KOMI DE */
+$config['0500_052f'][] = array('upper' => 1282, 'status' => 'C', 'lower' => array(1283)); /* CYRILLIC CAPITAL LETTER KOMI DJE */
+$config['0500_052f'][] = array('upper' => 1284, 'status' => 'C', 'lower' => array(1285)); /* CYRILLIC CAPITAL LETTER KOMI ZJE */
+$config['0500_052f'][] = array('upper' => 1286, 'status' => 'C', 'lower' => array(1287)); /* CYRILLIC CAPITAL LETTER KOMI DZJE */
+$config['0500_052f'][] = array('upper' => 1288, 'status' => 'C', 'lower' => array(1289)); /* CYRILLIC CAPITAL LETTER KOMI LJE */
+$config['0500_052f'][] = array('upper' => 1290, 'status' => 'C', 'lower' => array(1291)); /* CYRILLIC CAPITAL LETTER KOMI NJE */
+$config['0500_052f'][] = array('upper' => 1292, 'status' => 'C', 'lower' => array(1293)); /* CYRILLIC CAPITAL LETTER KOMI SJE */
+$config['0500_052f'][] = array('upper' => 1294, 'status' => 'C', 'lower' => array(1295)); /* CYRILLIC CAPITAL LETTER KOMI TJE */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/ff00_ffef.php
===================================================================
--- cake/config/unicode/casefolding/ff00_ffef.php	(revision 0)
+++ cake/config/unicode/casefolding/ff00_ffef.php	(revision 0)
@@ -0,0 +1,72 @@
+<?php
+/* SVN FILE: $Id: ff00_ffef.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+FF00 through U+FFEF
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['ff00_ffef'][] = array('upper' => 65313, 'status' => 'C', 'lower' => array(65345)); /* FULLWIDTH LATIN CAPITAL LETTER A */
+$config['ff00_ffef'][] = array('upper' => 65314, 'status' => 'C', 'lower' => array(65346)); /* FULLWIDTH LATIN CAPITAL LETTER B */
+$config['ff00_ffef'][] = array('upper' => 65315, 'status' => 'C', 'lower' => array(65347)); /* FULLWIDTH LATIN CAPITAL LETTER C */
+$config['ff00_ffef'][] = array('upper' => 65316, 'status' => 'C', 'lower' => array(65348)); /* FULLWIDTH LATIN CAPITAL LETTER D */
+$config['ff00_ffef'][] = array('upper' => 65317, 'status' => 'C', 'lower' => array(65349)); /* FULLWIDTH LATIN CAPITAL LETTER E */
+$config['ff00_ffef'][] = array('upper' => 65318, 'status' => 'C', 'lower' => array(65350)); /* FULLWIDTH LATIN CAPITAL LETTER F */
+$config['ff00_ffef'][] = array('upper' => 65319, 'status' => 'C', 'lower' => array(65351)); /* FULLWIDTH LATIN CAPITAL LETTER G */
+$config['ff00_ffef'][] = array('upper' => 65320, 'status' => 'C', 'lower' => array(65352)); /* FULLWIDTH LATIN CAPITAL LETTER H */
+$config['ff00_ffef'][] = array('upper' => 65321, 'status' => 'C', 'lower' => array(65353)); /* FULLWIDTH LATIN CAPITAL LETTER I */
+$config['ff00_ffef'][] = array('upper' => 65322, 'status' => 'C', 'lower' => array(65354)); /* FULLWIDTH LATIN CAPITAL LETTER J */
+$config['ff00_ffef'][] = array('upper' => 65323, 'status' => 'C', 'lower' => array(65355)); /* FULLWIDTH LATIN CAPITAL LETTER K */
+$config['ff00_ffef'][] = array('upper' => 65324, 'status' => 'C', 'lower' => array(65356)); /* FULLWIDTH LATIN CAPITAL LETTER L */
+$config['ff00_ffef'][] = array('upper' => 65325, 'status' => 'C', 'lower' => array(65357)); /* FULLWIDTH LATIN CAPITAL LETTER M */
+$config['ff00_ffef'][] = array('upper' => 65326, 'status' => 'C', 'lower' => array(65358)); /* FULLWIDTH LATIN CAPITAL LETTER N */
+$config['ff00_ffef'][] = array('upper' => 65327, 'status' => 'C', 'lower' => array(65359)); /* FULLWIDTH LATIN CAPITAL LETTER O */
+$config['ff00_ffef'][] = array('upper' => 65328, 'status' => 'C', 'lower' => array(65360)); /* FULLWIDTH LATIN CAPITAL LETTER P */
+$config['ff00_ffef'][] = array('upper' => 65329, 'status' => 'C', 'lower' => array(65361)); /* FULLWIDTH LATIN CAPITAL LETTER Q */
+$config['ff00_ffef'][] = array('upper' => 65330, 'status' => 'C', 'lower' => array(65362)); /* FULLWIDTH LATIN CAPITAL LETTER R */
+$config['ff00_ffef'][] = array('upper' => 65331, 'status' => 'C', 'lower' => array(65363)); /* FULLWIDTH LATIN CAPITAL LETTER S */
+$config['ff00_ffef'][] = array('upper' => 65332, 'status' => 'C', 'lower' => array(65364)); /* FULLWIDTH LATIN CAPITAL LETTER T */
+$config['ff00_ffef'][] = array('upper' => 65333, 'status' => 'C', 'lower' => array(65365)); /* FULLWIDTH LATIN CAPITAL LETTER U */
+$config['ff00_ffef'][] = array('upper' => 65334, 'status' => 'C', 'lower' => array(65366)); /* FULLWIDTH LATIN CAPITAL LETTER V */
+$config['ff00_ffef'][] = array('upper' => 65335, 'status' => 'C', 'lower' => array(65367)); /* FULLWIDTH LATIN CAPITAL LETTER W */
+$config['ff00_ffef'][] = array('upper' => 65336, 'status' => 'C', 'lower' => array(65368)); /* FULLWIDTH LATIN CAPITAL LETTER X */
+$config['ff00_ffef'][] = array('upper' => 65337, 'status' => 'C', 'lower' => array(65369)); /* FULLWIDTH LATIN CAPITAL LETTER Y */
+$config['ff00_ffef'][] = array('upper' => 65338, 'status' => 'C', 'lower' => array(65370)); /* FULLWIDTH LATIN CAPITAL LETTER Z */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2c00_2c5f.php
===================================================================
--- cake/config/unicode/casefolding/2c00_2c5f.php	(revision 0)
+++ cake/config/unicode/casefolding/2c00_2c5f.php	(revision 0)
@@ -0,0 +1,93 @@
+<?php
+/* SVN FILE: $Id: 2c00_2c5f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2C00 through U+2C5F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2c00_2c5f'][] = array('upper' => 11264, 'status' => 'C', 'lower' => array(11312)); /* GLAGOLITIC CAPITAL LETTER AZU */
+$config['2c00_2c5f'][] = array('upper' => 11265, 'status' => 'C', 'lower' => array(11313)); /* GLAGOLITIC CAPITAL LETTER BUKY */
+$config['2c00_2c5f'][] = array('upper' => 11266, 'status' => 'C', 'lower' => array(11314)); /* GLAGOLITIC CAPITAL LETTER VEDE */
+$config['2c00_2c5f'][] = array('upper' => 11267, 'status' => 'C', 'lower' => array(11315)); /* GLAGOLITIC CAPITAL LETTER GLAGOLI */
+$config['2c00_2c5f'][] = array('upper' => 11268, 'status' => 'C', 'lower' => array(11316)); /* GLAGOLITIC CAPITAL LETTER DOBRO */
+$config['2c00_2c5f'][] = array('upper' => 11269, 'status' => 'C', 'lower' => array(11317)); /* GLAGOLITIC CAPITAL LETTER YESTU */
+$config['2c00_2c5f'][] = array('upper' => 11270, 'status' => 'C', 'lower' => array(11318)); /* GLAGOLITIC CAPITAL LETTER ZHIVETE */
+$config['2c00_2c5f'][] = array('upper' => 11271, 'status' => 'C', 'lower' => array(11319)); /* GLAGOLITIC CAPITAL LETTER DZELO */
+$config['2c00_2c5f'][] = array('upper' => 11272, 'status' => 'C', 'lower' => array(11320)); /* GLAGOLITIC CAPITAL LETTER ZEMLJA */
+$config['2c00_2c5f'][] = array('upper' => 11273, 'status' => 'C', 'lower' => array(11321)); /* GLAGOLITIC CAPITAL LETTER IZHE */
+$config['2c00_2c5f'][] = array('upper' => 11274, 'status' => 'C', 'lower' => array(11322)); /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */
+$config['2c00_2c5f'][] = array('upper' => 11275, 'status' => 'C', 'lower' => array(11323)); /* GLAGOLITIC CAPITAL LETTER I */
+$config['2c00_2c5f'][] = array('upper' => 11276, 'status' => 'C', 'lower' => array(11324)); /* GLAGOLITIC CAPITAL LETTER DJERVI */
+$config['2c00_2c5f'][] = array('upper' => 11277, 'status' => 'C', 'lower' => array(11325)); /* GLAGOLITIC CAPITAL LETTER KAKO */
+$config['2c00_2c5f'][] = array('upper' => 11278, 'status' => 'C', 'lower' => array(11326)); /* GLAGOLITIC CAPITAL LETTER LJUDIJE */
+$config['2c00_2c5f'][] = array('upper' => 11279, 'status' => 'C', 'lower' => array(11327)); /* GLAGOLITIC CAPITAL LETTER MYSLITE */
+$config['2c00_2c5f'][] = array('upper' => 11280, 'status' => 'C', 'lower' => array(11328)); /* GLAGOLITIC CAPITAL LETTER NASHI */
+$config['2c00_2c5f'][] = array('upper' => 11281, 'status' => 'C', 'lower' => array(11329)); /* GLAGOLITIC CAPITAL LETTER ONU */
+$config['2c00_2c5f'][] = array('upper' => 11282, 'status' => 'C', 'lower' => array(11330)); /* GLAGOLITIC CAPITAL LETTER POKOJI */
+$config['2c00_2c5f'][] = array('upper' => 11283, 'status' => 'C', 'lower' => array(11331)); /* GLAGOLITIC CAPITAL LETTER RITSI */
+$config['2c00_2c5f'][] = array('upper' => 11284, 'status' => 'C', 'lower' => array(11332)); /* GLAGOLITIC CAPITAL LETTER SLOVO */
+$config['2c00_2c5f'][] = array('upper' => 11285, 'status' => 'C', 'lower' => array(11333)); /* GLAGOLITIC CAPITAL LETTER TVRIDO */
+$config['2c00_2c5f'][] = array('upper' => 11286, 'status' => 'C', 'lower' => array(11334)); /* GLAGOLITIC CAPITAL LETTER UKU */
+$config['2c00_2c5f'][] = array('upper' => 11287, 'status' => 'C', 'lower' => array(11335)); /* GLAGOLITIC CAPITAL LETTER FRITU */
+$config['2c00_2c5f'][] = array('upper' => 11288, 'status' => 'C', 'lower' => array(11336)); /* GLAGOLITIC CAPITAL LETTER HERU */
+$config['2c00_2c5f'][] = array('upper' => 11289, 'status' => 'C', 'lower' => array(11337)); /* GLAGOLITIC CAPITAL LETTER OTU */
+$config['2c00_2c5f'][] = array('upper' => 11290, 'status' => 'C', 'lower' => array(11338)); /* GLAGOLITIC CAPITAL LETTER PE */
+$config['2c00_2c5f'][] = array('upper' => 11291, 'status' => 'C', 'lower' => array(11339)); /* GLAGOLITIC CAPITAL LETTER SHTA */
+$config['2c00_2c5f'][] = array('upper' => 11292, 'status' => 'C', 'lower' => array(11340)); /* GLAGOLITIC CAPITAL LETTER TSI */
+$config['2c00_2c5f'][] = array('upper' => 11293, 'status' => 'C', 'lower' => array(11341)); /* GLAGOLITIC CAPITAL LETTER CHRIVI */
+$config['2c00_2c5f'][] = array('upper' => 11294, 'status' => 'C', 'lower' => array(11342)); /* GLAGOLITIC CAPITAL LETTER SHA */
+$config['2c00_2c5f'][] = array('upper' => 11295, 'status' => 'C', 'lower' => array(11343)); /* GLAGOLITIC CAPITAL LETTER YERU */
+$config['2c00_2c5f'][] = array('upper' => 11296, 'status' => 'C', 'lower' => array(11344)); /* GLAGOLITIC CAPITAL LETTER YERI */
+$config['2c00_2c5f'][] = array('upper' => 11297, 'status' => 'C', 'lower' => array(11345)); /* GLAGOLITIC CAPITAL LETTER YATI */
+$config['2c00_2c5f'][] = array('upper' => 11298, 'status' => 'C', 'lower' => array(11346)); /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */
+$config['2c00_2c5f'][] = array('upper' => 11299, 'status' => 'C', 'lower' => array(11347)); /* GLAGOLITIC CAPITAL LETTER YU */
+$config['2c00_2c5f'][] = array('upper' => 11300, 'status' => 'C', 'lower' => array(11348)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS */
+$config['2c00_2c5f'][] = array('upper' => 11301, 'status' => 'C', 'lower' => array(11349)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */
+$config['2c00_2c5f'][] = array('upper' => 11302, 'status' => 'C', 'lower' => array(11350)); /* GLAGOLITIC CAPITAL LETTER YO */
+$config['2c00_2c5f'][] = array('upper' => 11303, 'status' => 'C', 'lower' => array(11351)); /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */
+$config['2c00_2c5f'][] = array('upper' => 11304, 'status' => 'C', 'lower' => array(11352)); /* GLAGOLITIC CAPITAL LETTER BIG YUS */
+$config['2c00_2c5f'][] = array('upper' => 11305, 'status' => 'C', 'lower' => array(11353)); /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */
+$config['2c00_2c5f'][] = array('upper' => 11306, 'status' => 'C', 'lower' => array(11354)); /* GLAGOLITIC CAPITAL LETTER FITA */
+$config['2c00_2c5f'][] = array('upper' => 11307, 'status' => 'C', 'lower' => array(11355)); /* GLAGOLITIC CAPITAL LETTER IZHITSA */
+$config['2c00_2c5f'][] = array('upper' => 11308, 'status' => 'C', 'lower' => array(11356)); /* GLAGOLITIC CAPITAL LETTER SHTAPIC */
+$config['2c00_2c5f'][] = array('upper' => 11309, 'status' => 'C', 'lower' => array(11357)); /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */
+$config['2c00_2c5f'][] = array('upper' => 11310, 'status' => 'C', 'lower' => array(11358)); /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/0180_024F.php
===================================================================
--- cake/config/unicode/casefolding/0180_024F.php	(revision 0)
+++ cake/config/unicode/casefolding/0180_024F.php	(revision 0)
@@ -0,0 +1,154 @@
+<?php
+/* SVN FILE: $Id: 0180_024F.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+0180 through U+024F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['0180_024F'][] = array('upper' => 385, 'status' => 'C', 'lower' => array(595)); /* LATIN CAPITAL LETTER B WITH HOOK */
+$config['0180_024F'][] = array('upper' => 386, 'status' => 'C', 'lower' => array(387)); /* LATIN CAPITAL LETTER B WITH TOPBAR */
+$config['0180_024F'][] = array('upper' => 388, 'status' => 'C', 'lower' => array(389)); /* LATIN CAPITAL LETTER TONE SIX */
+$config['0180_024F'][] = array('upper' => 390, 'status' => 'C', 'lower' => array(596)); /* LATIN CAPITAL LETTER OPEN O */
+$config['0180_024F'][] = array('upper' => 391, 'status' => 'C', 'lower' => array(392)); /* LATIN CAPITAL LETTER C WITH HOOK */
+$config['0180_024F'][] = array('upper' => 393, 'status' => 'C', 'lower' => array(598)); /* LATIN CAPITAL LETTER AFRICAN D */
+$config['0180_024F'][] = array('upper' => 394, 'status' => 'C', 'lower' => array(599)); /* LATIN CAPITAL LETTER D WITH HOOK */
+$config['0180_024F'][] = array('upper' => 395, 'status' => 'C', 'lower' => array(396)); /* LATIN CAPITAL LETTER D WITH TOPBAR */
+$config['0180_024F'][] = array('upper' => 398, 'status' => 'C', 'lower' => array(477)); /* LATIN CAPITAL LETTER REVERSED E */
+$config['0180_024F'][] = array('upper' => 399, 'status' => 'C', 'lower' => array(601)); /* LATIN CAPITAL LETTER SCHWA */
+$config['0180_024F'][] = array('upper' => 400, 'status' => 'C', 'lower' => array(603)); /* LATIN CAPITAL LETTER OPEN E */
+$config['0180_024F'][] = array('upper' => 401, 'status' => 'C', 'lower' => array(402)); /* LATIN CAPITAL LETTER F WITH HOOK */
+$config['0180_024F'][] = array('upper' => 403, 'status' => 'C', 'lower' => array(608)); /* LATIN CAPITAL LETTER G WITH HOOK */
+$config['0180_024F'][] = array('upper' => 404, 'status' => 'C', 'lower' => array(611)); /* LATIN CAPITAL LETTER GAMMA */
+$config['0180_024F'][] = array('upper' => 406, 'status' => 'C', 'lower' => array(617)); /* LATIN CAPITAL LETTER IOTA */
+$config['0180_024F'][] = array('upper' => 407, 'status' => 'C', 'lower' => array(616)); /* LATIN CAPITAL LETTER I WITH STROKE */
+$config['0180_024F'][] = array('upper' => 408, 'status' => 'C', 'lower' => array(409)); /* LATIN CAPITAL LETTER K WITH HOOK */
+$config['0180_024F'][] = array('upper' => 412, 'status' => 'C', 'lower' => array(623)); /* LATIN CAPITAL LETTER TURNED M */
+$config['0180_024F'][] = array('upper' => 413, 'status' => 'C', 'lower' => array(626)); /* LATIN CAPITAL LETTER N WITH LEFT HOOK */
+$config['0180_024F'][] = array('upper' => 415, 'status' => 'C', 'lower' => array(629)); /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE */
+$config['0180_024F'][] = array('upper' => 416, 'status' => 'C', 'lower' => array(417)); /* LATIN CAPITAL LETTER O WITH HORN */
+$config['0180_024F'][] = array('upper' => 418, 'status' => 'C', 'lower' => array(419)); /* LATIN CAPITAL LETTER OI */
+$config['0180_024F'][] = array('upper' => 420, 'status' => 'C', 'lower' => array(421)); /* LATIN CAPITAL LETTER P WITH HOOK */
+$config['0180_024F'][] = array('upper' => 422, 'status' => 'C', 'lower' => array(640)); /* LATIN LETTER YR */
+$config['0180_024F'][] = array('upper' => 423, 'status' => 'C', 'lower' => array(424)); /* LATIN CAPITAL LETTER TONE TWO */
+$config['0180_024F'][] = array('upper' => 425, 'status' => 'C', 'lower' => array(643)); /* LATIN CAPITAL LETTER ESH */
+$config['0180_024F'][] = array('upper' => 428, 'status' => 'C', 'lower' => array(429)); /* LATIN CAPITAL LETTER T WITH HOOK */
+$config['0180_024F'][] = array('upper' => 430, 'status' => 'C', 'lower' => array(648)); /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */
+$config['0180_024F'][] = array('upper' => 431, 'status' => 'C', 'lower' => array(432)); /* LATIN CAPITAL LETTER U WITH HORN */
+$config['0180_024F'][] = array('upper' => 433, 'status' => 'C', 'lower' => array(650)); /* LATIN CAPITAL LETTER UPSILON */
+$config['0180_024F'][] = array('upper' => 434, 'status' => 'C', 'lower' => array(651)); /* LATIN CAPITAL LETTER V WITH HOOK */
+$config['0180_024F'][] = array('upper' => 435, 'status' => 'C', 'lower' => array(436)); /* LATIN CAPITAL LETTER Y WITH HOOK */
+$config['0180_024F'][] = array('upper' => 437, 'status' => 'C', 'lower' => array(438)); /* LATIN CAPITAL LETTER Z WITH STROKE */
+$config['0180_024F'][] = array('upper' => 439, 'status' => 'C', 'lower' => array(658)); /* LATIN CAPITAL LETTER EZH */
+$config['0180_024F'][] = array('upper' => 440, 'status' => 'C', 'lower' => array(441)); /* LATIN CAPITAL LETTER EZH REVERSED */
+$config['0180_024F'][] = array('upper' => 444, 'status' => 'C', 'lower' => array(445)); /* LATIN CAPITAL LETTER TONE FIVE */
+$config['0180_024F'][] = array('upper' => 452, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER DZ WITH CARON */
+$config['0180_024F'][] = array('upper' => 453, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON */
+$config['0180_024F'][] = array('upper' => 455, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER LJ */
+$config['0180_024F'][] = array('upper' => 456, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER L WITH SMALL LETTER J */
+$config['0180_024F'][] = array('upper' => 458, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER NJ */
+$config['0180_024F'][] = array('upper' => 459, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER N WITH SMALL LETTER J */
+$config['0180_024F'][] = array('upper' => 461, 'status' => 'C', 'lower' => array(462)); /* LATIN CAPITAL LETTER A WITH CARON */
+$config['0180_024F'][] = array('upper' => 463, 'status' => 'C', 'lower' => array(464)); /* LATIN CAPITAL LETTER I WITH CARON */
+$config['0180_024F'][] = array('upper' => 465, 'status' => 'C', 'lower' => array(466)); /* LATIN CAPITAL LETTER O WITH CARON */
+$config['0180_024F'][] = array('upper' => 467, 'status' => 'C', 'lower' => array(468)); /* LATIN CAPITAL LETTER U WITH CARON */
+$config['0180_024F'][] = array('upper' => 469, 'status' => 'C', 'lower' => array(470)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */
+$config['0180_024F'][] = array('upper' => 471, 'status' => 'C', 'lower' => array(472)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */
+$config['0180_024F'][] = array('upper' => 473, 'status' => 'C', 'lower' => array(474)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */
+$config['0180_024F'][] = array('upper' => 475, 'status' => 'C', 'lower' => array(476)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */
+$config['0180_024F'][] = array('upper' => 478, 'status' => 'C', 'lower' => array(479)); /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */
+$config['0180_024F'][] = array('upper' => 480, 'status' => 'C', 'lower' => array(481)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */
+$config['0180_024F'][] = array('upper' => 482, 'status' => 'C', 'lower' => array(483)); /* LATIN CAPITAL LETTER AE WITH MACRON */
+$config['0180_024F'][] = array('upper' => 484, 'status' => 'C', 'lower' => array(485)); /* LATIN CAPITAL LETTER G WITH STROKE */
+$config['0180_024F'][] = array('upper' => 486, 'status' => 'C', 'lower' => array(487)); /* LATIN CAPITAL LETTER G WITH CARON */
+$config['0180_024F'][] = array('upper' => 488, 'status' => 'C', 'lower' => array(489)); /* LATIN CAPITAL LETTER K WITH CARON */
+$config['0180_024F'][] = array('upper' => 490, 'status' => 'C', 'lower' => array(491)); /* LATIN CAPITAL LETTER O WITH OGONEK */
+$config['0180_024F'][] = array('upper' => 492, 'status' => 'C', 'lower' => array(493)); /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */
+$config['0180_024F'][] = array('upper' => 494, 'status' => 'C', 'lower' => array(495)); /* LATIN CAPITAL LETTER EZH WITH CARON */
+$config['0180_024F'][] = array('upper' => 496, 'status' => 'F', 'lower' => array(106, 780)); /* LATIN SMALL LETTER J WITH CARON */
+$config['0180_024F'][] = array('upper' => 497, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER DZ */
+$config['0180_024F'][] = array('upper' => 498, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z */
+$config['0180_024F'][] = array('upper' => 500, 'status' => 'C', 'lower' => array(501)); /* LATIN CAPITAL LETTER G WITH ACUTE */
+$config['0180_024F'][] = array('upper' => 502, 'status' => 'C', 'lower' => array(405)); /* LATIN CAPITAL LETTER HWAIR */
+$config['0180_024F'][] = array('upper' => 503, 'status' => 'C', 'lower' => array(447)); /* LATIN CAPITAL LETTER WYNN */
+$config['0180_024F'][] = array('upper' => 504, 'status' => 'C', 'lower' => array(505)); /* LATIN CAPITAL LETTER N WITH GRAVE */
+$config['0180_024F'][] = array('upper' => 506, 'status' => 'C', 'lower' => array(507)); /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */
+$config['0180_024F'][] = array('upper' => 508, 'status' => 'C', 'lower' => array(509)); /* LATIN CAPITAL LETTER AE WITH ACUTE */
+$config['0180_024F'][] = array('upper' => 510, 'status' => 'C', 'lower' => array(511)); /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */
+$config['0180_024F'][] = array('upper' => 512, 'status' => 'C', 'lower' => array(513)); /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 514, 'status' => 'C', 'lower' => array(515)); /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 516, 'status' => 'C', 'lower' => array(517)); /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 518, 'status' => 'C', 'lower' => array(519)); /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 520, 'status' => 'C', 'lower' => array(521)); /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 522, 'status' => 'C', 'lower' => array(523)); /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 524, 'status' => 'C', 'lower' => array(525)); /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 526, 'status' => 'C', 'lower' => array(527)); /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 528, 'status' => 'C', 'lower' => array(529)); /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 530, 'status' => 'C', 'lower' => array(531)); /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 532, 'status' => 'C', 'lower' => array(533)); /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */
+$config['0180_024F'][] = array('upper' => 534, 'status' => 'C', 'lower' => array(535)); /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */
+$config['0180_024F'][] = array('upper' => 536, 'status' => 'C', 'lower' => array(537)); /* LATIN CAPITAL LETTER S WITH COMMA BELOW */
+$config['0180_024F'][] = array('upper' => 538, 'status' => 'C', 'lower' => array(539)); /* LATIN CAPITAL LETTER T WITH COMMA BELOW */
+$config['0180_024F'][] = array('upper' => 540, 'status' => 'C', 'lower' => array(541)); /* LATIN CAPITAL LETTER YOGH */
+$config['0180_024F'][] = array('upper' => 542, 'status' => 'C', 'lower' => array(543)); /* LATIN CAPITAL LETTER H WITH CARON */
+$config['0180_024F'][] = array('upper' => 544, 'status' => 'C', 'lower' => array(414)); /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */
+$config['0180_024F'][] = array('upper' => 546, 'status' => 'C', 'lower' => array(547)); /* LATIN CAPITAL LETTER OU */
+$config['0180_024F'][] = array('upper' => 548, 'status' => 'C', 'lower' => array(549)); /* LATIN CAPITAL LETTER Z WITH HOOK */
+$config['0180_024F'][] = array('upper' => 550, 'status' => 'C', 'lower' => array(551)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE */
+$config['0180_024F'][] = array('upper' => 552, 'status' => 'C', 'lower' => array(553)); /* LATIN CAPITAL LETTER E WITH CEDILLA */
+$config['0180_024F'][] = array('upper' => 554, 'status' => 'C', 'lower' => array(555)); /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */
+$config['0180_024F'][] = array('upper' => 556, 'status' => 'C', 'lower' => array(557)); /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */
+$config['0180_024F'][] = array('upper' => 558, 'status' => 'C', 'lower' => array(559)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE */
+$config['0180_024F'][] = array('upper' => 560, 'status' => 'C', 'lower' => array(561)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */
+$config['0180_024F'][] = array('upper' => 562, 'status' => 'C', 'lower' => array(563)); /* LATIN CAPITAL LETTER Y WITH MACRON */
+$config['0180_024F'][] = array('upper' => 570, 'status' => 'C', 'lower' => array(11365)); /* LATIN CAPITAL LETTER A WITH STROKE */
+$config['0180_024F'][] = array('upper' => 571, 'status' => 'C', 'lower' => array(572)); /* LATIN CAPITAL LETTER C WITH STROKE */
+$config['0180_024F'][] = array('upper' => 573, 'status' => 'C', 'lower' => array(410)); /* LATIN CAPITAL LETTER L WITH BAR */
+$config['0180_024F'][] = array('upper' => 574, 'status' => 'C', 'lower' => array(11366)); /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */
+$config['0180_024F'][] = array('upper' => 577, 'status' => 'C', 'lower' => array(578)); /* LATIN CAPITAL LETTER GLOTTAL STOP */
+$config['0180_024F'][] = array('upper' => 579, 'status' => 'C', 'lower' => array(384)); /* LATIN CAPITAL LETTER B WITH STROKE */
+$config['0180_024F'][] = array('upper' => 580, 'status' => 'C', 'lower' => array(649)); /* LATIN CAPITAL LETTER U BAR */
+$config['0180_024F'][] = array('upper' => 581, 'status' => 'C', 'lower' => array(652)); /* LATIN CAPITAL LETTER TURNED V */
+$config['0180_024F'][] = array('upper' => 582, 'status' => 'C', 'lower' => array(583)); /* LATIN CAPITAL LETTER E WITH STROKE */
+$config['0180_024F'][] = array('upper' => 584, 'status' => 'C', 'lower' => array(585)); /* LATIN CAPITAL LETTER J WITH STROKE */
+$config['0180_024F'][] = array('upper' => 586, 'status' => 'C', 'lower' => array(587)); /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL */
+$config['0180_024F'][] = array('upper' => 588, 'status' => 'C', 'lower' => array(589)); /* LATIN CAPITAL LETTER R WITH STROKE */
+$config['0180_024F'][] = array('upper' => 590, 'status' => 'C', 'lower' => array(591)); /* LATIN CAPITAL LETTER Y WITH STROKE */
+?>
\ No newline at end of file
Index: cake/config/unicode/casefolding/2150_218f.php
===================================================================
--- cake/config/unicode/casefolding/2150_218f.php	(revision 0)
+++ cake/config/unicode/casefolding/2150_218f.php	(revision 0)
@@ -0,0 +1,63 @@
+<?php
+/* SVN FILE: $Id: 2150_218f.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Case Folding Properties.
+ *
+ * Provides case mapping of Unicode characters for code points U+2150 through U+218F
+ *
+ * @see http://www.unicode.org/Public/UNIDATA/UCD.html
+ * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+ * @see http://www.unicode.org/reports/tr21/tr21-5.html
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * The upper field is the decimal value of the upper case character
+ *
+ * The lower filed is an array of the decimal values that form the lower case version of a character.
+ *
+ *	The status field is:
+ * C: common case folding, common mappings shared by both simple and full mappings.
+ * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+ * S: simple case folding, mappings to single characters where different from F.
+ * T: special case for uppercase I and dotted uppercase I
+ *   - For non-Turkic languages, this mapping is normally not used.
+ *   - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+ *     Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+ *     See the discussions of case mapping in the Unicode Standard for more information.
+ */
+$config['2150_218f'][] = array('upper' => 8544, 'status' => 'C', 'lower' => array(8560)); /* ROMAN NUMERAL ONE */
+$config['2150_218f'][] = array('upper' => 8545, 'status' => 'C', 'lower' => array(8561)); /* ROMAN NUMERAL TWO */
+$config['2150_218f'][] = array('upper' => 8546, 'status' => 'C', 'lower' => array(8562)); /* ROMAN NUMERAL THREE */
+$config['2150_218f'][] = array('upper' => 8547, 'status' => 'C', 'lower' => array(8563)); /* ROMAN NUMERAL FOUR */
+$config['2150_218f'][] = array('upper' => 8548, 'status' => 'C', 'lower' => array(8564)); /* ROMAN NUMERAL FIVE */
+$config['2150_218f'][] = array('upper' => 8549, 'status' => 'C', 'lower' => array(8565)); /* ROMAN NUMERAL SIX */
+$config['2150_218f'][] = array('upper' => 8550, 'status' => 'C', 'lower' => array(8566)); /* ROMAN NUMERAL SEVEN */
+$config['2150_218f'][] = array('upper' => 8551, 'status' => 'C', 'lower' => array(8567)); /* ROMAN NUMERAL EIGHT */
+$config['2150_218f'][] = array('upper' => 8552, 'status' => 'C', 'lower' => array(8568)); /* ROMAN NUMERAL NINE */
+$config['2150_218f'][] = array('upper' => 8553, 'status' => 'C', 'lower' => array(8569)); /* ROMAN NUMERAL TEN */
+$config['2150_218f'][] = array('upper' => 8554, 'status' => 'C', 'lower' => array(8570)); /* ROMAN NUMERAL ELEVEN */
+$config['2150_218f'][] = array('upper' => 8555, 'status' => 'C', 'lower' => array(8571)); /* ROMAN NUMERAL TWELVE */
+$config['2150_218f'][] = array('upper' => 8556, 'status' => 'C', 'lower' => array(8572)); /* ROMAN NUMERAL FIFTY */
+$config['2150_218f'][] = array('upper' => 8557, 'status' => 'C', 'lower' => array(8573)); /* ROMAN NUMERAL ONE HUNDRED */
+$config['2150_218f'][] = array('upper' => 8558, 'status' => 'C', 'lower' => array(8574)); /* ROMAN NUMERAL FIVE HUNDRED */
+$config['2150_218f'][] = array('upper' => 8559, 'status' => 'C', 'lower' => array(8575)); /* ROMAN NUMERAL ONE THOUSAND */
+$config['2150_218f'][] = array('upper' => 8579, 'status' => 'C', 'lower' => array(8580)); /* ROMAN NUMERAL REVERSED ONE HUNDRED */
+?>
\ No newline at end of file
Index: cake/config/config.php
===================================================================
--- cake/config/config.php	(revision 0)
+++ cake/config/config.php	(revision 0)
@@ -0,0 +1,26 @@
+<?php
+/* SVN FILE: $Id: config.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Core Configurations.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 1.1.11.4062
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+return $config['Cake.version'] = '1.2.1.8004';
+?>
\ No newline at end of file
Index: cake/libs/error.php
===================================================================
--- cake/libs/error.php	(revision 0)
+++ cake/libs/error.php	(revision 0)
@@ -0,0 +1,378 @@
+<?php
+/* SVN FILE: $Id: error.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Error handler
+ *
+ * Provides Error Capturing for Framework errors.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.10.5.1732
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Controller', 'App');
+/**
+ * Error Handling Controller
+ *
+ * Controller used by ErrorHandler to render error views.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CakeErrorController extends AppController {
+	var $name = 'CakeError';
+/**
+ * Uses Property
+ *
+ * @var array
+ */
+	var $uses = array();
+/**
+ * __construct
+ *
+ * @access public
+ * @return void
+ */
+	function __construct() {
+		parent::__construct();
+		$this->_set(Router::getPaths());
+		$this->params = Router::getParams();
+		$this->constructClasses();
+		$this->Component->initialize($this);
+		$this->_set(array('cacheAction' => false, 'viewPath' => 'errors'));
+	}
+}
+/**
+ * Error Handler.
+ *
+ * Captures and handles all cakeError() calls.
+ * Displays helpful framework errors when debug > 1.
+ * When debug < 1 cakeError() will render 404 or 500 errors.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class ErrorHandler extends Object {
+/**
+ * Controller instance.
+ *
+ * @var object
+ * @access public
+ */
+	var $controller = null;
+/**
+ * Class constructor.
+ *
+ * @param string $method Method producing the error
+ * @param array $messages Error messages
+ */
+	function __construct($method, $messages) {
+		App::import('Core', 'Sanitize');
+		static $__previousError = null;
+
+		if ($__previousError != array($method, $messages)) {
+			$__previousError = array($method, $messages);
+			$this->controller =& new CakeErrorController();
+		} else {
+			$this->controller =& new Controller();
+			$this->controller->viewPath = 'errors';
+		}
+
+		$options = array('escape' => false);
+		$messages = Sanitize::clean($messages, $options);
+
+		if (!isset($messages[0])) {
+			$messages = array($messages);
+		}
+
+		if (method_exists($this->controller, 'apperror')) {
+			return $this->controller->appError($method, $messages);
+		}
+
+		if (!in_array(strtolower($method), array_map('strtolower', get_class_methods($this)))) {
+			$method = 'error';
+		}
+
+		if ($method !== 'error') {
+			if (Configure::read() == 0) {
+				$method = 'error404';
+				if (isset($code) && $code == 500) {
+					$method = 'error500';
+				}
+			}
+		}
+		$this->dispatchMethod($method, $messages);
+		$this->_stop();
+	}
+/**
+ * Displays an error page (e.g. 404 Not found).
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function error($params) {
+		extract($params, EXTR_OVERWRITE);
+		$this->controller->set(array(
+			'code' => $code,
+			'name' => $name,
+			'message' => $message,
+			'title' => $code . ' ' . $name
+		));
+		$this->_outputMessage('error404');
+	}
+/**
+ * Convenience method to display a 404 page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function error404($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		if (!isset($url)) {
+			$url = $this->controller->here;
+		}
+		$url = Router::normalize($url);
+		header("HTTP/1.0 404 Not Found");
+		$this->controller->set(array(
+			'code' => '404',
+			'name' => __('Not Found', true),
+			'message' => $url,
+			'base' => $this->controller->base
+		));
+		$this->_outputMessage('error404');
+	}
+/**
+ * Renders the Missing Controller web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingController($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$controllerName = str_replace('Controller', '', $className);
+		$this->controller->set(array(
+			'controller' => $className,
+			'controllerName' => $controllerName,
+			'title' => __('Missing Controller', true)
+		));
+		$this->_outputMessage('missingController');
+	}
+/**
+ * Renders the Missing Action web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingAction($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$controllerName = str_replace('Controller', '', $className);
+		$this->controller->set(array(
+			'controller' => $className,
+			'controllerName' => $controllerName,
+			'action' => $action,
+			'title' => __('Missing Method in Controller', true)
+		));
+		$this->_outputMessage('missingAction');
+	}
+/**
+ * Renders the Private Action web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function privateAction($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'controller' => $className,
+			'action' => $action,
+			'title' => __('Trying to access private method in class', true)
+		));
+		$this->_outputMessage('privateAction');
+	}
+/**
+ * Renders the Missing Table web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingTable($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'model' => $className,
+			'table' => $table,
+			'title' => __('Missing Database Table', true)
+		));
+		$this->_outputMessage('missingTable');
+	}
+/**
+ * Renders the Missing Database web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingDatabase($params = array()) {
+		$this->controller->set(array(
+			'title' => __('Scaffold Missing Database Connection', true)
+		));
+		$this->_outputMessage('missingScaffolddb');
+	}
+/**
+ * Renders the Missing View web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingView($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'controller' => $className,
+			'action' => $action,
+			'file' => $file,
+			'title' => __('Missing View', true)
+		));
+		$this->_outputMessage('missingView');
+	}
+/**
+ * Renders the Missing Layout web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingLayout($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->layout = 'default';
+		$this->controller->set(array(
+			'file' => $file,
+			'title' => __('Missing Layout', true)
+		));
+		$this->_outputMessage('missingLayout');
+	}
+/**
+ * Renders the Database Connection web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingConnection($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'model' => $className,
+			'title' => __('Missing Database Connection', true)
+		));
+		$this->_outputMessage('missingConnection');
+	}
+/**
+ * Renders the Missing Helper file web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingHelperFile($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'helperClass' => Inflector::camelize($helper) . "Helper",
+			'file' => $file,
+			'title' => __('Missing Helper File', true)
+		));
+		$this->_outputMessage('missingHelperFile');
+	}
+/**
+ * Renders the Missing Helper class web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingHelperClass($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'helperClass' => Inflector::camelize($helper) . "Helper",
+			'file' => $file,
+			'title' => __('Missing Helper Class', true)
+		));
+		$this->_outputMessage('missingHelperClass');
+	}
+/**
+ * Renders the Missing Component file web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingComponentFile($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'controller' => $className,
+			'component' => $component,
+			'file' => $file,
+			'title' => __('Missing Component File', true)
+		));
+		$this->_outputMessage('missingComponentFile');
+	}
+/**
+ * Renders the Missing Component class web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingComponentClass($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'controller' => $className,
+			'component' => $component,
+			'file' => $file,
+			'title' => __('Missing Component Class', true)
+		));
+		$this->_outputMessage('missingComponentClass');
+	}
+/**
+ * Renders the Missing Model class web page.
+ *
+ * @param unknown_type $params Parameters for controller
+ * @access public
+ */
+	function missingModel($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'model' => $className,
+			'title' => __('Missing Model', true)
+		));
+		$this->_outputMessage('missingModel');
+	}
+/**
+ * Output message
+ *
+ * @access protected
+ */
+	function _outputMessage($template) {
+		$this->controller->render($template);
+		$this->controller->afterFilter();
+		echo $this->controller->output;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/validation.php
===================================================================
--- cake/libs/validation.php	(revision 0)
+++ cake/libs/validation.php	(revision 0)
@@ -0,0 +1,928 @@
+<?php
+/* SVN FILE: $Id: validation.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Validation Class.  Used for validation of model data
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.3830
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Deprecated
+ */
+/**
+ * Not empty.
+ */
+	define('VALID_NOT_EMPTY', '/.+/');
+/**
+ * Numbers [0-9] only.
+ */
+	define('VALID_NUMBER', '/^[-+]?\\b[0-9]*\\.?[0-9]+\\b$/');
+/**
+ * A valid email address.
+ */
+	define('VALID_EMAIL', "/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[a-z]{2,4}|museum|travel)$/i");
+/**
+ * A valid year (1000-2999).
+ */
+	define('VALID_YEAR', '/^[12][0-9]{3}$/');
+/**
+ * Offers different validation methods.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v 1.2.0.3830
+ */
+class Validation extends Object {
+/**
+ * Set the the value of methods $check param.
+ *
+ * @var string
+ * @access public
+ */
+	var $check = null;
+/**
+ * Set to a valid regular expression in the class methods.
+ * Can be set from $regex param also
+ *
+ * @var string
+ * @access public
+ */
+	var $regex = null;
+/**
+ * Some complex patterns needed in multiple places
+ *
+ * @var array
+ * @access private
+ */
+	var $__pattern = array(
+		'ip' => '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])',
+		'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)'
+	);
+/**
+ * Some class methods use a country to determine proper validation.
+ * This can be passed to methods in the $country param
+ *
+ * @var string
+ * @access public
+ */
+	var $country = null;
+/**
+ * Some class methods use a deeper validation when set to true
+ *
+ * @var string
+ * @access public
+ */
+	var $deep = null;
+/**
+ * Some class methods use the $type param to determine which validation to perfom in the method
+ *
+ * @var string
+ * @access public
+ */
+	var $type = null;
+/**
+ * Holds an array of errors messages set in this class.
+ * These are used for debugging purposes
+ *
+ * @var array
+ * @access public
+ */
+	var $errors = array();
+/**
+ * Gets a reference to the Validation object instance
+ *
+ * @return object Validation instance
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Validation();
+		}
+		return $instance[0];
+	}
+/**
+ * Checks that a string contains something other than whitespace
+ *
+ * Returns true if string contains something other than whitespace
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param mixed $check Value to check
+ * @return boolean Success
+ * @access public
+ */
+	function notEmpty($check) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (empty($_this->check) && $_this->check != '0') {
+			return false;
+		}
+		$_this->regex = '/[^\s]+/m';
+		return $_this->_check();
+	}
+/**
+ * Checks that a string contains only integer or letters
+ *
+ * Returns true if string contains only integer or letters
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param mixed $check Value to check
+ * @return boolean Success
+ * @access public
+ */
+	function alphaNumeric($check) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (empty($_this->check) && $_this->check != '0') {
+			return false;
+		}
+		$_this->regex = '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu';
+		return $_this->_check();
+	}
+/**
+ * Checks that a string length is within s specified range.
+ * Spaces are included in the character count.
+ * Returns true is string matches value min, max, or between min and max,
+ *
+ * @param string $check Value to check for length
+ * @param integer $min Minimum value in range (inclusive)
+ * @param integer $max Maximum value in range (inclusive)
+ * @return boolean Success
+ * @access public
+ */
+	function between($check, $min, $max) {
+		$length = strlen($check);
+		return ($length >= $min && $length <= $max);
+	}
+/**
+ * Returns true if field is left blank -OR- only whitespace characters are present in it's value
+ * Whitespace characters include Space, Tab, Carriage Return, Newline
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param mixed $check Value to check
+ * @return boolean Success
+ * @access public
+ */
+	function blank($check) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		$_this->regex = '/[^\\s]/';
+		return !$_this->_check();
+	}
+/**
+ * Validation of credit card numbers.
+ * Returns true if $check is in the proper credit card format.
+ *
+ * @param mixed $check credit card number to validate
+ * @param mixed $type 'all' may be passed as a sting, defaults to fast which checks format of most major credit cards
+ * 							if an array is used only the values of the array are checked.
+ * 							Example: array('amex', 'bankcard', 'maestro')
+ * @param boolean $deep set to true this will check the Luhn algorithm of the credit card.
+ * @param string $regex A custom regex can also be passed, this will be used instead of the defined regex values
+ * @return boolean Success
+ * @access public
+ * @see Validation::_luhn()
+ */
+	function cc($check, $type = 'fast', $deep = false, $regex = null) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+		$_this->type = $type;
+		$_this->deep = $deep;
+		$_this->regex = $regex;
+
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+		$_this->check = str_replace(array('-', ' '), '', $_this->check);
+
+		if (strlen($_this->check) < 13) {
+			return false;
+		}
+
+		if (!is_null($_this->regex)) {
+			if ($_this->_check()) {
+				return $_this->_luhn();
+			}
+		}
+		$cards = array('all' => array('amex' => '/^3[4|7]\\d{13}$/',
+									'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/',
+									'diners'   => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/',
+									'disc'     => '/^(?:6011|650\\d)\\d{12}$/',
+									'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/',
+									'enroute'  => '/^2(?:014|149)\\d{11}$/',
+									'jcb'      => '/^(3\\d{4}|2100|1800)\\d{11}$/',
+									'maestro'  => '/^(?:5020|6\\d{3})\\d{12}$/',
+									'mc'       => '/^5[1-5]\\d{14}$/',
+									'solo'     => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/',
+									'switch'   => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/',
+									'visa'     => '/^4\\d{12}(\\d{3})?$/',
+									'voyager'  => '/^8699[0-9]{11}$/'),
+							'fast'   => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/');
+
+		if (is_array($_this->type)) {
+			foreach ($_this->type as $value) {
+				$_this->regex = $cards['all'][strtolower($value)];
+
+				if ($_this->_check()) {
+					return $_this->_luhn();
+				}
+			}
+		} elseif ($_this->type == 'all') {
+			foreach ($cards['all'] as $value) {
+				$_this->regex = $value;
+
+				if ($_this->_check()) {
+					return $_this->_luhn();
+				}
+			}
+		} else {
+			$_this->regex = $cards['fast'];
+
+			if ($_this->_check()) {
+				return $_this->_luhn();
+			}
+		}
+	}
+/**
+ * Used to compare 2 numeric values.
+ *
+ * @param mixed $check1 if string is passed for a string must also be passed for $check2
+ * 							used as an array it must be passed as array('check1' => value, 'operator' => 'value', 'check2' -> value)
+ * @param string $operator Can be either a word or operand
+ * 								is greater >, is less <, greater or equal >=
+ * 								less or equal <=, is less <, equal to ==, not equal !=
+ * @param integer $check2 only needed if $check1 is a string
+ * @return boolean Success
+ * @access public
+ */
+	function comparison($check1, $operator = null, $check2 = null) {
+		if (is_array($check1)) {
+			extract($check1, EXTR_OVERWRITE);
+		}
+		$operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator));
+
+		switch ($operator) {
+			case 'isgreater':
+			case '>':
+				if ($check1 > $check2) {
+					return true;
+				}
+				break;
+			case 'isless':
+			case '<':
+				if ($check1 < $check2) {
+					return true;
+				}
+				break;
+			case 'greaterorequal':
+			case '>=':
+				if ($check1 >= $check2) {
+					return true;
+				}
+				break;
+			case 'lessorequal':
+			case '<=':
+				if ($check1 <= $check2) {
+					return true;
+				}
+				break;
+			case 'equalto':
+			case '==':
+				if ($check1 == $check2) {
+					return true;
+				}
+				break;
+			case 'notequal':
+			case '!=':
+				if ($check1 != $check2) {
+					return true;
+				}
+				break;
+			default:
+				$_this =& Validation::getInstance();
+				$_this->errors[] = __('You must define the $operator parameter for Validation::comparison()', true);
+				break;
+		}
+		return false;
+	}
+/**
+ * Used when a custom regular expression is needed.
+ *
+ * @param mixed $check When used as a string, $regex must also be a valid regular expression.
+ *								As and array: array('check' => value, 'regex' => 'valid regular expression')
+ * @param string $regex If $check is passed as a string, $regex must also be set to valid regular expression
+ * @return boolean Success
+ * @access public
+ */
+	function custom($check, $regex = null) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+		$_this->regex = $regex;
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+		if ($_this->regex === null) {
+			$_this->errors[] = __('You must define a regular expression for Validation::custom()', true);
+			return false;
+		}
+		return $_this->_check();
+	}
+/**
+ * Date validation, determines if the string passed is a valid date.
+ * keys that expect full month, day and year will validate leap years
+ *
+ * @param string $check a valid date string
+ * @param mixed $format Use a string or an array of the keys below. Arrays should be passed as array('dmy', 'mdy', etc)
+ * 					Keys: dmy 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash
+ * 							mdy 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash
+ * 							ymd 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash
+ * 							dMy 27 December 2006 or 27 Dec 2006
+ * 							Mdy December 27, 2006 or Dec 27, 2006 comma is optional
+ * 							My December 2006 or Dec 2006
+ * 							my 12/2006 separators can be a space, period, dash, forward slash
+ * @param string $regex If a custom regular expression is used this is the only validation that will occur.
+ * @return boolean Success
+ * @access public
+ */
+	function date($check, $format = 'ymd', $regex = null) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+		$_this->regex = $regex;
+
+		if (!is_null($_this->regex)) {
+			return $_this->_check();
+		}
+
+		$regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
+		$regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
+		$regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%';
+		$regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/';
+		$regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/';
+		$regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%';
+		$regex['my'] = '%^(((0[123456789]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%';
+
+		$format = (is_array($format)) ? array_values($format) : array($format);
+		foreach ($format as $key) {
+			$_this->regex = $regex[$key];
+
+			if ($_this->_check() === true) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+/**
+ * Time validation, determines if the string passed is a valid time.
+ * Validates time as 24hr (HH:MM) or am/pm ([H]H:MM[a|p]m)
+ * Does not allow/validate seconds.
+ *
+ * @param string $check a valid time string
+ * @return boolean Success
+ * @access public
+ */
+
+	function time($check) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+		$_this->regex = '%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%';
+		return $_this->_check();
+	}
+
+/**
+ * Boolean validation, determines if value passed is a boolean integer or true/false.
+ *
+ * @param string $check a valid boolean
+ * @return boolean Success
+ * @access public
+ */
+	function boolean($check) {
+		$booleanList = array(0, 1, '0', '1', true, false);
+		return in_array($check, $booleanList, true);
+	}
+
+/**
+ * Checks that a value is a valid decimal. If $places is null, the $check is allowed to be a scientific float
+ * If no decimal point is found a false will be returned. Both the sign and exponent are optional.
+ *
+ * @param integer $check The value the test for decimal
+ * @param integer $places if set $check value must have exactly $places after the decimal point
+ * @param string $regex If a custom regular expression is used this is the only validation that will occur.
+ * @return boolean Success
+ * @access public
+ */
+	function decimal($check, $places = null, $regex = null) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->regex = $regex;
+		$_this->check = $check;
+
+		if (is_null($_this->regex)) {
+			if (is_null($places)) {
+				$_this->regex = '/^[-+]?[0-9]*\\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?$/';
+			} else {
+				$_this->regex = '/^[-+]?[0-9]*\\.{1}[0-9]{'.$places.'}$/';
+			}
+		}
+		return $_this->_check();
+	}
+/**
+ * Validates for an email address.
+ *
+ * @param string $check Value to check
+ * @param boolean $deep Perform a deeper validation (if true), by also checking availability of host
+ * @param string $regex Regex to use (if none it will use built in regex)
+ * @return boolean Success
+ * @access public
+ */
+	function email($check, $deep = false, $regex = null) {
+		$_this =& Validation::getInstance();
+		$_this->__reset();
+		$_this->check = $check;
+		$_this->regex = $regex;
+		$_this->deep = $deep;
+
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (is_null($_this->regex)) {
+			$_this->regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . $_this->__pattern['hostname'] . '$/i';
+		}
+		$return = $_this->_check();
+
+		if ($_this->deep === false || $_this->deep === null) {
+			return $return;
+		}
+
+		if ($return === true && preg_match('/@(' . $_this->__pattern['hostname'] . ')$/i', $_this->check, $regs)) {
+			$host = gethostbynamel($regs[1]);
+			return is_array($host);
+		}
+		return false;
+	}
+/**
+ * Check that value is exactly $comparedTo.
+ *
+ * @param mixed $check Value to check
+ * @param mixed $comparedTo Value to compare
+ * @return boolean Success
+ * @access public
+ */
+	function equalTo($check, $comparedTo) {
+		return ($check === $comparedTo);
+	}
+/**
+ * Check that value has a valid file extension.
+ *
+ * @param mixed $check Value to check
+ * @param array $extensions file extenstions to allow
+ * @return boolean Success
+ * @access public
+ */
+	function extension($check, $extensions = array('gif', 'jpeg', 'png', 'jpg')) {
+		if (is_array($check)) {
+			return Validation::extension(array_shift($check), $extensions);
+		}
+		$extension = strtolower(array_pop(explode('.', $check)));
+		foreach ($extensions as $value) {
+			if ($extension == strtolower($value)) {
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Check that value is a file name
+ *
+ * @param mixed $check Value to check
+ * @access public
+ * @todo finish implementation
+ */
+	function file($check) {
+		// if (is_array($check)) {
+		// 	foreach ($check as $value) {
+		// 		if (!Validation::file($value)) {
+		// 			return false;
+		// 		}
+		// 	}
+		// 	return true;
+		// }
+		//
+		// return preg_match('/[\w| |_]+\.[\w]+/', $check);
+	}
+/**
+ * Validation of an IPv4 address.
+ *
+ * @param string $check The string to test.
+ * @return boolean Success
+ * @access public
+ */
+	function ip($check) {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$_this->regex = '/^' . $_this->__pattern['ip'] . '$/';
+		return $_this->_check();
+	}
+/**
+ * Checks whether the length of a string is greater or equal to a minimal length.
+ *
+ * @param string $check The string to test
+ * @param integer $min The minimal string length
+ * @return boolean Success
+ * @access public
+ */
+	function minLength($check, $min) {
+		$length = strlen($check);
+		return ($length >= $min);
+	}
+/**
+ * Checks whether the length of a string is smaller or equal to a maximal length..
+ *
+ * @param string $check The string to test
+ * @param integer $max The maximal string length
+ * @return boolean Success
+ * @access public
+ */
+	function maxLength($check, $max) {
+		$length = strlen($check);
+		return ($length <= $max);
+	}
+/**
+ * Checks that a value is a monetary amount.
+ *
+ * @param string $check Value to check
+ * @param string $symbolPosition Where symbol is located (left/right)
+ * @return boolean Success
+ * @access public
+ */
+	function money($check, $symbolPosition = 'left') {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+
+		if ($symbolPosition == 'right') {
+			$_this->regex = '/^(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?(?<!\x{00a2})\p{Sc}?$/u';
+		} else {
+			$_this->regex = '/^(?!\x{00a2})\p{Sc}?(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?$/u';
+		}
+		return $_this->_check();
+	}
+/**
+ * Validate a multiple select.
+ *
+ * @param mixed $check Value to check
+ * @param mixed $options Options for the check.
+ * 	Valid options
+ *	  in => provide a list of choices that selections must be made from
+ *	  max => maximun number of non-zero choices that can be made
+ * 	  min => minimum number of non-zero choices that can be made
+ * @return boolean Success
+ * @access public
+ */
+	function multiple($check, $options = array()) {
+		$defaults = array('in' => null, 'max' => null, 'min' => null);
+		$options = array_merge($defaults, $options);
+		$check = array_filter((array)$check);
+		if (empty($check)) {
+			return false;
+		}
+		if ($options['max'] && sizeof($check) > $options['max']) {
+			return false;
+		}
+		if ($options['min'] && sizeof($check) < $options['min']) {
+			return false;
+		}
+		if ($options['in'] && is_array($options['in'])) {
+			foreach ($check as $val) {
+				if (!in_array($val, $options['in'])) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Checks if a value is numeric.
+ *
+ * @param string $check Value to check
+ * @return boolean Succcess
+ * @access public
+ */
+	function numeric($check) {
+		return is_numeric($check);
+	}
+/**
+ * Check that a value is a valid phone number.
+ *
+ * @param mixed $check Value to check (string or array)
+ * @param string $regex Regular expression to use
+ * @param string $country Country code (defaults to 'all')
+ * @return boolean Success
+ * @access public
+ */
+	function phone($check, $regex = null, $country = 'all') {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$_this->regex = $regex;
+		$_this->country = $country;
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (is_null($_this->regex)) {
+			switch ($_this->country) {
+				case 'us':
+				// includes all NANPA members. see http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories
+				default:
+					$_this->regex  = '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/';
+				break;
+			}
+		}
+		return $_this->_check();
+	}
+/**
+ * Checks that a given value is a valid postal code.
+ *
+ * @param mixed $check Value to check
+ * @param string $regex Regular expression to use
+ * @param string $country Country to use for formatting
+ * @return boolean Success
+ * @access public
+ */
+	function postal($check, $regex = null, $country = null) {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$_this->regex = $regex;
+		$_this->country = $country;
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (is_null($_this->regex)) {
+			switch ($_this->country) {
+				case 'uk':
+					$_this->regex  = '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i';
+					break;
+				case 'ca':
+					$_this->regex  = '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z] [0-9][A-Z][0-9]\\b\\z/i';
+					break;
+				case 'it':
+				case 'de':
+					$_this->regex  = '/^[0-9]{5}$/i';
+					break;
+				case 'be':
+					$_this->regex  = '/^[1-9]{1}[0-9]{3}$/i';
+					break;
+				case 'us':
+				default:
+					$_this->regex  = '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i';
+					break;
+			}
+		}
+		return $_this->_check();
+	}
+/**
+ * Validate that a number is in specified range.
+ * if $lower and $upper are not set, will return true if
+ * $check is a legal finite on this platform
+ *
+ * @param string $check Value to check
+ * @param integer $lower Lower limit
+ * @param integer $upper Upper limit
+ * @return boolean Success
+ * @access public
+ */
+	function range($check, $lower = null, $upper = null ) {
+		if (!is_numeric($check)) {
+			return false;
+		}
+		if (isset($lower) && isset($upper)) {
+			return ($check > $lower && $check < $upper);
+		}
+		return is_finite($check);
+	}
+/**
+ * Checks that a value is a valid Social Security Number.
+ *
+ * @param mixed $check Value to check
+ * @param string $regex Regular expression to use
+ * @param string $country Country
+ * @return boolean Success
+ * @access public
+ */
+	function ssn($check, $regex = null, $country = null) {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$_this->regex = $regex;
+		$_this->country = $country;
+		if (is_array($check)) {
+			$_this->_extract($check);
+		}
+
+		if (is_null($_this->regex)) {
+			switch ($_this->country) {
+				case 'dk':
+					$_this->regex  = '/\\A\\b[0-9]{6}-[0-9]{4}\\b\\z/i';
+					break;
+				case 'nl':
+					$_this->regex  = '/\\A\\b[0-9]{9}\\b\\z/i';
+					break;
+				case 'us':
+				default:
+					$_this->regex  = '/\\A\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b\\z/i';
+					break;
+			}
+		}
+		return $_this->_check();
+	}
+/**
+ * Checks that a value is a valid URL according to http://www.w3.org/Addressing/URL/url-spec.txt
+ *
+ * The regex checks for the following component parts:
+ * 	a valid, optional, scheme
+ * 		a valid ip address OR
+ * 		a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt
+ *	  with an optional port number
+ *	an optional valid path
+ *	an optional query string (get parameters)
+ *	an optional fragment (anchor tag)
+ *
+ * @param string $check Value to check
+ * @return boolean Success
+ * @access public
+ */
+	function url($check, $strict = false) {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=') . '\/0-9a-z]|(%[0-9a-f]{2}))';
+		$_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . ife($strict, '', '?') .
+			'(?:' . $_this->__pattern['ip'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' .
+			'(?:\/?|\/' . $validChars . '*)?' .
+			'(?:\?' . $validChars . '*)?' .
+			'(?:#' . $validChars . '*)?$/i';
+		return $_this->_check();
+	}
+/**
+ * Checks if a value is in a given list.
+ *
+ * @param string $check Value to check
+ * @param array $list List to check against
+ * @return boolean Succcess
+ * @access public
+ */
+	function inList($check, $list) {
+		return in_array($check, $list);
+	}
+/**
+ * Runs an user-defined validation.
+ *
+ * @param mixed $check value that will be validated in user-defined methods.
+ * @param object $object class that holds validation method
+ * @param string $method class method name for validation to run
+ * @param array $args arguments to send to method
+ * @return mixed user-defined class class method returns
+ * @access public
+ */
+	function userDefined($check, $object, $method, $args = null) {
+		return call_user_func_array(array(&$object, $method), array($check, $args));
+	}
+/**
+ * Runs a regular expression match.
+ *
+ * @return boolean Success of match
+ * @access protected
+ */
+	function _check() {
+		$_this =& Validation::getInstance();
+		if (preg_match($_this->regex, $_this->check)) {
+			$_this->error[] = false;
+			return true;
+		} else {
+			$_this->error[] = true;
+			return false;
+		}
+	}
+/**
+ * Get the values to use when value sent to validation method is
+ * an array.
+ *
+ * @param array $params Parameters sent to validation method
+ * @return void
+ * @access protected
+ */
+	function _extract($params) {
+		$_this =& Validation::getInstance();
+		extract($params, EXTR_OVERWRITE);
+
+		if (isset($check)) {
+			$_this->check = $check;
+		}
+		if (isset($regex)) {
+			$_this->regex = $regex;
+		}
+		if (isset($country)) {
+			$_this->country = strtolower($country);
+		}
+		if (isset($deep)) {
+			$_this->deep = $deep;
+		}
+		if (isset($type)) {
+			$_this->type = $type;
+		}
+	}
+/**
+ * Luhn algorithm
+ *
+ * @see http://en.wikipedia.org/wiki/Luhn_algorithm
+ * @return boolean Success
+ * @access protected
+ */
+	function _luhn() {
+		$_this =& Validation::getInstance();
+		if ($_this->deep !== true) {
+			return true;
+		}
+		if ($_this->check == 0) {
+			return false;
+		}
+		$sum = 0;
+		$length = strlen($_this->check);
+
+		for ($position = 1 - ($length % 2); $position < $length; $position += 2) {
+			$sum += $_this->check[$position];
+		}
+
+		for ($position = ($length % 2); $position < $length; $position += 2) {
+			$number = $_this->check[$position] * 2;
+			$sum += ($number < 10) ? $number : $number - 9;
+		}
+
+		return ($sum % 10 == 0);
+	}
+/**
+ * Reset internal variables for another validation run.
+ *
+ * @return void
+ * @access private
+ */
+	function __reset() {
+		$this->check = null;
+		$this->regex = null;
+		$this->country = null;
+		$this->deep = null;
+		$this->type = null;
+		$this->error = array();
+		$this->errors = array();
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/overloadable_php4.php
===================================================================
--- cake/libs/overloadable_php4.php	(revision 0)
+++ cake/libs/overloadable_php4.php	(revision 0)
@@ -0,0 +1,164 @@
+<?php
+/* SVN FILE: $Id: overloadable_php4.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Overloadable class selector
+ *
+ * Load the interface class based on the version of PHP.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Overloadable extends Object {
+/**
+ * Constructor.
+ *
+ * @access private
+ */
+	function __construct() {
+		$this->overload();
+		parent::__construct();
+	}
+/**
+ * Overload implementation.
+ *
+ * @access public
+ */
+	function overload() {
+		if (function_exists('overload')) {
+			if (func_num_args() > 0) {
+				foreach (func_get_args() as $class) {
+					if (is_object($class)) {
+						overload(get_class($class));
+					} elseif (is_string($class)) {
+						overload($class);
+					}
+				}
+			} else {
+				overload(get_class($this));
+			}
+		}
+	}
+
+/**
+ * Magic method handler.
+ *
+ * @param string $method Method name
+ * @param array $params Parameters to send to method
+ * @param mixed $return Where to store return value from method
+ * @return boolean Success
+ * @access private
+ */
+	function __call($method, $params, &$return) {
+		if (!method_exists($this, 'call__')) {
+			trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		$return = $this->call__($method, $params);
+		return true;
+	}
+}
+Overloadable::overload('Overloadable');
+
+/**
+ * Overloadable2 class selector
+ *
+ * Load the interface class based on the version of PHP.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Overloadable2 extends Object {
+/**
+ * Constructor
+ *
+ * @access private
+ */
+	function __construct() {
+		$this->overload();
+		parent::__construct();
+	}
+/**
+ * Overload implementation.
+ *
+ * @access public
+ */
+	function overload() {
+		if (function_exists('overload')) {
+			if (func_num_args() > 0) {
+				foreach (func_get_args() as $class) {
+					if (is_object($class)) {
+						overload(get_class($class));
+					} elseif (is_string($class)) {
+						overload($class);
+					}
+				}
+			} else {
+				overload(get_class($this));
+			}
+		}
+	}
+/**
+ * Magic method handler.
+ *
+ * @param string $method Method name
+ * @param array $params Parameters to send to method
+ * @param mixed $return Where to store return value from method
+ * @return boolean Success
+ * @access private
+ */
+	function __call($method, $params, &$return) {
+		if (!method_exists($this, 'call__')) {
+			trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		$return = $this->call__($method, $params);
+		return true;
+	}
+/**
+ * Getter.
+ *
+ * @param mixed $name What to get
+ * @param mixed $value Where to store returned value
+ * @return boolean Success
+ * @access private
+ */
+	function __get($name, &$value) {
+		$value = $this->get__($name);
+		return true;
+	}
+/**
+ * Setter.
+ *
+ * @param mixed $name What to set
+ * @param mixed $value Value to set
+ * @return boolean Success
+ * @access private
+ */
+	function __set($name, $value) {
+		$this->set__($name, $value);
+		return true;
+	}
+}
+Overloadable::overload('Overloadable2');
+
+?>
\ No newline at end of file
Index: cake/libs/overloadable_php5.php
===================================================================
--- cake/libs/overloadable_php5.php	(revision 0)
+++ cake/libs/overloadable_php5.php	(revision 0)
@@ -0,0 +1,107 @@
+<?php
+/* SVN FILE: $Id: overloadable_php5.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Overloadable class selector
+ *
+ * Load the interface class based on the version of PHP.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Overloadable extends Object {
+/**
+ * Overload implementation. No need for implementation in PHP5.
+ *
+ * @access public
+ */
+	function overload() { }
+/**
+ * Magic method handler.
+ *
+ * @param string $method Method name
+ * @param array $params Parameters to send to method
+ * @return mixed Return value from method
+ * @access private
+ */
+	function __call($method, $params) {
+		if (!method_exists($this, 'call__')) {
+			trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		return $this->call__($method, $params);
+	}
+}
+
+/**
+ * Overloadable2 class selector
+ *
+ * Load the interface class based on the version of PHP.
+ *
+ * @package       cake
+ */
+class Overloadable2 extends Object {
+/**
+ * Overload implementation. No need for implementation in PHP5.
+ *
+ * @access public
+ */
+	function overload() { }
+/**
+ * Magic method handler.
+ *
+ * @param string $method Method name
+ * @param array $params Parameters to send to method
+ * @return mixed Return value from method
+ * @access private
+ */
+	function __call($method, $params) {
+		if (!method_exists($this, 'call__')) {
+			trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		return $this->call__($method, $params);
+	}
+/**
+ * Getter.
+ *
+ * @param mixed $name What to get
+ * @param mixed $value Where to store returned value
+ * @return boolean Success
+ * @access private
+ */
+	function __get($name) {
+		return $this->get__($name);
+	}
+/**
+ * Setter.
+ *
+ * @param mixed $name What to set
+ * @param mixed $value Value to set
+ * @return boolean Success
+ * @access private
+ */
+	function __set($name, $value) {
+		return $this->set__($name, $value);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/flay.php
===================================================================
--- cake/libs/flay.php	(revision 0)
+++ cake/libs/flay.php	(revision 0)
@@ -0,0 +1,285 @@
+<?php
+/* SVN FILE: $Id: flay.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Text-to-HTML parser.
+ *
+ * Text-to-html parser, similar to {@link http://textism.com/tools/textile/ Textile} or {@link http://www.whytheluckystiff.net/ruby/redcloth/ RedCloth}.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	uses('object');
+}
+/**
+ * Text-to-HTML parser.
+ *
+ * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Flay extends Object{
+/**
+ * Text to be parsed.
+ *
+ * @var string
+ * @access public
+ */
+	var $text = null;
+/**
+ * Set this to allow HTML in the markup.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $allow_html = false;
+/**
+ * Constructor.
+ *
+ * @param string $text Text to transform
+ */
+	function __construct($text = null) {
+		$this->text = $text;
+		parent::__construct();
+	}
+/**
+ * Returns given text translated to HTML using the Flay syntax.
+ *
+ * @param string $text String to format
+ * @param boolean $bare	Set this to only do <p> transforms and > to &gt;, no typography additions.
+ * @param boolean $allowHtml Set this to trim whitespace and disable all HTML
+ * @return string Formatted text
+ * @access public
+ */
+	function toHtml($text = null, $bare = false, $allowHtml = false) {
+		if (empty($text) && empty($this->text)) {
+			return false;
+		}
+		$text = $text ? $text : $this->text;
+		// trim whitespace and disable all HTML
+		if ($allowHtml) {
+			$text = trim($text);
+		} else {
+			$text = str_replace('<', '&lt;', str_replace('>', '&gt;', trim($text)));
+		}
+
+		if (!$bare) {
+			// multi-paragraph functions
+			$text=preg_replace('#(?:[\n]{0,2})"""(.*)"""(?:[\n]{0,2})#s', "\n\n%BLOCKQUOTE%\n\n\\1\n\n%ENDBLOCKQUOTE%\n\n", $text);
+			$text=preg_replace('#(?:[\n]{0,2})===(.*)===(?:[\n]{0,2})#s', "\n\n%CENTER%\n\n\\1\n\n%ENDCENTER%\n\n", $text);
+		}
+
+		// pre-parse newlines
+		$text=preg_replace("#\r\n#", "\n", $text);
+		$text=preg_replace("#[\n]{2,}#", "%PARAGRAPH%", $text);
+		$text=preg_replace('#[\n]{1}#', "%LINEBREAK%", $text);
+		$out ='';
+
+		foreach (split('%PARAGRAPH%', $text)as $line) {
+			if ($line) {
+				if (!$bare) {
+					$links = array();
+					$regs = null;
+
+					if (preg_match_all('#\[([^\[]{4,})\]#', $line, $regs)) {
+						foreach ($regs[1] as $reg) {
+							$links[] = $reg;
+							$line = str_replace("[{$reg}]", '%LINK' . (count($links) - 1) . '%', $line);
+						}
+					}
+					// bold
+					$line = ereg_replace("\*([^\*]*)\*", "<strong>\\1</strong>", $line);
+					// italic
+					$line = ereg_replace("_([^_]*)_", "<em>\\1</em>", $line);
+				}
+				// entities
+				$line = str_replace(' - ', ' &ndash; ', $line);
+				$line = str_replace(' -- ', ' &mdash; ', $line);
+				$line = str_replace('(C)', '&copy;', $line);
+				$line = str_replace('(R)', '&reg;', $line);
+				$line = str_replace('(TM)', '&trade;', $line);
+				// guess e-mails
+				$emails = null;
+				if (preg_match_all("#([_A-Za-z0-9+-+]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#", $line, $emails)) {
+					foreach ($emails[1] as $email) {
+						$line = str_replace($email, "<a href=\"mailto:{$email}\">{$email}</a>", $line);
+					}
+				}
+
+				if (!$bare) {
+					$urls = null;
+					if (preg_match_all("#((?:http|https|ftp|nntp)://[^ ]+)#", $line, $urls)) {
+						foreach ($urls[1] as $url) {
+							$line = str_replace($url, "<a href=\"{$url}\">{$url}</a>", $line);
+						}
+					}
+
+					if (preg_match_all("#(www\.[^\n\%\ ]+[^\n\%\,\.\ ])#", $line, $urls)) {
+						foreach ($urls[1] as $url) {
+							$line = str_replace($url, "<a href=\"http://{$url}\">{$url}</a>", $line);
+						}
+					}
+
+					if ($count = count($links)) {
+						for ($ii = 0; $ii < $count; $ii++) {
+							if (preg_match("#^(http|https|ftp|nntp)://#", $links[$ii])) {
+								$prefix = null;
+							} else {
+								$prefix = 'http://';
+							}
+							if (preg_match('#^[^\ ]+\.(jpg|jpeg|gif|png)$#', $links[$ii])) {
+								$with = "<img src=\"{$prefix}{$links[$ii]}\" alt=\"\" />";
+							} elseif (preg_match('#^([^\]\ ]+)(?:\ ([^\]]+))?$#', $links[$ii], $regs)) {
+								if (isset($regs[2])) {
+									if (preg_match('#\.(jpg|jpeg|gif|png)$#', $regs[2])) {
+										$body = "<img src=\"{$prefix}{$regs[2]}\" alt=\"\" />";
+									} else {
+										$body = $regs[2];
+									}
+								} else {
+									$body = $links[$ii];
+								}
+								$with = "<a href=\"{$prefix}{$regs[1]}\" target=\"_blank\">{$body}</a>";
+							} else {
+								$with = $prefix . $links[$ii];
+							}
+							$line = str_replace("%LINK{$ii}%", $with, $line);
+						}
+					}
+				}
+				$out .= str_replace('%LINEBREAK%', "<br />\n", "<p>{$line}</p>\n");
+			}
+		}
+
+		if (!$bare) {
+			$out = str_replace('<p>%BLOCKQUOTE%</p>', "<blockquote>", $out);
+			$out = str_replace('<p>%ENDBLOCKQUOTE%</p>', "</blockquote>", $out);
+			$out = str_replace('<p>%CENTER%</p>', "<center>", $out);
+			$out = str_replace('<p>%ENDCENTER%</p>', "</center>", $out);
+		}
+		return $out;
+	}
+/**
+ * Return the words of the string as an array.
+ *
+ * @param string $string
+ * @return array Array of words
+ * @access public
+ */
+	function extractWords($string) {
+		$split = preg_split('/[\s,\.:\/="!\(\)<>~\[\]]+/', $string);
+		return $split;
+	 }
+/**
+ * Return given string with words in array colorMarked, up to a number of times (defaults to 5).
+ *
+ * @param array $words			Words to look for and markup
+ * @param string $string		String to look in
+ * @param integer $max_snippets	Max number of snippets to extract
+ * @return string String with words marked
+ * @see colorMark
+ * @access public
+ */
+	function markedSnippets($words, $string, $max_snippets = 5) {
+		$string = strip_tags($string);
+		$snips = array();
+		$rest = $string;
+		foreach ($words as $word) {
+			if (preg_match_all("/[\s,]+.{0,40}{$word}.{0,40}[\s,]+/i", $rest, $r)) {
+				foreach ($r as $result) {
+					$rest = str_replace($result, '', $rest);
+				}
+				$snips = array_merge($snips, $r[0]);
+			}
+		}
+
+		if (count($snips) > $max_snippets) {
+			$snips = array_slice($snips, 0, $max_snippets);
+		}
+		$joined = join(' <b>...</b> ', $snips);
+		$snips = $joined ? "<b>...</b> {$joined} <b>...</b>" : substr($string, 0, 80) . '<b>...</b>';
+		return $this->colorMark($words, $snips);
+	}
+/**
+ * Returns string with EM elements with color classes added.
+ *
+ * @param array $words Array of words to be colorized
+ * @param string $string Text in which the words might be found
+ * @return string String with words colorized
+ * @access public
+ */
+	function colorMark($words, $string) {
+		$colors=array('yl', 'gr', 'rd', 'bl', 'fu', 'cy');
+		$nextColorIndex = 0;
+		foreach ($words as $word) {
+			$string = preg_replace("/({$word})/i", '<em class="' . $colors[$nextColorIndex % count($colors)] . "\">\\1</em>", $string);
+			$nextColorIndex++;
+		}
+		return $string;
+	}
+/**
+ * Returns given text with tags stripped out.
+ *
+ * @param string $text Text to clean
+ * @return string Cleaned text
+ * @access public
+ */
+	function toClean($text) {
+		$strip = strip_tags(html_entity_decode($text, ENT_QUOTES));
+		return $strip;
+	}
+/**
+ * Return parsed text with tags stripped out.
+ *
+ * @param string $text Text to parse and clean
+ * @return string Cleaned text
+ * @access public
+ */
+	function toParsedAndClean($text) {
+		return $this->toClean(Flay::toHtml($text));
+	}
+/**
+ * Return a fragment of a text, up to $length characters long, with an ellipsis after it.
+ *
+ * @param string $text		Text to be truncated.
+ * @param integer $length	Max length of text.
+ * @param string $ellipsis	Sign to print after truncated text.
+ * @return string Fragment
+ * @access public
+ */
+	function fragment($text, $length, $ellipsis = '...') {
+		$soft = $length - 5;
+		$hard = $length + 5;
+		$rx = '/(.{' . $soft . ',' . $hard . '})[\s,\.:\/="!\(\)<>~\[\]]+.*/';
+
+		if (preg_match($rx, $text, $r)) {
+			$out = $r[1];
+		} else {
+			$out = substr($text, 0, $length);
+		}
+		$out = $out . (strlen($out) < strlen($text) ? $ellipsis : null);
+		return $out;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/class_registry.php
===================================================================
--- cake/libs/class_registry.php	(revision 0)
+++ cake/libs/class_registry.php	(revision 0)
@@ -0,0 +1,347 @@
+<?php
+/* SVN FILE: $Id: class_registry.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Class collections.
+ *
+ * A repository for class objects, each registered with a key.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.9.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Class Collections.
+ *
+ * A repository for class objects, each registered with a key.
+ * If you try to add an object with the same key twice, nothing will come of it.
+ * If you need a second instance of an object, give it another key.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class ClassRegistry {
+/**
+ * Names of classes with their objects.
+ *
+ * @var array
+ * @access private
+ */
+	var $__objects = array();
+/**
+ * Names of class names mapped to the object in the registry.
+ *
+ * @var array
+ * @access private
+ */
+	var $__map = array();
+/**
+ * Default constructor parameter settings, indexed by type
+ *
+ * @var array
+ * @access private
+ */
+	var $__config = array();
+/**
+ * Return a singleton instance of the ClassRegistry.
+ *
+ * @return ClassRegistry instance
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new ClassRegistry();
+		}
+		return $instance[0];
+	}
+/**
+ * Loads a class, registers the object in the registry and returns instance of the object.
+ *
+ * @param mixed $class as a string or a single key => value array instance will be created,
+ * 	stored in the registry and returned.
+ *   Required: array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass');
+ *   Model Classes can accept optional array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);
+ * When $class is a numeric keyed array, multiple class instances will be stored in the registry,
+ *   no instance of the object will be returned
+ *   array(
+ *  	array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
+ *  	array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
+ *  	array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass')
+ *  );
+ *
+ * @param string $type TypeOfClass
+ * @return object intance of ClassName
+ * @access public
+ * @static
+ */
+	function &init($class, $type = null) {
+		$_this =& ClassRegistry::getInstance();
+		$id = $false = false;
+		$true = true;
+
+		if (!$type) {
+			$type = 'Model';
+		}
+
+		if (is_array($class)) {
+			$objects = $class;
+			if (!isset($class[0])) {
+				$objects = array($class);
+			}
+		} else {
+			$objects = array(array('class' => $class));
+		}
+		$defaults = isset($_this->__config[$type]) ? $_this->__config[$type] : array();
+		$count = count($objects);
+
+		foreach ($objects as $key => $settings) {
+			if (is_array($settings)) {
+				$plugin = $pluginPath = null;
+				$settings = array_merge($defaults, $settings);
+				$class = $settings['class'];
+
+				if (strpos($class, '.') !== false) {
+					list($plugin, $class) = explode('.', $class);
+					$pluginPath = $plugin . '.';
+				}
+
+				if (empty($settings['alias'])) {
+					$settings['alias'] = $class;
+				}
+				$alias = $settings['alias'];
+
+				if ($model =& $_this->__duplicate($alias, $class)) {
+					$_this->map($alias, $class);
+					return $model;
+				}
+
+				if (class_exists($class) || App::import($type, $pluginPath . $class)) {
+					${$class} =& new $class($settings);
+				} elseif ($type === 'Model') {
+					if ($plugin && class_exists($plugin . 'AppModel')) {
+						$appModel = $plugin . 'AppModel';
+					} else {
+						$appModel = 'AppModel';
+					}
+					$settings['name'] = $class;
+					${$class} =& new $appModel($settings);
+				}
+
+				if (!isset(${$class})) {
+					trigger_error(sprintf(__('(ClassRegistry::init() could not create instance of %1$s class %2$s ', true), $class, $type), E_USER_WARNING);
+					return $false;
+				}
+
+				if ($type !== 'Model') {
+					$_this->addObject($alias, ${$class});
+				} else {
+					$_this->map($alias, $class);
+				}
+			} elseif (is_numeric($settings)) {
+				trigger_error(__('(ClassRegistry::init() Attempted to create instance of a class with a numeric name', true), E_USER_WARNING);
+				return $false;
+			}
+		}
+
+		if ($count > 1) {
+			return $true;
+		}
+		return ${$class};
+	}
+/**
+ * Add $object to the registry, associating it with the name $key.
+ *
+ * @param string $key	Key for the object in registry
+ * @param mixed $object	Object to store
+ * @return boolean True if the object was written, false if $key already exists
+ * @access public
+ * @static
+ */
+	function addObject($key, &$object) {
+		$_this =& ClassRegistry::getInstance();
+		$key = Inflector::underscore($key);
+		if (!isset($_this->__objects[$key])) {
+			$_this->__objects[$key] =& $object;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Remove object which corresponds to given key.
+ *
+ * @param string $key	Key of object to remove from registry
+ * @return void
+ * @access public
+ * @static
+ */
+	function removeObject($key) {
+		$_this =& ClassRegistry::getInstance();
+		$key = Inflector::underscore($key);
+		if (isset($_this->__objects[$key])) {
+			unset($_this->__objects[$key]);
+		}
+	}
+/**
+ * Returns true if given key is present in the ClassRegistry.
+ *
+ * @param string $key Key to look for
+ * @return boolean true if key exists in registry, false otherwise
+ * @access public
+ * @static
+ */
+	function isKeySet($key) {
+		$_this =& ClassRegistry::getInstance();
+		$key = Inflector::underscore($key);
+		if (isset($_this->__objects[$key])) {
+			return true;
+		} elseif (isset($_this->__map[$key])) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Get all keys from the registry.
+ *
+ * @return array Set of keys stored in registry
+ * @access public
+ * @static
+ */
+	function keys() {
+		$_this =& ClassRegistry::getInstance();
+		return array_keys($_this->__objects);
+	}
+/**
+ * Return object which corresponds to given key.
+ *
+ * @param string $key Key of object to look for
+ * @return mixed Object stored in registry
+ * @access public
+ * @static
+ */
+	function &getObject($key) {
+		$_this =& ClassRegistry::getInstance();
+		$key = Inflector::underscore($key);
+		$return = false;
+		if (isset($_this->__objects[$key])) {
+			$return =& $_this->__objects[$key];
+		} else {
+			$key = $_this->__getMap($key);
+			if (isset($_this->__objects[$key])) {
+				$return =& $_this->__objects[$key];
+			}
+		}
+		return $return;
+	}
+/**
+ * Sets the default constructor parameter for an object type
+ *
+ * @param string $type Type of object.  If this parameter is omitted, defaults to "Model"
+ * @param array $param The parameter that will be passed to object constructors when objects
+ *                      of $type are created
+ * @return mixed Void if $param is being set.  Otherwise, if only $type is passed, returns
+ *               the previously-set value of $param, or null if not set.
+ * @access public
+ * @static
+ */
+	function config($type, $param = array()) {
+		$_this =& ClassRegistry::getInstance();
+
+		if (empty($param) && is_array($type)) {
+			$param = $type;
+			$type = 'Model';
+		} elseif (is_null($param)) {
+			unset($_this->__config[$type]);
+		} elseif (empty($param) && is_string($type)) {
+			return isset($_this->__config[$type]) ? $_this->__config[$type] : null;
+		}
+		$_this->__config[$type] = $param;
+	}
+/**
+ * Checks to see if $alias is a duplicate $class Object
+ *
+ * @param string $alias
+ * @param string $class
+ * @return boolean
+ * @access private
+ * @static
+ */
+	function &__duplicate($alias,  $class) {
+		$duplicate = false;
+		if ($this->isKeySet($alias)) {
+			$model =& $this->getObject($alias);
+			if (is_object($model) && (is_a($model, $class) || $model->alias === $class)) {
+				$duplicate =& $model;
+			}
+			unset($model);
+		}
+		return $duplicate;
+	}
+/**
+ * Add a key name pair to the registry to map name to class in the registry.
+ *
+ * @param string $key Key to include in map
+ * @param string $name Key that is being mapped
+ * @access public
+ * @static
+ */
+	function map($key, $name) {
+		$_this =& ClassRegistry::getInstance();
+		$key = Inflector::underscore($key);
+		$name = Inflector::underscore($name);
+		if (!isset($_this->__map[$key])) {
+			$_this->__map[$key] = $name;
+		}
+	}
+/**
+ * Get all keys from the map in the registry.
+ *
+ * @return array Keys of registry's map
+ * @access public
+ * @static
+ */
+	function mapKeys() {
+		$_this =& ClassRegistry::getInstance();
+		return array_keys($_this->__map);
+	}
+/**
+ * Return the name of a class in the registry.
+ *
+ * @param string $key Key to find in map
+ * @return string Mapped value
+ * @access private
+ * @static
+ */
+	function __getMap($key) {
+		if (isset($this->__map[$key])) {
+			return $this->__map[$key];
+		}
+	}
+/**
+ * Flushes all objects from the ClassRegistry.
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+	function flush() {
+		$_this =& ClassRegistry::getInstance();
+		$_this->__objects = array();
+		$_this->__map = array();
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/components/cookie.php
===================================================================
--- cake/libs/controller/components/cookie.php	(revision 0)
+++ cake/libs/controller/components/cookie.php	(revision 0)
@@ -0,0 +1,485 @@
+<?php
+/* SVN FILE: $Id: cookie.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.4213
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Load Security class
+ */
+App::import('Core', 'Security');
+/**
+ * Cookie Component.
+ *
+ * Cookie handling for the controller.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ *
+ */
+class CookieComponent extends Object {
+/**
+ * The name of the cookie.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->name = 'CookieName';
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'CakeCookie';
+/**
+ * The time a cookie will remain valid.
+ *
+ * Can be either integer Unix timestamp or a date string.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->time = '5 Days';
+ *
+ * @var mixed
+ * @access public
+ */
+	var $time = null;
+/**
+ * Cookie path.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->path = '/';
+ *
+ * The path on the server in which the cookie will be available on.
+ * If  var $cookiePath is set to '/foo/', the cookie will only be available
+ * within the /foo/ directory and all sub-directories such as /foo/bar/ of domain.
+ * The default value is the entire domain.
+ *
+ * @var string
+ * @access public
+ */
+	var $path = '/';
+/**
+ * Domain path.
+ *
+ * The domain that the cookie is available.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->domain = '.example.com';
+ *
+ * To make the cookie available on all subdomains of example.com.
+ * Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter
+ *
+ * @var string
+ * @access public
+ */
+	var $domain = '';
+/**
+ * Secure HTTPS only cookie.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->secure = true;
+ *
+ * Indicates that the cookie should only be transmitted over a secure HTTPS connection.
+ * When set to true, the cookie will only be set if a secure connection exists.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $secure = false;
+/**
+ * Encryption key.
+ *
+ * Overridden with the controller beforeFilter();
+ * $this->Cookie->key = 'SomeRandomString';
+ *
+ * @var string
+ * @access protected
+ */
+	var $key = null;
+/**
+ * Values stored in the cookie.
+ *
+ * Accessed in the controller using $this->Cookie->read('Name.key');
+ *
+ * @see CookieComponent::read();
+ * @var string
+ * @access private
+ */
+	var $__values = array();
+/**
+ * Type of encryption to use.
+ *
+ * Currently only one method is available
+ * Defaults to Security::cipher();
+ *
+ * @var string
+ * @access private
+ * @todo add additional encryption methods
+ */
+	var $__type = 'cipher';
+/**
+ * Used to reset cookie time if $expire is passed to CookieComponent::write()
+ *
+ * @var string
+ * @access private
+ */
+	var $__reset = null;
+/**
+ * Expire time of the cookie
+ *
+ * This is controlled by CookieComponent::time;
+ *
+ * @var string
+ * @access private
+ */
+	var $__expires = 0;
+/**
+ * Main execution method.
+ *
+ * @param object $controller A reference to the instantiating controller object
+ * @access public
+ */
+	function initialize(&$controller, $settings) {
+		$this->key = Configure::read('Security.salt');
+		$this->_set($settings);
+	}
+/**
+ * Start CookieComponent for use in the controller
+ *
+ * @access public
+ */
+	function startup() {
+		$this->__expire($this->time);
+
+		if (isset($_COOKIE[$this->name])) {
+			$this->__values = $this->__decrypt($_COOKIE[$this->name]);
+		}
+	}
+/**
+ * Write a value to the $_COOKIE[$key];
+ *
+ * Optional [Name.], reguired key, optional $value, optional $encrypt, optional $expires
+ * $this->Cookie->write('[Name.]key, $value);
+ *
+ * By default all values are encrypted.
+ * You must pass $encrypt false to store values in clear test
+ *
+ * You must use this method before any output is sent to the browser.
+ * Failure to do so will result in header already sent errors.
+ *
+ * @param mixed $key Key for the value
+ * @param mixed $value Value
+ * @param boolean $encrypt Set to true to encrypt value, false otherwise
+ * @param string $expires Can be either Unix timestamp, or date string
+ * @access public
+ */
+	function write($key, $value = null, $encrypt = true, $expires = null) {
+		if (is_null($encrypt)) {
+			$encrypt = true;
+		}
+
+		$this->__encrypted = $encrypt;
+		$this->__expire($expires);
+
+		if (!is_array($key) && $value !== null) {
+			$name = $this->__cookieVarNames($key);
+
+			if (count($name) > 1) {
+				$this->__values[$name[0]][$name[1]] = $value;
+				$this->__write("[".$name[0]."][".$name[1]."]", $value);
+			} else {
+				$this->__values[$name[0]] = $value;
+				$this->__write("[".$name[0]."]", $value);
+			}
+		} else {
+			foreach ($key as $names => $value) {
+				$name = $this->__cookieVarNames($names);
+
+				if (count($name) > 1) {
+					$this->__values[$name[0]][$name[1]] = $value;
+					$this->__write("[".$name[0]."][".$name[1]."]", $value);
+				} else {
+					$this->__values[$name[0]] = $value;
+					$this->__write("[".$name[0]."]", $value);
+				}
+			}
+		}
+		$this->__encrypted = true;
+	}
+/**
+ * Read the value of the $_COOKIE[$key];
+ *
+ * Optional [Name.], reguired key
+ * $this->Cookie->read(Name.key);
+ *
+ * @param mixed $key Key of the value to be obtained. If none specified, obtain map key => values
+ * @return string or null, value for specified key
+ * @access public
+ */
+	function read($key = null) {
+		if (empty($this->__values) && isset($_COOKIE[$this->name])) {
+			$this->__values = $this->__decrypt($_COOKIE[$this->name]);
+		}
+
+		if (is_null($key)) {
+			return $this->__values;
+		}
+		$name = $this->__cookieVarNames($key);
+
+		if (count($name) > 1) {
+			if (isset($this->__values[$name[0]])) {
+				if (isset($this->__values[$name[0]][$name[1]])) {
+					return $this->__values[$name[0]][$name[1]];
+				}
+			}
+			return null;
+		} else {
+			if (isset($this->__values[$name[0]])) {
+				$value = $this->__values[$name[0]];
+				return $value;
+			}
+			return null;
+		}
+	}
+/**
+ * Delete a cookie value
+ *
+ * Optional [Name.], reguired key
+ * $this->Cookie->read('Name.key);
+ *
+ * You must use this method before any output is sent to the browser.
+ * Failure to do so will result in header already sent errors.
+ *
+ * @param string $key Key of the value to be deleted
+ * @return void
+ * @access public
+ */
+	function del($key) {
+		if (empty($this->__values)) {
+			$this->read();
+		}
+		$name = $this->__cookieVarNames($key);
+		if (count($name) > 1) {
+			if (isset($this->__values[$name[0]])) {
+				$this->__delete("[".$name[0]."][".$name[1]."]");
+				unset($this->__values[$name[0]][$name[1]]);
+			}
+		} else {
+			if (isset($this->__values[$name[0]])) {
+				if (is_array($this->__values[$name[0]])) {
+					foreach ($this->__values[$name[0]] as $key => $value) {
+						$this->__delete("[".$name[0]."][".$key."]");
+					}
+				}
+				$this->__delete("[".$name[0]."]");
+				unset($this->__values[$name[0]]);
+			}
+		}
+	}
+/**
+ * Destroy current cookie
+ *
+ * You must use this method before any output is sent to the browser.
+ * Failure to do so will result in header already sent errors.
+ *
+ * @return void
+ * @access public
+ */
+	function destroy() {
+		if (isset($_COOKIE[$this->name])) {
+			$this->__values = $this->__decrypt($_COOKIE[$this->name]);
+		}
+
+		foreach ($this->__values as $name => $value) {
+			if (is_array($value)) {
+				foreach ($value as $key => $val) {
+					unset($this->__values[$name][$key]);
+					$this->__delete("[$name][$key]");
+				}
+			}
+			unset($this->__values[$name]);
+			$this->__delete("[$name]");
+		}
+	}
+/**
+ * Will allow overriding default encryption method.
+ *
+ * @param string $type Encryption method
+ * @access public
+ * @todo NOT IMPLEMENTED
+ */
+	function type($type = 'cipher') {
+		$this->__type = 'cipher';
+	}
+/**
+ * Set the expire time for a session variable.
+ *
+ * Creates a new expire time for a session variable.
+ * $expire can be either integer Unix timestamp or a date string.
+ *
+ * Used by write()
+ * CookieComponent::write(string, string, boolean, 8400);
+ * CookieComponent::write(string, string, boolean, '5 Days');
+ *
+ * @param mixed $expires Can be either Unix timestamp, or date string
+ * @return int Unix timestamp
+ * @access private
+ */
+	function __expire($expires = null) {
+		$now = time();
+		if (is_null($expires)) {
+			return $this->__expires;
+		}
+		$this->__reset = $this->__expires;
+		if (is_integer($expires) || is_numeric($expires)) {
+			return $this->__expires = $now + intval($expires);
+		}
+		return $this->__expires = strtotime($expires, $now);
+	}
+/**
+ * Set cookie
+ *
+ * @param string $name Name for cookie
+ * @param string $value Value for cookie
+ * @access private
+ */
+	function __write($name, $value) {
+		setcookie($this->name . "$name", $this->__encrypt($value), $this->__expires, $this->path, $this->domain, $this->secure);
+
+		if (!is_null($this->__reset)) {
+			$this->__expires = $this->__reset;
+			$this->__reset = null;
+		}
+	}
+/**
+ * Sets a cookie expire time to remove cookie value
+ *
+ * @param string $name Name of cookie
+ * @access private
+ */
+	function __delete($name) {
+		setcookie($this->name . $name, '', time() - 42000, $this->path, $this->domain, $this->secure);
+	}
+/**
+ * Encrypts $value using var $type method in Security class
+ *
+ * @param string $value Value to encrypt
+ * @return string encrypted string
+ * @access private
+ */
+	function __encrypt($value) {
+		if (is_array($value)) {
+			$value = $this->__implode($value);
+		}
+
+		if ($this->__encrypted === true) {
+			$type = $this->__type;
+			$value = "Q2FrZQ==." .base64_encode(Security::$type($value, $this->key));
+		}
+		return($value);
+	}
+/**
+ * Decrypts $value using var $type method in Security class
+ *
+ * @param array $values Values to decrypt
+ * @return string decrypted string
+ * @access private
+ */
+	function __decrypt($values) {
+		$decrypted = array();
+		$type = $this->__type;
+
+		foreach ($values as $name => $value) {
+			if (is_array($value)) {
+				foreach ($value as $key => $val) {
+					$pos = strpos($val, 'Q2FrZQ==.');
+					$decrypted[$name][$key] = $this->__explode($val);
+
+					if ($pos !== false) {
+						$val = substr($val, 8);
+						$decrypted[$name][$key] = $this->__explode(Security::$type(base64_decode($val), $this->key));
+					}
+				}
+			} else {
+				$pos = strpos($value, 'Q2FrZQ==.');
+				$decrypted[$name] = $this->__explode($value);
+
+				if ($pos !== false) {
+					$value = substr($value, 8);
+					$decrypted[$name] = $this->__explode(Security::$type(base64_decode($value), $this->key));
+				}
+			}
+		}
+
+		return($decrypted);
+	}
+
+/**
+ * Creates an array from the $name parameter which allows the dot notation
+ * similar to one used by Session and Configure classes
+ *
+ * @param string $name Name with or without dot notation
+ * @return array Extracted names
+ * @access private
+ */
+	function __cookieVarNames($name) {
+		if (is_string($name)) {
+			if (strpos($name, ".")) {
+				$name = explode(".", $name);
+			} else {
+				$name = array($name);
+			}
+		}
+		return $name;
+	}
+/**
+ * Implode method to keep keys are multidimensional arrays
+ *
+ * @param array $array Map of key and values
+ * @return string String in the form key1|value1,key2|value2
+ * @access private
+ */
+	function __implode($array) {
+		$string = '';
+		foreach ($array as $key => $value) {
+			$string .= ',' . $key . '|' . $value;
+		}
+		return substr($string, 1);
+	}
+/**
+ * Explode method to return array from string set in CookieComponent::__implode()
+ *
+ * @param string $string String in the form key1|value1,key2|value2
+ * @return array Map of key and values
+ * @access private
+ */
+	function __explode($string) {
+		$array = array();
+		foreach (explode(',', $string) as $pair) {
+			$key = explode('|', $pair);
+			if (!isset($key[1])) {
+				return $key[0];
+			}
+			$array[$key[0]] = $key[1];
+		}
+		return $array;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/components/acl.php
===================================================================
--- cake/libs/controller/components/acl.php	(revision 0)
+++ cake/libs/controller/components/acl.php	(revision 0)
@@ -0,0 +1,588 @@
+<?php
+/* SVN FILE: $Id: acl.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Access Control List factory class.
+ *
+ * Permissions system.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Access Control List factory class.
+ *
+ * Looks for ACL implementation class in core config, and returns an instance of that class.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ */
+class AclComponent extends Object {
+/**
+ * Instance of an ACL class
+ *
+ * @var object
+ * @access protected
+ */
+	var $_Instance = null;
+/**
+ * Constructor. Will return an instance of the correct ACL class.
+ *
+ */
+	function __construct() {
+		$name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
+		if (!class_exists($name)) {
+			if (App::import('Component', $name)) {
+				if (strpos($name, '.') !== false) {
+					list($plugin, $name) = explode('.', $name);
+				}
+				$name .= 'Component';
+			} else {
+				trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
+			}
+		}
+		$this->_Instance =& new $name();
+		$this->_Instance->initialize($this);
+	}
+/**
+ * Startup is not used
+ *
+ * @param object $controller Controller using this component
+ * @return boolean Proceed with component usage (true), or fail (false)
+ * @access public
+ */
+	function startup(&$controller) {
+		return true;
+	}
+/**
+ * Empty class defintion, to be overridden in subclasses.
+ *
+ * @access protected
+ */
+	function _initACL() {
+	}
+/**
+ * Pass-thru function for ACL check instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function check($aro, $aco, $action = "*") {
+		return $this->_Instance->check($aro, $aco, $action);
+	}
+/**
+ * Pass-thru function for ACL allow instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function allow($aro, $aco, $action = "*") {
+		return $this->_Instance->allow($aro, $aco, $action);
+	}
+/**
+ * Pass-thru function for ACL deny instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function deny($aro, $aco, $action = "*") {
+		return $this->_Instance->deny($aro, $aco, $action);
+	}
+/**
+ * Pass-thru function for ACL inherit instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function inherit($aro, $aco, $action = "*") {
+		return $this->_Instance->inherit($aro, $aco, $action);
+	}
+/**
+ * Pass-thru function for ACL grant instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function grant($aro, $aco, $action = "*") {
+		return $this->_Instance->grant($aro, $aco, $action);
+	}
+/**
+ * Pass-thru function for ACL grant instance.
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function revoke($aro, $aco, $action = "*") {
+		return $this->_Instance->revoke($aro, $aco, $action);
+	}
+}
+/**
+ * Access Control List abstract class. Not to be instantiated.
+ * Subclasses of this class are used by AclComponent to perform ACL checks in Cake.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @abstract
+ */
+class AclBase extends Object {
+/**
+ * This class should never be instantiated, just subclassed.
+ *
+ */
+	function __construct() {
+		if (strcasecmp(get_class($this), "AclBase") == 0 || !is_subclass_of($this, "AclBase")) {
+			trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration.", true), E_USER_ERROR);
+			return NULL;
+		}
+	}
+/**
+ * Empty method to be overridden in subclasses
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @access public
+ */
+	function check($aro, $aco, $action = "*") {
+	}
+/**
+ * Empty method to be overridden in subclasses
+ *
+ * @param object $component Component
+ * @access public
+ */
+	function initialize(&$component) {
+	}
+}
+/**
+ * In this file you can extend the AclBase.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class DbAcl extends AclBase {
+/**
+ * Constructor
+ *
+ */
+	function __construct() {
+		parent::__construct();
+		if (!class_exists('AclNode')) {
+			uses('model' . DS . 'db_acl');
+		}
+		$this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
+		$this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
+	}
+/**
+ * Enter description here...
+ *
+ * @param object $component
+ * @return void
+ * @access public
+ */
+	function initialize(&$component) {
+		$component->Aro = $this->Aro;
+		$component->Aco = $this->Aco;
+	}
+/**
+ * Checks if the given $aro has access to action $action in $aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $action Action (defaults to *)
+ * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
+ * @access public
+ */
+	function check($aro, $aco, $action = "*") {
+		if ($aro == null || $aco == null) {
+			return false;
+		}
+
+		$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
+		$aroPath = $this->Aro->node($aro);
+		$acoPath = $this->Aco->node($aco);
+
+		if (empty($aroPath) || empty($acoPath)) {
+			trigger_error("DbAcl::check() - Failed ARO/ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
+			return false;
+		}
+
+		if ($acoPath == null || $acoPath == array()) {
+			trigger_error("DbAcl::check() - Failed ACO node lookup in permissions check.  Node references:\nAro: " . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
+			return false;
+		}
+
+		$aroNode = $aroPath[0];
+		$acoNode = $acoPath[0];
+
+		if ($action != '*' && !in_array('_' . $action, $permKeys)) {
+			trigger_error(sprintf(__("ACO permissions key %s does not exist in DbAcl::check()", true), $action), E_USER_NOTICE);
+			return false;
+		}
+
+		$inherited = array();
+		$acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id');
+
+		$count = count($aroPath);
+		for ($i = 0 ; $i < $count; $i++) {
+			$permAlias = $this->Aro->Permission->alias;
+
+			$perms = $this->Aro->Permission->find('all', array(
+				'conditions' => array(
+					"{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
+					"{$permAlias}.aco_id" => $acoIDs
+				),
+				'order' => array($this->Aco->alias . '.lft' => 'desc'),
+				'recursive' => 0
+			));
+
+			if (empty($perms)) {
+				continue;
+			} else {
+				$perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
+				foreach ($perms as $perm) {
+					if ($action == '*') {
+
+						foreach ($permKeys as $key) {
+							if (!empty($perm)) {
+								if ($perm[$key] == -1) {
+									return false;
+								} elseif ($perm[$key] == 1) {
+									$inherited[$key] = 1;
+								}
+							}
+						}
+
+						if (count($inherited) === count($permKeys)) {
+							return true;
+						}
+					} else {
+						switch ($perm['_' . $action]) {
+							case -1:
+								return false;
+							case 0:
+								continue;
+							break;
+							case 1:
+								return true;
+							break;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+/**
+ * Allow $aro to have access to action $actions in $aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $actions Action (defaults to *)
+ * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
+ * @return boolean Success
+ * @access public
+ */
+	function allow($aro, $aco, $actions = "*", $value = 1) {
+		$perms = $this->getAclLink($aro, $aco);
+		$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
+		$save = array();
+
+		if ($perms == false) {
+			trigger_error(__('DbAcl::allow() - Invalid node', true), E_USER_WARNING);
+			return false;
+		}
+		if (isset($perms[0])) {
+			$save = $perms[0][$this->Aro->Permission->alias];
+		}
+
+		if ($actions == "*") {
+			$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
+			$save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
+		} else {
+			if (!is_array($actions)) {
+				$actions = array('_' . $actions);
+			}
+			if (is_array($actions)) {
+				foreach ($actions as $action) {
+					if ($action{0} != '_') {
+						$action = '_' . $action;
+					}
+					if (in_array($action, $permKeys)) {
+						$save[$action] = $value;
+					}
+				}
+			}
+		}
+		list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
+
+		if ($perms['link'] != null && count($perms['link']) > 0) {
+			$save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
+		} else {
+			unset($save['id']);
+			$this->Aro->Permission->id = null;
+		}
+		return ($this->Aro->Permission->save($save) !== false);
+	}
+/**
+ * Deny access for $aro to action $action in $aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $actions Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function deny($aro, $aco, $action = "*") {
+		return $this->allow($aro, $aco, $action, -1);
+	}
+/**
+ * Let access for $aro to action $action in $aco be inherited
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $actions Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ */
+	function inherit($aro, $aco, $action = "*") {
+		return $this->allow($aro, $aco, $action, 0);
+	}
+/**
+ * Allow $aro to have access to action $actions in $aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $actions Action (defaults to *)
+ * @return boolean Success
+ * @see allow()
+ * @access public
+ */
+	function grant($aro, $aco, $action = "*") {
+		return $this->allow($aro, $aco, $action);
+	}
+/**
+ * Deny access for $aro to action $action in $aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $actions Action (defaults to *)
+ * @return boolean Success
+ * @see deny()
+ * @access public
+ */
+	function revoke($aro, $aco, $action = "*") {
+		return $this->deny($aro, $aco, $action);
+	}
+/**
+ * Get an array of access-control links between the given Aro and Aco
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @return array Indexed array with: 'aro', 'aco' and 'link'
+ * @access public
+ */
+	function getAclLink($aro, $aco) {
+		$obj = array();
+		$obj['Aro'] = $this->Aro->node($aro);
+		$obj['Aco'] = $this->Aco->node($aco);
+
+		if (empty($obj['Aro']) || empty($obj['Aco'])) {
+			return false;
+		}
+
+		return array(
+			'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
+			'aco'  => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'),
+			'link' => $this->Aro->Permission->find('all', array('conditions' => array(
+				$this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
+				$this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id')
+			)))
+		);
+	}
+/**
+ * Get the keys used in an ACO
+ *
+ * @param array $keys Permission model info
+ * @return array ACO keys
+ * @access protected
+ */
+	function _getAcoKeys($keys) {
+		$newKeys = array();
+		$keys = array_keys($keys);
+		foreach ($keys as $key) {
+			if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
+				$newKeys[] = $key;
+			}
+		}
+		return $newKeys;
+	}
+}
+/**
+ * In this file you can extend the AclBase.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.iniacl
+ */
+class IniAcl extends AclBase {
+/**
+ * Array with configuration, parsed from ini file
+ *
+ * @var array
+ * @access public
+ */
+	var $config = null;
+/**
+ * The constructor must be overridden, as AclBase is abstract.
+ *
+ */
+	function __construct() {
+	}
+/**
+ * Main ACL check function. Checks to see if the ARO (access request object) has access to the ACO (access control object).
+ * Looks at the acl.ini.php file for permissions (see instructions in /config/acl.ini.php).
+ *
+ * @param string $aro ARO
+ * @param string $aco ACO
+ * @param string $aco_action Action
+ * @return boolean Success
+ * @access public
+ */
+	function check($aro, $aco, $aco_action = null) {
+		if ($this->config == null) {
+			$this->config = $this->readConfigFile(CONFIGS . 'acl.ini.php');
+		}
+		$aclConfig = $this->config;
+
+		if (isset($aclConfig[$aro]['deny'])) {
+			$userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
+
+			if (array_search($aco, $userDenies)) {
+				return false;
+			}
+		}
+
+		if (isset($aclConfig[$aro]['allow'])) {
+			$userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
+
+			if (array_search($aco, $userAllows)) {
+				return true;
+			}
+		}
+
+		if (isset($aclConfig[$aro]['groups'])) {
+			$userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
+
+			foreach ($userGroups as $group) {
+				if (array_key_exists($group, $aclConfig)) {
+					if (isset($aclConfig[$group]['deny'])) {
+						$groupDenies=$this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
+
+						if (array_search($aco, $groupDenies)) {
+							return false;
+						}
+					}
+
+					if (isset($aclConfig[$group]['allow'])) {
+						$groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
+
+						if (array_search($aco, $groupAllows)) {
+							return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+/**
+ * Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
+ *
+ * @param string $fileName File
+ * @return array INI section structure
+ * @access public
+ */
+	function readConfigFile($fileName) {
+		$fileLineArray = file($fileName);
+
+		foreach ($fileLineArray as $fileLine) {
+			$dataLine = trim($fileLine);
+			$firstChar = substr($dataLine, 0, 1);
+
+			if ($firstChar != ';' && $dataLine != '') {
+				if ($firstChar == '[' && substr($dataLine, -1, 1) == ']') {
+					$sectionName = preg_replace('/[\[\]]/', '', $dataLine);
+				} else {
+					$delimiter = strpos($dataLine, '=');
+
+					if ($delimiter > 0) {
+						$key = strtolower(trim(substr($dataLine, 0, $delimiter)));
+						$value = trim(substr($dataLine, $delimiter + 1));
+
+						if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') {
+							$value = substr($value, 1, -1);
+						}
+
+						$iniSetting[$sectionName][$key]=stripcslashes($value);
+					} else {
+						if (!isset($sectionName)) {
+							$sectionName = '';
+						}
+
+						$iniSetting[$sectionName][strtolower(trim($dataLine))]='';
+					}
+				}
+			}
+		}
+
+		return $iniSetting;
+	}
+/**
+ * Removes trailing spaces on all array elements (to prepare for searching)
+ *
+ * @param array $array Array to trim
+ * @return array Trimmed array
+ * @access public
+ */
+	function arrayTrim($array) {
+		foreach ($array as $key => $value) {
+			$array[$key] = trim($value);
+		}
+		array_unshift($array, "");
+		return $array;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/components/auth.php
===================================================================
--- cake/libs/controller/components/auth.php	(revision 0)
+++ cake/libs/controller/components/auth.php	(revision 0)
@@ -0,0 +1,862 @@
+<?php
+/* SVN FILE: $Id: auth.php 8004 2009-01-16 20:15:21Z gwoo $ */
+
+/**
+ * Authentication component
+ *
+ * Manages user logins and permissions.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+App::import(array('Router', 'Security'));
+
+/**
+ * Authentication control component class
+ *
+ * Binds access control with user authentication and session management.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ */
+class AuthComponent extends Object {
+/**
+ * Maintains current user login state.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $_loggedIn = false;
+/**
+ * Other components utilized by AuthComponent
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Session', 'RequestHandler');
+/**
+ * A reference to the object used for authentication
+ *
+ * @var object
+ * @access public
+ */
+	var $authenticate = null;
+/**
+ * The name of the component to use for Authorization or set this to
+ * 'controller' will validate against Controller::isAuthorized()
+ * 'actions' will validate Controller::action against an AclComponent::check()
+ * 'crud' will validate mapActions against an AclComponent::check()
+ * array('model'=> 'name'); will validate mapActions against model $name::isAuthorized(user, controller, mapAction)
+ * 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
+ *
+ * @var mixed
+ * @access public
+ */
+	var $authorize = false;
+/**
+ * The name of an optional view element to render when an Ajax request is made
+ * with an invalid or expired session
+ *
+ * @var string
+ * @access public
+ */
+	var $ajaxLogin = null;
+/**
+ * The name of the model that represents users which will be authenticated.  Defaults to 'User'.
+ *
+ * @var string
+ * @access public
+ */
+	var $userModel = 'User';
+/**
+ * Additional query conditions to use when looking up and authenticating users,
+ * i.e. array('User.is_active' => 1).
+ *
+ * @var array
+ * @access public
+ */
+	var $userScope = array();
+/**
+ * Allows you to specify non-default login name and password fields used in
+ * $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array('username' => 'username', 'password' => 'password');
+/**
+ * The session key name where the record of the current user is stored.  If
+ * unspecified, it will be "Auth.{$userModel name}".
+ *
+ * @var string
+ * @access public
+ */
+	var $sessionKey = null;
+/**
+ * If using action-based access control, this defines how the paths to action
+ * ACO nodes is computed.  If, for example, all controller nodes are nested
+ * under an ACO node named 'Controllers', $actionPath should be set to
+ * "Controllers/".
+ *
+ * @var string
+ * @access public
+ */
+	var $actionPath = null;
+/**
+ * A URL (defined as a string or array) to the controller action that handles
+ * logins.
+ *
+ * @var mixed
+ * @access public
+ */
+	var $loginAction = null;
+/**
+ * Normally, if a user is redirected to the $loginAction page, the location they
+ * were redirected from will be stored in the session so that they can be
+ * redirected back after a successful login.  If this session value is not
+ * set, the user will be redirected to the page specified in $loginRedirect.
+ *
+ * @var mixed
+ * @access public
+ */
+	var $loginRedirect = null;
+/**
+ * The the default action to redirect to after the user is logged out.  While AuthComponent does
+ * not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
+ * Defaults to AuthComponent::$loginAction.
+ *
+ * @var mixed
+ * @access public
+ * @see AuthComponent::$loginAction
+ * @see AuthComponent::logout()
+ */
+	var $logoutRedirect = null;
+/**
+ * The name of model or model object, or any other object has an isAuthorized method.
+ *
+ * @var string
+ * @access public
+ */
+	var $object = null;
+/**
+ * Error to display when user login fails.  For security purposes, only one error is used for all
+ * login failures, so as not to expose information on why the login failed.
+ *
+ * @var string
+ * @access public
+ */
+	var $loginError = null;
+/**
+ * Error to display when user attempts to access an object or action to which they do not have
+ * acccess.
+ *
+ * @var string
+ * @access public
+ */
+	var $authError = null;
+/**
+ * Determines whether AuthComponent will automatically redirect and exit if login is successful.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoRedirect = true;
+/**
+ * Controller actions for which user validation is not required.
+ *
+ * @var array
+ * @access public
+ * @see AuthComponent::allow()
+ */
+	var $allowedActions = array();
+/**
+ * Maps actions to CRUD operations.  Used for controller-based validation ($validate = 'controller').
+ *
+ * @var array
+ * @access public
+ * @see AuthComponent::mapActions()
+ */
+	var $actionMap = array(
+		'index'		=> 'read',
+		'add'		=> 'create',
+		'edit'		=> 'update',
+		'view'		=> 'read',
+		'remove'	=> 'delete'
+	);
+/**
+ * Form data from Controller::$data
+ *
+ * @var array
+ * @access public
+ */
+	var $data = array();
+/**
+ * Parameter data from Controller::$params
+ *
+ * @var array
+ * @access public
+ */
+	var $params = array();
+/**
+ * Method list for bound controller
+ *
+ * @var array
+ * @access protected
+ */
+	var $_methods = array();
+/**
+ * Initializes AuthComponent for use in the controller
+ *
+ * @param object $controller A reference to the instantiating controller object
+ * @return void
+ * @access public
+ */
+	function initialize(&$controller) {
+		$this->params = $controller->params;
+		$crud = array('create', 'read', 'update', 'delete');
+		$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
+		$this->_methods = $controller->methods;
+
+		$admin = Configure::read('Routing.admin');
+		if (!empty($admin)) {
+			$this->actionMap = array_merge($this->actionMap, array(
+				$admin . '_index'	=> 'read',
+				$admin . '_add'		=> 'create',
+				$admin . '_edit'	=> 'update',
+				$admin . '_view'	=> 'read',
+				$admin . '_remove'	=> 'delete',
+				$admin . '_create'	=> 'create',
+				$admin . '_read'	=> 'read',
+				$admin . '_update'	=> 'update',
+				$admin . '_delete'	=> 'delete'
+			));
+		}
+		if (Configure::read() > 0) {
+			App::import('Debugger');
+			Debugger::checkSessionKey();
+		}
+	}
+/**
+ * Main execution method.  Handles redirecting of invalid users, and processing
+ * of login form data.
+ *
+ * @param object $controller A reference to the instantiating controller object
+ * @return boolean
+ * @access public
+ */
+	function startup(&$controller) {
+		$isErrorOrTests = (
+			strtolower($controller->name) == 'cakeerror' ||
+			(strtolower($controller->name) == 'tests' && Configure::read() > 0) ||
+			!in_array($controller->params['action'], $controller->methods)
+		);
+		if ($isErrorOrTests) {
+			return true;
+		}
+		if (!$this->__setDefaults()) {
+			return false;
+		}
+
+		$this->data = $controller->data = $this->hashPasswords($controller->data);
+		$url = '';
+
+		if (isset($controller->params['url']['url'])) {
+			$url = $controller->params['url']['url'];
+		}
+		$url = Router::normalize($url);
+		$loginAction = Router::normalize($this->loginAction);
+		$isAllowed = (
+			$this->allowedActions == array('*') ||
+			in_array($controller->params['action'], $this->allowedActions)
+		);
+
+		if ($loginAction != $url && $isAllowed) {
+			return true;
+		}
+
+		if ($loginAction == $url) {
+			if (empty($controller->data) || !isset($controller->data[$this->userModel])) {
+				if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) {
+					$this->Session->write('Auth.redirect', $controller->referer(null, true));
+				}
+				return false;
+			}
+
+			$isValid = !empty($controller->data[$this->userModel][$this->fields['username']]) &&
+				!empty($controller->data[$this->userModel][$this->fields['password']]);
+
+			if ($isValid) {
+				$username = $controller->data[$this->userModel][$this->fields['username']];
+				$password = $controller->data[$this->userModel][$this->fields['password']];
+
+				$data = array(
+					$this->userModel . '.' . $this->fields['username'] => $username,
+					$this->userModel . '.' . $this->fields['password'] => $password
+				);
+
+				if ($this->login($data)) {
+					if ($this->autoRedirect) {
+						$controller->redirect($this->redirect(), null, true);
+					}
+					return true;
+				}
+			}
+
+			$this->Session->setFlash($this->loginError, 'default', array(), 'auth');
+			$controller->data[$this->userModel][$this->fields['password']] = null;
+			return false;
+		} else {
+			if (!$this->user()) {
+				if (!$this->RequestHandler->isAjax()) {
+					$this->Session->setFlash($this->authError, 'default', array(), 'auth');
+					$this->Session->write('Auth.redirect', $url);
+					$controller->redirect($loginAction);
+					return false;
+				} elseif (!empty($this->ajaxLogin)) {
+					$controller->viewPath = 'elements';
+					echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
+					$this->_stop();
+					return false;
+				} else {
+					$controller->redirect(null, 403);
+				}
+			}
+		}
+
+		if (!$this->authorize) {
+			return true;
+		}
+
+		extract($this->__authType());
+		switch ($type) {
+			case 'controller':
+				$this->object =& $controller;
+			break;
+			case 'crud':
+			case 'actions':
+				if (isset($controller->Acl)) {
+					$this->Acl =& $controller->Acl;
+				} else {
+					$err = 'Could not find AclComponent. Please include Acl in ';
+					$err .= 'Controller::$components.';
+					trigger_error(__($err, true), E_USER_WARNING);
+				}
+			break;
+			case 'model':
+				if (!isset($object)) {
+					$hasModel = (
+						isset($controller->{$controller->modelClass}) &&
+						is_object($controller->{$controller->modelClass})
+					);
+					$isUses = (
+						!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
+						is_object($controller->{$controller->uses[0]})
+					);
+
+					if ($hasModel) {
+						$object = $controller->modelClass;
+					} elseif ($isUses) {
+						$object = $controller->uses[0];
+					}
+				}
+				$type = array('model' => $object);
+			break;
+		}
+
+		if ($this->isAuthorized($type)) {
+			return true;
+		}
+
+		$this->Session->setFlash($this->authError, 'default', array(), 'auth');
+		$controller->redirect($controller->referer(), null, true);
+		return false;
+	}
+/**
+ * Attempts to introspect the correct values for object properties including
+ * $userModel and $sessionKey.
+ *
+ * @param object $controller A reference to the instantiating controller object
+ * @return boolean
+ * @access private
+ */
+	function __setDefaults() {
+		if (empty($this->userModel)) {
+			trigger_error(__("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter().", true), E_USER_WARNING);
+			return false;
+		}
+		$defaults = array(
+			'loginAction' => Router::normalize(array(
+				'controller'=> Inflector::underscore(Inflector::pluralize($this->userModel)),
+				'action' => 'login'
+			)),
+			'sessionKey' => 'Auth.' . $this->userModel,
+			'logoutRedirect' => $this->loginAction,
+			'loginError' => __('Login failed. Invalid username or password.', true),
+			'authError' => __('You are not authorized to access that location.', true)
+		);
+		foreach ($defaults as $key => $value) {
+			if (empty($this->{$key})) {
+				$this->{$key} = $value;
+			}
+		}
+		return true;
+	}
+/**
+ * Determines whether the given user is authorized to perform an action.  The type of
+ * authorization used is based on the value of AuthComponent::$authorize or the
+ * passed $type param.
+ *
+ * Types:
+ * 'controller' will validate against Controller::isAuthorized() if controller instance is
+ * 				passed in $object
+ * 'actions' will validate Controller::action against an AclComponent::check()
+ * 'crud' will validate mapActions against an AclComponent::check()
+ * 		array('model'=> 'name'); will validate mapActions against model
+ * 		$name::isAuthorized(user, controller, mapAction)
+ * 'object' will validate Controller::action against
+ * 		object::isAuthorized(user, controller, action)
+ *
+ * @param string $type Type of authorization
+ * @param mixed $object object, model object, or model name
+ * @param mixed $user The user to check the authorization of
+ * @return boolean True if $user is authorized, otherwise false
+ * @access public
+ */
+	function isAuthorized($type = null, $object = null, $user = null) {
+		if (empty($user) && !$this->user()) {
+			return false;
+		} elseif (empty($user)) {
+			$user = $this->user();
+		}
+
+		extract($this->__authType($type));
+
+		if (!$object) {
+			$object = $this->object;
+		}
+
+		$valid = false;
+		switch ($type) {
+			case 'controller':
+				$valid = $object->isAuthorized();
+			break;
+			case 'actions':
+				$valid = $this->Acl->check($user, $this->action());
+			break;
+			case 'crud':
+				$this->mapActions();
+				if (!isset($this->actionMap[$this->params['action']])) {
+					$err = 'Auth::startup() - Attempted access of un-mapped action "%1$s" in';
+					$err .= ' controller "%2$s"';
+					trigger_error(
+						sprintf(__($err, true), $this->params['action'], $this->params['controller']),
+						E_USER_WARNING
+					);
+				} else {
+					$valid = $this->Acl->check(
+						$user,
+						$this->action(':controller'),
+						$this->actionMap[$this->params['action']]
+					);
+				}
+			break;
+			case 'model':
+				$this->mapActions();
+				$action = $this->params['action'];
+				if (isset($this->actionMap[$action])) {
+					$action = $this->actionMap[$action];
+				}
+				if (is_string($object)) {
+					$object = $this->getModel($object);
+				}
+			case 'object':
+				if (!isset($action)) {
+					$action = $this->action(':action');
+				}
+				if (empty($object)) {
+					trigger_error(sprintf(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', true), get_class($object)), E_USER_WARNING);
+					return;
+				}
+				if (method_exists($object, 'isAuthorized')) {
+					$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
+				} elseif ($object) {
+					trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), get_class($object)), E_USER_WARNING);
+				}
+			break;
+			case null:
+			case false:
+				return true;
+			break;
+			default:
+				trigger_error(__('Auth::isAuthorized() - $authorize is set to an incorrect value.  Allowed settings are: "actions", "crud", "model" or null.', true), E_USER_WARNING);
+			break;
+		}
+		return $valid;
+	}
+/**
+ * Get authorization type
+ *
+ * @param string $auth Type of authorization
+ * @return array Associative array with: type, object
+ * @access private
+ */
+	function __authType($auth = null) {
+		if ($auth == null) {
+			$auth = $this->authorize;
+		}
+		$object = null;
+		if (is_array($auth)) {
+			$type = key($auth);
+			$object = $auth[$type];
+		} else {
+			$type = $auth;
+			return compact('type');
+		}
+		return compact('type', 'object');
+	}
+/**
+ * Takes a list of actions in the current controller for which authentication is not required, or
+ * no parameters to allow all actions.
+ *
+ * @param string $action Controller action name
+ * @param string $action Controller action name
+ * @param string ... etc.
+ * @return void
+ * @access public
+ */
+	function allow() {
+		$args = func_get_args();
+		if (empty($args) || $args == array('*')) {
+			$this->allowedActions = $this->_methods;
+		} else {
+			if (isset($args[0]) && is_array($args[0])) {
+				$args = $args[0];
+			}
+			$this->allowedActions = array_merge($this->allowedActions, $args);
+		}
+	}
+/**
+ * Removes items from the list of allowed actions.
+ *
+ * @param string $action Controller action name
+ * @param string $action Controller action name
+ * @param string ... etc.
+ * @return void
+ * @see AuthComponent::allow()
+ * @access public
+ */
+	function deny() {
+		$args = func_get_args();
+		foreach ($args as $arg) {
+			$i = array_search($arg, $this->allowedActions);
+			if (is_int($i)) {
+				unset($this->allowedActions[$i]);
+			}
+		}
+		$this->allowedActions = array_values($this->allowedActions);
+	}
+/**
+ * Maps action names to CRUD operations. Used for controller-based authentication.
+ *
+ * @param array $map Actions to map
+ * @return void
+ * @access public
+ */
+	function mapActions($map = array()) {
+		$crud = array('create', 'read', 'update', 'delete');
+		foreach ($map as $action => $type) {
+			if (in_array($action, $crud) && is_array($type)) {
+				foreach ($type as $typedAction) {
+					$this->actionMap[$typedAction] = $action;
+				}
+			} else {
+				$this->actionMap[$action] = $type;
+			}
+		}
+	}
+/**
+ * Manually log-in a user with the given parameter data.  The $data provided can be any data
+ * structure used to identify a user in AuthComponent::identify().  If $data is empty or not
+ * specified, POST data from Controller::$data will be used automatically.
+ *
+ * After (if) login is successful, the user record is written to the session key specified in
+ * AuthComponent::$sessionKey.
+ *
+ * @param mixed $data User object
+ * @return boolean True on login success, false on failure
+ * @access public
+ */
+	function login($data = null) {
+		$this->__setDefaults();
+		$this->_loggedIn = false;
+
+		if (empty($data)) {
+			$data = $this->data;
+		}
+
+		if ($user = $this->identify($data)) {
+			$this->Session->write($this->sessionKey, $user);
+			$this->_loggedIn = true;
+		}
+		return $this->_loggedIn;
+	}
+/**
+ * Logs a user out, and returns the login action to redirect to.
+ *
+ * @param mixed $url Optional URL to redirect the user to after logout
+ * @return string AuthComponent::$loginAction
+ * @see AuthComponent::$loginAction
+ * @access public
+ */
+	function logout() {
+		$this->__setDefaults();
+		$this->Session->del($this->sessionKey);
+		$this->Session->del('Auth.redirect');
+		$this->_loggedIn = false;
+		return Router::normalize($this->logoutRedirect);
+	}
+/**
+ * Get the current user from the session.
+ *
+ * @param string $key field to retrive.  Leave null to get entire User record
+ * @return mixed User record. or null if no user is logged in.
+ * @access public
+ */
+	function user($key = null) {
+		$this->__setDefaults();
+		if (!$this->Session->check($this->sessionKey)) {
+			return null;
+		}
+
+		if ($key == null) {
+			return array($this->userModel => $this->Session->read($this->sessionKey));
+		} else {
+			$user = $this->Session->read($this->sessionKey);
+			if (isset($user[$key])) {
+				return $user[$key];
+			}
+			return null;
+		}
+	}
+/**
+ * If no parameter is passed, gets the authentication redirect URL.
+ *
+ * @param mixed $url Optional URL to write as the login redirect URL.
+ * @return string Redirect URL
+ * @access public
+ */
+	function redirect($url = null) {
+		if (!is_null($url)) {
+			$redir = $url;
+			$this->Session->write('Auth.redirect', $redir);
+		} elseif ($this->Session->check('Auth.redirect')) {
+			$redir = $this->Session->read('Auth.redirect');
+			$this->Session->delete('Auth.redirect');
+
+			if (Router::normalize($redir) == Router::normalize($this->loginAction)) {
+				$redir = $this->loginRedirect;
+			}
+		} else {
+			$redir = $this->loginRedirect;
+		}
+		return Router::normalize($redir);
+	}
+/**
+ * Validates a user against an abstract object.
+ *
+ * @param mixed $object  The object to validate the user against.
+ * @param mixed $user    Optional.  The identity of the user to be validated.
+ *                       Uses the current user session if none specified.  For
+ *                       valid forms of identifying users, see
+ *                       AuthComponent::identify().
+ * @param string $action Optional. The action to validate against.
+ * @see AuthComponent::identify()
+ * @return boolean True if the user validates, false otherwise.
+ * @access public
+ */
+	function validate($object, $user = null, $action = null) {
+		if (empty($user)) {
+			$user = $this->user();
+		}
+		if (empty($user)) {
+			return false;
+		}
+		return $this->Acl->check($user, $object, $action);
+	}
+/**
+ * Returns the path to the ACO node bound to a controller/action.
+ *
+ * @param string $action  Optional.  The controller/action path to validate the
+ *                        user against.  The current request action is used if
+ *                        none is specified.
+ * @return boolean ACO node path
+ * @access public
+ */
+	function action($action = ':controller/:action') {
+		return str_replace(
+			array(':controller', ':action'),
+			array(Inflector::camelize($this->params['controller']), $this->params['action']),
+			$this->actionPath . $action
+		);
+	}
+/**
+ * Returns a reference to the model object specified, and attempts
+ * to load it if it is not found.
+ *
+ * @param string $name Model name (defaults to AuthComponent::$userModel)
+ * @return object A reference to a model object
+ * @access public
+ */
+	function &getModel($name = null) {
+		$model = null;
+		if (!$name) {
+			$name = $this->userModel;
+		}
+
+		if (PHP5) {
+			$model = ClassRegistry::init($name);
+		} else {
+			$model =& ClassRegistry::init($name);
+		}
+
+		if (empty($model)) {
+			trigger_error(__('Auth::getModel() - Model is not set or could not be found', true), E_USER_WARNING);
+			return null;
+		}
+
+		return $model;
+	}
+/**
+ * Identifies a user based on specific criteria.
+ *
+ * @param mixed $user Optional. The identity of the user to be validated.
+ *              Uses the current user session if none specified.
+ * @param array $conditions Optional. Additional conditions to a find.
+ * @return array User record data, or null, if the user could not be identified.
+ * @access public
+ */
+	function identify($user = null, $conditions = null) {
+		if ($conditions === false) {
+			$conditions = null;
+		} elseif (is_array($conditions)) {
+			$conditions = array_merge((array)$this->userScope, $conditions);
+		} else {
+			$conditions = $this->userScope;
+		}
+		if (empty($user)) {
+			$user = $this->user();
+			if (empty($user)) {
+				return null;
+			}
+		} elseif (is_object($user) && is_a($user, 'Model')) {
+			if (!$user->exists()) {
+				return null;
+			}
+			$user = $user->read();
+			$user = $user[$this->userModel];
+		} elseif (is_array($user) && isset($user[$this->userModel])) {
+			$user = $user[$this->userModel];
+		}
+
+		if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$this->userModel . '.' . $this->fields['username']]))) {
+
+			if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']])  && !empty($user[$this->fields['password']])) {
+				if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
+					return false;
+				}
+				$find = array(
+					$this->userModel.'.'.$this->fields['username'] => $user[$this->fields['username']],
+					$this->userModel.'.'.$this->fields['password'] => $user[$this->fields['password']]
+				);
+			} elseif (isset($user[$this->userModel . '.' . $this->fields['username']]) && !empty($user[$this->userModel . '.' . $this->fields['username']])) {
+				if (trim($user[$this->userModel . '.' . $this->fields['username']]) == '=' || trim($user[$this->userModel . '.' . $this->fields['password']]) == '=') {
+					return false;
+				}
+				$find = array(
+					$this->userModel.'.'.$this->fields['username'] => $user[$this->userModel . '.' . $this->fields['username']],
+					$this->userModel.'.'.$this->fields['password'] => $user[$this->userModel . '.' . $this->fields['password']]
+				);
+			} else {
+				return false;
+			}
+			$model =& $this->getModel();
+			$data = $model->find(array_merge($find, $conditions), null, null, 0);
+			if (empty($data) || empty($data[$this->userModel])) {
+				return null;
+			}
+		} elseif (!empty($user) && is_string($user)) {
+			$model =& $this->getModel();
+			$data = $model->find(array_merge(array($model->escapeField() => $user), $conditions));
+
+			if (empty($data) || empty($data[$this->userModel])) {
+				return null;
+			}
+		}
+
+		if (!empty($data)) {
+			if (!empty($data[$this->userModel][$this->fields['password']])) {
+				unset($data[$this->userModel][$this->fields['password']]);
+			}
+			return $data[$this->userModel];
+		}
+		return null;
+	}
+/**
+ * Hash any passwords found in $data using $userModel and $fields['password']
+ *
+ * @param array $data Set of data to look for passwords
+ * @return array Data with passwords hashed
+ * @access public
+ */
+	function hashPasswords($data) {
+		if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
+			return $this->authenticate->hashPasswords($data);
+		}
+
+		if (is_array($data) && isset($data[$this->userModel])) {
+			if (isset($data[$this->userModel][$this->fields['username']]) && isset($data[$this->userModel][$this->fields['password']])) {
+				$data[$this->userModel][$this->fields['password']] = $this->password($data[$this->userModel][$this->fields['password']]);
+			}
+		}
+		return $data;
+	}
+/**
+ * Hash a password with the application's salt value (as defined with Configure::write('Security.salt');
+ *
+ * @param string $password Password to hash
+ * @return string Hashed password
+ * @access public
+ */
+	function password($password) {
+		return Security::hash($password, null, true);
+	}
+/**
+ * Component shutdown.  If user is logged in, wipe out redirect.
+ *
+ * @param object $controller Instantiating controller
+ * @access public
+ */
+	function shutdown(&$controller) {
+		if ($this->_loggedIn) {
+			$this->Session->del('Auth.redirect');
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/components/session.php
===================================================================
--- cake/libs/controller/components/session.php	(revision 0)
+++ cake/libs/controller/components/session.php	(revision 0)
@@ -0,0 +1,315 @@
+<?php
+/* SVN FILE: $Id: session.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1232
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('cakesession')) {
+	require LIBS . 'session.php';
+}
+/**
+ * Session Component.
+ *
+ * Session handling from the controller.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ *
+ */
+class SessionComponent extends CakeSession {
+/**
+ * Used to determine if methods implementation is used, or bypassed
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__active = true;
+/**
+ * Used to determine if Session has been started
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__started = false;
+/**
+ * Used to determine if request are from an Ajax request
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__bare = 0;
+/**
+ * Class constructor
+ *
+ * @param string $base The base path for the Session
+ */
+	function __construct($base = null) {
+		if (Configure::read('Session.start') === true) {
+			parent::__construct($base);
+		} else {
+			$this->__active = false;
+		}
+	}
+/**
+ * Initializes the component, gets a reference to Controller::$param['bare'].
+ *
+ * @param object $controller A reference to the controller
+ * @return void
+ * @access public
+ */
+	function initialize(&$controller) {
+		if (isset($controller->params['bare'])) {
+			$this->__bare = $controller->params['bare'];
+		}
+	}
+/**
+ * Startup method.
+ *
+ * @param object $controller Instantiating controller
+ * @return void
+ * @access public
+ */
+	function startup(&$controller) {
+		if ($this->__started === false && $this->__active === true) {
+			$this->__start();
+		}
+	}
+/**
+ * Starts Session on if 'Session.start' is set to false in core.php
+ *
+ * @param string $base The base path for the Session
+ * @return void
+ * @access public
+ */
+	function activate($base = null) {
+		if ($this->__active === true) {
+			return;
+		}
+		parent::__construct($base);
+		$this->__active = true;
+	}
+/**
+ * Used to write a value to a session key.
+ *
+ * In your controller: $this->Session->write('Controller.sessKey', 'session value');
+ *
+ * @param string $name The name of the key your are setting in the session.
+ * 							This should be in a Controller.key format for better organizing
+ * @param string $value The value you want to store in a session.
+ * @return boolean Success
+ * @access public
+ */
+	function write($name, $value = null) {
+		if ($this->__active === true) {
+			$this->__start();
+			if (is_array($name)) {
+				foreach ($name as $key => $value) {
+					if (parent::write($key, $value) === false) {
+						return false;
+					}
+				}
+				return true;
+			}
+			if (parent::write($name, $value) === false) {
+				return false;
+			}
+			return true;
+		}
+		return false;
+	}
+/**
+ * Used to read a session values for a key or return values for all keys.
+ *
+ * In your controller: $this->Session->read('Controller.sessKey');
+ * Calling the method without a param will return all session vars
+ *
+ * @param string $name the name of the session key you want to read
+ * @return mixed value from the session vars
+ * @access public
+ */
+	function read($name = null) {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::read($name);
+		}
+		return false;
+	}
+/**
+ * Used to delete a session variable.
+ *
+ * In your controller: $this->Session->del('Controller.sessKey');
+ *
+ * @param string $name the name of the session key you want to delete
+ * @return boolean true is session variable is set and can be deleted, false is variable was not set.
+ * @access public
+ */
+	function del($name) {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::del($name);
+		}
+		return false;
+	}
+/**
+ * Wrapper for SessionComponent::del();
+ *
+ * In your controller: $this->Session->delete('Controller.sessKey');
+ *
+ * @param string $name the name of the session key you want to delete
+ * @return boolean true is session variable is set and can be deleted, false is variable was not set.
+ * @access public
+ */
+	function delete($name) {
+		if ($this->__active === true) {
+			$this->__start();
+			return $this->del($name);
+		}
+		return false;
+	}
+/**
+ * Used to check if a session variable is set
+ *
+ * In your controller: $this->Session->check('Controller.sessKey');
+ *
+ * @param string $name the name of the session key you want to check
+ * @return boolean true is session variable is set, false if not
+ * @access public
+ */
+	function check($name) {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::check($name);
+		}
+		return false;
+	}
+/**
+ * Used to determine the last error in a session.
+ *
+ * In your controller: $this->Session->error();
+ *
+ * @return string Last session error
+ * @access public
+ */
+	function error() {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::error();
+		}
+		return false;
+	}
+/**
+ * Used to set a session variable that can be used to output messages in the view.
+ *
+ * In your controller: $this->Session->setFlash('This has been saved');
+ *
+ * Additional params below can be passed to customize the output, or the Message.[key]
+ *
+ * @param string $message Message to be flashed
+ * @param string $layout Layout to wrap flash message in
+ * @param array $params Parameters to be sent to layout as view variables
+ * @param string $key Message key, default is 'flash'
+ * @access public
+ */
+	function setFlash($message, $layout = 'default', $params = array(), $key = 'flash') {
+		if ($this->__active === true) {
+			$this->__start();
+			$this->write('Message.' . $key, compact('message', 'layout', 'params'));
+		}
+	}
+/**
+ * Used to renew a session id
+ *
+ * In your controller: $this->Session->renew();
+ *
+ * @return void
+ * @access public
+ */
+	function renew() {
+		if ($this->__active === true) {
+			$this->__start();
+			parent::renew();
+		}
+	}
+/**
+ * Used to check for a valid session.
+ *
+ * In your controller: $this->Session->valid();
+ *
+ * @return boolean true is session is valid, false is session is invalid
+ * @access public
+ */
+	function valid() {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::valid();
+		}
+		return false;
+	}
+/**
+ * Used to destroy sessions
+ *
+ * In your controller: $this->Session->destroy();
+ *
+ * @return void
+ * @access public
+ */
+	function destroy() {
+		if ($this->__active === true) {
+			$this->__start();
+			parent::destroy();
+		}
+	}
+/**
+ * Returns Session id
+ *
+ * If $id is passed in a beforeFilter, the Session will be started
+ * with the specified id
+ *
+ * @param $id string
+ * @return string
+ * @access public
+ */
+	function id($id = null) {
+		return parent::id($id);
+	}
+/**
+ * Starts Session if SessionComponent is used in Controller::beforeFilter(),
+ * or is called from
+ *
+ * @return boolean
+ * @access private
+ */
+	function __start() {
+		if ($this->__started === false) {
+			if (!$this->id() && parent::start()) {
+				$this->__started = true;
+				parent::_checkValid();
+			} else {
+				$this->__started = parent::start();
+			}
+		}
+		return $this->__started;
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/controller/components/request_handler.php
===================================================================
--- cake/libs/controller/components/request_handler.php	(revision 0)
+++ cake/libs/controller/components/request_handler.php	(revision 0)
@@ -0,0 +1,736 @@
+<?php
+/* SVN FILE: $Id: request_handler.php 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ * Request object for handling alternative HTTP requests
+ *
+ * Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
+ * and the like.  These units have no use for Ajax requests, and this Component can tell how Cake
+ * should respond to the different needs of a handheld computer and a desktop machine.
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.4.1076
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+if (!defined('REQUEST_MOBILE_UA')) {
+	define('REQUEST_MOBILE_UA', '(iPhone|MIDP|AvantGo|BlackBerry|J2ME|Opera Mini|DoCoMo|NetFront|Nokia|PalmOS|PalmSource|portalmmm|Plucker|ReqwirelessWeb|SonyEricsson|Symbian|UP\.Browser|Windows CE|Xiino)');
+}
+
+/**
+ * Request object for handling HTTP requests
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ *
+ */
+class RequestHandlerComponent extends Object {
+/**
+ * The layout that will be switched to for Ajax requests
+ *
+ * @var string
+ * @access public
+ * @see RequestHandler::setAjax()
+ */
+	var $ajaxLayout = 'ajax';
+/**
+ * Determines whether or not callbacks will be fired on this component
+ *
+ * @var boolean
+ * @access public
+ */
+	var $enabled = true;
+/**
+ * Holds the content-type of the response that is set when using
+ * RequestHandler::respondAs()
+ *
+ * @var string
+ * @access private
+ */
+	var $__responseTypeSet = null;
+/**
+ * Holds the copy of Controller::$params
+ *
+ * @var array
+ * @access public
+ */
+	var $params = array();
+/**
+ * Friendly content-type mappings used to set response types and determine
+ * request types.  Can be modified with RequestHandler::setContent()
+ *
+ * @var array
+ * @access private
+ * @see RequestHandlerComponent::setContent
+ */
+	var $__requestContent = array(
+		'javascript'	=> 'text/javascript',
+		'js'			=> 'text/javascript',
+		'json'			=> 'application/json',
+		'css'			=> 'text/css',
+		'html'			=> array('text/html', '*/*'),
+		'text'			=> 'text/plain',
+		'txt'			=> 'text/plain',
+		'csv'			=> array('application/vnd.ms-excel', 'text/plain'),
+		'form'			=> 'application/x-www-form-urlencoded',
+		'file'			=> 'multipart/form-data',
+		'xhtml'			=> array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
+		'xhtml-mobile'	=> 'application/vnd.wap.xhtml+xml',
+		'xml'			=> array('application/xml', 'text/xml'),
+		'rss'			=> 'application/rss+xml',
+		'atom'			=> 'application/atom+xml',
+		'amf'			=> 'application/x-amf',
+		'wap'			=> array(
+			'text/vnd.wap.wml',
+			'text/vnd.wap.wmlscript',
+			'image/vnd.wap.wbmp'
+		),
+		'wml'			=> 'text/vnd.wap.wml',
+		'wmlscript'		=> 'text/vnd.wap.wmlscript',
+		'wbmp'			=> 'image/vnd.wap.wbmp',
+		'pdf'			=> 'application/pdf',
+		'zip'			=> 'application/x-zip',
+		'tar'			=> 'application/x-tar'
+	);
+/**
+ * Content-types accepted by the client.  If extension parsing is enabled in the
+ * Router, and an extension is detected, the corresponding content-type will be
+ * used as the overriding primary content-type accepted.
+ *
+ * @var array
+ * @access private
+ * @see Router::parseExtensions()
+ */
+	var $__acceptTypes = array();
+/**
+ * The template to use when rendering the given content type.
+ *
+ * @var string
+ * @access private
+ */
+	var $__renderType = null;
+/**
+ * Contains the file extension parsed out by the Router
+ *
+ * @var string
+ * @access public
+ * @see Router::parseExtensions()
+ */
+	var $ext = null;
+/**
+ * Flag set when MIME types have been initialized
+ *
+ * @var boolean
+ * @access private
+ * @see RequestHandler::__initializeTypes()
+ */
+	var $__typesInitialized = false;
+/**
+ * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
+ *
+ */
+	function __construct() {
+		$this->__acceptTypes = explode(',', env('HTTP_ACCEPT'));
+
+		foreach ($this->__acceptTypes as $i => $type) {
+			if (strpos($type, ';')) {
+				$type = explode(';', $type);
+				$this->__acceptTypes[$i] = $type[0];
+			}
+		}
+		parent::__construct();
+	}
+/**
+ * Initializes the component, gets a reference to Controller::$parameters, and
+ * checks to see if a file extension has been parsed by the Router.  If yes, the
+ * corresponding content-type is pushed onto the list of accepted content-types
+ * as the first item.
+ *
+ * @param object $controller A reference to the controller
+ * @return void
+ * @see Router::parseExtensions()
+ * @access public
+ */
+	function initialize(&$controller) {
+		if (isset($controller->params['url']['ext'])) {
+			$this->ext = $controller->params['url']['ext'];
+		}
+	}
+/**
+ * The startup method of the RequestHandler enables several automatic behaviors
+ * related to the detection of certain properties of the HTTP request, including:
+ *
+ * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header)
+ * - If Router::parseExtensions() is enabled, the layout and template type are
+ *   switched based on the parsed extension.  For example, if controller/action.xml
+ *   is requested, the view path becomes <i>app/views/controller/xml/action.ctp</i>.
+ * - If a helper with the same name as the extension exists, it is added to the controller.
+ * - If the extension is of a type that RequestHandler understands, it will set that
+ *   Content-type in the response header.
+ * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned
+ *   to the $data property of the controller, which can then be saved to a model object.
+ *
+ * @param object $controller A reference to the controller
+ * @return void
+ * @access public
+ */
+	function startup(&$controller) {
+		if (!$this->enabled) {
+			return;
+		}
+
+		$this->__initializeTypes();
+		$controller->params['isAjax'] = $this->isAjax();
+		$isRecognized = (
+			!in_array($this->ext, array('html', 'htm')) &&
+			in_array($this->ext, array_keys($this->__requestContent))
+		);
+
+		if (!empty($this->ext) && $isRecognized) {
+			$this->renderAs($controller, $this->ext);
+		} elseif ($this->isAjax()) {
+			$this->renderAs($controller, 'ajax');
+		}
+
+		if ($this->requestedWith('xml')) {
+			if (!class_exists('XmlNode')) {
+				App::import('Core', 'Xml');
+			}
+			$xml = new Xml(trim(file_get_contents('php://input')));
+
+			if (is_object($xml->child('data')) && count($xml->children) == 1) {
+				$controller->data = $xml->child('data');
+			} else {
+				$controller->data = $xml;
+			}
+		}
+	}
+/**
+ * Handles (fakes) redirects for Ajax requests using requestAction()
+ *
+ * @param object $controller A reference to the controller
+ * @param mixed $url A string or array containing the redirect location
+ * @access public
+ */
+	function beforeRedirect(&$controller, $url) {
+		if (!$this->isAjax()) {
+			return;
+		}
+		foreach ($_POST as $key => $val) {
+			unset($_POST[$key]);
+		}
+		echo $this->requestAction($url, array('return'));
+		$this->_stop();
+	}
+/**
+ * Returns true if the current HTTP request is Ajax, false otherwise
+ *
+ * @return boolean True if call is Ajax
+ * @access public
+ */
+	function isAjax() {
+		return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
+	}
+/**
+ * Returns true if the current HTTP request is coming from a Flash-based client
+ *
+ * @return boolean True if call is from Flash
+ * @access public
+ */
+	function isFlash() {
+		return (preg_match('/^(Shockwave|Adobe) Flash/', env('HTTP_USER_AGENT')) == 1);
+	}
+/**
+ * Returns true if the current request is over HTTPS, false otherwise.
+ *
+ * @return bool True if call is over HTTPS
+ * @access public
+ */
+	function isSSL() {
+		return env('HTTPS');
+	}
+/**
+ * Returns true if the current call accepts an XML response, false otherwise
+ *
+ * @return boolean True if client accepts an XML response
+ * @access public
+ */
+	function isXml() {
+		return $this->prefers('xml');
+	}
+/**
+ * Returns true if the current call accepts an RSS response, false otherwise
+ *
+ * @return boolean True if client accepts an RSS response
+ * @access public
+ */
+	function isRss() {
+		return $this->prefers('rss');
+	}
+/**
+ * Returns true if the current call accepts an Atom response, false otherwise
+ *
+ * @return boolean True if client accepts an RSS response
+ * @access public
+ */
+	function isAtom() {
+		return $this->prefers('atom');
+	}
+/**
+ * Returns true if user agent string matches a mobile web browser, or if the
+ * client accepts WAP content.
+ *
+ * @return boolean True if user agent is a mobile web browser
+ * @access public
+ */
+	function isMobile() {
+		preg_match('/' . REQUEST_MOBILE_UA . '/i', env('HTTP_USER_AGENT'), $match);
+		if (!empty($match) || $this->accepts('wap')) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns true if the client accepts WAP content
+ *
+ * @return bool
+ * @access public
+ */
+	function isWap() {
+		return $this->prefers('wap');
+	}
+/**
+ * Returns true if the current call a POST request
+ *
+ * @return boolean True if call is a POST
+ * @access public
+ */
+	function isPost() {
+		return (strtolower(env('REQUEST_METHOD')) == 'post');
+	}
+/**
+ * Returns true if the current call a PUT request
+ *
+ * @return boolean True if call is a PUT
+ * @access public
+ */
+	function isPut() {
+		return (strtolower(env('REQUEST_METHOD')) == 'put');
+	}
+/**
+ * Returns true if the current call a GET request
+ *
+ * @return boolean True if call is a GET
+ * @access public
+ */
+	function isGet() {
+		return (strtolower(env('REQUEST_METHOD')) == 'get');
+	}
+/**
+ * Returns true if the current call a DELETE request
+ *
+ * @return boolean True if call is a DELETE
+ * @access public
+ */
+	function isDelete() {
+		return (strtolower(env('REQUEST_METHOD')) == 'delete');
+	}
+/**
+ * Gets Prototype version if call is Ajax, otherwise empty string.
+ * The Prototype library sets a special "Prototype version" HTTP header.
+ *
+ * @return string Prototype version of component making Ajax call
+ * @access public
+ */
+	function getAjaxVersion() {
+		if (env('HTTP_X_PROTOTYPE_VERSION') != null) {
+			return env('HTTP_X_PROTOTYPE_VERSION');
+		}
+		return false;
+	}
+/**
+ * Adds/sets the Content-type(s) for the given name.  This method allows
+ * content-types to be mapped to friendly aliases (or extensions), which allows
+ * RequestHandler to automatically respond to requests of that type in the
+ * startup method.
+ *
+ * @param string $name The name of the Content-type, i.e. "html", "xml", "css"
+ * @param mixed $type The Content-type or array of Content-types assigned to the name,
+ *                    i.e. "text/html", or "application/xml"
+ * @return void
+ * @access public
+ */
+	function setContent($name, $type = null) {
+		if (is_array($name)) {
+			$this->__requestContent = array_merge($this->__requestContent, $name);
+			return;
+		}
+		$this->__requestContent[$name] = $type;
+	}
+/**
+ * Gets the server name from which this request was referred
+ *
+ * @return string Server address
+ * @access public
+ */
+	function getReferrer() {
+		if (env('HTTP_HOST') != null) {
+			$sessHost = env('HTTP_HOST');
+		}
+
+		if (env('HTTP_X_FORWARDED_HOST') != null) {
+			$sessHost = env('HTTP_X_FORWARDED_HOST');
+		}
+		return trim(preg_replace('/(?:\:.*)/', '', $sessHost));
+	}
+/**
+ * Gets remote client IP
+ *
+ * @return string Client IP address
+ * @access public
+ */
+	function getClientIP($safe = true) {
+		if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) {
+			$ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
+		} else {
+			if (env('HTTP_CLIENT_IP') != null) {
+				$ipaddr = env('HTTP_CLIENT_IP');
+			} else {
+				$ipaddr = env('REMOTE_ADDR');
+			}
+		}
+
+		if (env('HTTP_CLIENTADDRESS') != null) {
+			$tmpipaddr = env('HTTP_CLIENTADDRESS');
+
+			if (!empty($tmpipaddr)) {
+				$ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
+			}
+		}
+		return trim($ipaddr);
+	}
+/**
+ * Determines which content types the client accepts.  Acceptance is based on
+ * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
+ * header.
+ *
+ * @param mixed $type Can be null (or no parameter), a string type name, or an
+ *					array of types
+ * @return mixed If null or no parameter is passed, returns an array of content
+ *				types the client accepts.  If a string is passed, returns true
+ *				if the client accepts it.  If an array is passed, returns true
+ *				if the client accepts one or more elements in the array.
+ * @access public
+ * @see RequestHandlerComponent::setContent()
+ */
+	function accepts($type = null) {
+		$this->__initializeTypes();
+
+		if ($type == null) {
+			return $this->mapType($this->__acceptTypes);
+
+		} elseif (is_array($type)) {
+			foreach ($type as $t) {
+				if ($this->accepts($t) == true) {
+					return true;
+				}
+			}
+			return false;
+		} elseif (is_string($type)) {
+
+			if (!isset($this->__requestContent[$type])) {
+				return false;
+			}
+
+			$content = $this->__requestContent[$type];
+
+			if (is_array($content)) {
+				foreach ($content as $c) {
+					if (in_array($c, $this->__acceptTypes)) {
+						return true;
+					}
+				}
+			} else {
+				if (in_array($content, $this->__acceptTypes)) {
+					return true;
+				}
+			}
+		}
+	}
+/**
+ * Determines the content type of the data the client has sent (i.e. in a POST request)
+ *
+ * @param mixed $type Can be null (or no parameter), a string type name, or an array of types
+ * @return mixed
+ * @access public
+ */
+	function requestedWith($type = null) {
+		if (!$this->isPost() && !$this->isPut()) {
+			return null;
+		}
+
+		list($contentType) = explode(';', env('CONTENT_TYPE'));
+		if ($type == null) {
+			return $this->mapType($contentType);
+		} elseif (is_array($type)) {
+			foreach ($type as $t) {
+				if ($this->requestedWith($t)) {
+					return $this->mapType($t);
+				}
+			}
+			return false;
+		} elseif (is_string($type)) {
+			return ($type == $this->mapType($contentType));
+		}
+	}
+/**
+ * Determines which content-types the client prefers.  If no parameters are given,
+ * the content-type that the client most likely prefers is returned.  If $type is
+ * an array, the first item in the array that the client accepts is returned.
+ * Preference is determined primarily by the file extension parsed by the Router
+ * if provided, and secondarily by the list of content-types provided in
+ * HTTP_ACCEPT.
+ *
+ * @param mixed $type An optional array of 'friendly' content-type names, i.e.
+ *                     'html', 'xml', 'js', etc.
+ * @return mixed If $type is null or not provided, the first content-type in the
+ *                list, based on preference, is returned.
+ * @access public
+ * @see RequestHandlerComponent::setContent()
+ */
+	function prefers($type = null) {
+		$this->__initializeTypes();
+		$accept = $this->accepts();
+
+		if ($type == null) {
+			if (empty($this->ext)) {
+				if (is_array($accept)) {
+					return $accept[0];
+				}
+				return $accept;
+			}
+			return $this->ext;
+		}
+
+		if (is_string($type)) {
+			$types = array($type);
+		}
+
+		if (count($types) === 1) {
+			if (!empty($this->ext)) {
+				return ($types[0] == $this->ext);
+			}
+			return ($types[0] == $accept[0]);
+		}
+		$accepts = array();
+
+		foreach ($types as $type) {
+			if (in_array($type, $accept)) {
+				$accepts[] = $type;
+			}
+		}
+
+		if (count($accepts) === 0) {
+			return false;
+		} elseif (count($types) === 1) {
+			return ($types[0] === $accepts[0]);
+		} elseif (count($accepts) === 1) {
+            return $accepts[0];
+		}
+
+		$accepts = array_intersect($this->__acceptTypes, $accepts);
+		return $accepts[0];
+	}
+/**
+ * Sets the layout and template paths for the content type defined by $type.
+ *
+ * @param object $controller A reference to a controller object
+ * @param string $type Type of response to send (e.g: 'ajax')
+ * @return void
+ * @access public
+ * @see RequestHandlerComponent::setContent()
+ * @see RequestHandlerComponent::respondAs()
+ */
+	function renderAs(&$controller, $type) {
+		$this->__initializeTypes();
+		$options = array('charset' => 'UTF-8');
+
+		if (Configure::read('App.encoding') !== null) {
+			$options = array('charset' => Configure::read('App.encoding'));
+		}
+
+		if ($type == 'ajax') {
+			$controller->layout = $this->ajaxLayout;
+			return $this->respondAs('html', $options);
+		}
+		$controller->ext = '.ctp';
+
+		if (empty($this->__renderType)) {
+			$controller->viewPath .= '/' . $type;
+		} else {
+			$remove = preg_replace("/(?:\/{$type})$/", '/' . $type, $controller->viewPath);
+			$controller->viewPath = $remove;
+		}
+		$this->__renderType = $type;
+		$controller->layoutPath = $type;
+
+		if (isset($this->__requestContent[$type])) {
+			$this->respondAs($type, $options);
+		}
+
+		$helper = ucfirst($type);
+		$isAdded = (
+			in_array($helper, $controller->helpers) ||
+			array_key_exists($helper, $controller->helpers)
+		);
+
+		if (!$isAdded) {
+			if (App::import('Helper', $helper)) {
+				$controller->helpers[] = $helper;
+			}
+		}
+	}
+/**
+ * Sets the response header based on type map index name.  If DEBUG is greater than 2, the header
+ * is not set.
+ *
+ * @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
+ * 					  like 'application/x-shockwave'.
+ * @param array $options If $type is a friendly type name that is associated with
+ *                     more than one type of content, $index is used to select
+ *                     which content-type to use.
+ * @return boolean Returns false if the friendly type name given in $type does
+ *                 not exist in the type map, or if the Content-type header has
+ *                 already been set by this method.
+ * @access public
+ * @see RequestHandlerComponent::setContent()
+ */
+	function respondAs($type, $options = array()) {
+		$this->__initializeTypes();
+		if ($this->__responseTypeSet != null) {
+			return false;
+		}
+		if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
+			return false;
+		}
+		$defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
+		$options = array_merge($defaults, $options);
+
+		if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
+			$cType = null;
+			if (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][$options['index']])) {
+				$cType = $this->__requestContent[$type][$options['index']];
+			} elseif (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][0])) {
+				$cType = $this->__requestContent[$type][0];
+			} elseif (isset($this->__requestContent[$type])) {
+				$cType = $this->__requestContent[$type];
+			} else {
+				return false;
+			}
+
+			if (is_array($cType)) {
+				if ($this->prefers($cType)) {
+					$cType = $this->prefers($cType);
+				} else {
+					$cType = $cType[0];
+				}
+			}
+		} else {
+			$cType = $type;
+		}
+
+		if ($cType != null) {
+			$header = 'Content-type: ' . $cType;
+
+			if (!empty($options['charset'])) {
+				$header .= '; charset=' . $options['charset'];
+			}
+			if (!empty($options['attachment'])) {
+				header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
+			}
+			if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
+				@header($header);
+			}
+			$this->__responseTypeSet = $cType;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns the current response type (Content-type header), or null if none has been set
+ *
+ * @return mixed A string content type alias, or raw content type if no alias map exists,
+ *               otherwise null
+ * @access public
+ */
+	function responseType() {
+		if ($this->__responseTypeSet == null) {
+			return null;
+		}
+		return $this->mapType($this->__responseTypeSet);
+	}
+/**
+ * Maps a content-type back to an alias
+ *
+ * @param mixed $type Content type
+ * @return mixed Alias
+ * @access public
+ */
+	function mapType($ctype) {
+		if (is_array($ctype)) {
+			$out = array();
+			foreach ($ctype as $t) {
+				$out[] = $this->mapType($t);
+			}
+			return $out;
+		} else {
+			$keys = array_keys($this->__requestContent);
+			$count = count($keys);
+
+			for ($i = 0; $i < $count; $i++) {
+				$name = $keys[$i];
+				$type = $this->__requestContent[$name];
+
+				if (is_array($type) && in_array($ctype, $type)) {
+					return $name;
+				} elseif (!is_array($type) && $type == $ctype) {
+					return $name;
+				}
+			}
+			return $ctype;
+		}
+	}
+/**
+ * Initializes MIME types
+ *
+ * @return void
+ * @access private
+ */
+	function __initializeTypes() {
+		if ($this->__typesInitialized) {
+			return;
+		}
+		if (isset($this->__requestContent[$this->ext])) {
+			$content = $this->__requestContent[$this->ext];
+			if (is_array($content)) {
+				$content = $content[0];
+			}
+			array_unshift($this->__acceptTypes, $content);
+		}
+		$this->__typesInitialized = true;
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/controller/components/email.php
===================================================================
--- cake/libs/controller/components/email.php	(revision 0)
+++ cake/libs/controller/components/email.php	(revision 0)
@@ -0,0 +1,780 @@
+<?php
+/* SVN FILE: $Id: email.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.3467
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * EmailComponent
+ *
+ * This component is used for handling Internet Message Format based
+ * based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ *
+ */
+App::import('Core', 'Multibyte');
+class EmailComponent extends Object{
+/**
+ * Recipient of the email
+ *
+ * @var string
+ * @access public
+ */
+	var $to = null;
+/**
+ * The mail which the email is sent from
+ *
+ * @var string
+ * @access public
+ */
+	var $from = null;
+/**
+ * The email the recipient will reply to
+ *
+ * @var string
+ * @access public
+ */
+	var $replyTo = null;
+/**
+ * The read receipt email
+ *
+ * @var string
+ * @access public
+ */
+	var $readReceipt = null;
+/**
+ * The mail that will be used in case of any errors like
+ * - Remote mailserver down
+ * - Remote user has exceeded his quota
+ * - Unknown user
+ *
+ * @var string
+ * @access public
+ */
+	var $return = null;
+/**
+ * Carbon Copy
+ *
+ * List of email's that should receive a copy of the email.
+ * The Recipient WILL be able to see this list
+ *
+ * @var array
+ * @access public
+ */
+	var $cc = array();
+/**
+ * Blind Carbon Copy
+ *
+ * List of email's that should receive a copy of the email.
+ * The Recipient WILL NOT be able to see this list
+ *
+ * @var array
+ * @access public
+ */
+	var $bcc = array();
+/**
+ * The subject of the email
+ *
+ * @var string
+ * @access public
+ */
+	var $subject = null;
+/**
+ * Associative array of a user defined headers
+ * Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
+ *
+ * @var array
+ * @access public
+ */
+	var $headers = array();
+/**
+ * List of additional headers
+ *
+ * These will NOT be used if you are using safemode and mail()
+ *
+ * @var string
+ * @access public
+ */
+	var $additionalParams = null;
+/**
+ * Layout for the View
+ *
+ * @var string
+ * @access public
+ */
+	var $layout = 'default';
+/**
+ * Template for the view
+ *
+ * @var string
+ * @access public
+ */
+	var $template = null;
+/**
+ * as per RFC2822 Section 2.1.1
+ *
+ * @var integer
+ * @access public
+ */
+	var $lineLength = 70;
+/**
+ * @deprecated see lineLength
+ */
+	var $_lineLength = null;
+/**
+ * What format should the email be sent in
+ *
+ * Supported formats:
+ * - text
+ * - html
+ * - both
+ *
+ * @var string
+ * @access public
+ */
+	var $sendAs = 'text';
+/**
+ * What method should the email be sent by
+ *
+ * Supported methods:
+ * - mail
+ * - smtp
+ * - debug
+ *
+ * @var string
+ * @access public
+ */
+	var $delivery = 'mail';
+/**
+ * charset the email is sent in
+ *
+ * @var string
+ * @access public
+ */
+	var $charset = 'utf-8';
+/**
+ * List of files that should be attached to the email.
+ *
+ * Can be both absolute and relative paths
+ *
+ * @var array
+ * @access public
+ */
+	var $attachments = array();
+/**
+ * What mailer should EmailComponent identify itself as
+ *
+ * @var string
+ * @access public
+ */
+	var $xMailer = 'CakePHP Email Component';
+/**
+ * The list of paths to search if an attachment isnt absolute
+ *
+ * @var array
+ * @access public
+ */
+	var $filePaths = array();
+/**
+ * List of options to use for smtp mail method
+ *
+ * Options is:
+ * - port
+ * - host
+ * - timeout
+ * - username
+ * - password
+ *
+ * @var array
+ * @access public
+ */
+	var $smtpOptions = array(
+		'port'=> 25, 'host' => 'localhost', 'timeout' => 30
+	);
+/**
+ * Placeholder for any errors that might happen with the
+ * smtp mail methods
+ *
+ * @var string
+ * @access public
+ */
+	var $smtpError = null;
+/**
+ * If set to true, the mail method will be auto-set to 'debug'
+ *
+ * @var string
+ * @access protected
+ */
+	var $_debug = false;
+/**
+ * Temporary store of message header lines
+ *
+ * @var array
+ * @access private
+ */
+	var $__header = array();
+/**
+ * If set, boundary to use for multipart mime messages
+ *
+ * @var string
+ * @access private
+ */
+	var $__boundary = null;
+/**
+ * Temporary store of message lines
+ *
+ * @var array
+ * @access private
+ */
+	var $__message = array();
+/**
+ * Variable that holds SMTP connection
+ *
+ * @var resource
+ * @access private
+ */
+	var $__smtpConnection = null;
+/**
+ * Initialize component
+ *
+ * @param object $controller Instantiating controller
+ * @access public
+ */
+	function initialize(&$controller, $settings = array()) {
+		$this->Controller =& $controller;
+		if (Configure::read('App.encoding') !== null) {
+			$this->charset = Configure::read('App.encoding');
+		}
+		$this->_set($settings);
+	}
+/**
+ * Startup component
+ *
+ * @param object $controller Instantiating controller
+ * @access public
+ */
+	function startup(&$controller) {}
+/**
+ * Send an email using the specified content, template and layout
+ *
+ * @param mixed $content Either an array of text lines, or a string with contents
+ * @param string $template Template to use when sending email
+ * @param string $layout Layout to use to enclose email body
+ * @return boolean Success
+ * @access public
+ */
+	function send($content = null, $template = null, $layout = null) {
+		$this->__createHeader();
+
+		if ($template) {
+			$this->template = $template;
+		}
+
+		if ($layout) {
+			$this->layout = $layout;
+		}
+
+		if (is_array($content)) {
+			$content = implode("\n", $content) . "\n";
+		}
+
+		$message = $this->__wrap($content);
+		if ($this->template === null) {
+			$message = $this->__formatMessage($message);
+		} else {
+			$message = $this->__renderTemplate($message);
+		}
+		$message[] = '';
+		$this->__message = $message;
+
+		if (!empty($this->attachments)) {
+			$this->__attachFiles();
+		}
+
+		if (!is_null($this->__boundary)) {
+			$this->__message[] = '';
+			$this->__message[] = '--' . $this->__boundary . '--';
+			$this->__message[] = '';
+		}
+
+		if ($this->_debug) {
+			return $this->__debug();
+		}
+		$__method = '__' . $this->delivery;
+		$sent = $this->$__method();
+
+		$this->__header = array();
+		$this->__message = array();
+
+		return $sent;
+	}
+/**
+ * Reset all EmailComponent internal variables to be able to send out a new email.
+ *
+ * @access public
+ */
+	function reset() {
+		$this->template = null;
+		$this->to = null;
+		$this->from = null;
+		$this->replyTo = null;
+		$this->return = null;
+		$this->cc = array();
+		$this->bcc = array();
+		$this->subject = null;
+		$this->additionalParams = null;
+		$this->__header = array();
+		$this->__boundary = null;
+		$this->__message = array();
+	}
+/**
+ * Render the contents using the current layout and template.
+ *
+ * @param string $content Content to render
+ * @return array Email ready to be sent
+ * @access private
+ */
+	function __renderTemplate($content) {
+		$viewClass = $this->Controller->view;
+
+		if ($viewClass != 'View') {
+			if (strpos($viewClass, '.') !== false) {
+				list($plugin, $viewClass) = explode('.', $viewClass);
+			}
+			$viewClass = $viewClass . 'View';
+			App::import('View', $this->Controller->view);
+		}
+		$View = new $viewClass($this->Controller, false);
+		$View->layout = $this->layout;
+		$msg = array();
+
+		$content = implode("\n", $content);
+
+		if ($this->sendAs === 'both') {
+			$htmlContent = $content;
+			if (!empty($this->attachments)) {
+				$msg[] = '--' . $this->__boundary;
+				$msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
+				$msg[] = '';
+			}
+			$msg[] = '--alt-' . $this->__boundary;
+			$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
+			$msg[] = 'Content-Transfer-Encoding: 7bit';
+			$msg[] = '';
+
+			$content = $View->element('email' . DS . 'text' . DS . $this->template, array('content' => $content), true);
+			$View->layoutPath = 'email' . DS . 'text';
+			$content = explode("\n", str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
+			$msg = array_merge($msg, $content);
+
+			$msg[] = '';
+			$msg[] = '--alt-' . $this->__boundary;
+			$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
+			$msg[] = 'Content-Transfer-Encoding: 7bit';
+			$msg[] = '';
+
+			$htmlContent = $View->element('email' . DS . 'html' . DS . $this->template, array('content' => $htmlContent), true);
+			$View->layoutPath = 'email' . DS . 'html';
+			$htmlContent = explode("\n", str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($htmlContent)));
+			$msg = array_merge($msg, $htmlContent);
+			$msg[] = '';
+			$msg[] = '--alt-' . $this->__boundary . '--';
+			$msg[] = '';
+
+			return $msg;
+		}
+
+		if (!empty($this->attachments)) {
+			if ($this->sendAs === 'html') {
+				$msg[] = '';
+				$msg[] = '--' . $this->__boundary;
+				$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
+				$msg[] = 'Content-Transfer-Encoding: 7bit';
+				$msg[] = '';
+			} else {
+				$msg[] = '--' . $this->__boundary;
+				$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
+				$msg[] = 'Content-Transfer-Encoding: 7bit';
+				$msg[] = '';
+			}
+		}
+
+		$content = $View->element('email' . DS . $this->sendAs . DS . $this->template, array('content' => $content), true);
+		$View->layoutPath = 'email' . DS . $this->sendAs;
+		$content = explode("\n", str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
+		$msg = array_merge($msg, $content);
+
+		return $msg;
+	}
+/**
+ * Create unique boundary identifier
+ *
+ * @access private
+ */
+	function __createBoundary() {
+		$this->__boundary = md5(uniqid(time()));
+	}
+/**
+ * Create emails headers including (but not limited to) from email address, reply to,
+ * bcc and cc.
+ *
+ * @access private
+ */
+	function __createHeader() {
+		if ($this->delivery == 'smtp') {
+			$this->__header[] = 'To: ' . $this->__formatAddress($this->to);
+		}
+		$this->__header[] = 'From: ' . $this->__formatAddress($this->from);
+
+		if (!empty($this->replyTo)) {
+			$this->__header[] = 'Reply-To: ' . $this->__formatAddress($this->replyTo);
+		}
+		if (!empty($this->return)) {
+			$this->__header[] = 'Return-Path: ' . $this->__formatAddress($this->return);
+		}
+		if (!empty($this->readReceipt)) {
+			$this->__header[] = 'Disposition-Notification-To: ' . $this->__formatAddress($this->readReceipt);
+		}
+
+		if (!empty($this->cc)) {
+			$this->__header[] = 'cc: ' .implode(', ', array_map(array($this, '__formatAddress'), $this->cc));
+		}
+
+		if (!empty($this->bcc) && $this->delivery != 'smtp') {
+			$this->__header[] = 'Bcc: ' .implode(', ', array_map(array($this, '__formatAddress'), $this->bcc));
+		}
+		if ($this->delivery == 'smtp') {
+			$this->__header[] = 'Subject: ' . $this->__encode($this->subject);
+		}
+		$this->__header[] = 'X-Mailer: ' . $this->xMailer;
+
+		if (!empty($this->headers)) {
+			foreach ($this->headers as $key => $val) {
+				$this->__header[] = 'X-' . $key . ': ' . $val;
+			}
+		}
+
+		if (!empty($this->attachments)) {
+			$this->__createBoundary();
+			$this->__header[] = 'MIME-Version: 1.0';
+			$this->__header[] = 'Content-Type: multipart/mixed; boundary="' . $this->__boundary . '"';
+			$this->__header[] = 'This part of the E-mail should never be seen. If';
+			$this->__header[] = 'you are reading this, consider upgrading your e-mail';
+			$this->__header[] = 'client to a MIME-compatible client.';
+		} elseif ($this->sendAs === 'text') {
+			$this->__header[] = 'Content-Type: text/plain; charset=' . $this->charset;
+		} elseif ($this->sendAs === 'html') {
+			$this->__header[] = 'Content-Type: text/html; charset=' . $this->charset;
+		} elseif ($this->sendAs === 'both') {
+			$this->__header[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
+			$this->__header[] = '';
+		}
+
+		$this->__header[] = 'Content-Transfer-Encoding: 7bit';
+	}
+/**
+ * Format the message by seeing if it has attachments.
+ *
+ * @param string $message Message to format
+ * @access private
+ */
+	function __formatMessage($message) {
+		if (!empty($this->attachments)) {
+			$prefix = array(
+				'--' . $this->__boundary,
+				'Content-Type: text/plain; charset=' . $this->charset,
+				'Content-Transfer-Encoding: 7bit',
+				''
+			);
+			$message = array_merge($prefix, $message);
+		}
+		return $message;
+	}
+/**
+ * Attach files by adding file contents inside boundaries.
+ *
+ * @access private
+ * @TODO: modify to use the core File class?
+ */
+	function __attachFiles() {
+		$files = array();
+		foreach ($this->attachments as $attachment) {
+			$file = $this->__findFiles($attachment);
+			if (!empty($file)) {
+				$files[] = $file;
+			}
+		}
+
+		foreach ($files as $file) {
+			$handle = fopen($file, 'rb');
+			$data = fread($handle, filesize($file));
+			$data = chunk_split(base64_encode($data)) ;
+			fclose($handle);
+
+			$this->__message[] = '--' . $this->__boundary;
+			$this->__message[] = 'Content-Type: application/octet-stream';
+			$this->__message[] = 'Content-Transfer-Encoding: base64';
+			$this->__message[] = 'Content-Disposition: attachment; filename="' . basename($file) . '"';
+			$this->__message[] = '';
+			$this->__message[] = $data;
+			$this->__message[] = '';
+		}
+	}
+/**
+ * Find the specified attachment in the list of file paths
+ *
+ * @param string $attachment Attachment file name to find
+ * @return string Path to located file
+ * @access private
+ */
+	function __findFiles($attachment) {
+		if (file_exists($attachment)) {
+			return $attachment;
+		}
+		foreach ($this->filePaths as $path) {
+			if (file_exists($path . DS . $attachment)) {
+				$file = $path . DS . $attachment;
+				return $file;
+			}
+		}
+		return null;
+	}
+/**
+ * Wrap the message using EmailComponent::$lineLength
+ *
+ * @param string $message Message to wrap
+ * @return array Wrapped message
+ * @access private
+ */
+	function __wrap($message) {
+		$message = $this->__strip($message, true);
+		$message = str_replace(array("\r\n","\r"), "\n", $message);
+		$lines = explode("\n", $message);
+		$formatted = array();
+
+		if ($this->_lineLength !== null) {
+			trigger_error('_lineLength cannot be accessed please use lineLength', E_USER_WARNING);
+			$this->lineLength = $this->_lineLength;
+		}
+
+		foreach ($lines as $line) {
+			if (substr($line, 0, 1) == '.') {
+				$line = '.' . $line;
+			}
+			$formatted = array_merge($formatted, explode("\n", wordwrap($line, $this->lineLength, "\n", true)));
+		}
+		$formatted[] = '';
+		return $formatted;
+	}
+/**
+ * Encode the specified string using the current charset
+ *
+ * @param string $subject String to encode
+ * @return string Encoded string
+ * @access private
+ */
+	function __encode($subject) {
+		$subject = $this->__strip($subject);
+
+		$nl = "\r\n";
+		if ($this->delivery == 'mail') {
+			$nl = '';
+		}
+		return mb_encode_mimeheader($subject, $this->charset, 'B', $nl);
+	}
+/**
+ * Format a string as an email address
+ *
+ * @param string $string String representing an email address
+ * @return string Email address suitable for email headers or smtp pipe
+ * @access private
+ */
+	function __formatAddress($string, $smtp = false) {
+		if (strpos($string, '<') !== false) {
+			$value = explode('<', $string);
+			if ($smtp) {
+				$string = '<' . $value[1];
+			} else {
+				$string = $this->__encode($value[0]) . ' <' . $value[1];
+			}
+		}
+		return $this->__strip($string);
+	}
+/**
+ * Remove certain elements (such as bcc:, to:, %0a) from given value
+ *
+ * @param string $value Value to strip
+ * @param boolean $message Set to true to indicate main message content
+ * @return string Stripped value
+ * @access private
+ */
+	function __strip($value, $message = false) {
+		$search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:|charset\=|mime-version\:|multipart/mixed|(?:to|b?cc)\:.*';
+		if ($message !== true) {
+			$search .= '|\r|\n';
+		}
+		$search = '#(?:' . $search . ')#i';
+		while (preg_match($search, $value)) {
+			$value = preg_replace($search, '', $value);
+		}
+		return $value;
+	}
+/**
+ * Wrapper for PHP mail function used for sending out emails
+ *
+ * @return bool Success
+ * @access private
+ */
+	function __mail() {
+		$header = implode("\n", $this->__header);
+		$message = implode("\n", $this->__message);
+		if (ini_get('safe_mode')) {
+			return @mail($this->to, $this->__encode($this->subject), $message, $header);
+		}
+		return @mail($this->to, $this->__encode($this->subject), $message, $header, $this->additionalParams);
+	}
+/**
+ * Sends out email via SMTP
+ *
+ * @return bool Success
+ * @access private
+ */
+	function __smtp() {
+		App::import('Core', array('Socket'));
+
+		$this->__smtpConnection =& new CakeSocket(array_merge(array('protocol'=>'smtp'), $this->smtpOptions));
+
+		if (!$this->__smtpConnection->connect()) {
+			$this->smtpError = $this->__smtpConnection->lastError();
+			return false;
+		} elseif (!$this->__smtpSend(null, '220')) {
+			return false;
+		}
+
+		if (!$this->__smtpSend('HELO cake', '250')) {
+			return false;
+		}
+
+		if (isset($this->smtpOptions['username']) && isset($this->smtpOptions['password'])) {
+			$authRequired = $this->__smtpSend('AUTH LOGIN', '334|503');
+			if ($authRequired == '334') {
+				if (!$this->__smtpSend(base64_encode($this->smtpOptions['username']), '334')) {
+					return false;
+				}
+				if (!$this->__smtpSend(base64_encode($this->smtpOptions['password']), '235')) {
+					return false;
+				}
+			} elseif ($authRequired != '503') {
+				return false;
+			}
+		}
+
+		if (!$this->__smtpSend('MAIL FROM: ' . $this->__formatAddress($this->from, true))) {
+			return false;
+		}
+
+		if (!$this->__smtpSend('RCPT TO: ' . $this->__formatAddress($this->to, true))) {
+			return false;
+		}
+
+		foreach ($this->cc as $cc) {
+			if (!$this->__smtpSend('RCPT TO: ' . $this->__formatAddress($cc, true))) {
+				return false;
+			}
+		}
+		foreach ($this->bcc as $bcc) {
+			if (!$this->__smtpSend('RCPT TO: ' . $this->__formatAddress($bcc, true))) {
+				return false;
+			}
+		}
+
+		if (!$this->__smtpSend('DATA', '354')) {
+			return false;
+		}
+
+		$header = implode("\r\n", $this->__header);
+		$message = implode("\r\n", $this->__message);
+		if (!$this->__smtpSend($header . "\r\n\r\n" . $message . "\r\n\r\n\r\n.")) {
+			return false;
+		}
+		$this->__smtpSend('QUIT', false);
+
+		$this->__smtpConnection->disconnect();
+		return true;
+	}
+/**
+ * Private method for sending data to SMTP connection
+ *
+ * @param string $data data to be sent to SMTP server
+ * @param mixed $checkCode code to check for in server response, false to skip
+ * @return bool Success
+ * @access private
+ */
+	function __smtpSend($data, $checkCode = '250') {
+		if (!is_null($data)) {
+			$this->__smtpConnection->write($data . "\r\n");
+		}
+		if ($checkCode !== false) {
+			$response = $this->__smtpConnection->read();
+
+			if (preg_match('/^(' . $checkCode . ')/', $response, $code)) {
+				return $code[0];
+			}
+			$this->smtpError = $response;
+			return false;
+		}
+		return true;
+	}
+/**
+ * Set as controller flash message a debug message showing current settings in component
+ *
+ * @return boolean Success
+ * @access private
+ */
+	function __debug() {
+		$nl = "\n";
+		$header = implode($nl, $this->__header);
+		$message = implode($nl, $this->__message);
+		$fm = '<pre>';
+
+		if ($this->delivery == 'smtp') {
+			$fm .= sprintf('%s %s%s', 'Host:', $this->smtpOptions['host'], $nl);
+			$fm .= sprintf('%s %s%s', 'Port:', $this->smtpOptions['port'], $nl);
+			$fm .= sprintf('%s %s%s', 'Timeout:', $this->smtpOptions['timeout'], $nl);
+		}
+		$fm .= sprintf('%s %s%s', 'To:', $this->to, $nl);
+		$fm .= sprintf('%s %s%s', 'From:', $this->from, $nl);
+		$fm .= sprintf('%s %s%s', 'Subject:', $this->__encode($this->subject), $nl);
+		$fm .= sprintf('%s%3$s%3$s%s', 'Header:', $header, $nl);
+		$fm .= sprintf('%s%3$s%3$s%s', 'Parameters:', $this->additionalParams, $nl);
+		$fm .= sprintf('%s%3$s%3$s%s', 'Message:', $message, $nl);
+		$fm .= '</pre>';
+
+		$this->Controller->Session->setFlash($fm, 'default', null, 'email');
+		return true;
+	}
+
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/components/security.php
===================================================================
--- cake/libs/controller/components/security.php	(revision 0)
+++ cake/libs/controller/components/security.php	(revision 0)
@@ -0,0 +1,692 @@
+<?php
+/* SVN FILE: $Id: security.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.8.2156
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ */
+class SecurityComponent extends Object {
+/**
+ * The controller method that will be called if this request is black-hole'd
+ *
+ * @var string
+ * @access public
+ */
+	var $blackHoleCallback = null;
+/**
+ * List of controller actions for which a POST request is required
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requirePost()
+ */
+	var $requirePost = array();
+/**
+ * List of controller actions for which a GET request is required
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireGet()
+ */
+	var $requireGet = array();
+/**
+ * List of controller actions for which a PUT request is required
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requirePut()
+ */
+	var $requirePut = array();
+/**
+ * List of controller actions for which a DELETE request is required
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireDelete()
+ */
+	var $requireDelete = array();
+/**
+ * List of actions that require an SSL-secured connection
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireSecure()
+ */
+	var $requireSecure = array();
+/**
+ * List of actions that require a valid authentication key
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireAuth()
+ */
+	var $requireAuth = array();
+/**
+ * List of actions that require an HTTP-authenticated login (basic or digest)
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireLogin()
+ */
+	var $requireLogin = array();
+/**
+ * Login options for SecurityComponent::requireLogin()
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireLogin()
+ */
+	var $loginOptions = array('type' => '', 'prompt' => null);
+/**
+ * An associative array of usernames/passwords used for HTTP-authenticated logins.
+ * If using digest authentication, passwords should be MD5-hashed.
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireLogin()
+ */
+	var $loginUsers = array();
+/**
+ * Controllers from which actions of the current controller are allowed to receive
+ * requests.
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireAuth()
+ */
+	var $allowedControllers = array();
+/**
+ * Actions from which actions of the current controller are allowed to receive
+ * requests.
+ *
+ * @var array
+ * @access public
+ * @see SecurityComponent::requireAuth()
+ */
+	var $allowedActions = array();
+/**
+ * Form fields to disable
+ *
+ * @var array
+ * @access public
+ */
+	var $disabledFields = array();
+/**
+ * Whether to validate POST data.  Set to false to disable for data coming from 3rd party
+ * services, etc.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $validatePost = true;
+/**
+ * Other components used by the Security component
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('RequestHandler', 'Session');
+/**
+ * Holds the current action of the controller
+ *
+ * @var string
+ */
+	var $_action = null;
+/**
+ * Component startup. All security checking happens here.
+ *
+ * @param object $controller Instantiating controller
+ * @access public
+ */
+	function startup(&$controller) {
+		$this->_action = strtolower($controller->action);
+		$this->_methodsRequired($controller);
+		$this->_secureRequired($controller);
+		$this->_authRequired($controller);
+		$this->_loginRequired($controller);
+
+		$isPost = ($this->RequestHandler->isPost() || $this->RequestHandler->isPut());
+		$isRequestAction = (
+			!isset($controller->params['requested']) ||
+			$controller->params['requested'] != 1
+		);
+
+		if ($isPost && $isRequestAction && $this->validatePost) {
+			if ($this->_validatePost($controller) === false) {
+				if (!$this->blackHole($controller, 'auth')) {
+					return null;
+				}
+			}
+		}
+		$this->_generateToken($controller);
+	}
+/**
+ * Sets the actions that require a POST request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requirePost() {
+		$args = func_get_args();
+		$this->_requireMethod('Post', $args);
+	}
+/**
+ * Sets the actions that require a GET request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requireGet() {
+		$args = func_get_args();
+		$this->_requireMethod('Get', $args);
+	}
+/**
+ * Sets the actions that require a PUT request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requirePut() {
+		$args = func_get_args();
+		$this->_requireMethod('Put', $args);
+	}
+/**
+ * Sets the actions that require a DELETE request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requireDelete() {
+		$args = func_get_args();
+		$this->_requireMethod('Delete', $args);
+	}
+/**
+ * Sets the actions that require a request that is SSL-secured, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requireSecure() {
+		$args = func_get_args();
+		$this->_requireMethod('Secure', $args);
+	}
+/**
+ * Sets the actions that require an authenticated request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requireAuth() {
+		$args = func_get_args();
+		$this->_requireMethod('Auth', $args);
+	}
+/**
+ * Sets the actions that require an HTTP-authenticated request, or empty for all actions
+ *
+ * @return void
+ * @access public
+ */
+	function requireLogin() {
+		$args = func_get_args();
+		$base = $this->loginOptions;
+
+		foreach ($args as $i => $arg) {
+			if (is_array($arg)) {
+				$this->loginOptions = $arg;
+				unset($args[$i]);
+			}
+		}
+		$this->loginOptions = array_merge($base, $this->loginOptions);
+		$this->_requireMethod('Login', $args);
+
+		if (isset($this->loginOptions['users'])) {
+			$this->loginUsers =& $this->loginOptions['users'];
+		}
+	}
+/**
+ * Attempts to validate the login credentials for an HTTP-authenticated request
+ *
+ * @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
+ * @return mixed If successful, returns an array with login name and password, otherwise null.
+ * @access public
+ */
+	function loginCredentials($type = null) {
+		switch (strtolower($type)) {
+			case 'basic':
+				$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
+				if (!empty($login['username'])) {
+					return $login;
+				}
+			break;
+			case 'digest':
+			default:
+				$digest = null;
+
+				if (version_compare(PHP_VERSION, '5.1') != -1) {
+					$digest = env('PHP_AUTH_DIGEST');
+				} elseif (function_exists('apache_request_headers')) {
+					$headers = apache_request_headers();
+					if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
+						$digest = substr($headers['Authorization'], 7);
+					}
+				} else {
+					// Server doesn't support digest-auth headers
+					trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication', true), E_USER_WARNING);
+				}
+
+				if (!empty($digest)) {
+					return $this->parseDigestAuthData($digest);
+				}
+			break;
+		}
+		return null;
+	}
+/**
+ * Generates the text of an HTTP-authentication request header from an array of options.
+ *
+ * @param array $options Set of options for header
+ * @return string HTTP-authentication request header
+ * @access public
+ */
+	function loginRequest($options = array()) {
+		$options = array_merge($this->loginOptions, $options);
+		$this->_setLoginDefaults($options);
+		$auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
+		$out = array('realm="' . $options['realm'] . '"');
+
+		if (strtolower($options['type']) == 'digest') {
+			$out[] = 'qop="auth"';
+			$out[] = 'nonce="' . uniqid() . '"';
+			$out[] = 'opaque="' . md5($options['realm']).'"';
+		}
+
+		return $auth . ' ' . join(',', $out);
+	}
+/**
+ * Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
+ *
+ * @param string $digest Digest authentication response
+ * @return array Digest authentication parameters
+ * @access public
+ */
+	function parseDigestAuthData($digest) {
+		if (substr($digest, 0, 7) == 'Digest ') {
+			$digest = substr($digest, 7);
+		}
+		$keys = array();
+		$match = array();
+		$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
+		preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $digest, $match, PREG_SET_ORDER);
+
+		foreach ($match as $i) {
+			$keys[$i[1]] = $i[3];
+			unset($req[$i[1]]);
+		}
+
+		if (empty($req)) {
+			return $keys;
+		}
+		return null;
+	}
+/**
+ * Generates a hash to be compared with an HTTP digest-authenticated response
+ *
+ * @param array $data HTTP digest response data, as parsed by SecurityComponent::parseDigestAuthData()
+ * @return string Digest authentication hash
+ * @access public
+ * @see SecurityComponent::parseDigestAuthData()
+ */
+	function generateDigestResponseHash($data) {
+		return md5(
+			md5($data['username'] . ':' . $this->loginOptions['realm'] . ':' . $this->loginUsers[$data['username']]) .
+			':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
+			md5(env('REQUEST_METHOD') . ':' . $data['uri'])
+		);
+	}
+/**
+ * Black-hole an invalid request with a 404 error or custom callback. If SecurityComponent::$blackHoleCallback
+ * is specified, it will use this callback by executing the method indicated in $error
+ *
+ * @param object $controller Instantiating controller
+ * @param string $error Error method
+ * @return mixed If specified, controller blackHoleCallback's response, or no return otherwise
+ * @access public
+ * @see SecurityComponent::$blackHoleCallback
+ */
+	function blackHole(&$controller, $error = '') {
+		$this->Session->del('_Token');
+
+		if ($this->blackHoleCallback == null) {
+			$code = 404;
+			if ($error == 'login') {
+				$code = 401;
+				$controller->header($this->loginRequest());
+			}
+			$controller->redirect(null, $code, true);
+		} else {
+			return $this->_callback($controller, $this->blackHoleCallback, array($error));
+		}
+	}
+/**
+ * Sets the actions that require a $method HTTP request, or empty for all actions
+ *
+ * @param string $method The HTTP method to assign controller actions to
+ * @param array $actions Controller actions to set the required HTTP method to.
+ * @return void
+ * @access protected
+ */
+	function _requireMethod($method, $actions = array()) {
+		$this->{'require' . $method} = (empty($actions)) ? array('*'): $actions;
+	}
+/**
+ * Check if HTTP methods are required
+ *
+ * @param object $controller Instantiating controller
+ * @return bool true if $method is required
+ * @access protected
+ */
+	function _methodsRequired(&$controller) {
+		foreach (array('Post', 'Get', 'Put', 'Delete') as $method) {
+			$property = 'require' . $method;
+			if (is_array($this->$property) && !empty($this->$property)) {
+				$require = array_map('strtolower', $this->$property);
+
+				if (in_array($this->_action, $require) || $this->$property == array('*')) {
+					if (!$this->RequestHandler->{'is' . $method}()) {
+						if (!$this->blackHole($controller, strtolower($method))) {
+							return null;
+						}
+					}
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Check if access requires secure connection
+ *
+ * @param object $controller Instantiating controller
+ * @return bool true if secure connection required
+ * @access protected
+ */
+	function _secureRequired(&$controller) {
+		if (is_array($this->requireSecure) && !empty($this->requireSecure)) {
+			$requireSecure = array_map('strtolower', $this->requireSecure);
+
+			if (in_array($this->_action, $requireSecure) || $this->requireSecure == array('*')) {
+				if (!$this->RequestHandler->isSSL()) {
+					if (!$this->blackHole($controller, 'secure')) {
+						return null;
+					}
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Check if authentication is required
+ *
+ * @param object $controller Instantiating controller
+ * @return bool true if authentication required
+ * @access protected
+ */
+	function _authRequired(&$controller) {
+		if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($controller->data)) {
+			$requireAuth = array_map('strtolower', $this->requireAuth);
+
+			if (in_array($this->_action, $requireAuth) || $this->requireAuth == array('*')) {
+				if (!isset($controller->data['_Token'] )) {
+					if (!$this->blackHole($controller, 'auth')) {
+						return null;
+					}
+				}
+
+				if ($this->Session->check('_Token')) {
+					$tData = unserialize($this->Session->read('_Token'));
+
+					if (!empty($tData['allowedControllers']) && !in_array($controller->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($controller->params['action'], $tData['allowedActions'])) {
+						if (!$this->blackHole($controller, 'auth')) {
+							return null;
+						}
+					}
+				} else {
+					if (!$this->blackHole($controller, 'auth')) {
+						return null;
+					}
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Check if login is required
+ *
+ * @param object $controller Instantiating controller
+ * @return bool true if login is required
+ * @access protected
+ */
+	function _loginRequired(&$controller) {
+		if (is_array($this->requireLogin) && !empty($this->requireLogin)) {
+			$requireLogin = array_map('strtolower', $this->requireLogin);
+
+			if (in_array($this->_action, $requireLogin) || $this->requireLogin == array('*')) {
+				$login = $this->loginCredentials($this->loginOptions['type']);
+
+				if ($login == null) {
+					$controller->header($this->loginRequest());
+
+					if (!empty($this->loginOptions['prompt'])) {
+						$this->_callback($controller, $this->loginOptions['prompt']);
+					} else {
+						$this->blackHole($controller, 'login');
+					}
+				} else {
+					if (isset($this->loginOptions['login'])) {
+						$this->_callback($controller, $this->loginOptions['login'], array($login));
+					} else {
+						if (strtolower($this->loginOptions['type']) == 'digest') {
+							if ($login && isset($this->loginUsers[$login['username']])) {
+								if ($login['response'] == $this->generateDigestResponseHash($login)) {
+									return true;
+								}
+							}
+							$this->blackHole($controller, 'login');
+						} else {
+							if (
+								!(in_array($login['username'], array_keys($this->loginUsers)) &&
+								$this->loginUsers[$login['username']] == $login['password'])
+							) {
+								$this->blackHole($controller, 'login');
+							}
+						}
+					}
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Validate submitted form
+ *
+ * @param object $controller Instantiating controller
+ * @return bool true if submitted form is valid
+ * @access protected
+ */
+	function _validatePost(&$controller) {
+		if (empty($controller->data)) {
+			return true;
+		}
+		$data = $controller->data;
+
+		if (!isset($data['_Token']) || !isset($data['_Token']['fields'])) {
+			return false;
+		}
+		$token = $data['_Token']['key'];
+
+		if ($this->Session->check('_Token')) {
+			$tokenData = unserialize($this->Session->read('_Token'));
+
+			if ($tokenData['expires'] < time() || $tokenData['key'] !== $token) {
+				return false;
+			}
+		}
+
+		$locked = null;
+		$check = $controller->data;
+		$token = urldecode($check['_Token']['fields']);
+
+		if (strpos($token, ':')) {
+			list($token, $locked) = explode(':', $token, 2);
+		}
+		unset($check['_Token']);
+
+		$lockedFields = array();
+		$fields = Set::flatten($check);
+		$fieldList = array_keys($fields);
+		$locked = unserialize(str_rot13($locked));
+		$multi = array();
+
+		foreach ($fieldList as $i => $key) {
+			if (preg_match('/\.\d+$/', $key)) {
+				$multi[$i] = preg_replace('/\.\d+$/', '', $key);
+				unset($fieldList[$i]);
+			}
+		}
+		if (!empty($multi)) {
+			$fieldList += array_unique($multi);
+		}
+
+		foreach ($fieldList as $i => $key) {
+			$isDisabled = false;
+			$isLocked = (is_array($locked) && in_array($key, $locked));
+
+			if (!empty($this->disabledFields)) {
+				foreach ((array)$this->disabledFields as $disabled) {
+					$disabled = explode('.', $disabled);
+					$field = array_values(array_intersect(explode('.', $key), $disabled));
+					$isDisabled = ($field === $disabled);
+					if ($isDisabled) {
+						break;
+					}
+				}
+			}
+
+			if ($isDisabled || $isLocked) {
+				unset($fieldList[$i]);
+				if ($isLocked) {
+					$lockedFields[$key] = $fields[$key];
+				}
+			}
+		}
+		sort($fieldList, SORT_STRING);
+		ksort($lockedFields, SORT_STRING);
+
+		$fieldList += $lockedFields;
+		$check = Security::hash(serialize($fieldList) . Configure::read('Security.salt'));
+		return ($token === $check);
+	}
+/**
+ * Add authentication key for new form posts
+ *
+ * @param object $controller Instantiating controller
+ * @return bool Success
+ * @access protected
+ */
+	function _generateToken(&$controller) {
+		if (isset($controller->params['requested']) && $controller->params['requested'] === 1) {
+			return false;
+		}
+		$authKey = Security::generateAuthKey();
+		$expires = strtotime('+' . Security::inactiveMins() . ' minutes');
+		$token = array(
+			'key' => $authKey,
+			'expires' => $expires,
+			'allowedControllers' => $this->allowedControllers,
+			'allowedActions' => $this->allowedActions,
+			'disabledFields' => $this->disabledFields
+		);
+
+		if (!isset($controller->data)) {
+			$controller->data = array();
+		}
+
+		if ($this->Session->check('_Token')) {
+			$tokenData = unserialize($this->Session->read('_Token'));
+			$valid = (
+				isset($tokenData['expires']) &&
+				$tokenData['expires'] > time() &&
+				isset($tokenData['key'])
+			);
+
+			if ($valid) {
+				$token['key'] = $tokenData['key'];
+			}
+		}
+		$controller->params['_Token'] = $token;
+		$this->Session->write('_Token', serialize($token));
+
+		return true;
+	}
+/**
+ * Sets the default login options for an HTTP-authenticated request
+ *
+ * @param array $options Default login options
+ * @return void
+ * @access protected
+ */
+	function _setLoginDefaults(&$options) {
+		$options = array_merge(array(
+			'type' => 'basic',
+			'realm' => env('SERVER_NAME'),
+			'qop' => 'auth',
+			'nonce' => String::uuid()
+		), array_filter($options));
+		$options = array_merge(array('opaque' => md5($options['realm'])), $options);
+	}
+/**
+ * Calls a controller callback method
+ *
+ * @param object $controller Controller to run callback on
+ * @param string $method Method to execute
+ * @param array $params Parameters to send to method
+ * @return mixed Controller callback method's response
+ * @access protected
+ */
+	function _callback(&$controller, $method, $params = array()) {
+		if (is_callable(array($controller, $method))) {
+			return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
+		} else {
+			// Debug::warning('Callback method ' . $method . ' in controller ' . get_class($controller)
+			return null;
+		}
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/controller/scaffold.php
===================================================================
--- cake/libs/controller/scaffold.php	(revision 0)
+++ cake/libs/controller/scaffold.php	(revision 0)
@@ -0,0 +1,516 @@
+<?php
+/* SVN FILE: $Id: scaffold.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Scaffold.
+ *
+ * Automatic forms and actions generation for rapid web application development.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         Cake v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Scaffolding is a set of automatic views, forms and controllers for starting web development work faster.
+ *
+ * Scaffold inspects your database tables, and making educated guesses, sets up a
+ * number of pages for each of your Models. These pages have data forms that work,
+ * and afford the web developer an early look at the data, and the possibility to over-ride
+ * scaffolded actions with custom-made ones.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ */
+class Scaffold extends Object {
+/**
+ * Controller object
+ *
+ * @var object
+ * @access public
+ */
+	var $controller = null;
+/**
+ * Name of the controller to scaffold
+ *
+ * @var string
+ * @access public
+ */
+	var $name = null;
+/**
+ * Action to be performed.
+ *
+ * @var string
+ * @access public
+ */
+	var $action = null;
+/**
+ * Name of current model this view context is attached to
+ *
+ * @var string
+ * @access public
+ */
+	var $model = null;
+/**
+ * Path to View.
+ *
+ * @var string
+ * @access public
+ */
+	var $viewPath;
+/**
+ * Path parts for creating links in views.
+ *
+ * @var string Base URL
+ * @access public
+ */
+	var $base = null;
+/**
+ * Name of layout to use with this View.
+ *
+ * @var string
+ * @access public
+ */
+	var $layout = 'default';
+/**
+ * Array of parameter data
+ *
+ * @var array
+ * @access public
+ */
+	var $params;
+/**
+ * File extension. Defaults to Cake's template ".ctp".
+ *
+ * @var array
+ * @access public
+ */
+	var $ext = '.ctp';
+/**
+ * Sub-directory for this view file.
+ *
+ * @var string
+ * @access public
+ */
+	var $subDir = null;
+/**
+ * Plugin name.
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * List of variables to collect from the associated controller
+ *
+ * @var array
+ * @access private
+ */
+	var $__passedVars = array('action', 'base', 'webroot', 'layout', 'name', 'viewPath', 'ext', 'params', 'data', 'plugin', 'cacheAction');
+/**
+ * Title HTML element for current scaffolded view
+ *
+ * @var string
+ * @access public
+ */
+	var $scaffoldTitle = null;
+/**
+ * Construct and set up given controller with given parameters.
+ *
+ * @param string $controller_class Name of controller
+ * @param array $params Parameters for scaffolding
+ */
+	function __construct(&$controller, $params) {
+		$this->controller =& $controller;
+
+		$count = count($this->__passedVars);
+		for ($j = 0; $j < $count; $j++) {
+			$var = $this->__passedVars[$j];
+			$this->{$var} = $controller->{$var};
+		}
+
+		$this->redirect = array('action'=> 'index');
+
+		$this->modelClass = $controller->modelClass;
+		$this->modelKey = $controller->modelKey;
+
+		if (!is_object($this->controller->{$this->modelClass})) {
+			return $this->cakeError('missingModel', array(array('className' => $this->modelClass, 'webroot' => '', 'base' => $controller->base)));
+		}
+
+		$this->ScaffoldModel =& $this->controller->{$this->modelClass};
+		$this->scaffoldTitle = Inflector::humanize($this->viewPath);
+		$this->scaffoldActions = $controller->scaffold;
+		$this->controller->pageTitle = __('Scaffold :: ', true) . Inflector::humanize($this->action) . ' :: ' . $this->scaffoldTitle;
+
+		$modelClass = $this->controller->modelClass;
+		$primaryKey = $this->ScaffoldModel->primaryKey;
+		$displayField = $this->ScaffoldModel->displayField;
+		$singularVar = Inflector::variable($modelClass);
+		$pluralVar = Inflector::variable($this->controller->name);
+		$singularHumanName = Inflector::humanize($modelClass);
+		$pluralHumanName = Inflector::humanize($this->controller->name);
+		$scaffoldFields = array_keys($this->ScaffoldModel->schema());
+		$associations = $this->__associations();
+
+		$this->controller->set(compact('modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
+								'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations'));
+
+		if ($this->controller->view && $this->controller->view !== 'Theme') {
+			$this->controller->view = 'scaffold';
+		}
+		$this->__scaffold($params);
+	}
+/**
+ * Outputs the content of a scaffold method passing it through the Controller::afterFilter()
+ *
+ * @return void
+ * @access protected
+ */
+	function _output() {
+		$this->controller->afterFilter();
+		echo($this->controller->output);
+	}
+/**
+ * Renders a view action of scaffolded model.
+ *
+ * @param array $params Parameters for scaffolding
+ * @return mixed A rendered view of a row from Models database table
+ * @access private
+ */
+	function __scaffoldView($params) {
+		if ($this->controller->_beforeScaffold('view')) {
+
+			if (isset($params['pass'][0])) {
+				$this->ScaffoldModel->id = $params['pass'][0];
+			} elseif (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+				$this->controller->Session->setFlash(sprintf(__("No id set for %s::view()", true), Inflector::humanize($this->modelKey)));
+				$this->controller->redirect($this->redirect);
+			} else {
+				return $this->controller->flash(sprintf(__("No id set for %s::view()", true), Inflector::humanize($this->modelKey)),
+																		'/' . Inflector::underscore($this->controller->viewPath));
+			}
+			$this->ScaffoldModel->recursive = 1;
+			$this->controller->data = $this->ScaffoldModel->read();
+			$this->controller->set(Inflector::variable($this->controller->modelClass), $this->controller->data);
+			$this->controller->render($this->action, $this->layout);
+			$this->_output();
+		} elseif ($this->controller->_scaffoldError('view') === false) {
+			return $this->__scaffoldError();
+		}
+	}
+/**
+ * Renders index action of scaffolded model.
+ *
+ * @param array $params Parameters for scaffolding
+ * @return mixed A rendered view listing rows from Models database table
+ * @access private
+ */
+	function __scaffoldIndex($params) {
+		if ($this->controller->_beforeScaffold('index')) {
+			$this->ScaffoldModel->recursive = 0;
+			$this->controller->set(Inflector::variable($this->controller->name), $this->controller->paginate());
+			$this->controller->render($this->action, $this->layout);
+			$this->_output();
+		} elseif ($this->controller->_scaffoldError('index') === false) {
+			return $this->__scaffoldError();
+		}
+	}
+/**
+ * Renders an add or edit action for scaffolded model.
+ *
+ * @param string $action Action (add or edit)
+ * @return mixed A rendered view with a form to edit or add a record in the Models database table
+ * @access private
+ */
+	function __scaffoldForm($action = 'edit') {
+		$this->controller->render($action, $this->layout);
+		$this->_output();
+	}
+/**
+ * Saves or updates the scaffolded model.
+ *
+ * @param array $params Parameters for scaffolding
+ * @param string $action add or edt
+ * @return mixed Success on save/update, add/edit form if data is empty or error if save or update fails
+ * @access private
+ */
+	function __scaffoldSave($params = array(), $action = 'edit') {
+		$formAction = 'edit';
+		$success = __('updated', true);
+		if ($action === 'add') {
+			$formAction = 'add';
+			$success = __('saved', true);
+		}
+
+		if ($this->controller->_beforeScaffold($action)) {
+			if ($action == 'edit') {
+				if (isset($params['pass'][0])) {
+					$this->ScaffoldModel->id = $params['pass'][0];
+				}
+
+				if (!$this->ScaffoldModel->exists()) {
+					if (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+						$this->controller->Session->setFlash(sprintf(__("Invalid id for %s::edit()", true), Inflector::humanize($this->modelKey)));
+						$this->controller->redirect($this->redirect);
+					} else {
+						return $this->controller->flash(sprintf(__("Invalid id for %s::edit()", true), Inflector::humanize($this->modelKey)), $this->redirect);
+					}
+				}
+			}
+
+			if (!empty($this->controller->data)) {
+				if ($action == 'create') {
+					$this->ScaffoldModel->create();
+				}
+
+				if ($this->ScaffoldModel->save($this->controller->data)) {
+					if ($this->controller->_afterScaffoldSave($action)) {
+						if (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+							$this->controller->Session->setFlash(sprintf(__('The %1$s has been %2$s', true), Inflector::humanize($this->modelClass), $success));
+							$this->controller->redirect($this->redirect);
+						} else {
+							return $this->controller->flash(sprintf(__('The %1$s has been %2$s', true), Inflector::humanize($this->modelClass), $success), $this->redirect);
+						}
+					} else {
+						return $this->controller->_afterScaffoldSaveError($action);
+					}
+				} else {
+					if (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+						$this->controller->Session->setFlash(__('Please correct errors below.', true));
+					}
+				}
+			}
+
+			if (empty($this->controller->data)) {
+				if ($this->ScaffoldModel->id) {
+					$this->controller->data = $this->ScaffoldModel->read();
+				} else {
+					$this->controller->data = $this->ScaffoldModel->create();
+				}
+			}
+
+			foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) {
+				$varName = Inflector::variable(Inflector::pluralize(preg_replace('/(?:_id)$/', '', $assocData['foreignKey'])));
+				$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
+			}
+			foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) {
+				$varName = Inflector::variable(Inflector::pluralize($assocName));
+				$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
+			}
+
+			return $this->__scaffoldForm($formAction);
+		} elseif ($this->controller->_scaffoldError($action) === false) {
+			return $this->__scaffoldError();
+		}
+	}
+/**
+ * Performs a delete on given scaffolded Model.
+ *
+ * @param array $params Parameters for scaffolding
+ * @return mixed Success on delete, error if delete fails
+ * @access private
+ */
+	function __scaffoldDelete($params = array()) {
+		if ($this->controller->_beforeScaffold('delete')) {
+			if (isset($params['pass'][0])) {
+				$id = $params['pass'][0];
+			} elseif (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+				$this->controller->Session->setFlash(sprintf(__("No id set for %s::delete()", true), Inflector::humanize($this->modelKey)));
+				$this->controller->redirect($this->redirect);
+			} else {
+				return $this->controller->flash(sprintf(__("No id set for %s::delete()", true), Inflector::humanize($this->modelKey)),
+																	'/' . Inflector::underscore($this->controller->viewPath));
+			}
+
+			if ($this->ScaffoldModel->del($id)) {
+				if (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+					$this->controller->Session->setFlash(sprintf(__('The %1$s with id: %2$d has been deleted.', true), Inflector::humanize($this->modelClass), $id));
+					$this->controller->redirect($this->redirect);
+				} else {
+					return $this->controller->flash(sprintf(__('The %1$s with id: %2$d has been deleted.', true), Inflector::humanize($this->modelClass), $id), '/' . $this->viewPath);
+				}
+			} else {
+				if (isset($this->controller->Session) && $this->controller->Session->valid() != false) {
+					$this->controller->Session->setFlash(sprintf(__('There was an error deleting the %1$s with id: %2$d', true), Inflector::humanize($this->modelClass), $id));
+					$this->controller->redirect($this->redirect);
+				} else {
+					return $this->controller->flash(sprintf(__('There was an error deleting the %1$s with id: %2$d', true), Inflector::humanize($this->modelClass), $id), '/' . $this->viewPath);
+				}
+			}
+		} elseif ($this->controller->_scaffoldError('delete') === false) {
+			return $this->__scaffoldError();
+		}
+	}
+/**
+ * Show a scaffold error
+ *
+ * @return mixed A rendered view showing the error
+ * @access private
+ */
+	function __scaffoldError() {
+		return $this->controller->render('error', $this->layout);
+		$this->_output();
+	}
+/**
+ * When methods are now present in a controller
+ * scaffoldView is used to call default Scaffold methods if:
+ * <code>
+ * var $scaffold;
+ * </code>
+ * is placed in the controller's class definition.
+ *
+ * @param array $params Parameters for scaffolding
+ * @return mixed A rendered view of scaffold action, or showing the error
+ * @access private
+ */
+	function __scaffold($params) {
+		$db = &ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig);
+		$admin = Configure::read('Routing.admin');
+
+		if (isset($db)) {
+			if (empty($this->scaffoldActions)) {
+				$this->scaffoldActions = array('index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete');
+			} elseif (!empty($admin) && $this->scaffoldActions === $admin) {
+				$this->scaffoldActions = array($admin .'_index', $admin .'_list', $admin .'_view', $admin .'_add', $admin .'_create', $admin .'_edit', $admin .'_update', $admin .'_delete');
+			}
+
+			if (in_array($params['action'], $this->scaffoldActions)) {
+				if (!empty($admin)) {
+					$params['action'] = str_replace($admin . '_', '', $params['action']);
+				}
+				switch ($params['action']) {
+					case 'index':
+						$this->__scaffoldIndex($params);
+					break;
+					case 'view':
+						$this->__scaffoldView($params);
+					break;
+					case 'list':
+						$this->__scaffoldIndex($params);
+					break;
+					case 'add':
+						$this->__scaffoldSave($params, 'add');
+					break;
+					case 'edit':
+						$this->__scaffoldSave($params, 'edit');
+					break;
+					case 'create':
+						$this->__scaffoldSave($params, 'add');
+					break;
+					case 'update':
+						$this->__scaffoldSave($params, 'edit');
+					break;
+					case 'delete':
+						$this->__scaffoldDelete($params);
+					break;
+				}
+			} else {
+				return $this->cakeError('missingAction', array(array('className' => $this->controller->name . "Controller",
+																						'base' => $this->controller->base,
+																						'action' => $this->action,
+																						'webroot' => $this->controller->webroot)));
+			}
+		} else {
+			return $this->cakeError('missingDatabase', array(array('webroot' => $this->controller->webroot)));
+		}
+	}
+/**
+ * Returns associations for controllers models.
+ *
+ * @return array Associations for model
+ * @access private
+ */
+	function __associations() {
+		$keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+		$associations = array();
+
+		foreach ($keys as $key => $type) {
+			foreach ($this->ScaffoldModel->{$type} as $assocKey => $assocData) {
+				$associations[$type][$assocKey]['primaryKey'] = $this->ScaffoldModel->{$assocKey}->primaryKey;
+				$associations[$type][$assocKey]['displayField'] = $this->ScaffoldModel->{$assocKey}->displayField;
+				$associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey'];
+				$associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($assocData['className']));
+			}
+		}
+		return $associations;
+	}
+}
+
+/**
+ * Scaffold View.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+*/
+if (!class_exists('ThemeView')) {
+	App::import('View', 'Theme');
+}
+
+class ScaffoldView extends ThemeView {
+/**
+ * Override _getViewFileName
+ *
+ * @return string action
+ * @access protected
+ */
+	function _getViewFileName($name = null) {
+		if ($name === null) {
+			$name = $this->action;
+		}
+		$name = Inflector::underscore($name);
+		$admin = Configure::read('Routing.admin');
+
+		if (!empty($admin) && strpos($name, $admin . '_') !== false) {
+			$name = substr($name, strlen($admin) + 1);
+		}
+
+		if ($name === 'add') {
+			$name = 'edit';
+		}
+
+		$scaffoldAction = 'scaffold.' . $name;
+
+		if (!is_null($this->subDir)) {
+			$subDir = strtolower($this->subDir) . DS;
+		} else {
+			$subDir = null;
+		}
+
+		$names[] = $this->viewPath . DS . $subDir . $scaffoldAction;
+		$names[] = 'scaffolds' . DS . $subDir . $name;
+
+		$paths = $this->_paths($this->plugin);
+
+		$exts = array($this->ext, '.ctp', '.thtml');
+		foreach ($paths as $path) {
+			foreach ($names as $name) {
+				foreach ($exts as $ext) {
+					if (file_exists($path . $name . $ext)) {
+						return $path . $name . $ext;
+					}
+				}
+			}
+		}
+
+		if ($name === 'scaffolds' . DS . $subDir . 'error') {
+			return LIBS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp';
+		}
+
+		return $this->_missingView($paths[0] . $name . $this->ext, 'missingView');
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/component.php
===================================================================
--- cake/libs/controller/component.php	(revision 0)
+++ cake/libs/controller/component.php	(revision 0)
@@ -0,0 +1,259 @@
+<?php
+/* SVN FILE: $Id: component.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v TBD
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Handler for Controller::$components
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @link          http://book.cakephp.org/view/62/Components
+ */
+class Component extends Object {
+/**
+ * Contains various controller variable information (plugin, name, base).
+ *
+ * @var object
+ * @access private
+ */
+	var $__controllerVars = array('plugin' => null, 'name' => null, 'base' => null);
+/**
+ * List of loaded components.
+ *
+ * @var object
+ * @access protected
+ */
+	var $_loaded = array();
+/**
+ * List of components attached directly to the controller, which callbacks
+ * should be executed on.
+ *
+ * @var object
+ * @access protected
+ */
+	var $_primary = array();
+/**
+ * Settings for loaded components.
+ *
+ * @var array
+ * @access private
+ **/
+	var $__settings = array();
+/**
+ * Used to initialize the components for current controller.
+ *
+ * @param object $controller Controller with components to load
+ * @return void
+ * @access public
+ */
+	function init(&$controller) {
+		if (!is_array($controller->components)) {
+			return;
+		}
+		$this->__controllerVars = array(
+			'plugin' => $controller->plugin, 'name' => $controller->name,
+			'base' => $controller->base
+		);
+
+		if (!in_array('Session', $controller->components)) {
+			array_unshift($controller->components, 'Session');
+		}
+		$this->_loadComponents($controller);
+	}
+/**
+ * Called before the Controller::beforeFilter().
+ *
+ * @param object $controller Controller with components to initialize
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/65/MVC-Class-Access-Within-Components
+ */
+	function initialize(&$controller) {
+		foreach (array_keys($this->_loaded) as $name) {
+			$component =& $this->_loaded[$name];
+
+			if (method_exists($component,'initialize') && $component->enabled === true) {
+				$settings = array();
+				if (isset($this->__settings[$name])) {
+					$settings = $this->__settings[$name];
+				}
+				$component->initialize($controller, $settings);
+			}
+		}
+	}
+/**
+ * Called after the Controller::beforeFilter() and before the controller action
+ *
+ * @param object $controller Controller with components to startup
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/65/MVC-Class-Access-Within-Components
+ */
+	function startup(&$controller) {
+		foreach ($this->_primary as $name) {
+			$component =& $this->_loaded[$name];
+			if ($component->enabled === true && method_exists($component, 'startup')) {
+				$component->startup($controller);
+			}
+		}
+	}
+/**
+ * Called after the Controller::beforeRender(), after the view class is loaded, and before the
+ * Controller::render()
+ *
+ * @param object $controller Controller with components to beforeRender
+ * @return void
+ * @access public
+ */
+	function beforeRender(&$controller) {
+		foreach ($this->_primary as $name) {
+			$component =& $this->_loaded[$name];
+			if ($component->enabled === true && method_exists($component,'beforeRender')) {
+				$component->beforeRender($controller);
+			}
+		}
+	}
+/**
+ * Called before Controller::redirect().
+ *
+ * @param object $controller Controller with components to beforeRedirect
+ * @return void
+ * @access public
+ */
+	function beforeRedirect(&$controller, $url, $status = null, $exit = true) {
+		$response = array();
+
+		foreach ($this->_primary as $name) {
+			$component =& $this->_loaded[$name];
+
+			if ($component->enabled === true && method_exists($component, 'beforeRedirect')) {
+				$resp = $component->beforeRedirect($controller, $url, $status, $exit);
+				if ($resp === false) {
+					return false;
+				}
+				$response[] = $resp;
+			}
+		}
+		return $response;
+	}
+/**
+ * Called after Controller::render() and before the output is printed to the browser.
+ *
+ * @param object $controller Controller with components to shutdown
+ * @return void
+ * @access public
+ */
+	function shutdown(&$controller) {
+		foreach ($this->_primary as $name) {
+			$component =& $this->_loaded[$name];
+			if (method_exists($component,'shutdown') && $component->enabled === true) {
+				$component->shutdown($controller);
+			}
+		}
+	}
+/**
+ * Loads components used by this component.
+ *
+ * @param object $object Object with a Components array
+ * @param object $parent the parent of the current object
+ * @return void
+ * @access protected
+ */
+	function _loadComponents(&$object, $parent = null) {
+		$components = $object->components;
+		$base = $this->__controllerVars['base'];
+
+		if (is_array($object->components)) {
+			$normal = Set::normalize($object->components);
+			foreach ($normal as $component => $config) {
+				$plugin = null;
+
+				if (isset($this->__controllerVars['plugin'])) {
+					$plugin = $this->__controllerVars['plugin'] . '.';
+				}
+
+				if (strpos($component, '.') !== false) {
+					list($plugin, $component) = explode('.', $component);
+					$plugin = $plugin . '.';
+				}
+				$componentCn = $component . 'Component';
+
+				if (!class_exists($componentCn)) {
+					if (is_null($plugin) || !App::import('Component', $plugin . $component)) {
+						if (!App::import('Component', $component)) {
+							$this->cakeError('missingComponentFile', array(array(
+								'className' => $this->__controllerVars['name'],
+								'component' => $component,
+								'file' => Inflector::underscore($component) . '.php',
+								'base' => $base,
+								'code' => 500
+							)));
+							return false;
+						}
+					}
+
+					if (!class_exists($componentCn)) {
+						$this->cakeError('missingComponentClass', array(array(
+							'className' => $this->__controllerVars['name'],
+							'component' => $component,
+							'file' => Inflector::underscore($component) . '.php',
+							'base' => $base,
+							'code' => 500
+						)));
+						return false;
+					}
+				}
+
+				if ($parent === null) {
+					$this->_primary[] = $component;
+				}
+
+				if (isset($this->_loaded[$component])) {
+					$object->{$component} =& $this->_loaded[$component];
+
+					if (!empty($config) && isset($this->__settings[$component])) {
+						$this->__settings[$component] = array_merge($this->__settings[$component], $config);
+					} elseif (!empty($config)) {
+						$this->__settings[$component] = $config;
+					}
+				} else {
+					if ($componentCn === 'SessionComponent') {
+						$object->{$component} =& new $componentCn($base);
+					} else {
+						$object->{$component} =& new $componentCn();
+					}
+					$object->{$component}->enabled = true;
+					$this->_loaded[$component] =& $object->{$component};
+					if (!empty($config)) {
+						$this->__settings[$component] = $config;
+					}
+				}
+
+				if (isset($object->{$component}->components) && is_array($object->{$component}->components) && (!isset($object->{$component}->{$parent}))) {
+					$this->_loadComponents($object->{$component}, $component);
+				}
+			}
+		}
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/controller/pages_controller.php
===================================================================
--- cake/libs/controller/pages_controller.php	(revision 0)
+++ cake/libs/controller/pages_controller.php	(revision 0)
@@ -0,0 +1,86 @@
+<?php
+/* SVN FILE: $Id: pages_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Static content controller.
+ *
+ * This file will render views from views/pages/
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Static content controller
+ *
+ * Override this controller by placing a copy in controllers directory of an application
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ */
+class PagesController extends AppController {
+/**
+ * Controller name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Pages';
+/**
+ * Default helper
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html');
+/**
+ * This controller does not use a model
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+/**
+ * Displays a view
+ *
+ * @param mixed What page to display
+ * @access public
+ */
+	function display() {
+		$path = func_get_args();
+
+		$count = count($path);
+		if (!$count) {
+			$this->redirect('/');
+		}
+		$page = $subpage = $title = null;
+
+		if (!empty($path[0])) {
+			$page = $path[0];
+		}
+		if (!empty($path[1])) {
+			$subpage = $path[1];
+		}
+		if (!empty($path[$count - 1])) {
+			$title = Inflector::humanize($path[$count - 1]);
+		}
+		$this->set(compact('page', 'subpage', 'title'));
+		$this->render(join('/', $path));
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/controller/app_controller.php
===================================================================
--- cake/libs/controller/app_controller.php	(revision 0)
+++ cake/libs/controller/app_controller.php	(revision 0)
@@ -0,0 +1,40 @@
+<?php
+/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file is application-wide controller file. You can put all
+ * application-wide controller-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * This is a placeholder class.
+ * Create the same file in app/app_controller.php
+ *
+ * Add your application-wide methods in the class below, your controllers
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ */
+class AppController extends Controller {
+}
+?>
\ No newline at end of file
Index: cake/libs/controller/controller.php
===================================================================
--- cake/libs/controller/controller.php	(revision 0)
+++ cake/libs/controller/controller.php	(revision 0)
@@ -0,0 +1,1144 @@
+<?php
+/* SVN FILE: $Id: controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Base controller class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Include files
+ */
+App::import('Core', array('Component', 'View'));
+/**
+ * Controller
+ *
+ * Application controller class for organization of business logic.
+ * Provides basic functionality, such as rendering views inside layouts,
+ * automatic model availability, redirection, callbacks, and more.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @link          http://book.cakephp.org/view/49/Controllers
+ *
+ */
+class Controller extends Object {
+/**
+ * The name of this controller. Controller names are plural, named after the model they manipulate.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/52/name
+ */
+	var $name = null;
+/**
+ * Stores the current URL, relative to the webroot of the application.
+ *
+ * @var string
+ * @access public
+ */
+	var $here = null;
+/**
+ * The webroot of the application. Helpful if your application is placed in a folder under the current domain name.
+ *
+ * @var string
+ * @access public
+ */
+	var $webroot = null;
+/**
+ * The name of the currently requested controller action.
+ *
+ * @var string
+ * @access public
+ */
+	var $action = null;
+/**
+ * An array containing the class names of models this controller uses.
+ *
+ * Example: var $uses = array('Product', 'Post', 'Comment');
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @access protected
+ * @link http://book.cakephp.org/view/53/components-helpers-and-uses
+ */
+	var $uses = false;
+/**
+ * An array containing the names of helpers this controller uses. The array elements should
+ * not contain the "Helper" part of the classname.
+ *
+ * Example: var $helpers = array('Html', 'Javascript', 'Time', 'Ajax');
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @access protected
+ * @link http://book.cakephp.org/view/53/components-helpers-and-uses
+ */
+	var $helpers = array('Html', 'Form');
+/**
+ * Parameters received in the current request: GET and POST data, information
+ * about the request, etc.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/55/The-Parameters-Attribute-params
+ */
+	var $params = array();
+/**
+ * Data POSTed to the controller using the HtmlHelper. Data here is accessible
+ * using the $this->data['ModelName']['fieldName'] pattern.
+ *
+ * @var array
+ * @access public
+ */
+	var $data = array();
+/**
+ * Holds pagination defaults for controller actions. The keys that can be included
+ * in this array are: 'conditions', 'fields', 'order', 'limit', 'page', and 'recursive',
+ * similar to the keys in the second parameter of Model::find().
+ *
+ * Pagination defaults can also be supplied in a model-by-model basis by using
+ * the name of the model as a key for a pagination array:
+ *
+ * var $paginate = array(
+ * 		'Post' => array(...),
+ * 		'Comment' => array(...)
+ * 	);
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/164/Pagination
+ */
+	var $paginate = array('limit' => 20, 'page' => 1);
+/**
+ * The name of the views subfolder containing views for this controller.
+ *
+ * @var string
+ * @access public
+ */
+	var $viewPath = null;
+/**
+ * The name of the layouts subfolder containing layouts for this controller.
+ *
+ * @var string
+ * @access public
+ */
+	var $layoutPath = null;
+/**
+ * Contains variables to be handed to the view.
+ *
+ * @var array
+ * @access public
+ */
+	var $viewVars = array();
+/**
+ * Text to be used for the $title_for_layout layout variable (usually
+ * placed inside <title> tags.)
+ *
+ * @var boolean
+ * @access public
+ * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle
+ */
+	var $pageTitle = false;
+/**
+ * An array containing the class names of the models this controller uses.
+ *
+ * @var array Array of model objects.
+ * @access public
+ */
+	var $modelNames = array();
+/**
+ * Base URL path.
+ *
+ * @var string
+ * @access public
+ */
+	var $base = null;
+/**
+ * The name of the layout file to render the view inside of. The name specified
+ * is the filename of the layout in /app/views/layouts without the .ctp
+ * extension.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle
+ */
+	var $layout = 'default';
+/**
+ * Set to true to automatically render the view
+ * after action logic.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoRender = true;
+/**
+ * Set to true to automatically render the layout around views.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoLayout = true;
+/**
+ * Instance of Component used to handle callbacks.
+ *
+ * @var string
+ * @access public
+ */
+	var $Component = null;
+/**
+ * Array containing the names of components this controller uses. Component names
+ * should not contain the "Component" portion of the classname.
+ *
+ * Example: var $components = array('Session', 'RequestHandler', 'Acl');
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/53/components-helpers-and-uses
+ */
+	var $components = array();
+/**
+ * The name of the View class this controller sends output to.
+ *
+ * @var string
+ * @access public
+ */
+	var $view = 'View';
+/**
+ * File extension for view templates. Defaults to Cake's conventional ".ctp".
+ *
+ * @var string
+ * @access public
+ */
+	var $ext = '.ctp';
+/**
+ * The output of the requested action.  Contains either a variable
+ * returned from the action, or the data of the rendered view;
+ * You can use this var in child controllers' afterFilter() callbacks to alter output.
+ *
+ * @var string
+ * @access public
+ */
+	var $output = null;
+/**
+ * Automatically set to the name of a plugin.
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * Used to define methods a controller that will be cached. To cache a
+ * single action, the value is set to an array containing keys that match
+ * action names and values that denote cache expiration times (in seconds).
+ *
+ * Example: var $cacheAction = array(
+ *		'view/23/' => 21600,
+ *		'recalled/' => 86400
+ *	);
+ *
+ * $cacheAction can also be set to a strtotime() compatible string. This
+ * marks all the actions in the controller for view caching.
+ *
+ * @var mixed
+ * @access public
+ * @link http://book.cakephp.org/view/346/Caching-in-the-Controller
+ */
+	var $cacheAction = false;
+/**
+ * Used to create cached instances of models a controller uses.
+ * When set to true, all models related to the controller will be cached.
+ * This can increase performance in many cases.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $persistModel = false;
+/**
+ * Holds all params passed and named.
+ *
+ * @var mixed
+ * @access public
+ */
+	var $passedArgs = array();
+/**
+ * Triggers Scaffolding
+ *
+ * @var mixed
+ * @access public
+ * @link http://book.cakephp.org/view/105/Scaffolding
+ */
+	var $scaffold = false;
+/**
+ * Holds current methods of the controller
+ *
+ * @var array
+ * @access public
+ * @link
+ */
+	var $methods = array();
+/**
+ * Constructor.
+ *
+ */
+	function __construct() {
+		if ($this->name === null) {
+			$r = null;
+			if (!preg_match('/(.*)Controller/i', get_class($this), $r)) {
+				die (__("Controller::__construct() : Can not get or parse my own class name, exiting."));
+			}
+			$this->name = $r[1];
+		}
+
+		if ($this->viewPath == null) {
+			$this->viewPath = Inflector::underscore($this->name);
+		}
+		$this->modelClass = Inflector::classify($this->name);
+		$this->modelKey = Inflector::underscore($this->modelClass);
+		$this->Component =& new Component();
+
+		$childMethods = get_class_methods($this);
+		$parentMethods = get_class_methods('Controller');
+
+		foreach ($childMethods as $key => $value) {
+			$childMethods[$key] = strtolower($value);
+		}
+
+		foreach ($parentMethods as $key => $value) {
+			$parentMethods[$key] = strtolower($value);
+		}
+
+		$this->methods = array_diff($childMethods, $parentMethods);
+		parent::__construct();
+	}
+/**
+ * Merge components, helpers, and uses vars from AppController and PluginAppController.
+ *
+ * @return void
+ * @access protected
+ */
+	function __mergeVars() {
+		$pluginName = Inflector::camelize($this->plugin);
+		$pluginController = $pluginName . 'AppController';
+
+		if (is_subclass_of($this, 'AppController') || is_subclass_of($this, $pluginController)) {
+			$appVars = get_class_vars('AppController');
+			$uses = $appVars['uses'];
+			$merge = array('components', 'helpers');
+			$plugin = null;
+
+			if (!empty($this->plugin)) {
+				$plugin = $pluginName . '.';
+				if (!is_subclass_of($this, $pluginController)) {
+					$pluginController = null;
+				}
+			} else {
+				$pluginController = null;
+			}
+
+			if ($uses == $this->uses && !empty($this->uses)) {
+				if (!in_array($plugin . $this->modelClass, $this->uses)) {
+					array_unshift($this->uses, $plugin . $this->modelClass);
+				} elseif ($this->uses[0] !== $plugin . $this->modelClass) {
+					$this->uses = array_flip($this->uses);
+					unset($this->uses[$plugin . $this->modelClass]);
+					$this->uses = array_flip($this->uses);
+					array_unshift($this->uses, $plugin . $this->modelClass);
+				}
+			} elseif ($this->uses !== null || $this->uses !== false) {
+				$merge[] = 'uses';
+			}
+
+			foreach ($merge as $var) {
+				if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) {
+					if ($var === 'components') {
+						$normal = Set::normalize($this->{$var});
+						$app = Set::normalize($appVars[$var]);
+						$this->{$var} = Set::merge($normal, $app);
+					} else {
+						$this->{$var} = Set::merge($this->{$var}, array_diff($appVars[$var], $this->{$var}));
+					}
+				}
+			}
+		}
+
+		if ($pluginController) {
+			$appVars = get_class_vars($pluginController);
+			$uses = $appVars['uses'];
+			$merge = array('components', 'helpers');
+
+			if ($this->uses !== null || $this->uses !== false) {
+				$merge[] = 'uses';
+			}
+
+			foreach ($merge as $var) {
+				if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) {
+					if ($var === 'components') {
+						$normal = Set::normalize($this->{$var});
+						$app = Set::normalize($appVars[$var]);
+						$this->{$var} = Set::merge($normal, array_diff_assoc($app, $normal));
+					} else {
+						$this->{$var} = Set::merge($this->{$var}, array_diff($appVars[$var], $this->{$var}));
+					}
+				}
+			}
+		}
+	}
+/**
+ * Loads Model classes based on the the uses property
+ * see Controller::loadModel(); for more info.
+ * Loads Components and prepares them for initialization.
+ *
+ * @return mixed true if models found and instance created, or cakeError if models not found.
+ * @access public
+ * @see Controller::loadModel()
+ * @link http://book.cakephp.org/view/429/constructClasses
+ */
+	function constructClasses() {
+		$this->__mergeVars();
+		$this->Component->init($this);
+
+		if ($this->uses !== null || ($this->uses !== array())) {
+			if (empty($this->passedArgs) || !isset($this->passedArgs['0'])) {
+				$id = false;
+			} else {
+				$id = $this->passedArgs['0'];
+			}
+
+			if ($this->uses === false) {
+				$this->loadModel($this->modelClass, $id);
+			} elseif ($this->uses) {
+				$uses = is_array($this->uses) ? $this->uses : array($this->uses);
+				$modelClassName = $uses[0];
+				if (strpos($uses[0], '.') !== false) {
+					list($plugin, $modelClassName) = explode('.', $uses[0]);
+				}
+				$this->modelClass = $modelClassName;
+				foreach ($uses as $modelClass) {
+					$this->loadModel($modelClass);
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Loads and instantiates models required by this controller.
+ * If Controller::persistModel; is true, controller will create cached model instances on first request,
+ * additional request will used cached models.
+ * If the model is non existent, it will throw a missing database table error, as Cake generates
+ * dynamic models for the time being.
+ *
+ * @param string $modelClass Name of model class to load
+ * @param mixed $id Initial ID the instanced model class should have
+ * @return mixed true when single model found and instance created error returned if models not found.
+ * @access public
+ */
+	function loadModel($modelClass = null, $id = null) {
+		if ($modelClass === null) {
+			$modelClass = $this->modelClass;
+		}
+		$cached = false;
+		$object = null;
+		$plugin = null;
+		if ($this->uses === false) {
+			if ($this->plugin) {
+				$plugin = $this->plugin . '.';
+			}
+		}
+
+		if (strpos($modelClass, '.') !== false) {
+			list($plugin, $modelClass) = explode('.', $modelClass);
+			$plugin = $plugin . '.';
+		}
+
+		if ($this->persistModel === true) {
+			$cached = $this->_persist($modelClass, null, $object);
+		}
+
+		if (($cached === false)) {
+			$this->modelNames[] = $modelClass;
+
+			if (!PHP5) {
+				$this->{$modelClass} =& ClassRegistry::init(array('class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id));
+			} else {
+				$this->{$modelClass} = ClassRegistry::init(array('class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id));
+			}
+
+			if (!$this->{$modelClass}) {
+				return $this->cakeError('missingModel', array(array('className' => $modelClass, 'webroot' => '', 'base' => $this->base)));
+			}
+
+			if ($this->persistModel === true) {
+				$this->_persist($modelClass, true, $this->{$modelClass});
+				$registry = ClassRegistry::getInstance();
+				$this->_persist($modelClass . 'registry', true, $registry->__objects, 'registry');
+			}
+		} else {
+			$this->_persist($modelClass . 'registry', true, $object, 'registry');
+			$this->_persist($modelClass, true, $object);
+			$this->modelNames[] = $modelClass;
+		}
+	}
+/**
+ * Redirects to given $url, after turning off $this->autoRender.
+ * Script execution is halted after the redirect.
+ *
+ * @param mixed $url A string or array-based URL pointing to another location within the app, or an absolute URL
+ * @param integer $status Optional HTTP status code (eg: 404)
+ * @param boolean $exit If true, exit() will be called after the redirect
+ * @return mixed void if $exit = false. Terminates script if $exit = true
+ * @access public
+ * @link http://book.cakephp.org/view/425/redirect
+ */
+	function redirect($url, $status = null, $exit = true) {
+		$this->autoRender = false;
+
+		if (is_array($status)) {
+			extract($status, EXTR_OVERWRITE);
+		}
+		$response = $this->Component->beforeRedirect($this, $url, $status, $exit);
+
+		if ($response === false) {
+			return;
+		}
+		if (is_array($response)) {
+			foreach ($response as $resp) {
+				if (is_array($resp) && isset($resp['url'])) {
+					extract($resp, EXTR_OVERWRITE);
+				} elseif ($resp !== null) {
+					$url = $resp;
+				}
+			}
+		}
+
+		if (function_exists('session_write_close')) {
+			session_write_close();
+		}
+
+		if (!empty($status)) {
+			$codes = array(
+				100 => 'Continue',
+				101 => 'Switching Protocols',
+				200 => 'OK',
+				201 => 'Created',
+				202 => 'Accepted',
+				203 => 'Non-Authoritative Information',
+				204 => 'No Content',
+				205 => 'Reset Content',
+				206 => 'Partial Content',
+				300 => 'Multiple Choices',
+				301 => 'Moved Permanently',
+				302 => 'Found',
+				303 => 'See Other',
+				304 => 'Not Modified',
+				305 => 'Use Proxy',
+				307 => 'Temporary Redirect',
+				400 => 'Bad Request',
+				401 => 'Unauthorized',
+				402 => 'Payment Required',
+				403 => 'Forbidden',
+				404 => 'Not Found',
+				405 => 'Method Not Allowed',
+				406 => 'Not Acceptable',
+				407 => 'Proxy Authentication Required',
+				408 => 'Request Time-out',
+				409 => 'Conflict',
+				410 => 'Gone',
+				411 => 'Length Required',
+				412 => 'Precondition Failed',
+				413 => 'Request Entity Too Large',
+				414 => 'Request-URI Too Large',
+				415 => 'Unsupported Media Type',
+				416 => 'Requested range not satisfiable',
+				417 => 'Expectation Failed',
+				500 => 'Internal Server Error',
+				501 => 'Not Implemented',
+				502 => 'Bad Gateway',
+				503 => 'Service Unavailable',
+				504 => 'Gateway Time-out'
+			);
+			if (is_string($status)) {
+				$codes = array_combine(array_values($codes), array_keys($codes));
+			}
+
+			if (isset($codes[$status])) {
+				$code = $msg = $codes[$status];
+				if (is_numeric($status)) {
+					$code = $status;
+				}
+				if (is_string($status)) {
+					$msg = $status;
+				}
+				$status = "HTTP/1.1 {$code} {$msg}";
+			} else {
+				$status = null;
+			}
+		}
+
+		if (!empty($status)) {
+			$this->header($status);
+		}
+		if ($url !== null) {
+			$this->header('Location: ' . Router::url($url, true));
+		}
+
+		if (!empty($status) && ($status >= 300 && $status < 400)) {
+			$this->header($status);
+		}
+
+		if ($exit) {
+			$this->_stop();
+		}
+	}
+/**
+ * Convenience method for header()
+ *
+ * @param string $status
+ * @return void
+ * @access public
+ */
+	function header($status) {
+		header($status);
+	}
+/**
+ * Saves a variable for use inside a view template.
+ *
+ * @param mixed $one A string or an array of data.
+ * @param mixed $two Value in case $one is a string (which then works as the key).
+ * 				Unused if $one is an associative array, otherwise serves as the values to $one's keys.
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/427/set
+ */
+	function set($one, $two = null) {
+		$data = array();
+
+		if (is_array($one)) {
+			if (is_array($two)) {
+				$data = array_combine($one, $two);
+			} else {
+				$data = $one;
+			}
+		} else {
+			$data = array($one => $two);
+		}
+
+		foreach ($data as $name => $value) {
+			if ($name === 'title') {
+				$this->pageTitle = $value;
+			} else {
+				if ($two === null && is_array($one)) {
+					$this->viewVars[Inflector::variable($name)] = $value;
+				} else {
+					$this->viewVars[$name] = $value;
+				}
+			}
+		}
+	}
+/**
+ * Internally redirects one action to another. Examples:
+ *
+ * setAction('another_action');
+ * setAction('action_with_parameters', $parameter1);
+ *
+ * @param string $action The new action to be redirected to
+ * @param mixed  Any other parameters passed to this method will be passed as
+ *               parameters to the new action.
+ * @return mixed Returns the return value of the called action
+ * @access public
+ */
+	function setAction($action) {
+		$this->action = $action;
+		$args = func_get_args();
+		unset($args[0]);
+		return call_user_func_array(array(&$this, $action), $args);
+	}
+/**
+ * Controller callback to tie into Auth component. Only called when AuthComponent::authorize is set to 'controller'.
+ *
+ * @return bool true if authorized, false otherwise
+ * @access public
+ * @link http://book.cakephp.org/view/396/authorize
+ */
+	function isAuthorized() {
+		trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), $this->name), E_USER_WARNING);
+		return false;
+	}
+/**
+ * Returns number of errors in a submitted FORM.
+ *
+ * @return integer Number of errors
+ * @access public
+ */
+	function validate() {
+		$args = func_get_args();
+		$errors = call_user_func_array(array(&$this, 'validateErrors'), $args);
+
+		if ($errors === false) {
+			return 0;
+		}
+		return count($errors);
+	}
+/**
+ * Validates models passed by parameters. Example:
+ *
+ * $errors = $this->validateErrors($this->Article, $this->User);
+ *
+ * @param mixed A list of models as a variable argument
+ * @return array Validation errors, or false if none
+ * @access public
+ */
+	function validateErrors() {
+		$objects = func_get_args();
+
+		if (!count($objects)) {
+			return false;
+		}
+
+		$errors = array();
+		foreach ($objects as $object) {
+			$this->{$object->alias}->set($object->data);
+			$errors = array_merge($errors, $this->{$object->alias}->invalidFields());
+		}
+
+		return $this->validationErrors = (count($errors) ? $errors : false);
+	}
+/**
+ * Instantiates the correct view class, hands it its data, and uses it to render the view output.
+ *
+ * @param string $action Action name to render
+ * @param string $layout Layout to use
+ * @param string $file File to use for rendering
+ * @return string Full output string of view contents
+ * @access public
+ * @link http://book.cakephp.org/view/428/render
+ */
+	function render($action = null, $layout = null, $file = null) {
+		$this->beforeRender();
+
+		$viewClass = $this->view;
+		if ($this->view != 'View') {
+			if (strpos($viewClass, '.') !== false) {
+				list($plugin, $viewClass) = explode('.', $viewClass);
+			}
+			$viewClass = $viewClass . 'View';
+			App::import('View', $this->view);
+		}
+
+		$this->Component->beforeRender($this);
+
+		$this->params['models'] = $this->modelNames;
+
+		if (Configure::read() > 2) {
+			$this->set('cakeDebug', $this);
+		}
+
+		$View =& new $viewClass($this);
+
+		if (!empty($this->modelNames)) {
+			$models = array();
+			foreach ($this->modelNames as $currentModel) {
+				if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model')) {
+					$models[] = Inflector::underscore($currentModel);
+				}
+				if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model') && !empty($this->$currentModel->validationErrors)) {
+					$View->validationErrors[Inflector::camelize($currentModel)] =& $this->$currentModel->validationErrors;
+				}
+			}
+			$models = array_diff(ClassRegistry::keys(), $models);
+			foreach ($models as $currentModel) {
+				if (ClassRegistry::isKeySet($currentModel)) {
+					$currentObject =& ClassRegistry::getObject($currentModel);
+					if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
+						$View->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors;
+					}
+				}
+			}
+		}
+
+		$this->autoRender = false;
+		$this->output .= $View->render($action, $layout, $file);
+
+		return $this->output;
+	}
+/**
+ * Returns the referring URL for this request.
+ *
+ * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
+ * @param boolean $local If true, restrict referring URLs to local server
+ * @return string Referring URL
+ * @access public
+ * @link http://book.cakephp.org/view/430/referer
+ */
+	function referer($default = null, $local = false) {
+		$ref = env('HTTP_REFERER');
+		if (!empty($ref) && defined('FULL_BASE_URL')) {
+			$base = FULL_BASE_URL . $this->webroot;
+			if (strpos($ref, $base) === 0) {
+				$return =  substr($ref, strlen($base));
+				if ($return[0] != '/') {
+					$return = '/'.$return;
+				}
+				return $return;
+			} elseif (!$local) {
+				return $ref;
+			}
+		}
+
+		if ($default != null) {
+			return $default;
+		}
+		return '/';
+	}
+/**
+ * Forces the user's browser not to cache the results of the current request.
+ *
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/431/disableCache
+ */
+	function disableCache() {
+		header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+		header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+		header("Cache-Control: no-store, no-cache, must-revalidate");
+		header("Cache-Control: post-check=0, pre-check=0", false);
+		header("Pragma: no-cache");
+	}
+/**
+ * Shows a message to the user for $pause seconds, then redirects to $url.
+ * Uses flash.ctp as the default layout for the message.
+ * Does not work if the current debug level is higher than 0.
+ *
+ * @param string $message Message to display to the user
+ * @param string $url Relative URL to redirect to after the time expires
+ * @param integer $pause Time to show the message
+ * @return void Renders flash layout
+ * @access public
+ * @link http://book.cakephp.org/view/426/flash
+ */
+	function flash($message, $url, $pause = 1) {
+		$this->autoRender = false;
+		$this->set('url', Router::url($url));
+		$this->set('message', $message);
+		$this->set('pause', $pause);
+		$this->set('page_title', $message);
+		$this->render(false, 'flash');
+	}
+/**
+ * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call.
+ *
+ * @param array $data POST'ed data organized by model and field
+ * @param mixed $op A string containing an SQL comparison operator, or an array matching operators to fields
+ * @param string $bool SQL boolean operator: AND, OR, XOR, etc.
+ * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be included in the returned conditions
+ * @return array An array of model conditions
+ * @access public
+ * @link http://book.cakephp.org/view/432/postConditions
+ */
+	function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
+		if (!is_array($data) || empty($data)) {
+			if (!empty($this->data)) {
+				$data = $this->data;
+			} else {
+				return null;
+			}
+		}
+		$cond = array();
+
+		if ($op === null) {
+			$op = '';
+		}
+
+		foreach ($data as $model => $fields) {
+			foreach ($fields as $field => $value) {
+				$key = $model.'.'.$field;
+				$fieldOp = $op;
+				if (is_array($op) && array_key_exists($key, $op)) {
+					$fieldOp = $op[$key];
+				} elseif (is_array($op) && array_key_exists($field, $op)) {
+					$fieldOp = $op[$field];
+				} elseif (is_array($op)) {
+					$fieldOp = false;
+				}
+				if ($exclusive && $fieldOp === false) {
+					continue;
+				}
+				$fieldOp = strtoupper(trim($fieldOp));
+				if ($fieldOp === 'LIKE') {
+					$key = $key.' LIKE';
+					$value = '%'.$value.'%';
+				} elseif ($fieldOp && $fieldOp != '=') {
+					$key = $key.' '.$fieldOp;
+				}
+				$cond[$key] = $value;
+			}
+		}
+		if ($bool != null && strtoupper($bool) != 'AND') {
+			$cond = array($bool => $cond);
+		}
+		return $cond;
+	}
+/**
+ * Handles automatic pagination of model records.
+ *
+ * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
+ * @param mixed $scope Conditions to use while paginating
+ * @param array $whitelist List of allowed options for paging
+ * @return array Model query results
+ * @access public
+ * @link http://book.cakephp.org/view/165/Controller-Setup
+ */
+	function paginate($object = null, $scope = array(), $whitelist = array()) {
+		if (is_array($object)) {
+			$whitelist = $scope;
+			$scope = $object;
+			$object = null;
+		}
+		$assoc = null;
+
+		if (is_string($object)) {
+			$assoc = null;
+
+			if (strpos($object, '.') !== false) {
+				list($object, $assoc) = explode('.', $object);
+			}
+
+			if ($assoc && isset($this->{$object}->{$assoc})) {
+				$object = $this->{$object}->{$assoc};
+			} elseif ($assoc && isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$assoc})) {
+				$object = $this->{$this->modelClass}->{$assoc};
+			} elseif (isset($this->{$object})) {
+				$object = $this->{$object};
+			} elseif (isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$object})) {
+				$object = $this->{$this->modelClass}->{$object};
+			}
+		} elseif (empty($object) || $object === null) {
+			if (isset($this->{$this->modelClass})) {
+				$object = $this->{$this->modelClass};
+			} else {
+				$className = null;
+				$name = $this->uses[0];
+				if (strpos($this->uses[0], '.') !== false) {
+					list($name, $className) = explode('.', $this->uses[0]);
+				}
+				if ($className) {
+					$object = $this->{$className};
+				} else {
+					$object = $this->{$name};
+				}
+			}
+		}
+
+		if (!is_object($object)) {
+			trigger_error(sprintf(__('Controller::paginate() - can\'t find model %1$s in controller %2$sController', true), $object, $this->name), E_USER_WARNING);
+			return array();
+		}
+		$options = array_merge($this->params, $this->params['url'], $this->passedArgs);
+
+		if (isset($this->paginate[$object->alias])) {
+			$defaults = $this->paginate[$object->alias];
+		} else {
+			$defaults = $this->paginate;
+		}
+
+		if (isset($options['show'])) {
+			$options['limit'] = $options['show'];
+		}
+
+		if (isset($options['sort'])) {
+			$direction = null;
+			if (isset($options['direction'])) {
+				$direction = strtolower($options['direction']);
+			}
+			if ($direction != 'asc' && $direction != 'desc') {
+				$direction = 'asc';
+			}
+			$options['order'] = array($options['sort'] => $direction);
+		}
+
+		if (!empty($options['order']) && is_array($options['order'])) {
+			$alias = $object->alias ;
+			$key = $field = key($options['order']);
+
+			if (strpos($key, '.') !== false) {
+				list($alias, $field) = explode('.', $key);
+			}
+			$value = $options['order'][$key];
+			unset($options['order'][$key]);
+
+			if (isset($object->{$alias}) && $object->{$alias}->hasField($field)) {
+				$options['order'][$alias . '.' . $field] = $value;
+			} elseif ($object->hasField($field)) {
+				$options['order'][$alias . '.' . $field] = $value;
+			}
+		}
+		$vars = array('fields', 'order', 'limit', 'page', 'recursive');
+		$keys = array_keys($options);
+		$count = count($keys);
+
+		for ($i = 0; $i < $count; $i++) {
+			if (!in_array($keys[$i], $vars, true)) {
+				unset($options[$keys[$i]]);
+			}
+			if (empty($whitelist) && ($keys[$i] === 'fields' || $keys[$i] === 'recursive')) {
+				unset($options[$keys[$i]]);
+			} elseif (!empty($whitelist) && !in_array($keys[$i], $whitelist)) {
+				unset($options[$keys[$i]]);
+			}
+		}
+		$conditions = $fields = $order = $limit = $page = $recursive = null;
+
+		if (!isset($defaults['conditions'])) {
+			$defaults['conditions'] = array();
+		}
+		extract($options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options));
+
+		if (is_array($scope) && !empty($scope)) {
+			$conditions = array_merge($conditions, $scope);
+		} elseif (is_string($scope)) {
+			$conditions = array($conditions, $scope);
+		}
+		if ($recursive === null) {
+			$recursive = $object->recursive;
+		}
+		$type = 'all';
+
+		if (isset($defaults[0])) {
+			$type = array_shift($defaults);
+		}
+		$extra = array_diff_key($defaults, compact(
+			'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
+		));
+		if ($type !== 'all') {
+			$extra['type'] = $type;
+		}
+
+		if (method_exists($object, 'paginateCount')) {
+			$count = $object->paginateCount($conditions, $recursive, $extra);
+		} else {
+			$parameters = compact('conditions');
+			if ($recursive != $object->recursive) {
+				$parameters['recursive'] = $recursive;
+			}
+			$count = $object->find('count', array_merge($parameters, $extra));
+		}
+		$pageCount = intval(ceil($count / $limit));
+
+		if ($page === 'last' || $page >= $pageCount) {
+			$options['page'] = $page = $pageCount;
+		} elseif (intval($page) < 1) {
+			$options['page'] = $page = 1;
+		}
+
+		if (method_exists($object, 'paginate')) {
+			$results = $object->paginate($conditions, $fields, $order, $limit, $page, $recursive, $extra);
+		} else {
+			$parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
+			if ($recursive != $object->recursive) {
+				$parameters['recursive'] = $recursive;
+			}
+			$results = $object->find($type, array_merge($parameters, $extra));
+		}
+		$paging = array(
+			'page'		=> $page,
+			'current'	=> count($results),
+			'count'		=> $count,
+			'prevPage'	=> ($page > 1),
+			'nextPage'	=> ($count > ($page * $limit)),
+			'pageCount'	=> $pageCount,
+			'defaults'	=> array_merge(array('limit' => 20, 'step' => 1), $defaults),
+			'options'	=> $options
+		);
+		$this->params['paging'][$object->alias] = $paging;
+
+		if (!in_array('Paginator', $this->helpers) && !array_key_exists('Paginator', $this->helpers)) {
+			$this->helpers[] = 'Paginator';
+		}
+		return $results;
+	}
+/**
+ * Called before the controller action.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function beforeFilter() {
+	}
+/**
+ * Called after the controller action is run, but before the view is rendered.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function beforeRender() {
+	}
+/**
+ * Called after the controller action is run and rendered.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function afterFilter() {
+	}
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called example index, edit, etc.
+ * @return boolean Success
+ * @access protected
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function _beforeScaffold($method) {
+		return true;
+	}
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called either edit or update.
+ * @return boolean Success
+ * @access protected
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function _afterScaffoldSave($method) {
+		return true;
+	}
+/**
+ * This method should be overridden in child classes.
+ *
+ * @param string $method name of method called either edit or update.
+ * @return boolean Success
+ * @access protected
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function _afterScaffoldSaveError($method) {
+		return true;
+	}
+/**
+ * This method should be overridden in child classes.
+ * If not it will render a scaffold error.
+ * Method MUST return true in child classes
+ *
+ * @param string $method name of method called example index, edit, etc.
+ * @return boolean Success
+ * @access protected
+ * @link http://book.cakephp.org/view/60/Callbacks
+ */
+	function _scaffoldError($method) {
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/cache.php
===================================================================
--- cake/libs/cache.php	(revision 0)
+++ cake/libs/cache.php	(revision 0)
@@ -0,0 +1,519 @@
+<?php
+/* SVN FILE: $Id: cache.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Caching for CakePHP.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Caching for CakePHP.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Cache extends Object {
+/**
+ * Cache engine to use
+ *
+ * @var object
+ * @access protected
+ */
+	var $_Engine = null;
+/**
+ * Cache configuration stack
+ *
+ * @var array
+ * @access private
+ */
+	var $__config = array();
+/**
+ * Holds name of the current configuration being used
+ *
+ * @var array
+ * @access private
+ */
+	var $__name = 'default';
+/**
+ * whether to reset the settings with the next call to self::set();
+ *
+ * @var array
+ * @access private
+ */
+	var $__reset = false;
+/**
+ * Returns a singleton instance
+ *
+ * @return object
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new Cache();
+		}
+		return $instance[0];
+	}
+/**
+ * Tries to find and include a file for a cache engine and returns object instance
+ *
+ * @param $name	Name of the engine (without 'Engine')
+ * @return mixed $engine object or null
+ * @access private
+ */
+	function __loadEngine($name) {
+		if (!class_exists($name . 'Engine')) {
+			require LIBS . DS . 'cache' . DS . strtolower($name) . '.php';
+		}
+		return true;
+	}
+/**
+ * Set the cache configuration to use
+ *
+ * @see app/config/core.php for configuration settings
+ * @param string $name Name of the configuration
+ * @param array $settings Optional associative array of settings passed to the engine
+ * @return array(engine, settings) on success, false on failure
+ * @access public
+ * @static
+ */
+	function config($name = null, $settings = array()) {
+		$_this =& Cache::getInstance();
+		if (is_array($name)) {
+			$settings = $name;
+		}
+
+		if ($name === null || !is_string($name)) {
+			$name = $_this->__name;
+		}
+
+		$current = array();
+		if (isset($_this->__config[$name])) {
+			$current = $_this->__config[$name];
+		}
+
+		if (!empty($settings)) {
+			$_this->__name = null;
+			$_this->__config[$name] = array_merge($current, $settings);
+		}
+
+		if (empty($_this->__config[$name]['engine'])) {
+			return false;
+		}
+
+		$_this->__name = $name;
+		$engine = $_this->__config[$name]['engine'];
+
+		if (!$_this->isInitialized($engine)) {
+			if ($_this->engine($engine, $_this->__config[$name]) === false) {
+				return false;
+			}
+			$settings = $_this->__config[$name] = $_this->settings($engine);
+		} else {
+			$settings = $_this->__config[$name] = $_this->set($_this->__config[$name]);
+		}
+		return compact('engine', 'settings');
+	}
+/**
+ * Set the cache engine to use or modify settings for one instance
+ *
+ * @param string $name Name of the engine (without 'Engine')
+ * @param array $settings Optional associative array of settings passed to the engine
+ * @return boolean True on success, false on failure
+ * @access public
+ * @static
+ */
+	function engine($name = 'File', $settings = array()) {
+		if (!$name || Configure::read('Cache.disable')) {
+			return false;
+		}
+
+		$cacheClass = $name . 'Engine';
+		$_this =& Cache::getInstance();
+		if (!isset($_this->_Engine[$name])) {
+			if ($_this->__loadEngine($name) === false) {
+				return false;
+			}
+			$_this->_Engine[$name] =& new $cacheClass();
+		}
+
+		if ($_this->_Engine[$name]->init($settings)) {
+			if (time() % $_this->_Engine[$name]->settings['probability'] === 0) {
+				$_this->_Engine[$name]->gc();
+			}
+			return true;
+		}
+		$_this->_Engine[$name] = null;
+		return false;
+	}
+/**
+ * Temporarily change settings to current config options. if no params are passed, resets settings if needed
+ * Cache::write() will reset the configuration changes made
+ *
+ * @param mixed $settings Optional string for simple name-value pair or array
+ * @param string $value Optional for a simple name-value pair
+ * @return array of settings
+ * @access public
+ * @static
+ */
+	function set($settings = array(), $value = null) {
+		$_this =& Cache::getInstance();
+		if (!isset($_this->__config[$_this->__name])) {
+			return false;
+		}
+
+		$engine = $_this->__config[$_this->__name]['engine'];
+
+		if (!empty($settings)) {
+			$_this->__reset = true;
+		}
+
+		if ($_this->__reset === true) {
+			if (empty($settings)) {
+				$_this->__reset = false;
+				$settings = $_this->__config[$_this->__name];
+			} else {
+				if (is_string($settings) && $value !== null) {
+					$settings = array($settings => $value);
+				}
+				$settings = array_merge($_this->__config[$_this->__name], $settings);
+			}
+			$_this->_Engine[$engine]->init($settings);
+		}
+
+		return $_this->settings($engine);
+	}
+/**
+ * Garbage collection
+ *
+ * Permanently remove all expired and deleted data
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+	function gc() {
+		$_this =& Cache::getInstance();
+		$config = $_this->config();
+		$_this->_Engine[$config['engine']]->gc();
+	}
+/**
+ * Write data for key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached - anything except a resource
+ * @param mixed $config Optional - string configuration name, a duration for expiration,
+ *				or array('config' => 'string configuration name', 'duration' => 'duration for expiration')
+ * @return boolean True if the data was successfully cached, false on failure
+ * @access public
+ * @static
+ */
+	function write($key, $value, $config = null) {
+		$_this =& Cache::getInstance();
+		$thisDuration = null;
+		if (is_array($config)) {
+			extract($config);
+		} else if ($config && (is_numeric($config) || is_numeric($config[0]) || (isset($config[1]) && is_numeric($config[1])))) {
+			$thisDuration = $config;
+			$config = null;
+		}
+
+		if ($config && isset($_this->__config[$config])) {
+			$settings = $_this->set($_this->__config[$config]);
+		} else {
+			$settings = $_this->settings();
+		}
+
+		if (empty($settings)) {
+			return null;
+		}
+		extract($settings);
+
+		if (!$_this->isInitialized($engine)) {
+			return false;
+		}
+
+		if (!$key = $_this->_Engine[$engine]->key($key)) {
+			return false;
+		}
+
+		if (is_resource($value)) {
+			return false;
+		}
+
+		if ($thisDuration !== null) {
+			if (!is_numeric($thisDuration)) {
+				$thisDuration = strtotime($thisDuration) - time();
+			}
+			$duration = $thisDuration;
+		}
+
+		if ($duration < 1) {
+			return false;
+		}
+
+		$success = $_this->_Engine[$engine]->write($settings['prefix'] . $key, $value, $duration);
+		$settings = $_this->set();
+		return $success;
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @param string $config name of the configuration to use
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ * @static
+ */
+	function read($key, $config = null) {
+		$_this =& Cache::getInstance();
+
+		if (isset($_this->__config[$config])) {
+			$settings = $_this->set($_this->__config[$config]);
+		} else {
+			$settings = $_this->settings();
+		}
+
+		if (empty($settings)) {
+			return null;
+		}
+		extract($settings);
+
+		if (!$_this->isInitialized($engine)) {
+			return false;
+		}
+		if (!$key = $_this->_Engine[$engine]->key($key)) {
+			return false;
+		}
+		$success = $_this->_Engine[$engine]->read($settings['prefix'] . $key);
+
+		if ($config !== null && $config !== $_this->__name) {
+			$settings = $_this->set();
+		}
+		return $success;
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @param string $config name of the configuration to use
+ * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ * @static
+ */
+	function delete($key, $config = null) {
+		$_this =& Cache::getInstance();
+		if (isset($_this->__config[$config])) {
+			$settings = $_this->set($_this->__config[$config]);
+		} else {
+			$settings = $_this->settings();
+		}
+
+		if (empty($settings)) {
+			return null;
+		}
+		extract($settings);
+
+		if (!$_this->isInitialized($engine)) {
+			return false;
+		}
+
+		if (!$key = $_this->_Engine[$engine]->key($key)) {
+			return false;
+		}
+
+		$success = $_this->_Engine[$engine]->delete($settings['prefix'] . $key);
+		$settings = $_this->set();
+		return $success;
+	}
+/**
+ * Delete all keys from the cache
+ *
+ * @param boolean $check if true will check expiration, otherwise delete all
+ * @param string $config name of the configuration to use
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ * @static
+ */
+	function clear($check = false, $config = null) {
+		$_this =& Cache::getInstance();
+		if (isset($_this->__config[$config])) {
+			$settings = $_this->set($_this->__config[$config]);
+		} else {
+			$settings = $_this->settings();
+		}
+
+		if (empty($settings)) {
+			return null;
+		}
+		extract($settings);
+
+		if (isset($engine) && !$_this->isInitialized($engine)) {
+			return false;
+		}
+		$success = $_this->_Engine[$engine]->clear($check);
+		$settings = $_this->set();
+		return $success;
+	}
+/**
+ * Check if Cache has initialized a working storage engine
+ *
+ * @param string $engine Name of the engine
+ * @param string $config Name of the configuration setting
+ * @return bool
+ * @access public
+ * @static
+ */
+	function isInitialized($engine = null) {
+		if (Configure::read('Cache.disable')) {
+			return false;
+		}
+		$_this =& Cache::getInstance();
+		if (!$engine && isset($_this->__config[$_this->__name]['engine'])) {
+			$engine = $_this->__config[$_this->__name]['engine'];
+		}
+		return isset($_this->_Engine[$engine]);
+	}
+
+/**
+ * Return the settings for current cache engine
+ *
+ * @param string $engine Name of the engine
+ * @return array list of settings for this engine
+ * @access public
+ * @static
+ */
+	function settings($engine = null) {
+		$_this =& Cache::getInstance();
+		if (!$engine && isset($_this->__config[$_this->__name]['engine'])) {
+			$engine = $_this->__config[$_this->__name]['engine'];
+		}
+
+		if (isset($_this->_Engine[$engine]) && !is_null($_this->_Engine[$engine])) {
+			return $_this->_Engine[$engine]->settings();
+		}
+		return array();
+	}
+}
+/**
+ * Storage engine for CakePHP caching
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CacheEngine extends Object {
+/**
+ * settings of current engine instance
+ *
+ * @var int
+ * @access public
+ */
+	var $settings = array();
+/**
+ * Iitialize the cache engine
+ *
+ * Called automatically by the cache frontend
+ *
+ * @param array $params Associative array of parameters for the engine
+ * @return boolean True if the engine has been succesfully initialized, false if not
+ * @access public
+ */
+	function init($settings = array()) {
+		$this->settings = array_merge(array('prefix' => 'cake_', 'duration'=> 3600, 'probability'=> 100), $this->settings, $settings);
+		if (!is_numeric($this->settings['duration'])) {
+			$this->settings['duration'] = strtotime($this->settings['duration']) - time();
+		}
+		return true;
+	}
+/**
+ * Garbage collection
+ *
+ * Permanently remove all expired and deleted data
+ *
+ * @access public
+ */
+	function gc() {
+	}
+/**
+ * Write value for a key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached
+ * @param mixed $duration How long to cache the data, in seconds
+ * @return boolean True if the data was succesfully cached, false on failure
+ * @access public
+ */
+	function write($key, &$value, $duration) {
+		trigger_error(sprintf(__('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ */
+	function read($key) {
+		trigger_error(sprintf(__('Method read() not implemented in %s', true), get_class($this)), E_USER_ERROR);
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ */
+	function delete($key) {
+	}
+/**
+ * Delete all keys from the cache
+ *
+ * @param boolean $check if true will check expiration, otherwise delete all
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ */
+	function clear($check) {
+	}
+/**
+ * Cache Engine settings
+ *
+ * @return array settings
+ * @access public
+ */
+	function settings() {
+		return $this->settings;
+	}
+/**
+ * generates a safe key
+ *
+ * @param string $key the key passed over
+ * @return mixed string $key or false
+ * @access public
+ */
+	function key($key) {
+		if (empty($key)) {
+			return false;
+		}
+		$key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key)));
+		return $key;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/connection_manager.php
===================================================================
--- cake/libs/model/connection_manager.php	(revision 0)
+++ cake/libs/model/connection_manager.php	(revision 0)
@@ -0,0 +1,262 @@
+<?php
+/* SVN FILE: $Id: connection_manager.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.10.x.1402
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+uses ('model' . DS . 'datasources' . DS . 'datasource');
+config('database');
+
+/**
+ * Manages loaded instances of DataSource objects
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class ConnectionManager extends Object {
+/**
+ * Holds a loaded instance of the Connections object
+ *
+ * @var object
+ * @access public
+ */
+	var $config = null;
+/**
+ * Holds instances DataSource objects
+ *
+ * @var array
+ * @access protected
+ */
+	var $_dataSources = array();
+/**
+ * Contains a list of all file and class names used in Connection settings
+ *
+ * @var array
+ * @access protected
+ */
+	var $_connectionsEnum = array();
+/**
+ * Constructor.
+ *
+ */
+	function __construct() {
+		if (class_exists('DATABASE_CONFIG')) {
+			$this->config =& new DATABASE_CONFIG();
+		}
+	}
+/**
+ * Gets a reference to the ConnectionManger object instance
+ *
+ * @return object Instance
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new ConnectionManager();
+		}
+
+		return $instance[0];
+	}
+/**
+ * Gets a reference to a DataSource object
+ *
+ * @param string $name The name of the DataSource, as defined in app/config/connections
+ * @return object Instance
+ * @access public
+ * @static
+ */
+	function &getDataSource($name) {
+		$_this =& ConnectionManager::getInstance();
+
+		if (!empty($_this->_dataSources[$name])) {
+			$return =& $_this->_dataSources[$name];
+			return $return;
+		}
+
+		$connections = $_this->enumConnectionObjects();
+		if (!empty($connections[$name])) {
+			$conn = $connections[$name];
+			$class = $conn['classname'];
+			$_this->loadDataSource($name);
+			$_this->_dataSources[$name] =& new $class($_this->config->{$name});
+			$_this->_dataSources[$name]->configKeyName = $name;
+		} else {
+			trigger_error(sprintf(__("ConnectionManager::getDataSource - Non-existent data source %s", true), $name), E_USER_ERROR);
+			return null;
+		}
+
+		$return =& $_this->_dataSources[$name];
+		return $return;
+	}
+/**
+ * Gets the list of available DataSource connections
+ *
+ * @return array List of available connections
+ * @access public
+ * @static
+ */
+	function sourceList() {
+		$_this =& ConnectionManager::getInstance();
+		return array_keys($_this->_dataSources);
+	}
+/**
+ * Gets a DataSource name from an object reference
+ *
+ * @param object $source DataSource object
+ * @return string Datasource name
+ * @access public
+ * @static
+ */
+	function getSourceName(&$source) {
+		$_this =& ConnectionManager::getInstance();
+		$names = array_keys($_this->_dataSources);
+		for ($i = 0; $i < count($names); $i++) {
+			if ($_this->_dataSources[$names[$i]] === $source) {
+				return $names[$i];
+			}
+		}
+		return null;
+	}
+/**
+ * Loads the DataSource class for the given connection name
+ *
+ * @param mixed $connName A string name of the connection, as defined in Connections config,
+ *                        or an array containing the file and class name of the object.
+ * @return boolean True on success, null on failure or false if the class is already loaded
+ * @access public
+ * @static
+ */
+	function loadDataSource($connName) {
+		$_this =& ConnectionManager::getInstance();
+
+		if (is_array($connName)) {
+			$conn = $connName;
+		} else {
+			$connections = $_this->enumConnectionObjects();
+			$conn = $connections[$connName];
+		}
+
+		if (!empty($conn['parent'])) {
+			$_this->loadDataSource($conn['parent']);
+		}
+
+		if (class_exists($conn['classname'])) {
+			return false;
+		}
+
+		if (file_exists(MODELS . 'datasources' . DS . $conn['filename'] . '.php')) {
+			require (MODELS . 'datasources' . DS . $conn['filename'] . '.php');
+		} elseif (fileExistsInPath(LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php')) {
+			require (LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php');
+		} else {
+		    $error = __('Unable to load DataSource file %s.php', true);
+			trigger_error(sprintf($error, $conn['filename']), E_USER_ERROR);
+			return null;
+		}
+	}
+/**
+ * Gets a list of class and file names associated with the user-defined DataSource connections
+ *
+ * @return array An associative array of elements where the key is the connection name
+ *               (as defined in Connections), and the value is an array with keys 'filename' and 'classname'.
+ * @access public
+ * @static
+ */
+	function enumConnectionObjects() {
+		$_this =& ConnectionManager::getInstance();
+
+		if (!empty($_this->_connectionsEnum)) {
+			return $_this->_connectionsEnum;
+		}
+		$connections = get_object_vars($_this->config);
+
+		if ($connections != null) {
+			foreach ($connections as $name => $config) {
+				$_this->_connectionsEnum[$name] = $_this->__getDriver($config);
+			}
+			return $_this->_connectionsEnum;
+		} else {
+			$_this->cakeError('missingConnection', array(array('className' => 'ConnectionManager')));
+		}
+	}
+/**
+ * Dynamically creates a DataSource object at runtime, with the given name and settings
+ *
+ * @param string $name The DataSource name
+ * @param array $config The DataSource configuration settings
+ * @return object A reference to the DataSource object, or null if creation failed
+ * @access public
+ * @static
+ */
+	function &create($name = '', $config = array()) {
+		$_this =& ConnectionManager::getInstance();
+
+		if (empty($name) || empty($config) || array_key_exists($name, $_this->_connectionsEnum)) {
+			$null = null;
+			return $null;
+		}
+
+		$_this->config->{$name} = $config;
+		$_this->_connectionsEnum[$name] = $_this->__getDriver($config);
+		$return =& $_this->getDataSource($name);
+		return $return;
+	}
+/**
+ * Returns the file, class name, and parent for the given driver.
+ *
+ * @return array An indexed array with: filename, classname, and parent
+ * @access private
+ */
+	function __getDriver($config) {
+		if (!isset($config['datasource'])) {
+			$config['datasource'] = 'dbo';
+		}
+
+		if (isset($config['driver']) && $config['driver'] != null && !empty($config['driver'])) {
+			$filename = $config['datasource'] . DS . $config['datasource'] . '_' . $config['driver'];
+			$classname = Inflector::camelize(strtolower($config['datasource'] . '_' . $config['driver']));
+			$parent = $this->__getDriver(array('datasource' => $config['datasource']));
+		} else {
+			$filename = $config['datasource'] . '_source';
+			$classname = Inflector::camelize(strtolower($config['datasource'] . '_source'));
+			$parent = null;
+		}
+		return array('filename'  => $filename, 'classname' => $classname, 'parent' => $parent);
+	}
+/**
+ * Destructor.
+ *
+ * @access private
+ */
+	function __destruct() {
+		if (Configure::read('Session.save') == 'database' && function_exists('session_write_close')) {
+			session_write_close();
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/behaviors/translate.php
===================================================================
--- cake/libs/model/behaviors/translate.php	(revision 0)
+++ cake/libs/model/behaviors/translate.php	(revision 0)
@@ -0,0 +1,510 @@
+<?php
+/* SVN FILE: $Id: translate.php 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP(tm) v 1.2.0.4525
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ */
+class TranslateBehavior extends ModelBehavior {
+/**
+ * Used for runtime configuration of model
+ */
+	var $runtime = array();
+/**
+ * Callback
+ *
+ * $config for TranslateBehavior should be
+ * array( 'fields' => array('field_one',
+ * 'field_two' => 'FieldAssoc', 'field_three'))
+ *
+ * With above example only one permanent hasMany will be joined (for field_two
+ * as FieldAssoc)
+ *
+ * $config could be empty - and translations configured dynamically by
+ * bindTranslation() method
+ *
+ * @param array $config
+ * @return mixed
+ * @access public
+ */
+	function setup(&$model, $config = array()) {
+		$db =& ConnectionManager::getDataSource($model->useDbConfig);
+		if (!$db->connected) {
+			trigger_error(
+				sprintf(__('Datasource %s for TranslateBehavior of model %s is not connected', true), $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);
+	}
+/**
+ * Callback
+ *
+ * @return void
+ * @access public
+ */
+	function cleanup(&$model) {
+		$this->unbindTranslation($model);
+		unset($this->settings[$model->alias]);
+		unset($this->runtime[$model->alias]);
+	}
+/**
+ * beforeFind Callback
+ *
+ * @param array $query
+ * @return array Modified query
+ * @access public
+ */
+	function beforeFind(&$model, $query) {
+		$locale = $this->_getLocale($model);
+		if (empty($locale)) {
+			return $query;
+		}
+		$db =& ConnectionManager::getDataSource($model->useDbConfig);
+		$tablePrefix = $db->config['prefix'];
+		$RuntimeModel =& $this->translateModel($model);
+
+		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';
+			$query['joins'][] = array(
+				'type' => 'INNER',
+				'alias' => $RuntimeModel->alias,
+				'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
+				'conditions' => array(
+					$model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'),
+					$RuntimeModel->alias.'.model' => $model->name,
+					$RuntimeModel->alias.'.locale' => $locale
+				)
+			);
+			return $query;
+		}
+		$autoFields = false;
+
+		if (empty($query['fields'])) {
+			$query['fields'] = array($model->alias.'.*');
+
+			$recursive = $model->recursive;
+			if (isset($query['recursive'])) {
+				$recursive = $query['recursive'];
+			}
+
+			if ($recursive >= 0) {
+				foreach (array('hasOne', 'belongsTo') as $type) {
+					foreach ($model->{$type} as $key => $value) {
+
+						if (empty($value['fields'])) {
+							$query['fields'][] = $key.'.*';
+						} else {
+							foreach ($value['fields'] as $field) {
+								$query['fields'][] = $key.'.'.$field;
+							}
+						}
+					}
+				}
+			}
+			$autoFields = true;
+		}
+		$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
+		$addFields = array();
+		if (is_array($query['fields'])) {
+			foreach ($fields as $key => $value) {
+				$field = (is_numeric($key)) ? $value : $key;
+
+				if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
+					$addFields[] = $field;
+				}
+			}
+		}
+
+		if ($addFields) {
+			foreach ($addFields as $field) {
+				foreach (array($field, $model->alias.'.'.$field) as $_field) {
+					$key = array_search($_field, $query['fields']);
+
+					if ($key !== false) {
+						unset($query['fields'][$key]);
+					}
+				}
+
+				if (is_array($locale)) {
+					foreach ($locale as $_locale) {
+						$query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
+						$query['joins'][] = array(
+							'type' => 'LEFT',
+							'alias' => 'I18n__'.$field.'__'.$_locale,
+							'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
+							'conditions' => array(
+								$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
+								'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
+								'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
+								'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
+							)
+						);
+					}
+				} else {
+					$query['fields'][] = 'I18n__'.$field.'.content';
+					$query['joins'][] = array(
+						'type' => 'LEFT',
+						'alias' => 'I18n__'.$field,
+						'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
+						'conditions' => array(
+							$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
+							'I18n__'.$field.'.model' => $model->name,
+							'I18n__'.$field.'.'.$RuntimeModel->displayField => $field
+						)
+					);
+
+					if (is_string($query['conditions'])) {
+						$query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\'';
+					} else {
+						$query['conditions'][$db->name("I18n__{$field}.locale")] = $locale;
+					}
+				}
+			}
+		}
+		if (is_array($query['fields'])) {
+			$query['fields'] = array_merge($query['fields']);
+		}
+		$this->runtime[$model->alias]['beforeFind'] = $addFields;
+		return $query;
+	}
+/**
+ * afterFind Callback
+ *
+ * @param array $results
+ * @param boolean $primary
+ * @return array Modified results
+ * @access public
+ */
+	function afterFind(&$model, $results, $primary) {
+		$this->runtime[$model->alias]['fields'] = array();
+		$locale = $this->_getLocale($model);
+
+		if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
+			return $results;
+		}
+		$beforeFind = $this->runtime[$model->alias]['beforeFind'];
+
+		foreach ($results as $key => $row) {
+			$results[$key][$model->alias]['locale'] = (is_array($locale)) ? @$locale[0] : $locale;
+
+			foreach ($beforeFind as $field) {
+				if (is_array($locale)) {
+					foreach ($locale as $_locale) {
+						if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
+							$results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
+						}
+						unset($results[$key]['I18n__'.$field.'__'.$_locale]);
+					}
+
+					if (!isset($results[$key][$model->alias][$field])) {
+						$results[$key][$model->alias][$field] = '';
+					}
+				} else {
+					$value = '';
+					if (!empty($results[$key]['I18n__'.$field]['content'])) {
+						$value = $results[$key]['I18n__'.$field]['content'];
+					}
+					$results[$key][$model->alias][$field] = $value;
+					unset($results[$key]['I18n__'.$field]);
+				}
+			}
+		}
+		return $results;
+	}
+/**
+ * beforeValidate Callback
+ *
+ * @return boolean
+ * @access public
+ */
+	function beforeValidate(&$model) {
+		$locale = $this->_getLocale($model);
+		if (empty($locale)) {
+			return true;
+		}
+		$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];
+					} else {
+						$values = array_values($model->data[$model->alias][$field]);
+						$model->data[$model->alias][$field] = $values[0];
+					}
+				}
+			}
+		}
+		$this->runtime[$model->alias]['beforeSave'] = $tempData;
+		return true;
+	}
+/**
+ * afterSave Callback
+ *
+ * @param boolean $created
+ * @return void
+ * @access public
+ */
+	function afterSave(&$model, $created) {
+		if (!isset($this->runtime[$model->alias]['beforeSave'])) {
+			return true;
+		}
+		$locale = $this->_getLocale($model);
+		$tempData = $this->runtime[$model->alias]['beforeSave'];
+		unset($this->runtime[$model->alias]['beforeSave']);
+		$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
+		$RuntimeModel =& $this->translateModel($model);
+
+		foreach ($tempData as $field => $value) {
+			unset($conditions['content']);
+			$conditions['field'] = $field;
+			if (is_array($value)) {
+				$conditions['locale'] = array_keys($value);
+			} else {
+				$conditions['locale'] = $locale;
+				if (is_array($locale)) {
+					$value = array($locale[0] => $value);
+				} else {
+					$value = array($locale => $value);
+				}
+			}
+			$translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id')));
+			foreach ($value as $_locale => $_value) {
+				$RuntimeModel->create();
+				$conditions['locale'] = $_locale;
+				$conditions['content'] = $_value;
+				if (array_key_exists($_locale, $translations)) {
+					$RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale]))));
+				} else {
+					$RuntimeModel->save(array($RuntimeModel->alias => $conditions));
+				}
+			}
+		}
+	}
+/**
+ * afterDelete Callback
+ *
+ * @return void
+ * @access public
+ */
+	function afterDelete(&$model) {
+		$RuntimeModel =& $this->translateModel($model);
+		$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
+		$RuntimeModel->deleteAll($conditions);
+	}
+/**
+ * Get selected locale for model
+ *
+ * @return mixed string or false
+ * @access protected
+ */
+	function _getLocale(&$model) {
+		if (!isset($model->locale) || is_null($model->locale)) {
+			if (!class_exists('I18n')) {
+				App::import('Core', 'i18n');
+			}
+			$I18n =& I18n::getInstance();
+			$I18n->l10n->get(Configure::read('Config.language'));
+			$model->locale = $I18n->l10n->locale;
+		}
+
+		return $model->locale;
+	}
+/**
+ * Get instance of model for translations
+ *
+ * @return object
+ * @access public
+ */
+	function &translateModel(&$model) {
+		if (!isset($this->runtime[$model->alias]['model'])) {
+			if (!isset($model->translateModel) || empty($model->translateModel)) {
+				$className = 'I18nModel';
+			} else {
+				$className = $model->translateModel;
+			}
+
+			if (PHP5) {
+				$this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
+			} else {
+				$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);
+		}
+		return $this->runtime[$model->alias]['model'];
+	}
+/**
+ * Bind translation for fields, optionally with hasMany association for
+ * fake field
+ *
+ * @param object instance of model
+ * @param mixed string with field or array(field1, field2=>AssocName, field3)
+ * @param boolean $reset
+ * @return bool
+ */
+	function bindTranslation(&$model, $fields, $reset = true) {
+		if (is_string($fields)) {
+			$fields = array($fields);
+		}
+		$associations = array();
+		$RuntimeModel =& $this->translateModel($model);
+		$default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
+
+		foreach ($fields as $key => $value) {
+			if (is_numeric($key)) {
+				$field = $value;
+				$association = null;
+			} else {
+				$field = $key;
+				$association = $value;
+			}
+
+			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_assoc($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_assoc($this->runtime[$model->alias]['fields'], array($field)));
+			}
+
+			if (is_null($association)) {
+				if ($reset) {
+					$this->runtime[$model->alias]['fields'][] = $field;
+				} else {
+					$this->settings[$model->alias][] = $field;
+				}
+			} else {
+				if ($reset) {
+					$this->runtime[$model->alias]['fields'][$field] = $association;
+				} else {
+					$this->settings[$model->alias][$field] = $association;
+				}
+
+				foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
+					if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
+						trigger_error(
+							sprintf(__('Association %s is already binded to model %s', true), $association, $model->alias),
+							E_USER_ERROR
+						);
+						return false;
+					}
+				}
+				$associations[$association] = array_merge($default, array('conditions' => array(
+					'model' => $model->alias,
+					$RuntimeModel->displayField => $field
+				)));
+			}
+		}
+
+		if (!empty($associations)) {
+			$model->bindModel(array('hasMany' => $associations), $reset);
+		}
+		return true;
+	}
+/**
+ * Unbind translation for fields, optionally unbinds hasMany association for
+ * fake field
+ *
+ * @param object instance of model
+ * @param mixed string with field, or array(field1, field2=>AssocName, field3), or null for unbind all original translations
+ * @return bool
+ */
+	function unbindTranslation(&$model, $fields = null) {
+		if (empty($fields)) {
+			return $this->unbindTranslation($model, $this->settings[$model->alias]);
+		}
+
+		if (is_string($fields)) {
+			$fields = array($fields);
+		}
+		$RuntimeModel =& $this->translateModel($model);
+		$associations = array();
+
+		foreach ($fields as $key => $value) {
+			if (is_numeric($key)) {
+				$field = $value;
+				$association = null;
+			} else {
+				$field = $key;
+				$association = $value;
+			}
+
+			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_assoc($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_assoc($this->runtime[$model->alias]['fields'], array($field)));
+			}
+
+			if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
+				$associations[] = $association;
+			}
+		}
+
+		if (!empty($associations)) {
+			$model->unbindModel(array('hasMany' => $associations), false);
+		}
+		return true;
+	}
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+/**
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ */
+	class I18nModel extends AppModel {
+		var $name = 'I18nModel';
+		var $useTable = 'i18n';
+		var $displayField = 'field';
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/behaviors/acl.php
===================================================================
--- cake/libs/model/behaviors/acl.php	(revision 0)
+++ cake/libs/model/behaviors/acl.php	(revision 0)
@@ -0,0 +1,119 @@
+<?php
+/* SVN FILE: $Id: acl.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * ACL behavior class.
+ *
+ * Enables objects to easily tie into an ACL system
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2006-2008, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2006-2008, Cake Software Foundation, Inc.
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP v 1.2.0.4487
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ */
+class AclBehavior extends ModelBehavior {
+/**
+ * Maps ACL type options to ACL models
+ *
+ * @var array
+ * @access protected
+ */
+	var $__typeMaps = array('requester' => 'Aro', 'controlled' => 'Aco');
+/**
+ * Sets up the configuation for the model, and loads ACL models if they haven't been already
+ *
+ * @param mixed $config
+ * @return void
+ * @access public
+ */
+	function setup(&$model, $config = array()) {
+		if (is_string($config)) {
+			$config = array('type' => $config);
+		}
+		$this->settings[$model->name] = array_merge(array('type' => 'requester'), (array)$config);
+
+		$type = $this->__typeMaps[$this->settings[$model->name]['type']];
+		if (!class_exists('AclNode')) {
+			uses('model' . DS . 'db_acl');
+		}
+		$model->{$type} =& ClassRegistry::init($type);
+		if (!method_exists($model, 'parentNode')) {
+			trigger_error("Callback parentNode() not defined in {$model->alias}", E_USER_WARNING);
+		}
+	}
+/**
+ * Retrieves the Aro/Aco node for this model
+ *
+ * @param mixed $ref
+ * @return array
+ * @access public
+ */
+	function node(&$model, $ref = null) {
+		$type = $this->__typeMaps[strtolower($this->settings[$model->name]['type'])];
+		if (empty($ref)) {
+			$ref = array('model' => $model->name, 'foreign_key' => $model->id);
+		}
+		return $model->{$type}->node($ref);
+	}
+/**
+ * Creates a new ARO/ACO node bound to this record
+ *
+ * @param boolean $created True if this is a new record
+ * @return void
+ * @access public
+ */
+	function afterSave(&$model, $created) {
+		if ($created) {
+			$type = $this->__typeMaps[strtolower($this->settings[$model->name]['type'])];
+			$parent = $model->parentNode();
+			if (!empty($parent)) {
+				$parent = $this->node($model, $parent);
+			} else {
+				$parent = null;
+			}
+
+			$model->{$type}->create();
+			$model->{$type}->save(array(
+				'parent_id'		=> Set::extract($parent, "0.{$type}.id"),
+				'model'			=> $model->name,
+				'foreign_key'	=> $model->id
+			));
+		}
+	}
+/**
+ * Destroys the ARO/ACO node bound to the deleted record
+ *
+ * @return void
+ * @access public
+ */
+	function afterDelete(&$model) {
+		$type = $this->__typeMaps[strtolower($this->settings[$model->name]['type'])];
+		$node = Set::extract($this->node($model), "0.{$type}.id");
+		if (!empty($node)) {
+			$model->{$type}->delete($node);
+		}
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/model/behaviors/containable.php
===================================================================
--- cake/libs/model/behaviors/containable.php	(revision 0)
+++ cake/libs/model/behaviors/containable.php	(revision 0)
@@ -0,0 +1,429 @@
+<?php
+/* SVN FILE: $Id: containable.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Behavior for binding management.
+ *
+ * Behavior to simplify manipulating a model's bindings when doing a find operation
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Behavior to allow for dynamic and atomic manipulation of a Model's associations used for a find call. Most useful for limiting
+ * the amount of associations and data returned.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class ContainableBehavior extends ModelBehavior {
+/**
+ * Types of relationships available for models
+ *
+ * @var array
+ * @access private
+ */
+	var $types = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+/**
+ * Runtime configuration for this behavior
+ *
+ * @var array
+ * @access private
+ */
+	var $runtime = array();
+/**
+ * Initiate behavior for the model using specified settings. Available settings:
+ *
+ * - recursive: (boolean, optional) set to true to allow containable to automatically
+ * 				determine the recursiveness level needed to fetch specified models,
+ * 				and set the model recursiveness to this level. setting it to false
+ * 				disables this feature. DEFAULTS TO: true
+ *
+ * - notices:	(boolean, optional) issues E_NOTICES for bindings referenced in a
+ * 				containable call that are not valid. DEFAULTS TO: true
+ *
+ * - autoFields: (boolean, optional) auto-add needed fields to fetch requested
+ * 				bindings. DEFAULTS TO: true
+ *
+ * @param object $Model Model using the behavior
+ * @param array $settings Settings to override for model.
+ * @access public
+ */
+	function setup(&$Model, $settings = array()) {
+		if (!isset($this->settings[$Model->alias])) {
+			$this->settings[$Model->alias] = array('recursive' => true, 'notices' => true, 'autoFields' => true);
+		}
+		if (!is_array($settings)) {
+			$settings = array();
+		}
+		$this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
+	}
+/**
+ * Runs before a find() operation. Used to allow 'contain' setting
+ * as part of the find call, like this:
+ *
+ * Model->find('all', array('contain' => array('Model1', 'Model2')));
+ *
+ * Model->find('all', array('contain' => array(
+ * 	'Model1' => array('Model11', 'Model12'),
+ * 	'Model2',
+ * 	'Model3' => array(
+ * 		'Model31' => 'Model311',
+ * 		'Model32',
+ * 		'Model33' => array('Model331', 'Model332')
+ * )));
+ *
+ * @param object $Model	Model using the behavior
+ * @param array $query Query parameters as set by cake
+ * @return array
+ * @access public
+ */
+	function beforeFind(&$Model, $query) {
+		$reset = (isset($query['reset']) ? $query['reset'] : true);
+		$noContain = ((isset($this->runtime[$Model->alias]['contain']) && empty($this->runtime[$Model->alias]['contain'])) || (isset($query['contain']) && empty($query['contain'])));
+		$contain = array();
+		if (isset($this->runtime[$Model->alias]['contain'])) {
+			$contain = $this->runtime[$Model->alias]['contain'];
+			unset($this->runtime[$Model->alias]['contain']);
+		}
+		if (isset($query['contain'])) {
+			$contain = array_merge($contain, (array)$query['contain']);
+		}
+		if ($noContain || !$contain || in_array($contain, array(null, false), true) || (isset($contain[0]) && $contain[0] === null)) {
+			if ($noContain) {
+				$query['recursive'] = -1;
+			}
+			return $query;
+		}
+		if ((isset($contain[0]) && is_bool($contain[0])) || is_bool(end($contain))) {
+			$reset = is_bool(end($contain))
+				? array_pop($contain)
+				: array_shift($contain);
+		}
+		$containments = $this->containments($Model, $contain);
+		$map = $this->containmentsMap($containments);
+
+		$mandatory = array();
+		foreach ($containments['models'] as $name => $model) {
+			$instance =& $model['instance'];
+			$needed = $this->fieldDependencies($instance, $map, false);
+			if (!empty($needed)) {
+				$mandatory = array_merge($mandatory, $needed);
+			}
+			if ($contain) {
+				$backupBindings = array();
+				foreach ($this->types as $relation) {
+					$backupBindings[$relation] = $instance->{$relation};
+				}
+				foreach ($this->types as $type) {
+					$unbind = array();
+					foreach ($instance->{$type} as $assoc => $options) {
+						if (!isset($model['keep'][$assoc])) {
+							$unbind[] = $assoc;
+						}
+					}
+					if (!empty($unbind)) {
+						if (!$reset && empty($instance->__backOriginalAssociation)) {
+							$instance->__backOriginalAssociation = $backupBindings;
+						} else if ($reset && empty($instance->__backContainableAssociation)) {
+							$instance->__backContainableAssociation = $backupBindings;
+						}
+						$instance->unbindModel(array($type => $unbind), $reset);
+					}
+					foreach ($instance->{$type} as $assoc => $options) {
+						if (isset($model['keep'][$assoc]) && !empty($model['keep'][$assoc])) {
+							if (isset($model['keep'][$assoc]['fields'])) {
+								$model['keep'][$assoc]['fields'] = $this->fieldDependencies($containments['models'][$assoc]['instance'], $map, $model['keep'][$assoc]['fields']);
+							}
+							if (!$reset && empty($instance->__backOriginalAssociation)) {
+								$instance->__backOriginalAssociation = $backupBindings;
+							} else if ($reset) {
+								$instance->__backAssociation[$type] = $instance->{$type};
+							}
+							$instance->{$type}[$assoc] = array_merge($instance->{$type}[$assoc], $model['keep'][$assoc]);
+						}
+						if (!$reset) {
+							$instance->__backInnerAssociation[] = $assoc;
+						}
+					}
+				}
+			}
+		}
+
+		if ($this->settings[$Model->alias]['recursive']) {
+			$query['recursive'] = (isset($query['recursive'])) ? $query['recursive'] : $containments['depth'];
+		}
+
+		$autoFields = ($this->settings[$Model->alias]['autoFields']
+					&& !in_array($Model->findQueryType, array('list', 'count'))
+					&& !empty($query['fields']));
+		if (!$autoFields) {
+			return $query;
+		}
+
+		$query['fields'] = (array)$query['fields'];
+		if (!empty($Model->belongsTo)) {
+			foreach ($Model->belongsTo as $assoc => $data) {
+				if (!empty($data['fields'])) {
+					foreach ((array) $data['fields'] as $field) {
+						$query['fields'][] = (strpos($field, '.') === false ? $assoc . '.' : '') . $field;
+					}
+				}
+			}
+		}
+		if (!empty($mandatory[$Model->alias])) {
+			foreach ($mandatory[$Model->alias] as $field) {
+				if ($field == '--primaryKey--') {
+					$field = $Model->primaryKey;
+				} else if (preg_match('/^.+\.\-\-[^-]+\-\-$/', $field)) {
+					list($modelName, $field) = explode('.', $field);
+					$field = $modelName . '.' . (($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field);
+				}
+				$query['fields'][] = $field;
+			}
+		}
+		$query['fields'] = array_unique($query['fields']);
+		return $query;
+	}
+/**
+ * Resets original associations on models that may have receive multiple,
+ * subsequent unbindings.
+ *
+ * @param object $Model Model on which we are resetting
+ * @param array $results Results of the find operation
+ * @param bool $primary true if this is the primary model that issued the find operation, false otherwise
+ * @access public
+ */
+	function afterFind(&$Model, $results, $primary) {
+		if (!empty($Model->__backContainableAssociation)) {
+			foreach ($Model->__backContainableAssociation as $relation => $bindings) {
+				$Model->{$relation} = $bindings;
+				unset($Model->__backContainableAssociation);
+			}
+		}
+	}
+/**
+ * Unbinds all relations from a model except the specified ones. Calling this function without
+ * parameters unbinds all related models.
+ *
+ * @param object $Model Model on which binding restriction is being applied
+ * @return void
+ * @access public
+ */
+	function contain(&$Model) {
+		$args = func_get_args();
+		$contain = call_user_func_array('am', array_slice($args, 1));
+		$this->runtime[$Model->alias]['contain'] = $contain;
+	}
+/**
+ * Permanently restore the original binding settings of given model, useful
+ * for restoring the bindings after using 'reset' => false as part of the
+ * contain call.
+ *
+ * @param object $Model Model on which to reset bindings
+ * @return void
+ * @access public
+ */
+	function resetBindings(&$Model) {
+		if (!empty($Model->__backOriginalAssociation)) {
+			$Model->__backAssociation = $Model->__backOriginalAssociation;
+			unset($Model->__backOriginalAssociation);
+		}
+		$Model->resetAssociations();
+		if (!empty($Model->__backInnerAssociation)) {
+			$assocs = $Model->__backInnerAssociation;
+			unset($Model->__backInnerAssociation);
+			foreach ($assocs as $currentModel) {
+				$this->resetBindings($Model->$currentModel);
+			}
+		}
+	}
+/**
+ * Process containments for model.
+ *
+ * @param object $Model Model on which binding restriction is being applied
+ * @param array $contain Parameters to use for restricting this model
+ * @param array $containments Current set of containments
+ * @param bool $throwErrors Wether unexisting bindings show throw errors
+ * @return array Containments
+ * @access public
+ */
+	function containments(&$Model, $contain, $containments = array(), $throwErrors = null) {
+		$options = array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery');
+		$keep = array();
+		$depth = array();
+		if ($throwErrors === null) {
+			$throwErrors = (empty($this->settings[$Model->alias]) ? true : $this->settings[$Model->alias]['notices']);
+		}
+		foreach ((array)$contain as $name => $children) {
+			if (is_numeric($name)) {
+				$name = $children;
+				$children = array();
+			}
+			if (preg_match('/(?<!\.)\(/', $name)) {
+				$name = str_replace('(', '.(', $name);
+			}
+			if (strpos($name, '.') !== false) {
+				$chain = explode('.', $name);
+				$name = array_shift($chain);
+				$children = array(join('.', $chain) => $children);
+			}
+
+			$children = (array)$children;
+			foreach ($children as $key => $val) {
+				if (is_string($key) && is_string($val) && !in_array($key, $options, true)) {
+					$children[$key] = (array) $val;
+				}
+			}
+
+			$keys = array_keys($children);
+			if ($keys && isset($children[0])) {
+				$keys = array_merge(array_values($children), $keys);
+			}
+
+			foreach ($keys as $i => $key) {
+				if (is_array($key)) {
+					continue;
+				}
+				$optionKey = in_array($key, $options, true);
+				if (!$optionKey && is_string($key) && preg_match('/^[a-z(]/', $key) && (!isset($Model->{$key}) || !is_object($Model->{$key}))) {
+					$option = 'fields';
+					$val = array($key);
+					if ($key{0} == '(') {
+						$val = preg_split('/\s*,\s*/', substr(substr($key, 1), 0, -1));
+					} elseif (preg_match('/ASC|DESC$/', $key)) {
+						$option = 'order';
+						$val = $Model->{$name}->alias.'.'.$key;
+					} elseif (preg_match('/[ =!]/', $key)) {
+						$option = 'conditions';
+						$val = $Model->{$name}->alias.'.'.$key;
+					}
+					$children[$option] = isset($children[$option]) ? array_merge((array) $children[$option], (array) $val) : $val;
+					$newChildren = null;
+					if (!empty($name) && !empty($children[$key])) {
+						$newChildren = $children[$key];
+					}
+					unset($children[$key], $children[$i]);
+					$key = $option;
+					$optionKey = true;
+					if (!empty($newChildren)) {
+						$children = Set::merge($children, $newChildren);
+					}
+				}
+				if ($optionKey && isset($children[$key])) {
+					if (!empty($keep[$name][$key]) && is_array($keep[$name][$key])) {
+						$keep[$name][$key] = array_merge((isset($keep[$name][$key]) ? $keep[$name][$key] : array()), (array) $children[$key]);
+					} else {
+						$keep[$name][$key] = $children[$key];
+					}
+					unset($children[$key]);
+				}
+			}
+
+			if (!isset($Model->{$name}) || !is_object($Model->{$name})) {
+				if ($throwErrors) {
+					trigger_error(sprintf(__('Model "%s" is not associated with model "%s"', true), $Model->alias, $name), E_USER_WARNING);
+				}
+				continue;
+			}
+
+			$containments = $this->containments($Model->{$name}, $children, $containments);
+			$depths[] = $containments['depth'] + 1;
+			if (!isset($keep[$name])) {
+				$keep[$name] = array();
+			}
+		}
+
+		if (!isset($containments['models'][$Model->alias])) {
+			$containments['models'][$Model->alias] = array('keep' => array(),'instance' => &$Model);
+		}
+
+		$containments['models'][$Model->alias]['keep'] = array_merge($containments['models'][$Model->alias]['keep'], $keep);
+		$containments['depth'] = empty($depths) ? 0 : max($depths);
+		return $containments;
+	}
+/**
+ * Calculate needed fields to fetch the required bindings for the given model.
+ *
+ * @param object $Model Model
+ * @param array $map Map of relations for given model
+ * @param mixed $fields If array, fields to initially load, if false use $Model as primary model
+ * @return array Fields
+ * @access public
+ */
+	function fieldDependencies(&$Model, $map, $fields = array()) {
+		if ($fields === false) {
+			foreach ($map as $parent => $children) {
+				foreach ($children as $type => $bindings) {
+					foreach ($bindings as $dependency) {
+						if ($type == 'hasAndBelongsToMany') {
+							$fields[$parent][] = '--primaryKey--';
+						} else if ($type == 'belongsTo') {
+							$fields[$parent][] = $dependency . '.--primaryKey--';
+						}
+					}
+				}
+			}
+			return $fields;
+		}
+		if (empty($map[$Model->alias])) {
+			return $fields;
+		}
+		foreach ($map[$Model->alias] as $type => $bindings) {
+			foreach ($bindings as $dependency) {
+				$innerFields = array();
+				switch ($type) {
+					case 'belongsTo':
+						$fields[] = $Model->{$type}[$dependency]['foreignKey'];
+						break;
+					case 'hasOne':
+					case 'hasMany':
+						$innerFields[] = $Model->$dependency->primaryKey;
+						$fields[] = $Model->primaryKey;
+						break;
+				}
+				if (!empty($innerFields) && !empty($Model->{$type}[$dependency]['fields'])) {
+					$Model->{$type}[$dependency]['fields'] = array_unique(array_merge($Model->{$type}[$dependency]['fields'], $innerFields));
+				}
+			}
+		}
+		return array_unique($fields);
+	}
+/**
+ * Build the map of containments
+ *
+ * @param array $containments Containments
+ * @return array Built containments
+ * @access public
+ */
+	function containmentsMap($containments) {
+		$map = array();
+		foreach ($containments['models'] as $name => $model) {
+			$instance =& $model['instance'];
+			foreach ($this->types as $type) {
+				foreach ($instance->{$type} as $assoc => $options) {
+					if (isset($model['keep'][$assoc])) {
+						$map[$name][$type] = isset($map[$name][$type]) ? array_merge($map[$name][$type], (array)$assoc) : (array)$assoc;
+					}
+				}
+			}
+		}
+		return $map;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/behaviors/tree.php
===================================================================
--- cake/libs/model/behaviors/tree.php	(revision 0)
+++ cake/libs/model/behaviors/tree.php	(revision 0)
@@ -0,0 +1,931 @@
+<?php
+/* SVN FILE: $Id: tree.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Tree behavior class.
+ *
+ * Enables a model object to act as a node-based tree.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2006-2008, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2006-2008, Cake Software Foundation, Inc.
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP v 1.2.0.4487
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ */
+class TreeBehavior extends ModelBehavior {
+/**
+ * Errors
+ *
+ * @var array
+ */
+	var $errors = array();
+/**
+ * Defaults
+ *
+ * @var array
+ * @access protected
+ */
+	var $_defaults = array(
+		'parent' => 'parent_id', 'left' => 'lft', 'right' => 'rght',
+		'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1
+	);
+/**
+ * Initiate Tree behavior
+ *
+ * @param object $Model
+ * @param array $config
+ * @return void
+ * @access public
+ */
+	function setup(&$Model, $config = array()) {
+		if (!is_array($config)) {
+			$config = array('type' => $config);
+		}
+		$settings = array_merge($this->_defaults, $config);
+
+		if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) {
+			$data = $Model->getAssociated($settings['scope']);
+			$parent =& $Model->{$settings['scope']};
+			$settings['scope'] = $Model->alias . '.' . $data['foreignKey'] . ' = ' . $parent->alias . '.' . $parent->primaryKey;
+			$settings['recursive'] = 0;
+		}
+		$this->settings[$Model->alias] = $settings;
+	}
+/**
+ * After save method. Called after all saves
+ *
+ * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
+ * parameters to be saved.
+ *
+ * @param AppModel $Model
+ * @param boolean $created indicates whether the node just saved was created or updated
+ * @return boolean true on success, false on failure
+ * @access public
+ */
+	function afterSave(&$Model, $created) {
+		extract($this->settings[$Model->alias]);
+		if ($created) {
+			if ((isset($Model->data[$Model->alias][$parent])) && $Model->data[$Model->alias][$parent]) {
+				return $this->_setParent($Model, $Model->data[$Model->alias][$parent], $created);
+			}
+		} elseif ($__parentChange) {
+			$this->settings[$Model->alias]['__parentChange'] = false;
+			return $this->_setParent($Model, $Model->data[$Model->alias][$parent]);
+		}
+	}
+/**
+ * Before delete method. Called before all deletes
+ *
+ * Will delete the current node and all children using the deleteAll method and sync the table
+ *
+ * @param AppModel $Model
+ * @return boolean true to continue, false to abort the delete
+ * @access public
+ */
+	function beforeDelete(&$Model) {
+		extract($this->settings[$Model->alias]);
+		list($name, $data) = array($Model->alias, $Model->read());
+		$data = $data[$name];
+
+		if (!$data[$right] || !$data[$left]) {
+			return true;
+		}
+		$diff = $data[$right] - $data[$left] + 1;
+
+		if ($diff > 2) {
+			if (is_string($scope)) {
+				$scope = array($scope);
+			}
+			$scope[]["{$Model->alias}.{$left} BETWEEN ? AND ?"] = array($data[$left] + 1, $data[$right] - 1);
+			$Model->deleteAll($scope);
+		}
+		$this->__sync($Model, $diff, '-', '> ' . $data[$right]);
+		return true;
+	}
+/**
+ * Before save method. Called before all saves
+ *
+ * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
+ * parameters to be saved. For newly created nodes with NO parent the left and right field values are set directly by
+ * this method bypassing the setParent logic.
+ *
+ * @since         1.2
+ * @param AppModel $Model
+ * @return boolean true to continue, false to abort the save
+ * @access public
+ */
+	function beforeSave(&$Model) {
+		extract($this->settings[$Model->alias]);
+
+		if (isset($Model->data[$Model->alias][$Model->primaryKey])) {
+			if ($Model->data[$Model->alias][$Model->primaryKey]) {
+				if (!$Model->id) {
+					$Model->id = $Model->data[$Model->alias][$Model->primaryKey];
+				}
+			}
+			unset($Model->data[$Model->alias][$Model->primaryKey]);
+		}
+
+		$this->_addToWhitelist($Model, array($left, $right));
+		if (!$Model->id) {
+			if (array_key_exists($parent, $Model->data[$Model->alias]) && $Model->data[$Model->alias][$parent]) {
+				$parentNode = $Model->find('first', array(
+					'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]),
+					'fields' => array($Model->primaryKey, $right), 'recursive' => $recursive
+				));
+				if (!$parentNode) {
+					return false;
+				}
+				list($parentNode) = array_values($parentNode);
+				$Model->data[$Model->alias][$left] = 0; //$parentNode[$right];
+				$Model->data[$Model->alias][$right] = 0; //$parentNode[$right] + 1;
+			} else {
+				$edge = $this->__getMax($Model, $scope, $right, $recursive);
+				$Model->data[$Model->alias][$left] = $edge + 1;
+				$Model->data[$Model->alias][$right] = $edge + 2;
+			}
+		} elseif (array_key_exists($parent, $Model->data[$Model->alias])) {
+			if ($Model->data[$Model->alias][$parent] != $Model->field($parent)) {
+				$this->settings[$Model->alias]['__parentChange'] = true;
+			}
+			if (!$Model->data[$Model->alias][$parent]) {
+				$Model->data[$Model->alias][$parent] = null;
+				$this->_addToWhitelist($Model, $parent);
+			} else {
+				list($node) = array_values($Model->find('first', array(
+					'conditions' => array($scope,$Model->escapeField() => $Model->id),
+					'fields' => array($Model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive)
+				));
+
+				$parentNode = $Model->find('first', array(
+					'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]),
+					'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
+				));
+				if (!$parentNode) {
+					return false;
+				}
+				list($parentNode) = array_values($parentNode);
+
+				if (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
+					return false;
+				} elseif ($node[$Model->primaryKey] == $parentNode[$Model->primaryKey]) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Get the number of child nodes
+ *
+ * If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field)
+ * If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted.
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to read or false to read all top level nodes
+ * @param boolean $direct whether to count direct, or all, children
+ * @return integer number of child nodes
+ * @access public
+ */
+	function childcount(&$Model, $id = null, $direct = false) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		if ($id === null && $Model->id) {
+			$id = $Model->id;
+		} elseif (!$id) {
+			$id = null;
+		}
+		extract($this->settings[$Model->alias]);
+
+		if ($direct) {
+			return $Model->find('count', array('conditions' => array($scope, $Model->escapeField($parent) => $id)));
+		}
+
+		if ($id === null) {
+			return $Model->find('count', array('conditions' => $scope));
+		} elseif (isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) {
+			$data = $Model->data[$Model->alias];
+		} else {
+			$data = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'recursive' => $recursive));
+			if (!$data) {
+				return 0;
+			}
+			$data = $data[$Model->alias];
+		}
+		return ($data[$right] - $data[$left] - 1) / 2;
+	}
+/**
+ * Get the child nodes of the current model
+ *
+ * If the direct parameter is set to true, only the direct children are returned (based upon the parent_id field)
+ * If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted.
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to read
+ * @param boolean $direct whether to return only the direct, or all, children
+ * @param mixed $fields Either a single string of a field name, or an array of field names
+ * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order
+ * @param integer $limit SQL LIMIT clause, for calculating items per page.
+ * @param integer $page Page number, for accessing paged data
+ * @param integer $recursive The number of levels deep to fetch associated records
+ * @return array Array of child nodes
+ * @access public
+ */
+	function children(&$Model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		$overrideRecursive = $recursive;
+
+		if ($id === null && $Model->id) {
+			$id = $Model->id;
+		} elseif (!$id) {
+			$id = null;
+		}
+		$name = $Model->alias;
+		extract($this->settings[$Model->alias]);
+
+		if (!is_null($overrideRecursive)) {
+			$recursive = $overrideRecursive;
+		}
+		if (!$order) {
+			$order = $Model->alias . '.' . $left . ' asc';
+		}
+		if ($direct) {
+			$conditions = array($scope, $Model->escapeField($parent) => $id);
+			return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
+		}
+
+		if (!$id) {
+			$conditions = $scope;
+		} else {
+			$result = array_values($Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $id),
+				'fields' => array($left, $right),
+				'recursive' => $recursive
+			)));
+
+			if (empty($result) || !isset($result[0])) {
+				return array();
+			}
+			$conditions = array($scope,
+				$Model->escapeField($right) . ' <' => $result[0][$right],
+				$Model->escapeField($left) . ' >' => $result[0][$left]
+			);
+		}
+		return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
+	}
+/**
+ * A convenience method for returning a hierarchical array used for HTML select boxes
+ *
+ * @param AppModel $Model
+ * @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...)
+ * @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
+ * @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
+ * @param string $spacer The character or characters which will be repeated
+ * @param integer $recursive The number of levels deep to fetch associated records
+ * @return array An associative array of records, where the id is the key, and the display field is the value
+ * @access public
+ */
+	function generatetreelist(&$Model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null) {
+		$overrideRecursive = $recursive;
+		extract($this->settings[$Model->alias]);
+		if (!is_null($overrideRecursive)) {
+			$recursive = $overrideRecursive;
+		}
+
+		if ($keyPath == null && $valuePath == null && $Model->hasField($Model->displayField)) {
+			$fields = array($Model->primaryKey, $Model->displayField, $left, $right);
+		} else {
+			$fields = null;
+		}
+
+		if ($keyPath == null) {
+			$keyPath = '{n}.' . $Model->alias . '.' . $Model->primaryKey;
+		}
+
+		if ($valuePath == null) {
+			$valuePath = array('{0}{1}', '{n}.tree_prefix', '{n}.' . $Model->alias . '.' . $Model->displayField);
+
+		} elseif (is_string($valuePath)) {
+			$valuePath = array('{0}{1}', '{n}.tree_prefix', $valuePath);
+
+		} else {
+			$valuePath[0] = '{' . (count($valuePath) - 1) . '}' . $valuePath[0];
+			$valuePath[] = '{n}.tree_prefix';
+		}
+		$order = $Model->alias . '.' . $left . ' asc';
+		$results = $Model->find('all', compact('conditions', 'fields', 'order', 'recursive'));
+		$stack = array();
+
+		foreach ($results as $i => $result) {
+			while ($stack && ($stack[count($stack) - 1] < $result[$Model->alias][$right])) {
+				array_pop($stack);
+			}
+			$results[$i]['tree_prefix'] = str_repeat($spacer,count($stack));
+			$stack[] = $result[$Model->alias][$right];
+		}
+		if (empty($results)) {
+			return array();
+		}
+		return Set::combine($results, $keyPath, $valuePath);
+	}
+/**
+ * Get the parent node
+ *
+ * reads the parent id and returns this node
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to read
+ * @param integer $recursive The number of levels deep to fetch associated records
+ * @return array Array of data for the parent node
+ * @access public
+ */
+	function getparentnode(&$Model, $id = null, $fields = null, $recursive = null) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		$overrideRecursive = $recursive;
+		if (empty ($id)) {
+			$id = $Model->id;
+		}
+		extract($this->settings[$Model->alias]);
+		if (!is_null($overrideRecursive)) {
+			$recursive = $overrideRecursive;
+		}
+		$parentId = $Model->read($parent, $id);
+
+		if ($parentId) {
+			$parentId = $parentId[$Model->alias][$parent];
+			$parent = $Model->find('first', array('conditions' => array($Model->escapeField() => $parentId), 'fields' => $fields, 'recursive' => $recursive));
+
+			return $parent;
+		}
+		return false;
+	}
+/**
+ * Get the path to the given node
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to read
+ * @param mixed $fields Either a single string of a field name, or an array of field names
+ * @param integer $recursive The number of levels deep to fetch associated records
+ * @return array Array of nodes from top most parent to current node
+ * @access public
+ */
+	function getpath(&$Model, $id = null, $fields = null, $recursive = null) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		$overrideRecursive = $recursive;
+		if (empty ($id)) {
+			$id = $Model->id;
+		}
+		extract($this->settings[$Model->alias]);
+		if (!is_null($overrideRecursive)) {
+			$recursive = $overrideRecursive;
+		}
+		$result = $Model->find('first', array('conditions' => array($Model->escapeField() => $id), 'fields' => array($left, $right), 'recursive' => $recursive));
+		if ($result) {
+			$result = array_values($result);
+		} else {
+			return null;
+		}
+		$item = $result[0];
+		$results = $Model->find('all', array(
+			'conditions' => array($scope, $Model->escapeField($left) . ' <=' => $item[$left], $Model->escapeField($right) . ' >=' => $item[$right]),
+			'fields' => $fields, 'order' => array($Model->escapeField($left) => 'asc'), 'recursive' => $recursive
+		));
+		return $results;
+	}
+/**
+ * Reorder the node without changing the parent.
+ *
+ * If the node is the last child, or is a top level node with no subsequent node this method will return false
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to move
+ * @param mixed $number how many places to move the node or true to move to last position
+ * @return boolean true on success, false on failure
+ * @access public
+ */
+	function movedown(&$Model, $id = null, $number = 1) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		if (!$number) {
+			return false;
+		}
+		if (empty ($id)) {
+			$id = $Model->id;
+		}
+		extract($this->settings[$Model->alias]);
+		list($node) = array_values($Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField() => $id),
+			'fields' => array($Model->primaryKey, $left, $right, $parent), 'recursive' => $recursive
+		)));
+		if ($node[$parent]) {
+			list($parentNode) = array_values($Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
+				'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
+			)));
+			if (($node[$right] + 1) == $parentNode[$right]) {
+				return false;
+			}
+		}
+		$nextNode = $Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField($left) => ($node[$right] + 1)),
+			'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive)
+		);
+		if ($nextNode) {
+			list($nextNode)= array_values($nextNode);
+		} else {
+			return false;
+		}
+		$edge = $this->__getMax($Model, $scope, $right, $recursive);
+		$this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
+		$this->__sync($Model, $nextNode[$left] - $node[$left], '-', 'BETWEEN ' . $nextNode[$left] . ' AND ' . $nextNode[$right]);
+		$this->__sync($Model, $edge - $node[$left] - ($nextNode[$right] - $nextNode[$left]), '-', '> ' . $edge);
+
+		if (is_int($number)) {
+			$number--;
+		}
+		if ($number) {
+			$this->moveDown($Model, $id, $number);
+		}
+		return true;
+	}
+/**
+ * Reorder the node without changing the parent.
+ *
+ * If the node is the first child, or is a top level node with no previous node this method will return false
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to move
+ * @param mixed $number how many places to move the node, or true to move to first position
+ * @return boolean true on success, false on failure
+ * @access public
+ */
+	function moveup(&$Model, $id = null, $number = 1) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		if (!$number) {
+			return false;
+		}
+		if (empty ($id)) {
+			$id = $Model->id;
+		}
+		extract($this->settings[$Model->alias]);
+		list($node) = array_values($Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField() => $id),
+			'fields' => array($Model->primaryKey, $left, $right, $parent ), 'recursive' => $recursive
+		)));
+		if ($node[$parent]) {
+			list($parentNode) = array_values($Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
+				'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
+			)));
+			if (($node[$left] - 1) == $parentNode[$left]) {
+				return false;
+			}
+		}
+		$previousNode = $Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField($right) => ($node[$left] - 1)),
+			'fields' => array($Model->primaryKey, $left, $right),
+			'recursive' => $recursive
+		));
+
+		if ($previousNode) {
+			list($previousNode) = array_values($previousNode);
+		} else {
+			return false;
+		}
+		$edge = $this->__getMax($Model, $scope, $right, $recursive);
+		$this->__sync($Model, $edge - $previousNode[$left] +1, '+', 'BETWEEN ' . $previousNode[$left] . ' AND ' . $previousNode[$right]);
+		$this->__sync($Model, $node[$left] - $previousNode[$left], '-', 'BETWEEN ' .$node[$left] . ' AND ' . $node[$right]);
+		$this->__sync($Model, $edge - $previousNode[$left] - ($node[$right] - $node[$left]), '-', '> ' . $edge);
+		if (is_int($number)) {
+			$number--;
+		}
+		if ($number) {
+			$this->moveUp($Model, $id, $number);
+		}
+		return true;
+	}
+/**
+ * Recover a corrupted tree
+ *
+ * The mode parameter is used to specify the source of info that is valid/correct. The opposite source of data
+ * will be populated based upon that source of info. E.g. if the MPTT fields are corrupt or empty, with the $mode
+ * 'parent' the values of the parent_id field will be used to populate the left and right fields. The missingParentAction
+ * parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present.
+ *
+ * @todo Could be written to be faster, *maybe*. Ideally using a subquery and putting all the logic burden on the DB.
+ * @param AppModel $Model
+ * @param string $mode parent or tree
+ * @param mixed $missingParentAction 'return' to do nothing and return, 'delete' to
+ * delete, or the id of the parent to set as the parent_id
+ * @return boolean true on success, false on failure
+ * @access public
+ */
+	function recover(&$Model, $mode = 'parent', $missingParentAction = null) {
+		if (is_array($mode)) {
+			extract (array_merge(array('mode' => 'parent'), $mode));
+		}
+		extract($this->settings[$Model->alias]);
+		$Model->recursive = $recursive;
+		if ($mode == 'parent') {
+			$Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
+				'className' => $Model->alias,
+				'foreignKey' => $parent,
+				'fields' => array($Model->primaryKey, $left, $right, $parent),
+			))));
+			$missingParents = $Model->find('list', array(
+				'recursive' => 0,
+				'conditions' => array($scope, array(
+					'NOT' => array($Model->escapeField($parent) => null), $Model->VerifyParent->escapeField() => null
+				))
+			));
+			$Model->unbindModel(array('belongsTo' => array('VerifyParent')));
+			if ($missingParents) {
+				if ($missingParentAction == 'return') {
+					foreach ($missingParents as $id => $display) {
+						$this->errors[]	= 'cannot find the parent for ' . $Model->alias . ' with id ' . $id . '(' . $display . ')';
+
+					}
+					return false;
+				} elseif ($missingParentAction == 'delete') {
+					$Model->deleteAll(array($Model->primaryKey => array_flip($missingParents)));
+				} else {
+					$Model->updateAll(array($parent => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)));
+				}
+			}
+			$count = 1;
+			foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey), 'order' => $left)) as $array) {
+				$Model->id = $array[$Model->alias][$Model->primaryKey];
+				$lft = $count++;
+				$rght = $count++;
+				$Model->save(array($left => $lft, $right => $rght), array('callbacks' => false));
+			}
+			foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
+				$Model->create();
+				$Model->id = $array[$Model->alias][$Model->primaryKey];
+				$this->_setParent($Model, $array[$Model->alias][$parent]);
+			}
+		} else {
+			$db =& ConnectionManager::getDataSource($Model->useDbConfig);
+			foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
+				$path = $this->getpath($Model, $array[$Model->alias][$Model->primaryKey]);
+				if ($path == null || count($path) < 2) {
+					$parentId = null;
+				} else {
+					$parentId = $path[count($path) - 2][$Model->alias][$Model->primaryKey];
+				}
+				$Model->updateAll(array($parent => $db->value($parentId, $parent)), array($Model->escapeField() => $array[$Model->alias][$Model->primaryKey]));
+			}
+		}
+		return true;
+	}
+/**
+ * Reorder method.
+ *
+ * Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters.
+ * This method does not change the parent of any node.
+ *
+ * Requires a valid tree, by default it verifies the tree before beginning.
+ *
+ * @param AppModel $Model
+ * @param array $options
+ * @return boolean true on success, false on failure
+ */
+	function reorder(&$Model, $options = array()) {
+		$options = array_merge(array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true), $options);
+		extract($options);
+		if ($verify && !$this->verify($Model)) {
+			return false;
+		}
+		$verify = false;
+		extract($this->settings[$Model->alias]);
+		$fields = array($Model->primaryKey, $field, $left, $right);
+		$sort = $field . ' ' . $order;
+		$nodes = $this->children($Model, $id, true, $fields, $sort, null, null, $recursive);
+
+		if ($nodes) {
+			foreach ($nodes as $node) {
+				$id = $node[$Model->alias][$Model->primaryKey];
+				$this->moveDown($Model, $id, true);
+				if ($node[$Model->alias][$left] != $node[$Model->alias][$right] - 1) {
+					$this->reorder($Model, compact('id', 'field', 'order', 'verify'));
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Remove the current node from the tree, and reparent all children up one level.
+ *
+ * If the parameter delete is false, the node will become a new top level node. Otherwise the node will be deleted
+ * after the children are reparented.
+ *
+ * @param AppModel $Model
+ * @param mixed $id The ID of the record to remove
+ * @param boolean $delete whether to delete the node after reparenting children (if any)
+ * @return boolean true on success, false on failure
+ * @access public
+ */
+	function removefromtree(&$Model, $id = null, $delete = false) {
+		if (is_array($id)) {
+			extract (array_merge(array('id' => null), $id));
+		}
+		extract($this->settings[$Model->alias]);
+
+		list($node) = array_values($Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField() => $id),
+			'fields' => array($Model->primaryKey, $left, $right, $parent),
+			'recursive' => $recursive
+		)));
+
+		if ($node[$right] == $node[$left] + 1) {
+			if ($delete) {
+				return $Model->delete($id);
+			} else {
+				$Model->id = $id;
+				return $Model->saveField($parent, null);
+			}
+		} elseif ($node[$parent]) {
+			list($parentNode) = array_values($Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
+				'fields' => array($Model->primaryKey, $left, $right),
+				'recursive' => $recursive
+			)));
+		} else {
+			$parentNode[$right] = $node[$right] + 1;
+		}
+
+		$db =& ConnectionManager::getDataSource($Model->useDbConfig);
+		$Model->updateAll(array($parent => $db->value($node[$parent], $parent)), array($parent => $node[$Model->primaryKey]));
+		$this->__sync($Model, 1, '-', 'BETWEEN ' . ($node[$left] + 1) . ' AND ' . ($node[$right] - 1));
+		$this->__sync($Model, 2, '-', '> ' . ($node[$right]));
+		$Model->id = $id;
+
+		if ($delete) {
+			$Model->updateAll(
+				array(
+					$Model->escapeField($left) => 0,
+					$Model->escapeField($right) => 0,
+					$Model->escapeField($parent) => null
+				),
+				array($Model->escapeField() => $id)
+			);
+			return $Model->delete($id);
+		} else {
+			$edge = $this->__getMax($Model, $scope, $right, $recursive);
+			if ($node[$right] == $edge) {
+				$edge = $edge - 2;
+			}
+			$Model->id = $id;
+			return $Model->save(
+				array($left => $edge + 1, $right => $edge + 2, $parent => null),
+				array('callbacks' => false)
+			);
+		}
+	}
+/**
+ * Check if the current tree is valid.
+ *
+ * Returns true if the tree is valid otherwise an array of (type, incorrect left/right index, message)
+ *
+ * @param AppModel $Model
+ * @return mixed true if the tree is valid or empty, otherwise an array of (error type [index, node],
+ *  [incorrect left/right index,node id], message)
+ * @access public
+ */
+	function verify(&$Model) {
+		extract($this->settings[$Model->alias]);
+		if (!$Model->find('count', array('conditions' => $scope))) {
+			return true;
+		}
+		$min = $this->__getMin($Model, $scope, $left, $recursive);
+		$edge = $this->__getMax($Model, $scope, $right, $recursive);
+		$errors =  array();
+
+		for ($i = $min; $i <= $edge; $i++) {
+			$count = $Model->find('count', array('conditions' => array(
+				$scope, 'OR' => array($Model->escapeField($left) => $i, $Model->escapeField($right) => $i)
+			)));
+			if ($count != 1) {
+				if ($count == 0) {
+					$errors[] = array('index', $i, 'missing');
+				} else {
+					$errors[] = array('index', $i, 'duplicate');
+				}
+			}
+		}
+		$node = $Model->find('first', array('conditions' => array($scope, $Model->escapeField($right) . '< ' . $Model->escapeField($left)), 'recursive' => 0));
+		if ($node) {
+			$errors[] = array('node', $node[$Model->alias][$Model->primaryKey], 'left greater than right.');
+		}
+
+		$Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
+			'className' => $Model->alias,
+			'foreignKey' => $parent,
+			'fields' => array($Model->primaryKey, $left, $right, $parent)
+		))));
+
+		foreach ($Model->find('all', array('conditions' => $scope, 'recursive' => 0)) as $instance) {
+			if (is_null($instance[$Model->alias][$left]) || is_null($instance[$Model->alias][$right])) {
+				$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
+					'has invalid left or right values');
+			} elseif ($instance[$Model->alias][$left] == $instance[$Model->alias][$right]) {
+				$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
+					'left and right values identical');
+			} elseif ($instance[$Model->alias][$parent]) {
+				if (!$instance['VerifyParent'][$Model->primaryKey]) {
+					$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
+						'The parent node ' . $instance[$Model->alias][$parent] . ' doesn\'t exist');
+				} elseif ($instance[$Model->alias][$left] < $instance['VerifyParent'][$left]) {
+					$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
+						'left less than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
+				} elseif ($instance[$Model->alias][$right] > $instance['VerifyParent'][$right]) {
+					$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
+						'right greater than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
+				}
+			} elseif ($Model->find('count', array('conditions' => array($scope, $Model->escapeField($left) . ' <' => $instance[$Model->alias][$left], $Model->escapeField($right) . ' >' => $instance[$Model->alias][$right]), 'recursive' => 0))) {
+				$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], 'The parent field is blank, but has a parent');
+			}
+		}
+		if ($errors) {
+			return $errors;
+		}
+		return true;
+	}
+/**
+ * Sets the parent of the given node
+ *
+ * The force parameter is used to override the "don't change the parent to the current parent" logic in the event
+ * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
+ * method could be private, since calling save with parent_id set also calls setParent
+ *
+ * @param AppModel $Model
+ * @param mixed $parentId
+ * @return boolean true on success, false on failure
+ * @access protected
+ */
+	function _setParent(&$Model, $parentId = null, $created = false) {
+		extract($this->settings[$Model->alias]);
+		list($node) = array_values($Model->find('first', array(
+			'conditions' => array($scope, $Model->escapeField() => $Model->id),
+			'fields' => array($Model->primaryKey, $parent, $left, $right),
+			'recursive' => $recursive
+		)));
+		$edge = $this->__getMax($Model, $scope, $right, $recursive, $created);
+
+		if (empty ($parentId)) {
+			$this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
+			$this->__sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
+		} else {
+			$parentNode = array_values($Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $parentId),
+				'fields' => array($Model->primaryKey, $left, $right),
+				'recursive' => $recursive
+			)));
+
+			if (empty($parentNode) || empty($parentNode[0])) {
+				return false;
+			}
+			$parentNode = $parentNode[0];
+
+			if (($Model->id == $parentId)) {
+				return false;
+
+			} elseif (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
+				return false;
+			}
+			if (empty ($node[$left]) && empty ($node[$right])) {
+				$this->__sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
+				$result = $Model->save(
+					array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId),
+					array('validate' => false, 'callbacks' => false)
+				);
+				$Model->data = $result;
+			} else {
+				$this->__sync($Model, $edge - $node[$left] +1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
+				$diff = $node[$right] - $node[$left] + 1;
+
+				if ($node[$left] > $parentNode[$left]) {
+					if ($node[$right] < $parentNode[$right]) {
+						$this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
+						$this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
+					} else {
+						$this->__sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created);
+						$this->__sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created);
+					}
+				} else {
+					$this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
+					$this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * get the maximum index value in the table.
+ *
+ * @param AppModel $Model
+ * @param string $scope
+ * @param string $right
+ * @return int
+ * @access private
+ */
+	function __getMax($Model, $scope, $right, $recursive = -1, $created = false) {
+		$db =& ConnectionManager::getDataSource($Model->useDbConfig);
+		if ($created) {
+			if (is_string($scope)) {
+				$scope .= " AND {$Model->alias}.{$Model->primaryKey} <> ";
+				$scope .= $db->value($Model->id, $Model->getColumnType($Model->primaryKey));
+			} else {
+				$scope['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id;
+			}
+		}
+		list($edge) = array_values($Model->find('first', array(
+			'conditions' => $scope,
+			'fields' => $db->calculate($Model, 'max', array($right)),
+			'recursive' => $recursive
+		)));
+		return (empty($edge[$right])) ? 0 : $edge[$right];
+	}
+/**
+ * get the minimum index value in the table.
+ *
+ * @param AppModel $Model
+ * @param string $scope
+ * @param string $right
+ * @return int
+ * @access private
+ */
+	function __getMin($Model, $scope, $left, $recursive = -1) {
+		$db =& ConnectionManager::getDataSource($Model->useDbConfig);
+		list($edge) = array_values($Model->find('first', array(
+			'conditions' => $scope,
+			'fields' => $db->calculate($Model, 'min', array($left)),
+			'recursive' => $recursive
+		)));
+		return (empty($edge[$left])) ? 0 : $edge[$left];
+	}
+/**
+ * Table sync method.
+ *
+ * Handles table sync operations, Taking account of the behavior scope.
+ *
+ * @param AppModel $Model
+ * @param integer $shift
+ * @param string $direction
+ * @param array $conditions
+ * @param string $field
+ * @access private
+ */
+	function __sync(&$Model, $shift, $dir = '+', $conditions = array(), $created = false, $field = 'both') {
+		$ModelRecursive = $Model->recursive;
+		extract($this->settings[$Model->alias]);
+		$Model->recursive = $recursive;
+
+		if ($field == 'both') {
+			$this->__sync($Model, $shift, $dir, $conditions, $created, $left);
+			$field = $right;
+		}
+		if (is_string($conditions)) {
+			$conditions = array("{$Model->alias}.{$field} {$conditions}");
+		}
+		if (($scope != '1 = 1' && $scope !== true) && $scope) {
+			$conditions[] = $scope;
+		}
+		if ($created) {
+			$conditions['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id;
+		}
+		$Model->updateAll(array($Model->alias . '.' . $field => $Model->escapeField($field) . ' ' . $dir . ' ' . $shift), $conditions);
+		$Model->recursive = $ModelRecursive;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/behavior.php
===================================================================
--- cake/libs/model/behavior.php	(revision 0)
+++ cake/libs/model/behavior.php	(revision 0)
@@ -0,0 +1,478 @@
+<?php
+/* SVN FILE: $Id: behavior.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Model behaviors base class.
+ *
+ * Adds methods and automagic functionality to Cake Models.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 1.2.0.0
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Model behavior base class.
+ *
+ * Defines the Behavior interface, and contains common model interaction functionality.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class ModelBehavior extends Object {
+/**
+ * Contains configuration settings for use with individual model objects.  This
+ * is used because if multiple models use this Behavior, each will use the same
+ * object instance.  Individual model settings should be stored as an
+ * associative array, keyed off of the model name.
+ *
+ * @var array
+ * @access public
+ * @see Model::$alias
+ */
+	var $settings = array();
+/**
+ * Allows the mapping of preg-compatible regular expressions to public or
+ * private methods in this class, where the array key is a /-delimited regular
+ * expression, and the value is a class method.  Similar to the functionality of
+ * the findBy* / findAllBy* magic methods.
+ *
+ * @var array
+ * @access public
+ */
+	var $mapMethods = array();
+/**
+ * Setup this behavior with the specified configuration settings.
+ *
+ * @param object $model Model using this behavior
+ * @param array $config Configuration settings for $model
+ * @access public
+ */
+	function setup(&$model, $config = array()) { }
+/**
+ * Clean up any initialization this behavior has done on a model.  Called when a behavior is dynamically
+ * detached from a model using Model::detach().
+ *
+ * @param object $model Model using this behavior
+ * @access public
+ * @see BehaviorCollection::detach()
+ */
+	function cleanup(&$model) {
+		if (isset($this->settings[$model->alias])) {
+			unset($this->settings[$model->alias]);
+		}
+	}
+/**
+ * Before find callback
+ *
+ * @param object $model Model using this behavior
+ * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
+ * @return boolean True if the operation should continue, false if it should abort
+ * @access public
+ */
+	function beforeFind(&$model, $query) { }
+/**
+ * After find callback. Can be used to modify any results returned by find and findAll.
+ *
+ * @param object $model Model using this behavior
+ * @param mixed $results The results of the find operation
+ * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
+ * @return mixed Result of the find operation
+ * @access public
+ */
+	function afterFind(&$model, $results, $primary) { }
+/**
+ * Before validate callback
+ *
+ * @param object $model Model using this behavior
+ * @return boolean True if validate operation should continue, false to abort
+ * @access public
+ */
+	function beforeValidate(&$model) { }
+/**
+ * Before save callback
+ *
+ * @param object $model Model using this behavior
+ * @return boolean True if the operation should continue, false if it should abort
+ * @access public
+ */
+	function beforeSave(&$model) { }
+/**
+ * After save callback
+ *
+ * @param object $model Model using this behavior
+ * @param boolean $created True if this save created a new record
+ * @access public
+ */
+	function afterSave(&$model, $created) { }
+/**
+ * Before delete callback
+ *
+ * @param object $model Model using this behavior
+ * @param boolean $cascade If true records that depend on this record will also be deleted
+ * @return boolean True if the operation should continue, false if it should abort
+ * @access public
+ */
+	function beforeDelete(&$model, $cascade = true) { }
+/**
+ * After delete callback
+ *
+ * @param object $model Model using this behavior
+ * @access public
+ */
+	function afterDelete(&$model) { }
+/**
+ * DataSource error callback
+ *
+ * @param object $model Model using this behavior
+ * @param string $error Error generated in DataSource
+ * @access public
+ */
+	function onError(&$model, $error) { }
+/**
+ * Overrides Object::dispatchMethod to account for PHP4's broken reference support
+ *
+ * @see Object::dispatchMethod
+ * @access public
+ * @return mixed
+ */
+	function dispatchMethod(&$model, $method, $params = array()) {
+		if (empty($params)) {
+			return $this->{$method}($model);
+		}
+		$params = array_values($params);
+
+		switch (count($params)) {
+			case 1:
+				return $this->{$method}($model, $params[0]);
+			case 2:
+				return $this->{$method}($model, $params[0], $params[1]);
+			case 3:
+				return $this->{$method}($model, $params[0], $params[1], $params[2]);
+			case 4:
+				return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
+			case 5:
+				return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
+			default:
+				array_unshift($params, $model);
+				return call_user_func_array(array(&$this, $method), $params);
+			break;
+		}
+	}
+/**
+ * If $model's whitelist property is non-empty, $field will be added to it.
+ * Note: this method should *only* be used in beforeValidate or beforeSave to ensure
+ * that it only modifies the whitelist for the current save operation.  Also make sure
+ * you explicitly set the value of the field which you are allowing.
+ *
+ * @param object $model Model using this behavior
+ * @param string $field Field to be added to $model's whitelist
+ * @access protected
+ * @return void
+ */
+	function _addToWhitelist(&$model, $field) {
+		if (is_array($field)) {
+			foreach ($field as $f) {
+				$this->_addToWhitelist($model, $f);
+			}
+			return;
+		}
+		if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
+			$model->whitelist[] = $field;
+		}
+	}
+}
+
+/**
+ * Model behavior collection class.
+ *
+ * Defines the Behavior interface, and contains common model interaction functionality.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class BehaviorCollection extends Object {
+
+/**
+ * Stores a reference to the attached name
+ *
+ * @var object
+ */
+	var $modelName = null;
+/**
+ * Lists the currently-attached behavior objects
+ *
+ * @var array
+ * @access private
+ */
+	var $_attached = array();
+/**
+ * Lists the currently-attached behavior objects which are disabled
+ *
+ * @var array
+ * @access private
+ */
+	var $_disabled = array();
+/**
+ * Keeps a list of all methods of attached behaviors
+ *
+ * @var array
+ */
+	var $__methods = array();
+/**
+ * Keeps a list of all methods which have been mapped with regular expressions
+ *
+ * @var array
+ */
+	var $__mappedMethods = array();
+/**
+ * Attaches a model object and loads a list of behaviors
+ *
+ * @access public
+ * @return void
+ */
+	function init($modelName, $behaviors = array()) {
+		$this->modelName = $modelName;
+
+		if (!empty($behaviors)) {
+			foreach (Set::normalize($behaviors) as $behavior => $config) {
+				$this->attach($behavior, $config);
+			}
+		}
+	}
+/**
+ * Attaches a behavior to a model
+ *
+ * @param string $behavior CamelCased name of the behavior to load
+ * @param array $config Behavior configuration parameters
+ * @return boolean True on success, false on failure
+ * @access public
+ */
+	function attach($behavior, $config = array()) {
+		$name = $behavior;
+		if (strpos($behavior, '.')) {
+			list($plugin, $name) = explode('.', $behavior, 2);
+		}
+		$class = $name . 'Behavior';
+
+		if (!App::import('Behavior', $behavior)) {
+			return false;
+		}
+
+		if (!isset($this->{$name})) {
+			if (PHP5) {
+				$this->{$name} = new $class;
+			} else {
+				$this->{$name} =& new $class;
+			}
+		} elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
+			if (!empty($config)) {
+				$config = array_merge($this->{$name}->settings[$this->modelName], $config);
+			} else {
+				$config = array();
+			}
+		}
+		if (empty($config)) {
+			$config = array();
+		}
+		$this->{$name}->setup(ClassRegistry::getObject($this->modelName), $config);
+
+		foreach ($this->{$name}->mapMethods as $method => $alias) {
+			$this->__mappedMethods[$method] = array($alias, $name);
+		}
+		$methods = get_class_methods($this->{$name});
+		$parentMethods = array_flip(get_class_methods('ModelBehavior'));
+		$callbacks = array('setup' => true, 'cleanup' => true, 'beforeFind' => true, 'afterFind' => true, 'beforeSave' => true, 'afterSave' => true, 'beforeDelete' => true, 'afterDelete' => true, 'afterError' => true);
+
+		foreach ($methods as $m) {
+			if (!isset($parentMethods[$m])) {
+				if ($m[0] != '_' && !array_key_exists($m, $this->__methods) && !isset($callbacks[$m])) {
+					$this->__methods[$m] = array($m, $name);
+				}
+			}
+		}
+
+		if (!in_array($name, $this->_attached)) {
+			$this->_attached[] = $name;
+		}
+		if (in_array($name, $this->_disabled) && !(isset($config['enabled']) && $config['enabled'] === false)) {
+			$this->enable($name);
+		} elseif (isset($config['enabled']) && $config['enabled'] === false) {
+			$this->disable($name);
+		}
+		return true;
+	}
+/**
+ * Detaches a behavior from a model
+ *
+ * @param string $name CamelCased name of the behavior to unload
+ * @return void
+ * @access public
+ */
+	function detach($name) {
+		if (isset($this->{$name})) {
+			$this->{$name}->cleanup(ClassRegistry::getObject($this->modelName));
+			unset($this->{$name});
+		}
+		foreach ($this->__methods as $m => $callback) {
+			if (is_array($callback) && $callback[1] == $name) {
+				unset($this->__methods[$m]);
+			}
+		}
+		$this->_attached = array_values(array_diff($this->_attached, (array)$name));
+	}
+/**
+ * Enables callbacks on a behavior or array of behaviors
+ *
+ * @param mixed $name CamelCased name of the behavior(s) to enable (string or array)
+ * @return void
+ * @access public
+ */
+	function enable($name) {
+		$this->_disabled = array_diff($this->_disabled, (array)$name);
+	}
+/**
+ * Disables callbacks on a behavior or array of behaviors.  Public behavior methods are still
+ * callable as normal.
+ *
+ * @param mixed $name CamelCased name of the behavior(s) to disable (string or array)
+ * @return void
+ * @access public
+ */
+	function disable($name) {
+		foreach ((array)$name as $behavior) {
+			if (in_array($behavior, $this->_attached) && !in_array($behavior, $this->_disabled)) {
+				$this->_disabled[] = $behavior;
+			}
+		}
+	}
+/**
+ * Gets the list of currently-enabled behaviors, or, the current status of a single behavior
+ *
+ * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
+ *						returns an array of currently-enabled behaviors
+ * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
+ *               Otherwise, returns an array of all enabled behaviors.
+ * @access public
+ */
+	function enabled($name = null) {
+		if (!empty($name)) {
+			return (in_array($name, $this->_attached) && !in_array($name, $this->_disabled));
+		}
+		return array_diff($this->_attached, $this->_disabled);
+	}
+/**
+ * Dispatches a behavior method
+ *
+ * @return array All methods for all behaviors attached to this object
+ * @access public
+ */
+	function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
+		$methods = array_keys($this->__methods);
+		foreach ($methods as $key => $value) {
+			$methods[$key] = strtolower($value);
+		}
+		$method = strtolower($method);
+		$check = array_flip($methods);
+		$found = isset($check[$method]);
+		$call = null;
+
+		if ($strict && !$found) {
+			trigger_error("BehaviorCollection::dispatchMethod() - Method {$method} not found in any attached behavior", E_USER_WARNING);
+			return null;
+		} elseif ($found) {
+			$methods = array_combine($methods, array_values($this->__methods));
+			$call = $methods[$method];
+		} else {
+			$count = count($this->__mappedMethods);
+			$mapped = array_keys($this->__mappedMethods);
+
+			for ($i = 0; $i < $count; $i++) {
+				if (preg_match($mapped[$i] . 'i', $method)) {
+					$call = $this->__mappedMethods[$mapped[$i]];
+					array_unshift($params, $method);
+					break;
+				}
+			}
+		}
+
+		if (!empty($call)) {
+			return $this->{$call[1]}->dispatchMethod($model, $call[0], $params);
+		}
+		return array('unhandled');
+	}
+/**
+ * Dispatches a behavior callback on all attached behavior objects
+ *
+ * @param model $model
+ * @param string $callback
+ * @param array $params
+ * @param array $options
+ * @return mixed
+ * @access public
+ */
+	function trigger(&$model, $callback, $params = array(), $options = array()) {
+		if (empty($this->_attached)) {
+			return true;
+		}
+		$_params = $params;
+		$options = array_merge(array('break' => false, 'breakOn' => array(null, false), 'modParams' => false), $options);
+		$count = count($this->_attached);
+
+		for ($i = 0; $i < $count; $i++) {
+			$name = $this->_attached[$i];
+			if (in_array($name, $this->_disabled)) {
+				continue;
+			}
+			$result = $this->{$name}->dispatchMethod($model, $callback, $params);
+
+			if ($options['break'] && ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))) {
+				return $result;
+			} elseif ($options['modParams'] && is_array($result)) {
+				$params[0] = $result;
+			}
+		}
+		if ($options['modParams'] && isset($params[0])) {
+			return $params[0];
+		}
+		return true;
+	}
+/**
+ * Gets the method list for attached behaviors, i.e. all public, non-callback methods
+ *
+ * @return array All public methods for all behaviors attached to this collection
+ * @access public
+ */
+	function methods() {
+		return $this->__methods;
+	}
+/**
+ * Gets the list of attached behaviors, or, whether the given behavior is attached
+ *
+ * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
+ *						returns an array of currently-attached behaviors
+ * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
+ *               Otherwise, returns an array of all attached behaviors.
+ * @access public
+ */
+	function attached($name = null) {
+		if (!empty($name)) {
+			return (in_array($name, $this->_attached));
+		}
+		return $this->_attached;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/app_model.php
===================================================================
--- cake/libs/model/app_model.php	(revision 0)
+++ cake/libs/model/app_model.php	(revision 0)
@@ -0,0 +1,40 @@
+<?php
+/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Application model for Cake.
+ *
+ * This file is application-wide model file. You can put all
+ * application-wide model-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Application model for Cake.
+ *
+ * This is a placeholder class.
+ * Create the same file in app/app_model.php
+ * Add your application-wide methods to the class, your models will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class AppModel extends Model {
+}
+?>
\ No newline at end of file
Index: cake/libs/model/model.php
===================================================================
--- cake/libs/model/model.php	(revision 0)
+++ cake/libs/model/model.php	(revision 0)
@@ -0,0 +1,2856 @@
+<?php
+/* SVN FILE: $Id: model.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Object-relational mapper.
+ *
+ * DBO-backed object data model, for mapping database tables to Cake objects.
+ *
+ * PHP versions 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.10.0.0
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libs
+ */
+App::import('Core', array('ClassRegistry', 'Overloadable', 'Validation', 'Behavior', 'ConnectionManager', 'Set', 'String'));
+/**
+ * Object-relational mapper.
+ *
+ * DBO-backed object data model.
+ * Automatically selects a database table name based on a pluralized lowercase object class name
+ * (i.e. class 'User' => table 'users'; class 'Man' => table 'men')
+ * The table is required to have at least 'id auto_increment' primary key.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @link          http://book.cakephp.org/view/66/Models
+ */
+class Model extends Overloadable {
+/**
+ * The name of the DataSource connection that this Model uses
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/435/useDbConfig
+ */
+	var $useDbConfig = 'default';
+/**
+ * Custom database table name, or null/false if no table association is desired.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/436/useTable
+ */
+	var $useTable = null;
+/**
+ * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/438/displayField
+ */
+	var $displayField = null;
+/**
+ * Value of the primary key ID of the record that this model is currently pointing to.
+ * Automatically set after database insertions.
+ *
+ * @var mixed
+ * @access public
+ */
+	var $id = false;
+/**
+ * Container for the data that this model gets from persistent storage (usually, a database).
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/441/data
+ */
+	var $data = array();
+/**
+ * Table name for this Model.
+ *
+ * @var string
+ * @access public
+ */
+	var $table = false;
+/**
+ * The name of the primary key field for this model.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/437/primaryKey
+ */
+	var $primaryKey = null;
+/**
+ * Field-by-field table metadata.
+ *
+ * @var array
+ * @access protected
+ * @link http://book.cakephp.org/view/442/_schema
+ */
+	var $_schema = null;
+/**
+ * List of validation rules. Append entries for validation as ('field_name' => '/^perl_compat_regexp$/')
+ * that have to match with preg_match(). Use these rules with Model::validate()
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/443/validate
+ * @link http://book.cakephp.org/view/125/Data-Validation
+ */
+	var $validate = array();
+/**
+ * List of validation errors.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller
+ */
+	var $validationErrors = array();
+/**
+ * Database table prefix for tables in model.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/475/tablePrefix
+ */
+	var $tablePrefix = null;
+/**
+ * Name of the model.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/444/name
+ */
+	var $name = null;
+/**
+ * Alias name for model.
+ *
+ * @var string
+ * @access public
+ */
+	var $alias = null;
+/**
+ * List of table names included in the model description. Used for associations.
+ *
+ * @var array
+ * @access public
+ */
+	var $tableToModel = array();
+/**
+ * Whether or not to log transactions for this model.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $logTransactions = false;
+/**
+ * Whether or not to enable transactions for this model (i.e. BEGIN/COMMIT/ROLLBACK statements)
+ *
+ * @var boolean
+ * @access public
+ */
+	var $transactional = false;
+/**
+ * Whether or not to cache queries for this model.  This enables in-memory
+ * caching only, the results are not stored beyond the current request.
+ *
+ * @var boolean
+ * @access public
+ * @link http://book.cakephp.org/view/445/cacheQueries
+ */
+	var $cacheQueries = false;
+/**
+ * Detailed list of belongsTo associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/81/belongsTo
+ */
+	var $belongsTo = array();
+/**
+ * Detailed list of hasOne associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/80/hasOne
+ */
+	var $hasOne = array();
+/**
+ * Detailed list of hasMany associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/82/hasMany
+ */
+	var $hasMany = array();
+/**
+ * Detailed list of hasAndBelongsToMany associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/83/hasAndBelongsToMany-HABTM
+ */
+	var $hasAndBelongsToMany = array();
+/**
+ * List of behaviors to load when the model object is initialized. Settings can be
+ * passed to behaviors by using the behavior name as index. Eg:
+ *
+ * var $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1'))
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/90/Using-Behaviors
+ */
+	var $actsAs = null;
+/**
+ * Holds the Behavior objects currently bound to this model.
+ *
+ * @var object
+ * @access public
+ */
+	var $Behaviors = null;
+/**
+ * Whitelist of fields allowed to be saved.
+ *
+ * @var array
+ * @access public
+ */
+	var $whitelist = array();
+/**
+ * Whether or not to cache sources for this model.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $cacheSources = true;
+/**
+ * Type of find query currently executing.
+ *
+ * @var string
+ * @access public
+ */
+	var $findQueryType = null;
+/**
+ * Number of associations to recurse through during find calls. Fetches only
+ * the first level by default.
+ *
+ * @var integer
+ * @access public
+ * @link http://book.cakephp.org/view/439/recursive
+ */
+	var $recursive = 1;
+/**
+ * The column name(s) and direction(s) to order find results by default.
+ *
+ * var $order = "Post.created DESC";
+ * var $order = array("Post.view_count DESC", "Post.rating DESC");
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/440/order
+ */
+	var $order = null;
+/**
+ * Whether or not the model record exists, set by Model::exists().
+ *
+ * @var bool
+ * @access private
+ */
+	var $__exists = null;
+/**
+ * Default list of association keys.
+ *
+ * @var array
+ * @access private
+ */
+	var $__associationKeys = array(
+			'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'),
+			'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'),
+			'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'),
+			'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery'));
+/**
+ * Holds provided/generated association key names and other data for all associations.
+ *
+ * @var array
+ * @access private
+ */
+	var $__associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+/**
+ * Holds model associations temporarily to allow for dynamic (un)binding.
+ *
+ * @var array
+ * @access private
+ */
+	var $__backAssociation = array();
+/**
+ * The ID of the model record that was last inserted.
+ *
+ * @var integer
+ * @access private
+ */
+	var $__insertID = null;
+/**
+ * The number of records returned by the last query.
+ *
+ * @var integer
+ * @access private
+ */
+	var $__numRows = null;
+/**
+ * The number of records affected by the last query.
+ *
+ * @var integer
+ * @access private
+ */
+	var $__affectedRows = null;
+/**
+ * List of valid finder method options, supplied as the first parameter to find().
+ *
+ * @var array
+ * @access protected
+ */
+	var $_findMethods = array(
+		'all' => true, 'first' => true, 'count' => true,
+		'neighbors' => true, 'list' => true, 'threaded' => true
+	);
+/**
+ * Constructor. Binds the model's database table to the object.
+ *
+ * @param integer $id Set this ID for this model on startup
+ * @param string $table Name of database table to use.
+ * @param object $ds DataSource connection object.
+ */
+	function __construct($id = false, $table = null, $ds = null) {
+		parent::__construct();
+
+		if (is_array($id)) {
+			extract(array_merge(
+				array(
+					'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig,
+					'name' => $this->name, 'alias' => $this->alias
+				),
+				$id
+			));
+		}
+
+		if ($this->name === null) {
+			$this->name = (isset($name) ? $name : get_class($this));
+		}
+
+		if ($this->alias === null) {
+			$this->alias = (isset($alias) ? $alias : $this->name);
+		}
+
+		if ($this->primaryKey === null) {
+			$this->primaryKey = 'id';
+		}
+
+		ClassRegistry::addObject($this->alias, $this);
+
+		$this->id = $id;
+		unset($id);
+
+		if ($table === false) {
+			$this->useTable = false;
+		} elseif ($table) {
+			$this->useTable = $table;
+		}
+
+		if (is_subclass_of($this, 'AppModel')) {
+			$appVars = get_class_vars('AppModel');
+			$merge = array('_findMethods');
+
+			if ($this->actsAs !== null || $this->actsAs !== false) {
+				$merge[] = 'actsAs';
+			}
+			$parentClass = get_parent_class($this);
+			if (strtolower($parentClass) !== 'appmodel') {
+				$parentVars = get_class_vars($parentClass);
+				foreach ($merge as $var) {
+					if (isset($parentVars[$var]) && !empty($parentVars[$var])) {
+						$appVars[$var] = Set::merge($appVars[$var], $parentVars[$var]);
+					}
+				}
+			}
+
+			foreach ($merge as $var) {
+				if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) {
+					$this->{$var} = Set::merge($appVars[$var], $this->{$var});
+				}
+			}
+		}
+		$this->Behaviors = new BehaviorCollection();
+
+		if ($this->useTable !== false) {
+			$this->setDataSource($ds);
+
+			if ($this->useTable === null) {
+				$this->useTable = Inflector::tableize($this->name);
+			}
+			if (method_exists($this, 'setTablePrefix')) {
+				$this->setTablePrefix();
+			}
+			$this->setSource($this->useTable);
+
+			if ($this->displayField == null) {
+				$this->displayField = $this->hasField(array('title', 'name', $this->primaryKey));
+			}
+		} elseif ($this->table === false) {
+			$this->table = Inflector::tableize($this->name);
+		}
+		$this->__createLinks();
+		$this->Behaviors->init($this->alias, $this->actsAs);
+	}
+/**
+ * Handles custom method calls, like findBy<field> for DB models,
+ * and custom RPC calls for remote data sources.
+ *
+ * @param string $method Name of method to call.
+ * @param array $params Parameters for the method.
+ * @return mixed Whatever is returned by called method
+ * @access protected
+ */
+	function call__($method, $params) {
+		$result = $this->Behaviors->dispatchMethod($this, $method, $params);
+
+		if ($result !== array('unhandled')) {
+			return $result;
+		}
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		$return = $db->query($method, $params, $this);
+
+		if (!PHP5) {
+			$this->resetAssociations();
+		}
+		return $return;
+	}
+/**
+ * Bind model associations on the fly.
+ *
+ * If $permanent is true, association will not be reset
+ * to the originals defined in the model.
+ *
+ * @param mixed $model A model or association name (string) or set of binding options (indexed by model name type)
+ * @param array $options If $model is a string, this is the list of association properties with which $model will
+ * 						 be bound
+ * @param boolean $permanent Set to true to make the binding permanent
+ * @return void
+ * @access public
+ * @todo
+ */
+	function bind($model, $options = array(), $permanent = true) {
+		if (!is_array($model)) {
+			$model = array($model => $options);
+		}
+
+		foreach ($model as $name => $options) {
+			if (isset($options['type'])) {
+				$assoc = $options['type'];
+			} elseif (isset($options[0])) {
+				$assoc = $options[0];
+			} else {
+				$assoc = 'belongsTo';
+			}
+
+			if (!$permanent) {
+				$this->__backAssociation[$assoc] = $this->{$assoc};
+			}
+			foreach ($model as $key => $value) {
+				$assocName = $modelName = $key;
+
+				if (isset($this->{$assoc}[$assocName])) {
+					$this->{$assoc}[$assocName] = array_merge($this->{$assoc}[$assocName], $options);
+				} else {
+					if (isset($value['className'])) {
+						$modelName = $value['className'];
+					}
+
+					$this->__constructLinkedModel($assocName, $modelName);
+					$this->{$assoc}[$assocName] = $model[$assocName];
+					$this->__generateAssociation($assoc);
+				}
+				unset($this->{$assoc}[$assocName]['type'], $this->{$assoc}[$assocName][0]);
+			}
+		}
+	}
+/**
+ * Bind model associations on the fly.
+ *
+ * If $reset is false, association will not be reset
+ * to the originals defined in the model
+ *
+ * Example: Add a new hasOne binding to the Profile model not
+ * defined in the model source code:
+ * <code>
+ * $this->User->bindModel( array('hasOne' => array('Profile')) );
+ * </code>
+ *
+ * @param array $params Set of bindings (indexed by binding type)
+ * @param boolean $reset Set to false to make the binding permanent
+ * @return boolean Success
+ * @access public
+ * @link http://book.cakephp.org/view/86/Creating-and-Destroying-Associations-on-the-Fly
+ */
+	function bindModel($params, $reset = true) {
+		foreach ($params as $assoc => $model) {
+			if ($reset === true) {
+				$this->__backAssociation[$assoc] = $this->{$assoc};
+			}
+
+			foreach ($model as $key => $value) {
+				$assocName = $key;
+
+				if (is_numeric($key)) {
+					$assocName = $value;
+					$value = array();
+				}
+				$modelName = $assocName;
+				$this->{$assoc}[$assocName] = $value;
+			}
+		}
+		$this->__createLinks();
+		return true;
+	}
+/**
+ * Turn off associations on the fly.
+ *
+ * If $reset is false, association will not be reset
+ * to the originals defined in the model
+ *
+ * Example: Turn off the associated Model Support request,
+ * to temporarily lighten the User model:
+ * <code>
+ * $this->User->unbindModel( array('hasMany' => array('Supportrequest')) );
+ * </code>
+ *
+ * @param array $params Set of bindings to unbind (indexed by binding type)
+ * @param boolean $reset  Set to false to make the unbinding permanent
+ * @return boolean Success
+ * @access public
+ * @link http://book.cakephp.org/view/86/Creating-and-Destroying-Associations-on-the-Fly
+ */
+	function unbindModel($params, $reset = true) {
+		foreach ($params as $assoc => $models) {
+			if ($reset === true) {
+				$this->__backAssociation[$assoc] = $this->{$assoc};
+			}
+
+			foreach ($models as $model) {
+				$this->__backAssociation = array_merge($this->__backAssociation, $this->{$assoc});
+				unset ($this->__backAssociation[$model]);
+				unset ($this->{$assoc}[$model]);
+			}
+		}
+		return true;
+	}
+/**
+ * Create a set of associations.
+ *
+ * @return void
+ * @access private
+ */
+	function __createLinks() {
+		foreach ($this->__associations as $type) {
+			if (!is_array($this->{$type})) {
+				$this->{$type} = explode(',', $this->{$type});
+
+				foreach ($this->{$type} as $i => $className) {
+					$className = trim($className);
+					unset ($this->{$type}[$i]);
+					$this->{$type}[$className] = array();
+				}
+			}
+
+			if (!empty($this->{$type})) {
+				foreach ($this->{$type} as $assoc => $value) {
+					$plugin = null;
+
+					if (is_numeric($assoc)) {
+						unset ($this->{$type}[$assoc]);
+						$assoc = $value;
+						$value = array();
+						$this->{$type}[$assoc] = $value;
+
+						if (strpos($assoc, '.') !== false) {
+							$value = $this->{$type}[$assoc];
+							unset($this->{$type}[$assoc]);
+							list($plugin, $assoc) = explode('.', $assoc);
+							$this->{$type}[$assoc] = $value;
+							$plugin = $plugin . '.';
+						}
+					}
+					$className =  $assoc;
+
+					if (isset($value['className']) && !empty($value['className'])) {
+						$className = $value['className'];
+						if (strpos($className, '.') !== false) {
+							list($plugin, $className) = explode('.', $className);
+							$plugin = $plugin . '.';
+							$this->{$type}[$assoc]['className'] = $className;
+						}
+					}
+					$this->__constructLinkedModel($assoc, $plugin . $className);
+				}
+				$this->__generateAssociation($type);
+			}
+		}
+	}
+/**
+ * Private helper method to create associated models of a given class.
+ *
+ * @param string $assoc Association name
+ * @param string $className Class name
+ * @deprecated $this->$className use $this->$assoc instead. $assoc is the 'key' in the associations array;
+ * 	examples: var $hasMany = array('Assoc' => array('className' => 'ModelName'));
+ * 					usage: $this->Assoc->modelMethods();
+ *
+ * 				var $hasMany = array('ModelName');
+ * 					usage: $this->ModelName->modelMethods();
+ * @return void
+ * @access private
+ */
+	function __constructLinkedModel($assoc, $className = null) {
+		if (empty($className)) {
+			$className = $assoc;
+		}
+
+		if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) {
+			$model = array('class' => $className, 'alias' => $assoc);
+			if (PHP5) {
+				$this->{$assoc} = ClassRegistry::init($model);
+			} else {
+				$this->{$assoc} =& ClassRegistry::init($model);
+			}
+			if ($assoc) {
+				$this->tableToModel[$this->{$assoc}->table] = $assoc;
+			}
+		}
+	}
+/**
+ * Build an array-based association from string.
+ *
+ * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'
+ * @return void
+ * @access private
+ */
+	function __generateAssociation($type) {
+		foreach ($this->{$type} as $assocKey => $assocData) {
+			$class = $assocKey;
+			$dynamicWith = false;
+
+			foreach ($this->__associationKeys[$type] as $key) {
+
+				if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) {
+					$data = '';
+
+					switch ($key) {
+						case 'fields':
+							$data = '';
+						break;
+
+						case 'foreignKey':
+							$data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id';
+						break;
+
+						case 'associationForeignKey':
+							$data = Inflector::singularize($this->{$class}->table) . '_id';
+						break;
+
+						case 'with':
+							$data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable']));
+							$dynamicWith = true;
+						break;
+
+						case 'joinTable':
+							$tables = array($this->table, $this->{$class}->table);
+							sort ($tables);
+							$data = $tables[0] . '_' . $tables[1];
+						break;
+
+						case 'className':
+							$data = $class;
+						break;
+
+						case 'unique':
+							$data = true;
+						break;
+					}
+					$this->{$type}[$assocKey][$key] = $data;
+				}
+			}
+
+			if (!empty($this->{$type}[$assocKey]['with'])) {
+				$joinClass = $this->{$type}[$assocKey]['with'];
+				if (is_array($joinClass)) {
+					$joinClass = key($joinClass);
+				}
+				$plugin = null;
+
+				if (strpos($joinClass, '.') !== false) {
+					list($plugin, $joinClass) = explode('.', $joinClass);
+					$plugin = $plugin . '.';
+					$this->{$type}[$assocKey]['with'] = $joinClass;
+				}
+
+				if (!ClassRegistry::isKeySet($joinClass) && $dynamicWith === true) {
+					$this->{$joinClass} = new AppModel(array(
+						'name' => $joinClass,
+						'table' => $this->{$type}[$assocKey]['joinTable'],
+						'ds' => $this->useDbConfig
+					));
+				} else {
+					$this->__constructLinkedModel($joinClass, $plugin . $joinClass);
+					$this->{$type}[$assocKey]['joinTable'] = $this->{$joinClass}->table;
+				}
+
+				if (count($this->{$joinClass}->schema()) <= 2 && $this->{$joinClass}->primaryKey !== false) {
+					$this->{$joinClass}->primaryKey = $this->{$type}[$assocKey]['foreignKey'];
+				}
+			}
+		}
+	}
+/**
+ * Sets a custom table for your controller class. Used by your controller to select a database table.
+ *
+ * @param string $tableName Name of the custom table
+ * @return void
+ * @access public
+ */
+	function setSource($tableName) {
+		$this->setDataSource($this->useDbConfig);
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		$db->cacheSources = ($this->cacheSources && $db->cacheSources);
+
+		if ($db->isInterfaceSupported('listSources')) {
+			$sources = $db->listSources();
+			if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
+				return $this->cakeError('missingTable', array(array(
+					'className' => $this->alias,
+					'table' => $this->tablePrefix . $tableName
+				)));
+			}
+			$this->_schema = null;
+		}
+		$this->table = $this->useTable = $tableName;
+		$this->tableToModel[$this->table] = $this->alias;
+		$this->schema();
+	}
+/**
+ * This function does two things: 1) it scans the array $one for the primary key,
+ * and if that's found, it sets the current id to the value of $one[id].
+ * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object.
+ * 2) Returns an array with all of $one's keys and values.
+ * (Alternative indata: two strings, which are mangled to
+ * a one-item, two-dimensional array using $one for a key and $two as its value.)
+ *
+ * @param mixed $one Array or string of data
+ * @param string $two Value string for the alternative indata method
+ * @return array Data with all of $one's keys and values
+ * @access public
+ */
+	function set($one, $two = null) {
+		if (!$one) {
+			return;
+		}
+		if (is_object($one)) {
+			$one = Set::reverse($one);
+		}
+
+		if (is_array($one)) {
+			$data = $one;
+			if (empty($one[$this->alias])) {
+				if ($this->getAssociated(key($one)) === null) {
+					$data = array($this->alias => $one);
+				}
+			}
+		} else {
+			$data = array($this->alias => array($one => $two));
+		}
+
+		foreach ($data as $modelName => $fieldSet) {
+			if (is_array($fieldSet)) {
+
+				foreach ($fieldSet as $fieldName => $fieldValue) {
+					if (isset($this->validationErrors[$fieldName])) {
+						unset ($this->validationErrors[$fieldName]);
+					}
+
+					if ($modelName === $this->alias) {
+						if ($fieldName === $this->primaryKey) {
+							$this->id = $fieldValue;
+						}
+					}
+					if (is_array($fieldValue) || is_object($fieldValue)) {
+						$fieldValue = $this->deconstruct($fieldName, $fieldValue);
+					}
+					$this->data[$modelName][$fieldName] = $fieldValue;
+				}
+			}
+		}
+		return $data;
+	}
+/**
+ * Deconstructs a complex data type (array or object) into a single field value.
+ *
+ * @param string $field The name of the field to be deconstructed
+ * @param mixed $data An array or object to be deconstructed into a field
+ * @return mixed The resulting data that should be assigned to a field
+ * @access public
+ */
+	function deconstruct($field, $data) {
+		$copy = $data;
+		$type = $this->getColumnType($field);
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) {
+			$useNewDate = (isset($data['year']) || isset($data['month']) || isset($data['day']) || isset($data['hour']) || isset($data['minute']));
+			$dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec');
+			$format = $db->columns[$type]['format'];
+			$date = array();
+
+			if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) {
+				$data['hour'] = $data['hour'] + 12;
+			}
+			if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) {
+				$data['hour'] = '00';
+			}
+
+			foreach ($dateFields as $key => $val) {
+				if (in_array($val, array('hour', 'min', 'sec'))) {
+					if (!isset($data[$val]) || $data[$val] === '0' || empty($data[$val])) {
+						$data[$val] = '00';
+					} else {
+						$data[$val] = sprintf('%02d', $data[$val]);
+					}
+				}
+				if (in_array($type, array('datetime', 'timestamp', 'date')) && !isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) {
+					return null;
+				} elseif (isset($data[$val]) && !empty($data[$val])) {
+					$date[$key] = $data[$val];
+				}
+			}
+			$date = str_replace(array_keys($date), array_values($date), $format);
+			if ($type == 'time' && $date == '00:00:00') {
+				return null;
+			}
+
+			if ($useNewDate && (!empty($date))) {
+				return $date;
+			}
+		}
+		return $data;
+	}
+/**
+ * Returns an array of table metadata (column names and types) from the database.
+ * $field => keys(type, null, default, key, length, extra)
+ *
+ * @param mixed $field Set to true to reload schema, or a string to return a specific field
+ * @return array Array of table metadata
+ * @access public
+ */
+	function schema($field = false) {
+		if (!is_array($this->_schema) || $field === true) {
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+			$db->cacheSources = ($this->cacheSources && $db->cacheSources);
+			if ($db->isInterfaceSupported('describe') && $this->useTable !== false) {
+				$this->_schema = $db->describe($this, $field);
+			} elseif ($this->useTable === false) {
+				$this->_schema = array();
+			}
+		}
+		if (is_string($field)) {
+			if (isset($this->_schema[$field])) {
+				return $this->_schema[$field];
+			} else {
+				return null;
+			}
+		}
+		return $this->_schema;
+	}
+/**
+ * Returns an associative array of field names and column types.
+ *
+ * @return array Field types indexed by field name
+ * @access public
+ */
+	function getColumnTypes() {
+		$columns = $this->schema();
+		if (empty($columns)) {
+			trigger_error(__('(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()', true), E_USER_WARNING);
+		}
+		$cols = array();
+		foreach ($columns as $field => $values) {
+			$cols[$field] = $values['type'];
+		}
+		return $cols;
+	}
+/**
+ * Returns the column type of a column in the model.
+ *
+ * @param string $column The name of the model column
+ * @return string Column type
+ * @access public
+ */
+	function getColumnType($column) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		$cols = $this->schema();
+		$model = null;
+
+		$column = str_replace(array($db->startQuote, $db->endQuote), '', $column);
+
+		if (strpos($column, '.')) {
+			list($model, $column) = explode('.', $column);
+		}
+		if ($model != $this->alias && isset($this->{$model})) {
+			return $this->{$model}->getColumnType($column);
+		}
+		if (isset($cols[$column]) && isset($cols[$column]['type'])) {
+			return $cols[$column]['type'];
+		}
+		return null;
+	}
+/**
+ * Returns true if the supplied field exists in the model's database table.
+ *
+ * @param mixed $name Name of field to look for, or an array of names
+ * @return mixed If $name is a string, returns a boolean indicating whether the field exists.
+ *               If $name is an array of field names, returns the first field that exists,
+ *               or false if none exist.
+ * @access public
+ */
+	function hasField($name) {
+		if (is_array($name)) {
+			foreach ($name as $n) {
+				if ($this->hasField($n)) {
+					return $n;
+				}
+			}
+			return false;
+		}
+
+		if (empty($this->_schema)) {
+			$this->schema();
+		}
+
+		if ($this->_schema != null) {
+			return isset($this->_schema[$name]);
+		}
+		return false;
+	}
+/**
+ * Initializes the model for writing a new record, loading the default values
+ * for those fields that are not defined in $data. Especially helpful for
+ * saving data in loops.
+ *
+ * @param mixed $data Optional data array to assign to the model after it is created.  If null or false,
+ *                    schema data defaults are not merged.
+ * @param boolean $filterKey If true, overwrites any primary key input with an empty value
+ * @return array The current Model::data; after merging $data and/or defaults from database
+ * @access public
+ * @link http://book.cakephp.org/view/75/Saving-Your-Data
+ */
+	function create($data = array(), $filterKey = false) {
+		$defaults = array();
+		$this->id = false;
+		$this->data = array();
+		$this->__exists = null;
+		$this->validationErrors = array();
+
+		if ($data !== null && $data !== false) {
+			foreach ($this->schema() as $field => $properties) {
+				if ($this->primaryKey !== $field && isset($properties['default'])) {
+					$defaults[$field] = $properties['default'];
+				}
+			}
+			$this->set(Set::filter($defaults));
+			$this->set($data);
+		}
+		if ($filterKey) {
+			$this->set($this->primaryKey, false);
+		}
+		return $this->data;
+	}
+/**
+ * Returns a list of fields from the database, and sets the current model
+ * data (Model::$data) with the record found.
+ *
+ * @param mixed $fields String of single fieldname, or an array of fieldnames.
+ * @param mixed $id The ID of the record to read
+ * @return array Array of database fields, or false if not found
+ * @access public
+ */
+	function read($fields = null, $id = null) {
+		$this->validationErrors = array();
+
+		if ($id != null) {
+			$this->id = $id;
+		}
+
+		$id = $this->id;
+
+		if (is_array($this->id)) {
+			$id = $this->id[0];
+		}
+
+		if ($id !== null && $id !== false) {
+			$this->data = $this->find(array($this->alias . '.' . $this->primaryKey => $id), $fields);
+			return $this->data;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Returns the contents of a single field given the supplied conditions, in the
+ * supplied order.
+ *
+ * @param string $name Name of field to get
+ * @param array $conditions SQL conditions (defaults to NULL)
+ * @param string $order SQL ORDER BY fragment
+ * @return string field contents, or false if not found
+ * @access public
+ * @link http://book.cakephp.org/view/453/field
+ */
+	function field($name, $conditions = null, $order = null) {
+		if ($conditions === null && $this->id !== false) {
+			$conditions = array($this->alias . '.' . $this->primaryKey => $this->id);
+		}
+		if ($this->recursive >= 1) {
+			$recursive = -1;
+		} else {
+			$recursive = $this->recursive;
+		}
+		if ($data = $this->find($conditions, $name, $order, $recursive)) {
+			if (strpos($name, '.') === false) {
+				if (isset($data[$this->alias][$name])) {
+					return $data[$this->alias][$name];
+				}
+			} else {
+				$name = explode('.', $name);
+				if (isset($data[$name[0]][$name[1]])) {
+					return $data[$name[0]][$name[1]];
+				}
+			}
+			if (isset($data[0]) && count($data[0]) > 0) {
+				$name = key($data[0]);
+				return $data[0][$name];
+			}
+		} else {
+			return false;
+		}
+	}
+/**
+ * Saves the value of a single field to the database, based on the current
+ * model ID.
+ *
+ * @param string $name Name of the table field
+ * @param mixed $value Value of the field
+ * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed
+ * @return boolean See Model::save()
+ * @access public
+ * @see Model::save()
+ * @link http://book.cakephp.org/view/75/Saving-Your-Data
+ */
+	function saveField($name, $value, $validate = false) {
+		$id = $this->id;
+		$this->create(false);
+
+		if (is_array($validate)) {
+			$options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate);
+		} else {
+			$options = array('validate' => $validate, 'fieldList' => array($name));
+		}
+		return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options);
+	}
+/**
+ * Saves model data (based on white-list, if supplied) to the database. By
+ * default, validation occurs before save.
+ *
+ * @param array $data Data to save.
+ * @param mixed $validate Either a boolean, or an array.
+ * 			If a boolean, indicates whether or not to validate before saving.
+ *			If an array, allows control of validate, callbacks, and fieldList
+ * @param array $fieldList List of fields to allow to be written
+ * @return mixed On success Model::$data if its not empty or true, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/75/Saving-Your-Data
+ */
+	function save($data = null, $validate = true, $fieldList = array()) {
+		$defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true);
+		$_whitelist = $this->whitelist;
+		$fields = array();
+
+		if (!is_array($validate)) {
+			$options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks'));
+		} else {
+			$options = array_merge($defaults, $validate);
+		}
+
+		if (!empty($options['fieldList'])) {
+			$this->whitelist = $options['fieldList'];
+		} elseif ($options['fieldList'] === null) {
+			$this->whitelist = array();
+		}
+		$this->set($data);
+
+		if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) {
+			return false;
+		}
+
+		foreach (array('created', 'updated', 'modified') as $field) {
+			$keyPresentAndEmpty = (
+				isset($this->data[$this->alias]) &&
+				array_key_exists($field, $this->data[$this->alias]) &&
+				$this->data[$this->alias][$field] === null
+			);
+			if ($keyPresentAndEmpty) {
+				unset($this->data[$this->alias][$field]);
+			}
+		}
+
+		$this->exists();
+		$dateFields = array('modified', 'updated');
+
+		if (!$this->__exists) {
+			$dateFields[] = 'created';
+		}
+		if (isset($this->data[$this->alias])) {
+			$fields = array_keys($this->data[$this->alias]);
+		}
+		if ($options['validate'] && !$this->validates($options)) {
+			$this->whitelist = $_whitelist;
+			return false;
+		}
+
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		foreach ($dateFields as $updateCol) {
+			if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) {
+				$default = array('formatter' => 'date');
+				$colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]);
+				if (!array_key_exists('format', $colType)) {
+					$time = strtotime('now');
+				} else {
+					$time = $colType['formatter']($colType['format']);
+				}
+				if (!empty($this->whitelist)) {
+					$this->whitelist[] = $updateCol;
+				}
+				$this->set($updateCol, $time);
+			}
+		}
+
+		if ($options['callbacks'] === true || $options['callbacks'] === 'before') {
+			$result = $this->Behaviors->trigger($this, 'beforeSave', array($options), array(
+				'break' => true, 'breakOn' => false
+			));
+			if (!$result || !$this->beforeSave($options)) {
+				$this->whitelist = $_whitelist;
+				return false;
+			}
+		}
+		$fields = $values = array();
+
+		if (isset($this->data[$this->alias][$this->primaryKey]) && empty($this->data[$this->alias][$this->primaryKey])) {
+			unset($this->data[$this->alias][$this->primaryKey]);
+		}
+
+		foreach ($this->data as $n => $v) {
+			if (isset($this->hasAndBelongsToMany[$n])) {
+				if (isset($v[$n])) {
+					$v = $v[$n];
+				}
+				$joined[$n] = $v;
+			} else {
+				if ($n === $this->alias) {
+					foreach (array('created', 'updated', 'modified') as $field) {
+						if (array_key_exists($field, $v) && empty($v[$field])) {
+							unset($v[$field]);
+						}
+					}
+
+					foreach ($v as $x => $y) {
+						if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) {
+							list($fields[], $values[]) = array($x, $y);
+						}
+					}
+				}
+			}
+		}
+		$count = count($fields);
+
+		if (!$this->__exists && $count > 0) {
+			$this->id = false;
+		}
+		$success = true;
+		$created = false;
+
+		if ($count > 0) {
+			$cache = $this->_prepareUpdateFields(array_combine($fields, $values));
+
+			if (!empty($this->id)) {
+				$success = (bool)$db->update($this, $fields, $values);
+			} else {
+				foreach ($this->_schema as $field => $properties) {
+					if ($this->primaryKey === $field) {
+						$fInfo = $this->_schema[$field];
+						$isUUID = (
+							($fInfo['type'] === 'string' && $fInfo['length'] === 36) ||
+							($fInfo['type'] === 'binary' && $fInfo['length'] === 16)
+						);
+						if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) {
+							list($fields[], $values[]) = array($this->primaryKey, String::uuid());
+						}
+						break;
+					}
+				}
+
+				if (!$db->create($this, $fields, $values)) {
+					$success = $created = false;
+				} else {
+					$created = true;
+				}
+			}
+
+			if ($success && !empty($this->belongsTo)) {
+				$this->updateCounterCache($cache, $created);
+			}
+		}
+
+		if (!empty($joined) && $success === true) {
+			$this->__saveMulti($joined, $this->id);
+		}
+
+		if ($success && $count > 0) {
+			if (!empty($this->data)) {
+				$success = $this->data;
+			}
+			if ($options['callbacks'] === true || $options['callbacks'] === 'after') {
+				$this->Behaviors->trigger($this, 'afterSave', array($created, $options));
+				$this->afterSave($created);
+			}
+			if (!empty($this->data)) {
+				$success = Set::merge($success, $this->data);
+			}
+			$this->data = false;
+			$this->__exists = null;
+			$this->_clearCache();
+			$this->validationErrors = array();
+		}
+		$this->whitelist = $_whitelist;
+		return $success;
+	}
+/**
+ * Saves model hasAndBelongsToMany data to the database.
+ *
+ * @param array $joined Data to save
+ * @param mixed $id ID of record in this model
+ * @access private
+ */
+	function __saveMulti($joined, $id) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		foreach ($joined as $assoc => $data) {
+
+			if (isset($this->hasAndBelongsToMany[$assoc])) {
+				list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
+
+				$conditions = array($join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id);
+
+				$links = $this->{$join}->find('all', array(
+					'conditions' => $conditions,
+					'recursive' => -1,
+					'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey']
+				));
+
+				$isUUID = !empty($this->{$join}->primaryKey) && (($this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'string' && $this->{$join}->_schema[$this->{$join}->primaryKey]['length'] === 36)
+						|| ($this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'binary' && $this->{$join}->_schema[$this->{$join}->primaryKey]['length'] === 16));
+
+				$newData = $newValues = array();
+				$primaryAdded = false;
+
+				$fields =  array(
+					$db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']),
+					$db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey'])
+				);
+
+				$idField = $db->name($this->{$join}->primaryKey);
+				if ($isUUID && !in_array($idField, $fields)) {
+					$fields[] = $idField;
+					$primaryAdded = true;
+				}
+
+				foreach ((array)$data as $row) {
+					if ((is_string($row) && (strlen($row) == 36 || strlen($row) == 16)) || is_numeric($row)) {
+						$values = array(
+							$db->value($id, $this->getColumnType($this->primaryKey)),
+							$db->value($row)
+						);
+						if ($isUUID && $primaryAdded) {
+							$values[] = $db->value(String::uuid());
+						}
+						$values = join(',', $values);
+						$newValues[] = "({$values})";
+						unset($values);
+					} elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+						$newData[] = $row;
+					}
+				}
+
+				if ($this->hasAndBelongsToMany[$assoc]['unique']) {
+					$associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey'];
+					$oldLinks = Set::extract($links, "{n}.{$associationForeignKey}");
+					if (!empty($oldLinks)) {
+ 						$conditions[$associationForeignKey] = $oldLinks;
+						$db->delete($this->{$join}, $conditions);
+					}
+				}
+
+				if (!empty($newData)) {
+					foreach ($newData as $data) {
+						$data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id;
+						$this->{$join}->create($data);
+						$this->{$join}->save();
+					}
+				}
+
+				if (!empty($newValues)) {
+					$fields =  join(',', $fields);
+					$db->insertMulti($this->{$join}, $fields, $newValues);
+				}
+			}
+		}
+	}
+/**
+ * Updates the counter cache of belongsTo associations after a save or delete operation
+ *
+ * @param array $keys Optional foreign key data, defaults to the information $this->data
+ * @param boolean $created True if a new record was created, otherwise only associations with
+ *				  'counterScope' defined get updated
+ * @return void
+ * @access public
+ */
+	function updateCounterCache($keys = array(), $created = false) {
+		$keys = empty($keys) ? $this->data[$this->alias] : $keys;
+		$keys['old'] = isset($keys['old']) ? $keys['old'] : array();
+
+		foreach ($this->belongsTo as $parent => $assoc) {
+			$foreignKey = $assoc['foreignKey'];
+			$fkQuoted = $this->escapeField($assoc['foreignKey']);
+
+			if (!empty($assoc['counterCache'])) {
+				if ($assoc['counterCache'] === true) {
+					$assoc['counterCache'] = Inflector::underscore($this->alias) . '_count';
+				}
+				if (!$this->{$parent}->hasField($assoc['counterCache'])) {
+					continue;
+				}
+
+				if (!array_key_exists($foreignKey, $keys)) {
+					$keys[$foreignKey] = $this->field($foreignKey);
+				}
+				$recursive = (isset($assoc['counterScope']) ? 1 : -1);
+				$conditions = ($recursive == 1) ? (array)$assoc['counterScope'] : array();
+
+				if (isset($keys['old'][$foreignKey])) {
+					if ($keys['old'][$foreignKey] == $keys[$foreignKey]) {
+						continue;
+					}
+					$conditions[$fkQuoted] = $keys['old'][$foreignKey];
+					$count = intval($this->find('count', compact('conditions', 'recursive')));
+
+					$this->{$parent}->updateAll(
+						array($assoc['counterCache'] => $count),
+						array($this->{$parent}->escapeField() => $keys['old'][$foreignKey])
+					);
+				}
+				$conditions[$fkQuoted] = $keys[$foreignKey];
+
+				if ($recursive == 1) {
+					$conditions = array_merge($conditions, (array)$assoc['counterScope']);
+				}
+				$count = intval($this->find('count', compact('conditions', 'recursive')));
+
+				$this->{$parent}->updateAll(
+					array($assoc['counterCache'] => $count),
+					array($this->{$parent}->escapeField() => $keys[$foreignKey])
+				);
+			}
+		}
+	}
+/**
+ * Helper method for Model::updateCounterCache().  Checks the fields to be updated for
+ *
+ * @param array $data The fields of the record that will be updated
+ * @return array Returns updated foreign key values, along with an 'old' key containing the old
+ *               values, or empty if no foreign keys are updated.
+ * @access protected
+ */
+	function _prepareUpdateFields($data) {
+		$foreignKeys = array();
+		foreach ($this->belongsTo as $assoc => $info) {
+			if ($info['counterCache']) {
+				$foreignKeys[$assoc] = $info['foreignKey'];
+			}
+		}
+		$included = array_intersect($foreignKeys, array_keys($data));
+
+		if (empty($included) || empty($this->id)) {
+			return array();
+		}
+		$old = $this->find('first', array(
+			'conditions' => array('id' => $this->id),
+			'fields' => array_values($included),
+			'recursive' => -1
+		));
+		return array_merge($data, array('old' => $old[$this->alias]));
+	}
+/**
+ * Saves multiple individual records for a single model; Also works with a single record, as well as
+ * all its associated records.
+ *
+ * @param array $data Record data to save.  This can be either a numerically-indexed array (for saving multiple
+ * 						records of the same type), or an array indexed by association name.
+ * @param array $options Options to use when saving record data, which are as follows:
+ * 							- validate: Set to false to disable validation, true to validate each record before
+ * 							  saving, 'first' to validate *all* records before any are saved, or 'only' to only
+ * 							  validate the records, but not save them.
+ * 							- atomic: If true (default), will attempt to save all records in a single transaction.
+ *							  Should be set to false if database/table does not support transactions.
+ *								If false, we return an array similar to the $data array passed, but values are set to true/false
+ *								depending on whether each record saved successfully.
+ *							- fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * @return mixed True on success, or false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/84/Saving-Related-Model-Data-hasOne-hasMany-belongsTo
+ * @link http://book.cakephp.org/view/75/Saving-Your-Data
+ */
+	function saveAll($data = null, $options = array()) {
+		if (empty($data)) {
+			$data = $this->data;
+		}
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		$options = array_merge(array('validate' => true, 'atomic' => true), $options);
+		$this->validationErrors = $validationErrors = array();
+		$validates = true;
+		$return = array();
+
+		if ($options['atomic'] && $options['validate'] !== 'only') {
+			$db->begin($this);
+		}
+
+		if (Set::numeric(array_keys($data))) {
+			while ($validates) {
+				foreach ($data as $key => $record) {
+					if (!$currentValidates = $this->__save($this, $record, $options)) {
+						$validationErrors[$key] = $this->validationErrors;
+					}
+
+					if ($options['validate'] === 'only' || $options['validate'] === 'first') {
+						$validating = true;
+						if ($options['atomic']) {
+							$validates = $validates && $currentValidates;
+						} else {
+							$validates = $currentValidates;
+						}
+					} else {
+						$validating = false;
+						$validates = $currentValidates;
+					}
+
+					if (!$options['atomic']) {
+						$return[] = $validates;
+					} elseif (!$validates && !$validating) {
+						break;
+					}
+				}
+				$this->validationErrors = $validationErrors;
+
+				switch (true) {
+					case ($options['validate'] === 'only'):
+						return ($options['atomic'] ? $validates : $return);
+					break;
+					case ($options['validate'] === 'first'):
+						$options['validate'] = true;
+						continue;
+					break;
+					default:
+						if ($options['atomic']) {
+							if ($validates && ($db->commit($this) !== false)) {
+								return true;
+							}
+							$db->rollback($this);
+							return false;
+						}
+						return $return;
+					break;
+				}
+			}
+			return $return;
+		}
+		$associations = $this->getAssociated();
+
+		while ($validates) {
+			foreach ($data as $association => $values) {
+				if (isset($associations[$association])) {
+					switch ($associations[$association]) {
+						case 'belongsTo':
+							if ($this->__save($this->{$association}, $values, $options)) {
+								$data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
+								unset($data[$association]);
+							} else {
+								$validationErrors[$association] = $this->{$association}->validationErrors;
+								$validates = false;
+							}
+							if (!$options['atomic']) {
+								$return[$association][] = $validates;
+							}
+						break;
+					}
+				}
+			}
+			if (!$this->__save($this, $data, $options)) {
+				$validationErrors[$this->alias] = $this->validationErrors;
+				$validates = false;
+			}
+			if (!$options['atomic']) {
+				$return[$this->alias] = $validates;
+			}
+			$validating = ($options['validate'] === 'only' || $options['validate'] === 'first');
+
+			foreach ($data as $association => $values) {
+				if (!$validates && !$validating) {
+					break;
+				}
+				if (isset($associations[$association])) {
+					$type = $associations[$association];
+					switch ($type) {
+						case 'hasOne':
+							$values[$this->{$type}[$association]['foreignKey']] = $this->id;
+							if (!$this->__save($this->{$association}, $values, $options)) {
+								$validationErrors[$association] = $this->{$association}->validationErrors;
+								$validates = false;
+							}
+							if (!$options['atomic']) {
+								$return[$association][] = $validates;
+							}
+						break;
+						case 'hasMany':
+							foreach ($values as $i => $value) {
+								$values[$i][$this->{$type}[$association]['foreignKey']] =  $this->id;
+							}
+							$_options = array_merge($options, array('atomic' => false));
+
+							if ($_options['validate'] === 'first') {
+								$_options['validate'] = 'only';
+							}
+							$_return = $this->{$association}->saveAll($values, $_options);
+
+							if ($_return === false || (is_array($_return) && in_array(false, $_return, true))) {
+								$validationErrors[$association] = $this->{$association}->validationErrors;
+								$validates = false;
+							}
+							if (is_array($_return)) {
+								foreach ($_return as $val) {
+									if (!isset($return[$association])) {
+										$return[$association] = array();
+									} elseif (!is_array($return[$association])) {
+										$return[$association] = array($return[$association]);
+									}
+									$return[$association][] = $val;
+								}
+							} else {
+								$return[$association] = $_return;
+							}
+						break;
+					}
+				}
+			}
+			$this->validationErrors = $validationErrors;
+
+			if (isset($validationErrors[$this->alias])) {
+				$this->validationErrors = $validationErrors[$this->alias];
+			}
+
+			switch (true) {
+				case ($options['validate'] === 'only'):
+					return ($options['atomic'] ? $validates : $return);
+				break;
+				case ($options['validate'] === 'first'):
+					$options['validate'] = true;
+					continue;
+				break;
+				default:
+					if ($options['atomic']) {
+						if ($validates) {
+							return ($db->commit($this) !== false);
+						} else {
+							$db->rollback($this);
+						}
+					}
+					return $return;
+				break;
+			}
+		}
+	}
+/**
+ * Private helper method used by saveAll.
+ *
+ * @return boolean Success
+ * @access private
+ * @see Model::saveAll()
+ */
+	function __save(&$model, $data, $options) {
+		if ($options['validate'] === 'first' || $options['validate'] === 'only') {
+			if (!($model->create($data) && $model->validates($options))) {
+				return false;
+			}
+		} elseif (!($model->create(null) !== null && $model->save($data, $options))) {
+			return false;
+		}
+		return true;
+	}
+/**
+ * Updates multiple model records based on a set of conditions.
+ *
+ * @param array $fields Set of fields and values, indexed by fields.
+ * 						Fields are treated as SQL snippets, to insert literal values manually escape your data.
+ * @param mixed $conditions Conditions to match, true for all records
+ * @return boolean True on success, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/75/Saving-Your-Data
+ */
+	function updateAll($fields, $conditions = true) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return $db->update($this, $fields, null, $conditions);
+	}
+/**
+ * Alias for del().
+ *
+ * @param mixed $id ID of record to delete
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return boolean True on success
+ * @access public
+ * @see Model::del()
+ * @link http://book.cakephp.org/view/691/remove
+ */
+	function remove($id = null, $cascade = true) {
+		return $this->del($id, $cascade);
+	}
+/**
+ * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success.
+ *
+ * @param mixed $id ID of record to delete
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return boolean True on success
+ * @access public
+ * @link http://book.cakephp.org/view/690/del
+ */
+	function del($id = null, $cascade = true) {
+		if (!empty($id)) {
+			$this->id = $id;
+		}
+		$id = $this->id;
+
+		if ($this->exists() && $this->beforeDelete($cascade)) {
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+			if (!$this->Behaviors->trigger($this, 'beforeDelete', array($cascade), array('break' => true, 'breakOn' => false))) {
+				return false;
+			}
+			$this->_deleteDependent($id, $cascade);
+			$this->_deleteLinks($id);
+			$this->id = $id;
+
+			if (!empty($this->belongsTo)) {
+				$keys = $this->find('first', array('fields', $this->__collectForeignKeys()));
+			}
+
+			if ($db->delete($this)) {
+				if (!empty($this->belongsTo)) {
+					$this->updateCounterCache($keys[$this->alias]);
+				}
+				$this->Behaviors->trigger($this, 'afterDelete');
+				$this->afterDelete();
+				$this->_clearCache();
+				$this->id = false;
+				$this->__exists = null;
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Alias for del().
+ *
+ * @param mixed $id ID of record to delete
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return boolean True on success
+ * @access public
+ * @see Model::del()
+ */
+	function delete($id = null, $cascade = true) {
+		return $this->del($id, $cascade);
+	}
+/**
+ * Cascades model deletes through associated hasMany and hasOne child records.
+ *
+ * @param string $id ID of record that was deleted
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @return void
+ * @access protected
+ */
+	function _deleteDependent($id, $cascade) {
+		if (!empty($this->__backAssociation)) {
+			$savedAssociatons = $this->__backAssociation;
+			$this->__backAssociation = array();
+		}
+		foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) {
+			if ($data['dependent'] === true && $cascade === true) {
+
+				$model =& $this->{$assoc};
+				$conditions = array($model->escapeField($data['foreignKey']) => $id);
+				if ($data['conditions']) {
+					$conditions = array_merge($data['conditions'], $conditions);
+				}
+				$model->recursive = -1;
+
+				if (isset($data['exclusive']) && $data['exclusive']) {
+					$model->deleteAll($conditions);
+				} else {
+					$records = $model->find('all', array('conditions' => $conditions, 'fields' => $model->primaryKey));
+
+					if (!empty($records)) {
+						foreach ($records as $record) {
+							$model->delete($record[$model->alias][$model->primaryKey]);
+						}
+					}
+				}
+			}
+		}
+		if (isset($savedAssociatons)) {
+			$this->__backAssociation = $savedAssociatons;
+		}
+	}
+/**
+ * Cascades model deletes through HABTM join keys.
+ *
+ * @param string $id ID of record that was deleted
+ * @return void
+ * @access protected
+ */
+	function _deleteLinks($id) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		foreach ($this->hasAndBelongsToMany as $assoc => $data) {
+			$records = $this->{$data['with']}->find('all', array(
+				'conditions' => array_merge(array($this->{$data['with']}->escapeField($data['foreignKey']) => $id)),
+				'fields' => $this->{$data['with']}->primaryKey,
+				'recursive' => -1
+			));
+			if (!empty($records)) {
+				foreach ($records as $record) {
+					$this->{$data['with']}->delete($record[$this->{$data['with']}->alias][$this->{$data['with']}->primaryKey]);
+				}
+			}
+		}
+	}
+/**
+ * Deletes multiple model records based on a set of conditions.
+ *
+ * @param mixed $conditions Conditions to match
+ * @param boolean $cascade Set to true to delete records that depend on this record
+ * @param boolean $callbacks Run callbacks (not being used)
+ * @return boolean True on success, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/692/deleteAll
+ */
+	function deleteAll($conditions, $cascade = true, $callbacks = false) {
+		if (empty($conditions)) {
+			return false;
+		}
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+		if (!$cascade && !$callbacks) {
+			return $db->delete($this, $conditions);
+		} else {
+			$ids = Set::extract(
+				$this->find('all', array_merge(array('fields' => "{$this->alias}.{$this->primaryKey}", 'recursive' => 0), compact('conditions'))),
+				"{n}.{$this->alias}.{$this->primaryKey}"
+			);
+
+			if (empty($ids)) {
+				return false;
+			}
+
+			if ($callbacks) {
+				$_id = $this->id;
+				$result = true;
+				foreach ($ids as $id) {
+					$result = ($result && $this->delete($id, $cascade));
+				}
+				$this->id = $_id;
+				return $result;
+			} else {
+				foreach ($ids as $id) {
+					$this->_deleteLinks($id);
+					if ($cascade) {
+						$this->_deleteDependent($id, $cascade);
+					}
+				}
+				return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids));
+			}
+		}
+	}
+/**
+ * Collects foreign keys from associations.
+ *
+ * @return array
+ * @access private
+ */
+	function __collectForeignKeys($type = 'belongsTo') {
+		$result = array();
+
+		foreach ($this->{$type} as $assoc => $data) {
+			if (isset($data['foreignKey']) && is_string($data['foreignKey'])) {
+				$result[$assoc] = $data['foreignKey'];
+			}
+		}
+		return $result;
+	}
+/**
+ * Returns true if a record with the currently set ID exists.
+ *
+ * @param boolean $reset if true will force database query
+ * @return boolean True if such a record exists
+ * @access public
+ */
+	function exists($reset = false) {
+		if (is_array($reset)) {
+			extract($reset, EXTR_OVERWRITE);
+		}
+
+		if ($this->getID() === false || $this->useTable === false) {
+			return false;
+		}
+		if ($this->__exists !== null && $reset !== true) {
+			return $this->__exists;
+		}
+		$conditions = array($this->alias . '.' . $this->primaryKey => $this->getID());
+		$query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false);
+
+		if (is_array($reset)) {
+			$query = array_merge($query, $reset);
+		}
+		return $this->__exists = ($this->find('count', $query) > 0);
+	}
+/**
+ * Returns true if a record that meets given conditions exists.
+ *
+ * @param array $conditions SQL conditions array
+ * @return boolean True if such a record exists
+ * @access public
+ */
+	function hasAny($conditions = null) {
+		return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false);
+	}
+/**
+ * Returns a result set array.
+ *
+ * Also used to perform new-notation finds, where the first argument is type of find operation to perform
+ * (all / first / count / neighbors / list / threaded ),
+ * second parameter options for finding ( indexed array, including: 'conditions', 'limit',
+ * 'recursive', 'page', 'fields', 'offset', 'order')
+ *
+ * Eg: find('all', array(
+ * 					'conditions' => array('name' => 'Thomas Anderson'),
+ * 					'fields' => array('name', 'email'),
+ * 					'order' => 'field3 DESC',
+ * 					'recursive' => 2,
+ * 					'group' => 'type'));
+ *
+ * Specifying 'fields' for new-notation 'list':
+ *  - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value.
+ *  - If a single field is specified, 'id' is used for key and specified field is used for value.
+ *  - If three fields are specified, they are used (in order) for key, value and group.
+ *  - Otherwise, first and second fields are used for key and value.
+ *
+ * @param array $conditions SQL conditions array, or type of find operation (all / first / count / neighbors / list / threaded)
+ * @param mixed $fields Either a single string of a field name, or an array of field names, or options for matching
+ * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
+ * @param integer $recursive The number of levels deep to fetch associated records
+ * @return array Array of records
+ * @access public
+ * @link http://book.cakephp.org/view/449/find
+ */
+	function find($conditions = null, $fields = array(), $order = null, $recursive = null) {
+		if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $this->_findMethods))) {
+			$type = 'first';
+			$query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));
+		} else {
+			list($type, $query) = array($conditions, $fields);
+		}
+
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		$this->findQueryType = $type;
+		$this->id = $this->getID();
+
+		$query = array_merge(
+			array(
+				'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null,
+				'offset' => null, 'order' => null, 'page' => null, 'group' => null, 'callbacks' => true
+			),
+			(array)$query
+		);
+
+		if ($type != 'all') {
+			if ($this->_findMethods[$type] === true) {
+				$query = $this->{'_find' . ucfirst($type)}('before', $query);
+			}
+		}
+
+		if (!is_numeric($query['page']) || intval($query['page']) < 1) {
+			$query['page'] = 1;
+		}
+		if ($query['page'] > 1 && !empty($query['limit'])) {
+			$query['offset'] = ($query['page'] - 1) * $query['limit'];
+		}
+		if ($query['order'] === null && $this->order !== null) {
+			$query['order'] = $this->order;
+		}
+		$query['order'] = array($query['order']);
+
+		if ($query['callbacks'] === true || $query['callbacks'] === 'before') {
+			$return = $this->Behaviors->trigger($this, 'beforeFind', array($query), array(
+				'break' => true, 'breakOn' => false, 'modParams' => true
+			));
+			$query = (is_array($return)) ? $return : $query;
+
+			if ($return === false) {
+				return null;
+			}
+
+			$return = $this->beforeFind($query);
+			$query = (is_array($return)) ? $return : $query;
+
+			if ($return === false) {
+				return null;
+			}
+		}
+
+		$results = $db->read($this, $query);
+		$this->resetAssociations();
+		$this->findQueryType = null;
+
+		if ($query['callbacks'] === true || $query['callbacks'] === 'after') {
+			$results = $this->__filterResults($results);
+		}
+
+		if ($type === 'all') {
+			return $results;
+		} else {
+			if ($this->_findMethods[$type] === true) {
+				return $this->{'_find' . ucfirst($type)}('after', $query, $results);
+			}
+		}
+	}
+/**
+ * Handles the before/after filter logic for find('first') operations.  Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $data
+ * @return array
+ * @access protected
+ * @see Model::find()
+ */
+	function _findFirst($state, $query, $results = array()) {
+		if ($state == 'before') {
+			$query['limit'] = 1;
+			if (empty($query['conditions']) && !empty($this->id)) {
+				$query['conditions'] = array($this->escapeField() => $this->id);
+			}
+			return $query;
+		} elseif ($state == 'after') {
+			if (empty($results[0])) {
+				return false;
+			}
+			return $results[0];
+		}
+	}
+/**
+ * Handles the before/after filter logic for find('count') operations.  Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $data
+ * @return int The number of records found, or false
+ * @access protected
+ * @see Model::find()
+ */
+	function _findCount($state, $query, $results = array()) {
+		if ($state == 'before') {
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+			if (empty($query['fields'])) {
+				$query['fields'] = $db->calculate($this, 'count');
+			} elseif (is_string($query['fields'])  && !preg_match('/count/i', $query['fields'])) {
+				$query['fields'] = $db->calculate($this, 'count', array(
+					$db->expression($query['fields']), 'count'
+				));
+			}
+			$query['order'] = false;
+			return $query;
+		} elseif ($state == 'after') {
+			if (isset($results[0][0]['count'])) {
+				return intval($results[0][0]['count']);
+			} elseif (isset($results[0][$this->alias]['count'])) {
+				return intval($results[0][$this->alias]['count']);
+			}
+			return false;
+		}
+	}
+/**
+ * Handles the before/after filter logic for find('list') operations.  Only called by Model::find().
+ *
+ * @param string $state Either "before" or "after"
+ * @param array $query
+ * @param array $data
+ * @return array Key/value pairs of primary keys/display field values of all records found
+ * @access protected
+ * @see Model::find()
+ */
+	function _findList($state, $query, $results = array()) {
+		if ($state == 'before') {
+			if (empty($query['fields'])) {
+				$query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}");
+				$list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null);
+			} else {
+				if (!is_array($query['fields'])) {
+					$query['fields'] = String::tokenize($query['fields']);
+				}
+
+				if (count($query['fields']) == 1) {
+					if (strpos($query['fields'][0], '.') === false) {
+						$query['fields'][0] = $this->alias . '.' . $query['fields'][0];
+					}
+
+					$list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null);
+					$query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]);
+				} elseif (count($query['fields']) == 3) {
+					for ($i = 0; $i < 3; $i++) {
+						if (strpos($query['fields'][$i], '.') === false) {
+							$query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
+						}
+					}
+
+					$list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]);
+				} else {
+					for ($i = 0; $i < 2; $i++) {
+						if (strpos($query['fields'][$i], '.') === false) {
+							$query['fields'][$i] = $this->alias . '.' . $query['fields'][$i];
+						}
+					}
+
+					$list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null);
+				}
+			}
+			if (!isset($query['recursive']) || $query['recursive'] === null) {
+				$query['recursive'] = -1;
+			}
+			list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list;
+			return $query;
+		} elseif ($state == 'after') {
+			if (empty($results)) {
+				return array();
+			}
+			$lst = $query['list'];
+			return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']);
+		}
+	}
+/**
+ * Detects the previous field's value, then uses logic to find the 'wrapping'
+ * rows and return them.
+ *
+ * @param string $state Either "before" or "after"
+ * @param mixed $query
+ * @param array $results
+ * @return array
+ * @access protected
+ */
+	function _findNeighbors($state, $query, $results = array()) {
+		if ($state == 'before') {
+			$query = array_merge(array('recursive' => 0), $query);
+			extract($query);
+			$conditions = (array)$conditions;
+			if (isset($field) && isset($value)) {
+				if (strpos($field, '.') === false) {
+					$field = $this->alias . '.' . $field;
+				}
+			} else {
+				$field = $this->alias . '.' . $this->primaryKey;
+				$value = $this->id;
+			}
+			$query['conditions'] = 	array_merge($conditions, array($field . ' <' => $value));
+			$query['order'] = $field . ' DESC';
+			$query['limit'] = 1;
+			$query['field'] = $field;
+			$query['value'] = $value;
+			return $query;
+		} elseif ($state == 'after') {
+			extract($query);
+			unset($query['conditions'][$field . ' <']);
+			$return = array();
+			if (isset($results[0])) {
+				$prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]);
+				$query['conditions'][$field . ' >='] = $prevVal[0];
+				$query['conditions'][$field . ' !='] = $value;
+				$query['limit'] = 2;
+			} else {
+				$return['prev'] = null;
+				$query['conditions'][$field . ' >'] = $value;
+				$query['limit'] = 1;
+			}
+			$query['order'] = $field . ' ASC';
+			$return2 = $this->find('all', $query);
+			if (!array_key_exists('prev', $return)) {
+				$return['prev'] = $return2[0];
+			}
+			if (count($return2) == 2) {
+				$return['next'] = $return2[1];
+			} elseif (count($return2) == 1 && !$return['prev']) {
+				$return['next'] = $return2[0];
+			} else {
+				$return['next'] = null;
+			}
+			return $return;
+		}
+	}
+/**
+ * In the event of ambiguous results returned (multiple top level results, with different parent_ids)
+ * top level results with different parent_ids to the first result will be dropped
+ *
+ * @param mixed $state
+ * @param mixed $query
+ * @param array $results
+ * @return array Threaded results
+ * @access protected
+ */
+	function _findThreaded($state, $query, $results = array()) {
+		if ($state == 'before') {
+			return $query;
+		} elseif ($state == 'after') {
+			$return = $idMap = array();
+			$ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey);
+
+			foreach ($results as $result) {
+				$result['children'] = array();
+				$id = $result[$this->alias][$this->primaryKey];
+				$parentId = $result[$this->alias]['parent_id'];
+				if (isset($idMap[$id]['children'])) {
+					$idMap[$id] = array_merge($result, (array)$idMap[$id]);
+				} else {
+					$idMap[$id] = array_merge($result, array('children' => array()));
+				}
+				if (!$parentId || !in_array($parentId, $ids)) {
+					$return[] =& $idMap[$id];
+				} else {
+					$idMap[$parentId]['children'][] =& $idMap[$id];
+				}
+			}
+			if (count($return) > 1) {
+				$ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return));
+				if (count($ids) > 1) {
+					$root = $return[0][$this->alias]['parent_id'];
+					foreach ($return as $key => $value) {
+						if ($value[$this->alias]['parent_id'] != $root) {
+							unset($return[$key]);
+						}
+					}
+				}
+			}
+			return $return;
+		}
+	}
+/**
+ * Passes query results through model and behavior afterFilter() methods.
+ *
+ * @param array Results to filter
+ * @param boolean $primary If this is the primary model results (results from model where the find operation was performed)
+ * @return array Set of filtered results
+ * @access private
+ */
+	function __filterResults($results, $primary = true) {
+		$return = $this->Behaviors->trigger($this, 'afterFind', array($results, $primary), array('modParams' => true));
+		if ($return !== true) {
+			$results = $return;
+		}
+		return $this->afterFind($results, $primary);
+	}
+/**
+ * Called only when bindTo<ModelName>() is used.
+ * This resets the association arrays for the model back
+ * to those originally defined in the model.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function resetAssociations() {
+		if (!empty($this->__backAssociation)) {
+			foreach ($this->__associations as $type) {
+				if (isset($this->__backAssociation[$type])) {
+					$this->{$type} = $this->__backAssociation[$type];
+				}
+			}
+			$this->__backAssociation = array();
+		}
+
+		foreach ($this->__associations as $type) {
+			foreach ($this->{$type} as $key => $name) {
+				if (!empty($this->{$key}->__backAssociation)) {
+					$this->{$key}->resetAssociations();
+				}
+			}
+		}
+		$this->__backAssociation = array();
+		return true;
+	}
+/**
+ * Returns false if any fields passed match any (by default, all if $or = false) of their matching values.
+ *
+ * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data)
+ * @param boolean $or If false, all fields specified must match in order for a false return value
+ * @return boolean False if any records matching any fields are found
+ * @access public
+ */
+	function isUnique($fields, $or = true) {
+		if (!is_array($fields)) {
+			$fields = func_get_args();
+			if (is_bool($fields[count($fields) - 1])) {
+				$or = $fields[count($fields) - 1];
+				unset($fields[count($fields) - 1]);
+			}
+		}
+
+		foreach ($fields as $field => $value) {
+			if (is_numeric($field)) {
+				unset($fields[$field]);
+
+				$field = $value;
+				if (isset($this->data[$this->alias][$field])) {
+					$value = $this->data[$this->alias][$field];
+				} else {
+					$value = null;
+				}
+			}
+
+			if (strpos($field, '.') === false) {
+				unset($fields[$field]);
+				$fields[$this->alias . '.' . $field] = $value;
+			}
+		}
+		if ($or) {
+			$fields = array('or' => $fields);
+		}
+		if (!empty($this->id)) {
+			$fields[$this->alias . '.' . $this->primaryKey . ' !='] =  $this->id;
+		}
+		return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0);
+	}
+/**
+ * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method.
+ *
+ * @param string $sql SQL statement
+ * @return array Resultset
+ * @access public
+ * @link http://book.cakephp.org/view/456/query
+ */
+	function query() {
+		$params = func_get_args();
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return call_user_func_array(array(&$db, 'query'), $params);
+	}
+/**
+ * Returns true if all fields pass validation.
+ *
+ * @param string $options An optional array of custom options to be made available in the beforeValidate callback
+ * @return boolean True if there are no errors
+ * @access public
+ * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller
+ */
+	function validates($options = array()) {
+		$errors = $this->invalidFields($options);
+		if (is_array($errors)) {
+			return count($errors) === 0;
+		}
+		return $errors;
+	}
+/**
+ * Returns an array of fields that have failed validation.
+ *
+ * @param string $options An optional array of custom options to be made available in the beforeValidate callback
+ * @return array Array of invalid fields
+ * @access public
+ * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller
+ */
+	function invalidFields($options = array()) {
+		if (
+			!$this->Behaviors->trigger(
+				$this,
+				'beforeValidate',
+				array($options),
+				array('break' => true, 'breakOn' => false)
+			) ||
+			$this->beforeValidate($options) === false
+		) {
+			return $this->validationErrors;
+		}
+
+		if (!isset($this->validate) || empty($this->validate)) {
+			return $this->validationErrors;
+		}
+
+		$data = $this->data;
+		$methods = array_map('strtolower', get_class_methods($this));
+		$behaviorMethods = array_keys($this->Behaviors->methods());
+
+		if (isset($data[$this->alias])) {
+			$data = $data[$this->alias];
+		} elseif (!is_array($data)) {
+			$data = array();
+		}
+
+		$Validation =& Validation::getInstance();
+		$this->exists();
+
+		$_validate = $this->validate;
+		if (array_key_exists('fieldList', $options) && is_array($options['fieldList']) && !empty($options['fieldList'])) {
+			$validate = array();
+			foreach ($options['fieldList'] as $f) {
+				if (!empty($this->validate[$f])) {
+					$validate[$f] = $this->validate[$f];
+				}
+			}
+			$this->validate = $validate;
+		}
+
+		foreach ($this->validate as $fieldName => $ruleSet) {
+			if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
+				$ruleSet = array($ruleSet);
+			}
+			$default = array(
+				'allowEmpty' => null,
+				'required' => null,
+				'rule' => 'blank',
+				'last' => false,
+				'on' => null
+			);
+
+			foreach ($ruleSet as $index => $validator) {
+				if (!is_array($validator)) {
+					$validator = array('rule' => $validator);
+				}
+				$validator = array_merge($default, $validator);
+
+				if (isset($validator['message'])) {
+					$message = $validator['message'];
+				} else {
+					$message = __('This field cannot be left blank', true);
+				}
+
+				if (
+					empty($validator['on']) || ($validator['on'] == 'create' &&
+					!$this->__exists) || ($validator['on'] == 'update' && $this->__exists
+				)) {
+					$required = (
+						(!isset($data[$fieldName]) && $validator['required'] === true) ||
+						(
+							isset($data[$fieldName]) && (empty($data[$fieldName]) &&
+							!is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false
+						)
+					);
+
+					if ($required) {
+						$this->invalidate($fieldName, $message);
+						if ($validator['last']) {
+							break;
+						}
+					} elseif (array_key_exists($fieldName, $data)) {
+						if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) {
+							break;
+						}
+						if (is_array($validator['rule'])) {
+							$rule = $validator['rule'][0];
+							unset($validator['rule'][0]);
+							$ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule']));
+						} else {
+							$rule = $validator['rule'];
+							$ruleParams = array($data[$fieldName]);
+						}
+
+						$valid = true;
+
+						if (in_array(strtolower($rule), $methods)) {
+							$ruleParams[] = $validator;
+							$ruleParams[0] = array($fieldName => $ruleParams[0]);
+							$valid = $this->dispatchMethod($rule, $ruleParams);
+						} elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) {
+							$ruleParams[] = $validator;
+							$ruleParams[0] = array($fieldName => $ruleParams[0]);
+							$valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams);
+						} elseif (method_exists($Validation, $rule)) {
+							$valid = $Validation->dispatchMethod($rule, $ruleParams);
+						} elseif (!is_array($validator['rule'])) {
+							$valid = preg_match($rule, $data[$fieldName]);
+						}
+
+						if (!$valid || (is_string($valid) && strlen($valid) > 0)) {
+							if (is_string($valid) && strlen($valid) > 0) {
+								$validator['message'] = $valid;
+							} elseif (!isset($validator['message'])) {
+								if (is_string($index)) {
+									$validator['message'] = $index;
+								} elseif (is_numeric($index) && count($ruleSet) > 1) {
+									$validator['message'] = $index + 1;
+								} else {
+									$validator['message'] = $message;
+								}
+							}
+							$this->invalidate($fieldName, $validator['message']);
+
+							if ($validator['last']) {
+								break;
+							}
+						}
+					}
+				}
+			}
+		}
+		$this->validate = $_validate;
+		return $this->validationErrors;
+	}
+/**
+ * Marks a field as invalid, optionally setting the name of validation
+ * rule (in case of multiple validation for field) that was broken.
+ *
+ * @param string $field The name of the field to invalidate
+ * @param mixed $value Name of validation rule that was not failed. If no validation key
+ * 						is provided, defaults to true.
+ * @access public
+ */
+	function invalidate($field, $value = true) {
+		if (!is_array($this->validationErrors)) {
+			$this->validationErrors = array();
+		}
+		$this->validationErrors[$field] = $value;
+	}
+/**
+ * Returns true if given field name is a foreign key in this model.
+ *
+ * @param string $field Returns true if the input string ends in "_id"
+ * @return boolean True if the field is a foreign key listed in the belongsTo array.
+ * @access public
+ */
+	function isForeignKey($field) {
+		$foreignKeys = array();
+		if (!empty($this->belongsTo)) {
+			foreach ($this->belongsTo as $assoc => $data) {
+				$foreignKeys[] = $data['foreignKey'];
+			}
+		}
+		return in_array($field, $foreignKeys);
+	}
+/**
+ * Returns the display field for this model.
+ *
+ * @return string The name of the display field for this Model (i.e. 'name', 'title').
+ * @access public
+ * @deprecated
+ */
+	function getDisplayField() {
+		return $this->displayField;
+	}
+/**
+ * Escapes the field name and prepends the model name. Escaping is done according to the current database driver's rules.
+ *
+ * @param string $field Field to escape (e.g: id)
+ * @param string $alias Alias for the model (e.g: Post)
+ * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
+ * @access public
+ */
+	function escapeField($field = null, $alias = null) {
+		if (empty($alias)) {
+			$alias = $this->alias;
+		}
+		if (empty($field)) {
+			$field = $this->primaryKey;
+		}
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		if (strpos($field, $db->name($alias)) === 0) {
+			return $field;
+		}
+		return $db->name($alias . '.' . $field);
+	}
+/**
+ * Returns the current record's ID
+ *
+ * @param integer $list Index on which the composed ID is located
+ * @return mixed The ID of the current record, false if no ID
+ * @access public
+ */
+	function getID($list = 0) {
+		if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) {
+			return false;
+		}
+
+		if (!is_array($this->id)) {
+			return $this->id;
+		}
+
+		if (empty($this->id)) {
+			return false;
+		}
+
+		if (isset($this->id[$list]) && !empty($this->id[$list])) {
+			return $this->id[$list];
+		} elseif (isset($this->id[$list])) {
+			return false;
+		}
+
+		foreach ($this->id as $id) {
+			return $id;
+		}
+
+		return false;
+	}
+/**
+ * Returns the ID of the last record this model inserted.
+ *
+ * @return mixed Last inserted ID
+ * @access public
+ */
+	function getLastInsertID() {
+		return $this->getInsertID();
+	}
+/**
+ * Returns the ID of the last record this model inserted.
+ *
+ * @return mixed Last inserted ID
+ * @access public
+ */
+	function getInsertID() {
+		return $this->__insertID;
+	}
+/**
+ * Sets the ID of the last record this model inserted
+ *
+ * @param mixed Last inserted ID
+ * @access public
+ */
+	function setInsertID($id) {
+		$this->__insertID = $id;
+	}
+/**
+ * Returns the number of rows returned from the last query.
+ *
+ * @return int Number of rows
+ * @access public
+ */
+	function getNumRows() {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return $db->lastNumRows();
+	}
+/**
+ * Returns the number of rows affected by the last query.
+ *
+ * @return int Number of rows
+ * @access public
+ */
+	function getAffectedRows() {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return $db->lastAffected();
+	}
+/**
+ * Sets the DataSource to which this model is bound.
+ *
+ * @param string $dataSource The name of the DataSource, as defined in app/config/database.php
+ * @return boolean True on success
+ * @access public
+ */
+	function setDataSource($dataSource = null) {
+		$oldConfig = $this->useDbConfig;
+
+		if ($dataSource != null) {
+			$this->useDbConfig = $dataSource;
+		}
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		if (!empty($oldConfig) && isset($db->config['prefix'])) {
+			$oldDb =& ConnectionManager::getDataSource($oldConfig);
+
+			if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) {
+				$this->tablePrefix = $db->config['prefix'];
+			}
+		} elseif (isset($db->config['prefix'])) {
+			$this->tablePrefix = $db->config['prefix'];
+		}
+
+		if (empty($db) || $db == null || !is_object($db)) {
+			return $this->cakeError('missingConnection', array(array('className' => $this->alias)));
+		}
+	}
+/**
+ * Gets the DataSource to which this model is bound.
+ * Not safe for use with some versions of PHP4, because this class is overloaded.
+ *
+ * @return object A DataSource object
+ * @access public
+ */
+	function &getDataSource() {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return $db;
+	}
+/**
+ * Gets all the models with which this model is associated.
+ *
+ * @param string $type Only result associations of this type
+ * @return array Associations
+ * @access public
+ */
+	function getAssociated($type = null) {
+		if ($type == null) {
+			$associated = array();
+			foreach ($this->__associations as $assoc) {
+				if (!empty($this->{$assoc})) {
+					$models = array_keys($this->{$assoc});
+					foreach ($models as $m) {
+						$associated[$m] = $assoc;
+					}
+				}
+			}
+			return $associated;
+		} elseif (in_array($type, $this->__associations)) {
+			if (empty($this->{$type})) {
+				return array();
+			}
+			return array_keys($this->{$type});
+		} else {
+			$assoc = array_merge($this->hasOne, $this->hasMany, $this->belongsTo, $this->hasAndBelongsToMany);
+			if (array_key_exists($type, $assoc)) {
+				foreach ($this->__associations as $a) {
+					if (isset($this->{$a}[$type])) {
+						$assoc[$type]['association'] = $a;
+						break;
+					}
+				}
+				return $assoc[$type];
+			}
+			return null;
+		}
+	}
+/**
+ * Gets the name and fields to be used by a join model.  This allows specifying join fields in the association definition.
+ *
+ * @param object $model The model to be joined
+ * @param mixed $with The 'with' key of the model association
+ * @param array $keys Any join keys which must be merged with the keys queried
+ * @return array
+ * @access public
+ */
+	function joinModel($assoc, $keys = array()) {
+		if (is_string($assoc)) {
+			return array($assoc, array_keys($this->{$assoc}->schema()));
+		} elseif (is_array($assoc)) {
+			$with = key($assoc);
+			return array($with, array_unique(array_merge($assoc[$with], $keys)));
+		} else {
+			trigger_error(sprintf(__('Invalid join model settings in %s', true), $model->alias), E_USER_WARNING);
+		}
+	}
+/**
+ * Called before each find operation. Return false if you want to halt the find
+ * call, otherwise return the (modified) query data.
+ *
+ * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
+ * @return mixed true if the operation should continue, false if it should abort; or, modified $queryData to continue with new $queryData
+ * @access public
+ * @link http://book.cakephp.org/view/680/beforeFind
+ */
+	function beforeFind($queryData) {
+		return true;
+	}
+/**
+ * Called after each find operation. Can be used to modify any results returned by find().
+ * Return value should be the (modified) results.
+ *
+ * @param mixed $results The results of the find operation
+ * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
+ * @return mixed Result of the find operation
+ * @access public
+ * @link http://book.cakephp.org/view/681/afterFind
+ */
+	function afterFind($results, $primary = false) {
+		return $results;
+	}
+/**
+ * Called before each save operation, after validation. Return a non-true result
+ * to halt the save.
+ *
+ * @return boolean True if the operation should continue, false if it should abort
+ * @access public
+ * @link http://book.cakephp.org/view/683/beforeSave
+ */
+	function beforeSave($options = array()) {
+		return true;
+	}
+/**
+ * Called after each successful save operation.
+ *
+ * @param boolean $created True if this save created a new record
+ * @access public
+ * @link http://book.cakephp.org/view/684/afterSave
+ */
+	function afterSave($created) {
+	}
+/**
+ * Called after every deletion operation.
+ *
+ * @param boolean $cascade If true records that depend on this record will also be deleted
+ * @return boolean True if the operation should continue, false if it should abort
+ * @access public
+ * @link http://book.cakephp.org/view/685/beforeDelete
+ */
+	function beforeDelete($cascade = true) {
+		return true;
+	}
+/**
+ * Called after every deletion operation.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/686/afterDelete
+ */
+	function afterDelete() {
+	}
+/**
+ * Called during save operations, before validation. Please note that custom
+ * validation rules can be defined in $validate.
+ *
+ * @return boolean True if validate operation should continue, false to abort
+ * @param $options array Options passed from model::save(), see $options of model::save().
+ * @access public
+ * @link http://book.cakephp.org/view/682/beforeValidate
+ */
+	function beforeValidate($options = array()) {
+		return true;
+	}
+/**
+ * Called when a DataSource-level error occurs.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/687/onError
+ */
+	function onError() {
+	}
+/**
+ * Private method. Clears cache for this model.
+ *
+ * @param string $type If null this deletes cached views if Cache.check is true
+ *                     Will be used to allow deleting query cache also
+ * @return boolean true on delete
+ * @access protected
+ * @todo
+ */
+	function _clearCache($type = null) {
+		if ($type === null) {
+			if (Configure::read('Cache.check') === true) {
+				$assoc[] = strtolower(Inflector::pluralize($this->alias));
+				$assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias)));
+				foreach ($this->__associations as $key => $association) {
+					foreach ($this->$association as $key => $className) {
+						$check = strtolower(Inflector::pluralize($className['className']));
+						if (!in_array($check, $assoc)) {
+							$assoc[] = strtolower(Inflector::pluralize($className['className']));
+							$assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className'])));
+						}
+					}
+				}
+				clearCache($assoc);
+				return true;
+			}
+		} else {
+			//Will use for query cache deleting
+		}
+	}
+/**
+ * Called when serializing a model.
+ *
+ * @return array Set of object variable names this model has
+ * @access private
+ */
+	function __sleep() {
+		$return = array_keys(get_object_vars($this));
+		return $return;
+	}
+/**
+ * Called when de-serializing a model.
+ *
+ * @access private
+ * @todo
+ */
+	function __wakeup() {
+	}
+/**
+ * @deprecated
+ * @see Model::find('all')
+ */
+	function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
+		//trigger_error(__('(Model::findAll) Deprecated, use Model::find("all")', true), E_USER_WARNING);
+		return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
+	}
+/**
+ * @deprecated
+ * @see Model::find('count')
+ */
+	function findCount($conditions = null, $recursive = 0) {
+		//trigger_error(__('(Model::findCount) Deprecated, use Model::find("count")', true), E_USER_WARNING);
+		return $this->find('count', compact('conditions', 'recursive'));
+	}
+/**
+ * @deprecated
+ * @see Model::find('threaded')
+ */
+	function findAllThreaded($conditions = null, $fields = null, $order = null) {
+		//trigger_error(__('(Model::findAllThreaded) Deprecated, use Model::find("threaded")', true), E_USER_WARNING);
+		return $this->find('threaded', compact('conditions', 'fields', 'order'));
+	}
+/**
+ * @deprecated
+ * @see Model::find('neighbors')
+ */
+	function findNeighbours($conditions = null, $field, $value) {
+		//trigger_error(__('(Model::findNeighbours) Deprecated, use Model::find("neighbors")', true), E_USER_WARNING);
+		$query = compact('conditions', 'field', 'value');
+		$query['fields'] = $field;
+		if (is_array($field)) {
+			$query['field'] = $field[0];
+		}
+		return $this->find('neighbors', $query);
+	}
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	Overloadable::overload('Model');
+}
+?>
\ No newline at end of file
Index: cake/libs/model/schema.php
===================================================================
--- cake/libs/model/schema.php	(revision 0)
+++ cake/libs/model/schema.php	(revision 0)
@@ -0,0 +1,549 @@
+<?php
+/* SVN FILE: $Id: schema.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Schema database management for CakePHP.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Model', 'ConnectionManager');
+/**
+ * Base Class for Schema management
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class CakeSchema extends Object {
+/**
+ * Name of the App Schema
+ *
+ * @var string
+ * @access public
+ */
+	var $name = null;
+/**
+ * Path to write location
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+/**
+ * File to write
+ *
+ * @var string
+ * @access public
+ */
+	var $file = 'schema.php';
+/**
+ * Connection used for read
+ *
+ * @var string
+ * @access public
+ */
+	var $connection = 'default';
+/**
+ * Set of tables
+ *
+ * @var array
+ * @access public
+ */
+	var $tables = array();
+/**
+ * Constructor
+ *
+ * @param array $options optional load object properties
+ */
+	function __construct($options = array()) {
+		parent::__construct();
+
+		if (empty($options['name'])) {
+			$this->name = preg_replace('/schema$/i', '', get_class($this));
+		}
+
+		if (strtolower($this->name) === 'cake') {
+			$this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir')));
+		}
+
+		if (empty($options['path'])) {
+			$this->path = CONFIGS . 'sql';
+		}
+
+		$options = array_merge(get_object_vars($this), $options);
+		$this->_build($options);
+	}
+/**
+ * Builds schema object properties
+ *
+ * @param array $data loaded object properties
+ * @return void
+ * @access protected
+ */
+	function _build($data) {
+		$file = null;
+		foreach ($data as $key => $val) {
+			if (!empty($val)) {
+				if (!in_array($key, array('name', 'path', 'file', 'connection', 'tables', '_log'))) {
+					$this->tables[$key] = $val;
+					unset($this->{$key});
+				} elseif ($key !== 'tables') {
+					if ($key === 'name' && $val !== $this->name && !isset($data['file'])) {
+						$file = Inflector::underscore($val) . '.php';
+					}
+					$this->{$key} = $val;
+				}
+			}
+		}
+
+		if (file_exists($this->path . DS . $file) && is_file($this->path . DS . $file)) {
+			$this->file = $file;
+		}
+	}
+/**
+ * Before callback to be implemented in subclasses
+ *
+ * @param array $events schema object properties
+ * @return boolean Should process continue
+ * @access public
+ */
+	function before($event = array()) {
+		return true;
+	}
+/**
+ * After callback to be implemented in subclasses
+ *
+ * @param array $events schema object properties
+ * @access public
+ */
+	function after($event = array()) {
+	}
+/**
+ * Reads database and creates schema tables
+ *
+ * @param array $options schema object properties
+ * @return array Set of name and tables
+ * @access public
+ */
+	function load($options = array()) {
+		if (is_string($options)) {
+			$options = array('path' => $options);
+		}
+
+		$this->_build($options);
+		extract(get_object_vars($this));
+
+		$class =  $name .'Schema';
+		if (!class_exists($class)) {
+			if (file_exists($path . DS . $file) && is_file($path . DS . $file)) {
+				require_once($path . DS . $file);
+			} elseif (file_exists($path . DS . 'schema.php') && is_file($path . DS . 'schema.php')) {
+				require_once($path . DS . 'schema.php');
+			}
+		}
+
+		if (class_exists($class)) {
+			$Schema =& new $class($options);
+			return $Schema;
+		}
+
+		return false;
+	}
+/**
+ * Reads database and creates schema tables
+ *
+ * @param array $options schema object properties
+ *		'connection' - the db connection to use
+ *		'name' - name of the schema
+ *		'models' - a list of models to use, or false to ignore models
+ * @return array Array indexed by name and tables
+ * @access public
+ */
+	function read($options = array()) {
+		extract(array_merge(
+			array(
+				'connection' => $this->connection,
+				'name' => $this->name,
+				'models' => true,
+			),
+			$options
+		));
+		$db =& ConnectionManager::getDataSource($connection);
+
+		App::import('Model', 'AppModel');
+
+		$tables = array();
+		$currentTables = $db->listSources();
+
+		$prefix = null;
+		if (isset($db->config['prefix'])) {
+			$prefix = $db->config['prefix'];
+		}
+
+		if (!is_array($models) && $models !== false) {
+			$models = Configure::listObjects('model');
+		}
+
+		if (is_array($models)) {
+			foreach ($models as $model) {
+				if (PHP5) {
+					$Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection));
+				} else {
+					$Object =& ClassRegistry::init(array('class' => $model, 'ds' => $connection));
+				}
+
+				if (is_object($Object) && $Object->useTable !== false) {
+					$Object->setDataSource($connection);
+					$table = $db->fullTableName($Object, false);
+
+					if (in_array($table, $currentTables)) {
+						$key = array_search($table, $currentTables);
+						if (empty($tables[$Object->table])) {
+							$tables[$Object->table] = $this->__columns($Object);
+							$tables[$Object->table]['indexes'] = $db->index($Object);
+							unset($currentTables[$key]);
+						}
+						if (!empty($Object->hasAndBelongsToMany)) {
+							foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) {
+								if (isset($assocData['with'])) {
+									$class = $assocData['with'];
+								} elseif ($assocData['_with']) {
+									$class = $assocData['_with'];
+								}
+								if (is_object($Object->$class)) {
+									$table = $db->fullTableName($Object->$class, false);
+									if (in_array($table, $currentTables)) {
+										$key = array_search($table, $currentTables);
+										$tables[$Object->$class->table] = $this->__columns($Object->$class);
+										$tables[$Object->$class->table]['indexes'] = $db->index($Object->$class);
+										unset($currentTables[$key]);
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (!empty($currentTables)) {
+			foreach ($currentTables as $table) {
+				if ($prefix) {
+					if (strpos($table, $prefix) !== 0) {
+						continue;
+					}
+					$table = str_replace($prefix, '', $table);
+				}
+				$Object = new AppModel(array(
+					'name' => Inflector::classify($table), 'table' => $table, 'ds' => $connection
+				));
+
+				$systemTables = array(
+					'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n'
+				);
+
+				if (in_array($table, $systemTables)) {
+					$tables[$Object->table] = $this->__columns($Object);
+					$tables[$Object->table]['indexes'] = $db->index($Object);
+				} elseif ($models === false) {
+					$tables[$table] = $this->__columns($Object);
+					$tables[$table]['indexes'] = $db->index($Object);
+				} else {
+					$tables['missing'][$table] = $this->__columns($Object);
+					$tables['missing'][$table]['indexes'] = $db->index($Object);
+				}
+			}
+		}
+
+		ksort($tables);
+		return compact('name', 'tables');
+	}
+/**
+ * Writes schema file from object or options
+ *
+ * @param mixed $object schema object or options array
+ * @param array $options schema object properties to override object
+ * @return mixed false or string written to file
+ * @access public
+ */
+	function write($object, $options = array()) {
+		if (is_object($object)) {
+			$object = get_object_vars($object);
+			$this->_build($object);
+		}
+
+		if (is_array($object)) {
+			$options = $object;
+			unset($object);
+		}
+
+		extract(array_merge(
+			get_object_vars($this), $options
+		));
+
+		$out = "class {$name}Schema extends CakeSchema {\n";
+
+		$out .= "\tvar \$name = '{$name}';\n\n";
+
+		if ($path !== $this->path) {
+			$out .= "\tvar \$path = '{$path}';\n\n";
+		}
+
+		if ($file !== $this->file) {
+			$out .= "\tvar \$file = '{$file}';\n\n";
+		}
+
+		if ($connection !== 'default') {
+			$out .= "\tvar \$connection = '{$connection}';\n\n";
+		}
+
+		$out .= "\tfunction before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tfunction after(\$event = array()) {\n\t}\n\n";
+
+		if (empty($tables)) {
+			$this->read();
+		}
+
+		foreach ($tables as $table => $fields) {
+			if (!is_numeric($table) && $table !== 'missing') {
+				$out .= "\tvar \${$table} = array(\n";
+				if (is_array($fields)) {
+					$cols = array();
+					foreach ($fields as $field => $value) {
+						if ($field != 'indexes') {
+							if (is_string($value)) {
+								$type = $value;
+								$value = array('type'=> $type);
+							}
+							$col = "\t\t\t'{$field}' => array('type' => '" . $value['type'] . "', ";
+							unset($value['type']);
+							$col .= join(', ',  $this->__values($value));
+						} else {
+							$col = "\t\t\t'indexes' => array(";
+							$props = array();
+							foreach ((array)$value as $key => $index) {
+								$props[] = "'{$key}' => array(".join(', ',  $this->__values($index)).")";
+							}
+							$col .= join(', ', $props);
+						}
+						$col .= ")";
+						$cols[] = $col;
+					}
+					$out .= join(",\n", $cols);
+				}
+				$out .= "\n\t\t);\n";
+			}
+		}
+		$out .="}\n";
+
+
+		$File =& new File($path . DS . $file, true);
+		$header = '$Id';
+		$content = "<?php \n/* SVN FILE: $header$ */\n/* ". $name ." schema generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+		$content = $File->prepare($content);
+		if ($File->write($content)) {
+			return $content;
+		}
+		return false;
+	}
+/**
+ * Compares two sets of schemas
+ *
+ * @param mixed $old Schema object or array
+ * @param mixed $new Schema object or array
+ * @return array Tables (that are added, dropped, or changed)
+ * @access public
+ */
+	function compare($old, $new = null) {
+		if (empty($new)) {
+			$new = $this;
+		}
+		if (is_array($new)) {
+			if (isset($new['tables'])) {
+				$new = $new['tables'];
+			}
+		} else {
+			$new = $new->tables;
+		}
+
+		if (is_array($old)) {
+			if (isset($old['tables'])) {
+				$old = $old['tables'];
+			}
+		} else {
+			$old = $old->tables;
+		}
+		$tables = array();
+		foreach ($new as $table => $fields) {
+			if ($table == 'missing') {
+				break;
+			}
+			if (!array_key_exists($table, $old)) {
+				$tables[$table]['add'] = $fields;
+			} else {
+				$diff = array_diff_assoc($fields, $old[$table]);
+				if (!empty($diff)) {
+					$tables[$table]['add'] = $diff;
+				}
+				$diff = array_diff_assoc($old[$table], $fields);
+				if (!empty($diff)) {
+					$tables[$table]['drop'] = $diff;
+				}
+			}
+			foreach ($fields as $field => $value) {
+				if (isset($old[$table][$field])) {
+					$diff = array_diff_assoc($value, $old[$table][$field]);
+					if (!empty($diff) && $field !== 'indexes') {
+						$tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff);
+					}
+				}
+
+				if (isset($add[$table][$field])) {
+					$wrapper = array_keys($fields);
+					if ($column = array_search($field, $wrapper)) {
+						if (isset($wrapper[$column - 1])) {
+							$tables[$table]['add'][$field]['after'] = $wrapper[$column - 1];
+						}
+					}
+				}
+			}
+
+			if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) {
+				$diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']);
+				if ($diff) {
+					$tables[$table]['drop']['indexes'] = $diff['drop'];
+					$tables[$table]['add']['indexes'] = $diff['add'];
+				}
+			}
+		}
+		return $tables;
+	}
+/**
+ * Formats Schema columns from Model Object
+ *
+ * @param array $values options keys(type, null, default, key, length, extra)
+ * @return array Formatted values
+ * @access public
+ */
+	function __values($values) {
+		$vals = array();
+		if (is_array($values)) {
+			foreach ($values as $key => $val) {
+				if (is_array($val)) {
+					$vals[] = "'{$key}' => array('".join("', '",  $val)."')";
+				} else if (!is_numeric($key)) {
+					$val = var_export($val, true);
+					$vals[] = "'{$key}' => {$val}";
+				}
+			}
+		}
+		return $vals;
+	}
+/**
+ * Formats Schema columns from Model Object
+ *
+ * @param array $Obj model object
+ * @return array Formatted columns
+ * @access public
+ */
+	function __columns(&$Obj) {
+		$db =& ConnectionManager::getDataSource($Obj->useDbConfig);
+		$fields = $Obj->schema(true);
+		$columns = $props = array();
+		foreach ($fields as $name => $value) {
+			if ($Obj->primaryKey == $name) {
+				$value['key'] = 'primary';
+			}
+			if (!isset($db->columns[$value['type']])) {
+				trigger_error('Schema generation error: invalid column type ' . $value['type'] . ' does not exist in DBO', E_USER_NOTICE);
+				continue;
+			} else {
+				$defaultCol = $db->columns[$value['type']];
+				if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) {
+					unset($value['length']);
+				} elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) {
+					unset($value['length']);
+				}
+				unset($value['limit']);
+			}
+
+			if (isset($value['default']) && ($value['default'] === '' || $value['default'] === false)) {
+				unset($value['default']);
+			}
+			if (empty($value['length'])) {
+				unset($value['length']);
+			}
+			if (empty($value['key'])) {
+				unset($value['key']);
+			}
+			$columns[$name] = $value;
+		}
+
+		return $columns;
+	}
+/**
+ * Compare two schema indexes
+ *
+ * @param array $new New indexes
+ * @param array $old Old indexes
+ * @return mixed false on failure or array of indexes to add and drop
+ */
+	function _compareIndexes($new, $old) {
+		if (!is_array($new) || !is_array($old)) {
+			return false;
+		}
+
+		$add = $drop = array();
+
+		$diff = array_diff_assoc($new, $old);
+		if (!empty($diff)) {
+			$add = $diff;
+		}
+
+		$diff = array_diff_assoc($old, $new);
+		if (!empty($diff)) {
+			$drop = $diff;
+		}
+
+		foreach ($new as $name => $value) {
+			if (isset($old[$name])) {
+				$newUnique = isset($value['unique']) ? $value['unique'] : 0;
+				$oldUnique = isset($old[$name]['unique']) ? $old[$name]['unique'] : 0;
+				$newColumn = $value['column'];
+				$oldColumn = $old[$name]['column'];
+
+				$diff = false;
+
+				if ($newUnique != $oldUnique) {
+					$diff = true;
+				} elseif (is_array($newColumn) && is_array($oldColumn)) {
+					$diff = ($newColumn !== $oldColumn);
+				} elseif (is_string($newColumn) && is_string($oldColumn)) {
+					$diff = ($newColumn != $oldColumn);
+				} else {
+					$diff = true;
+				}
+				if ($diff) {
+					$drop[$name] = null;
+					$add[$name] = $value;
+				}
+			}
+		}
+		return array_filter(compact('add', 'drop'));
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/db_acl.php
===================================================================
--- cake/libs/model/db_acl.php	(revision 0)
+++ cake/libs/model/db_acl.php	(revision 0)
@@ -0,0 +1,317 @@
+<?php
+/* SVN FILE: $Id: db_acl.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Load Model and AppModel
+ */
+App::import('Model', 'App');
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class AclNode extends AppModel {
+/**
+ * Explicitly disable in-memory query caching for ACL models
+ *
+ * @var boolean
+ * @access public
+ */
+	var $cacheQueries = false;
+/**
+ * ACL models use the Tree behavior
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Tree' => 'nested');
+/**
+ * Constructor
+ *
+ */
+	function __construct() {
+		$config = Configure::read('Acl.database');
+		if (isset($config)) {
+			$this->useDbConfig = $config;
+		}
+		parent::__construct();
+	}
+/**
+ * Retrieves the Aro/Aco node for this model
+ *
+ * @param mixed $ref Array with 'model' and 'foreign_key', model object, or string value
+ * @return array Node found in database
+ * @access public
+ */
+	function node($ref = null) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		$type = $this->alias;
+		$result = null;
+
+		if (!empty($this->useTable)) {
+			$table = $this->useTable;
+		} else {
+			$table = Inflector::pluralize(Inflector::underscore($type));
+		}
+
+		if (empty($ref)) {
+			return null;
+		} elseif (is_string($ref)) {
+			$path = explode('/', $ref);
+			$start = $path[0];
+			unset($path[0]);
+
+			$queryData = array(
+				'conditions' => array(
+					$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"),
+					$db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght")),
+				'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'),
+				'joins' => array(array(
+					'table' => $db->fullTableName($this),
+					'alias' => "{$type}0",
+					'type' => 'LEFT',
+					'conditions' => array("{$type}0.alias" => $start)
+				)),
+				'order' => $db->name("{$type}.lft") . ' DESC'
+			);
+
+			foreach ($path as $i => $alias) {
+				$j = $i - 1;
+
+				$queryData['joins'][] = array(
+					'table' => $db->fullTableName($this),
+					'alias' => "{$type}{$i}",
+					'type'  => 'LEFT',
+					'conditions' => array(
+						$db->name("{$type}{$i}.lft") . ' > ' . $db->name("{$type}{$j}.lft"),
+						$db->name("{$type}{$i}.rght") . ' < ' . $db->name("{$type}{$j}.rght"),
+						$db->name("{$type}{$i}.alias") . ' = ' . $db->value($alias, 'string')
+					)
+				);
+
+				$queryData['conditions'] = array('or' => array(
+					$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght"),
+					$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}{$i}.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}{$i}.rght"))
+				);
+			}
+			$result = $db->read($this, $queryData, -1);
+			$path = array_values($path);
+
+			if (
+				!isset($result[0][$type]) ||
+				(!empty($path) && $result[0][$type]['alias'] != $path[count($path) - 1]) ||
+				(empty($path) && $result[0][$type]['alias'] != $start)
+			) {
+				return false;
+			}
+		} elseif (is_object($ref) && is_a($ref, 'Model')) {
+			$ref = array('model' => $ref->alias, 'foreign_key' => $ref->id);
+		} elseif (is_array($ref) && !(isset($ref['model']) && isset($ref['foreign_key']))) {
+			$name = key($ref);
+
+			if (PHP5) {
+				$model = ClassRegistry::init(array('class' => $name, 'alias' => $name));
+			} else {
+				$model =& ClassRegistry::init(array('class' => $name, 'alias' => $name));
+			}
+
+			if (empty($model)) {
+				trigger_error("Model class '$name' not found in AclNode::node() when trying to bind {$this->alias} object", E_USER_WARNING);
+				return null;
+			}
+
+			$tmpRef = null;
+			if (method_exists($model, 'bindNode')) {
+				$tmpRef = $model->bindNode($ref);
+			}
+			if (empty($tmpRef)) {
+				$ref = array('model' => $name, 'foreign_key' => $ref[$name][$model->primaryKey]);
+			} else {
+				if (is_string($tmpRef)) {
+					return $this->node($tmpRef);
+				}
+				$ref = $tmpRef;
+			}
+		}
+		if (is_array($ref)) {
+			if (is_array(current($ref)) && is_string(key($ref))) {
+				$name = key($ref);
+				$ref = current($ref);
+			}
+			foreach ($ref as $key => $val) {
+				if (strpos($key, $type) !== 0 && strpos($key, '.') === false) {
+					unset($ref[$key]);
+					$ref["{$type}0.{$key}"] = $val;
+				}
+			}
+			$queryData = array(
+				'conditions' => $ref,
+				'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'),
+				'joins' => array(array(
+					'table' => $db->fullTableName($this),
+					'alias' => "{$type}0",
+					'type' => 'LEFT',
+					'conditions' => array(
+						$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"),
+						$db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght")
+					)
+				)),
+				'order' => $db->name("{$type}.lft") . ' DESC'
+			);
+			$result = $db->read($this, $queryData, -1);
+
+			if (!$result) {
+				trigger_error("AclNode::node() - Couldn't find {$type} node identified by \"" . print_r($ref, true) . "\"", E_USER_WARNING);
+			}
+		}
+		return $result;
+	}
+}
+/**
+ * Access Control Object
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class Aco extends AclNode {
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Aco';
+/**
+ * Binds to ARO nodes through permissions settings
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Aro' => array('with' => 'Permission'));
+}
+/**
+ * Action for Access Control Object
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class AcoAction extends AppModel {
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'AcoAction';
+/**
+ * ACO Actions belong to ACOs
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Aco');
+}
+/**
+ * Access Request Object
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class Aro extends AclNode {
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Aro';
+/**
+ * AROs are linked to ACOs by means of Permission
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Aco' => array('with' => 'Permission'));
+}
+/**
+ * Permissions linking AROs with ACOs
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class Permission extends AppModel {
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Permission';
+/**
+ * Explicitly disable in-memory query caching
+ *
+ * @var boolean
+ * @access public
+ */
+	var $cacheQueries = false;
+/**
+ * Override default table name
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'aros_acos';
+/**
+ * Permissions link AROs with ACOs
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Aro', 'Aco');
+/**
+ * No behaviors for this model
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = null;
+/**
+ * Constructor, used to tell this model to use the
+ * database configured for ACL
+ */
+	function __construct() {
+		$config = Configure::read('Acl.database');
+		if (!empty($config)) {
+			$this->useDbConfig = $config;
+		}
+		parent::__construct();
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/datasource.php
===================================================================
--- cake/libs/model/datasources/datasource.php	(revision 0)
+++ cake/libs/model/datasources/datasource.php	(revision 0)
@@ -0,0 +1,509 @@
+<?php
+/* SVN FILE: $Id: datasource.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * DataSource base class
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * DataSource base class
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ */
+class DataSource extends Object {
+/**
+ * Are we connected to the DataSource?
+ *
+ * @var boolean
+ * @access public
+ */
+	var $connected = false;
+/**
+ * Print full query debug info?
+ *
+ * @var boolean
+ * @access public
+ */
+	var $fullDebug = false;
+/**
+ * Error description of last query
+ *
+ * @var unknown_type
+ * @access public
+ */
+	var $error = null;
+/**
+ * String to hold how many rows were affected by the last SQL operation.
+ *
+ * @var string
+ * @access public
+ */
+	var $affected = null;
+/**
+ * Number of rows in current resultset
+ *
+ * @var int
+ * @access public
+ */
+	var $numRows = null;
+/**
+ * Time the last query took
+ *
+ * @var int
+ * @access public
+ */
+	var $took = null;
+/**
+ * The starting character that this DataSource uses for quoted identifiers.
+ *
+ * @var string
+ */
+	var $startQuote = null;
+/**
+ * The ending character that this DataSource uses for quoted identifiers.
+ *
+ * @var string
+ */
+	var $endQuote = null;
+/**
+ * Enter description here...
+ *
+ * @var array
+ * @access private
+ */
+	var $_result = null;
+/**
+ * Queries count.
+ *
+ * @var int
+ * @access private
+ */
+	var $_queriesCnt = 0;
+/**
+ * Total duration of all queries.
+ *
+ * @var unknown_type
+ * @access private
+ */
+	var $_queriesTime = null;
+/**
+ * Log of queries executed by this DataSource
+ *
+ * @var unknown_type
+ * @access private
+ */
+	var $_queriesLog = array();
+/**
+ * Maximum number of items in query log, to prevent query log taking over
+ * too much memory on large amounts of queries -- I we've had problems at
+ * >6000 queries on one system.
+ *
+ * @var int Maximum number of queries in the queries log.
+ * @access private
+ */
+	var $_queriesLogMax = 200;
+/**
+ * Caches serialzed results of executed queries
+ *
+ * @var array Maximum number of queries in the queries log.
+ * @access private
+ */
+	var $_queryCache = array();
+/**
+ * The default configuration of a specific DataSource
+ *
+ * @var array
+ * @access public
+ */
+	var $_baseConfig = array();
+/**
+ * Holds references to descriptions loaded by the DataSource
+ *
+ * @var array
+ * @access private
+ */
+	var $__descriptions = array();
+/**
+ * Holds a list of sources (tables) contained in the DataSource
+ *
+ * @var array
+ * @access protected
+ */
+	var $_sources = null;
+/**
+ * A reference to the physical connection of this DataSource
+ *
+ * @var array
+ * @access public
+ */
+	var $connection = null;
+/**
+ * The DataSource configuration
+ *
+ * @var array
+ * @access public
+ */
+	var $config = array();
+/**
+ * The DataSource configuration key name
+ *
+ * @var string
+ * @access public
+ */
+	var $configKeyName = null;
+/**
+ * Whether or not this DataSource is in the middle of a transaction
+ *
+ * @var boolean
+ * @access protected
+ */
+	var $_transactionStarted = false;
+/**
+ * Whether or not source data like available tables and schema descriptions
+ * should be cached
+ *
+ * @var boolean
+ */
+	var $cacheSources = true;
+/**
+ * Constructor.
+ */
+	function __construct($config = array()) {
+		parent::__construct();
+		$this->setConfig($config);
+	}
+/**
+ * Caches/returns cached results for child instances
+ *
+ * @return array
+ */
+	function listSources($data = null) {
+		if ($this->cacheSources === false) {
+			return null;
+		}
+
+		if ($this->_sources !== null) {
+			return $this->_sources;
+		}
+
+		$key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list';
+		$key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key);
+		$sources = Cache::read($key, '_cake_model_');
+
+		if (empty($sources)) {
+			$sources = $data;
+			Cache::write($key, $data, '_cake_model_');
+		}
+
+		$this->_sources = $sources;
+		return $sources;
+	}
+/**
+ * Convenience method for DboSource::listSources().  Returns source names in lowercase.
+ *
+ * @return array
+ */
+	function sources($reset = false) {
+		if ($reset === true) {
+			$this->_sources = null;
+		}
+		return array_map('strtolower', $this->listSources());
+	}
+/**
+ * Returns a Model description (metadata) or null if none found.
+ *
+ * @param Model $model
+ * @return mixed
+ */
+	function describe($model) {
+		if ($this->cacheSources === false) {
+			return null;
+		}
+		if (isset($this->__descriptions[$model->tablePrefix . $model->table])) {
+			return $this->__descriptions[$model->tablePrefix . $model->table];
+		}
+		$cache = $this->__cacheDescription($model->tablePrefix . $model->table);
+
+		if ($cache !== null) {
+			$this->__descriptions[$model->tablePrefix . $model->table] =& $cache;
+			return $cache;
+		}
+		return null;
+	}
+/**
+ * Begin a transaction
+ *
+ * @return boolean Returns true if a transaction is not in progress
+ */
+	function begin(&$model) {
+		return !$this->_transactionStarted;
+	}
+/**
+ * Commit a transaction
+ *
+ * @return boolean Returns true if a transaction is in progress
+ */
+	function commit(&$model) {
+		return $this->_transactionStarted;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @return boolean Returns true if a transaction is in progress
+ */
+	function rollback(&$model) {
+		return $this->_transactionStarted;
+	}
+/**
+ * Converts column types to basic types
+ *
+ * @param string $real Real  column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		return false;
+	}
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param unknown_type $model
+ * @param unknown_type $fields
+ * @param unknown_type $values
+ * @return unknown
+ */
+	function create(&$model, $fields = null, $values = null) {
+		return false;
+	}
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param unknown_type $model
+ * @param unknown_type $queryData
+ * @return unknown
+ */
+	function read(&$model, $queryData = array()) {
+		return false;
+	}
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param unknown_type $model
+ * @param unknown_type $fields
+ * @param unknown_type $values
+ * @return unknown
+ */
+	function update(&$model, $fields = null, $values = null) {
+		return false;
+	}
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param unknown_type $model
+ * @param unknown_type $id
+ */
+	function delete(&$model, $id = null) {
+		if ($id == null) {
+			$id = $model->id;
+		}
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		return false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastNumRows($source = null) {
+		return false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastAffected($source = null) {
+		return false;
+	}
+/**
+ * Returns true if the DataSource supports the given interface (method)
+ *
+ * @param string $interface The name of the interface (method)
+ * @return boolean True on success
+ */
+	function isInterfaceSupported($interface) {
+		$methods = get_class_methods(get_class($this));
+		$methods = strtolower(implode('|', $methods));
+		$methods = explode('|', $methods);
+		$return = in_array(strtolower($interface), $methods);
+		return $return;
+	}
+/**
+ * Sets the configuration for the DataSource
+ *
+ * @param array $config The configuration array
+ * @return void
+ */
+	function setConfig($config = array()) {
+		$this->config = array_merge($this->_baseConfig, $this->config, $config);
+	}
+/**
+ * Cache the DataSource description
+ *
+ * @param string $object The name of the object (model) to cache
+ * @param mixed $data The description of the model, usually a string or array
+ */
+	function __cacheDescription($object, $data = null) {
+		if ($this->cacheSources === false) {
+			return null;
+		}
+
+		if ($data !== null) {
+			$this->__descriptions[$object] =& $data;
+		}
+
+		$key = ConnectionManager::getSourceName($this) . '_' . $object;
+		$cache = Cache::read($key, '_cake_model_');
+
+		if (empty($cache)) {
+			$cache = $data;
+			Cache::write($key, $cache, '_cake_model_');
+		}
+
+		return $cache;
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $query
+ * @param unknown_type $data
+ * @param unknown_type $association
+ * @param unknown_type $assocData
+ * @param Model $model
+ * @param Model $linkModel
+ * @param array $stack
+ * @return unknown
+ */
+	function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
+		$keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
+
+		foreach ($keys as $key) {
+			$val = null;
+
+			if (strpos($query, $key) !== false) {
+				switch ($key) {
+					case '{$__cakeID__$}':
+						if (isset($data[$model->alias]) || isset($data[$association])) {
+							if (isset($data[$model->alias][$model->primaryKey])) {
+								$val = $data[$model->alias][$model->primaryKey];
+							} elseif (isset($data[$association][$model->primaryKey])) {
+								$val = $data[$association][$model->primaryKey];
+							}
+						} else {
+							$found = false;
+							foreach (array_reverse($stack) as $assoc) {
+								if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
+									$val = $data[$assoc][$model->primaryKey];
+									$found = true;
+									break;
+								}
+							}
+							if (!$found) {
+								$val = '';
+							}
+						}
+					break;
+					case '{$__cakeForeignKey__$}':
+						foreach ($model->__associations as $id => $name) {
+							foreach ($model->$name as $assocName => $assoc) {
+								if ($assocName === $association) {
+									if (isset($assoc['foreignKey'])) {
+										$foreignKey = $assoc['foreignKey'];
+
+										if (isset($data[$model->alias][$foreignKey])) {
+											$val = $data[$model->alias][$foreignKey];
+										} elseif (isset($data[$association][$foreignKey])) {
+											$val = $data[$association][$foreignKey];
+										} else {
+											$found = false;
+											foreach (array_reverse($stack) as $assoc) {
+												if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
+													$val = $data[$assoc][$foreignKey];
+													$found = true;
+													break;
+												}
+											}
+											if (!$found) {
+												$val = '';
+											}
+										}
+									}
+									break 3;
+								}
+							}
+						}
+					break;
+				}
+				if (empty($val) && $val !== '0') {
+					return false;
+				}
+				$query = str_replace($key, $this->value($val, $model->getColumnType($model->primaryKey)), $query);
+			}
+		}
+		return $query;
+	}
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param unknown_type $model
+ * @param unknown_type $key
+ * @return unknown
+ */
+	function resolveKey($model, $key) {
+		return $model->alias . $key;
+	}
+/**
+ * Closes the current datasource.
+ *
+ */
+	function __destruct() {
+		if ($this->_transactionStarted) {
+			$null = null;
+			$this->rollback($null);
+		}
+		if ($this->connected) {
+			$this->close();
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_mysql.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_mysql.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_mysql.php	(revision 0)
@@ -0,0 +1,659 @@
+<?php
+/* SVN FILE: $Id: dbo_mysql.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * MySQL layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Provides common base for MySQL & MySQLi connections
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboMysqlBase extends DboSource {
+/**
+ * Description property.
+ * 
+ * @var string
+ */
+	var $description = "MySQL DBO Base Driver";
+/**
+ * Start quote
+ *
+ * @var string
+ */
+	var $startQuote = "`";
+/**
+ * End quote
+ *
+ * @var string
+ */
+	var $endQuote = "`";
+/**
+ * use alias for update and delete. Set to true if version >= 4.1
+ *
+ * @var boolean
+ * @access protected
+ */
+	var $_useAlias = true;
+/**
+ * Index of basic SQL commands
+ *
+ * @var array
+ * @access protected
+ */
+	var $_commands = array(
+		'begin'    => 'START TRANSACTION',
+		'commit'   => 'COMMIT',
+		'rollback' => 'ROLLBACK'
+	);
+/**
+ * MySQL column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
+		'string' => array('name' => 'varchar', 'limit' => '255'),
+		'text' => array('name' => 'text'),
+		'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
+		'float' => array('name' => 'float', 'formatter' => 'floatval'),
+		'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
+		'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' => array('name' => 'blob'),
+		'boolean' => array('name' => 'tinyint', 'limit' => '1')
+	);
+/**
+ * Generates and executes an SQL UPDATE statement for given model, fields, and values.
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @param mixed $conditions
+ * @return array
+ */
+	function update(&$model, $fields = array(), $values = null, $conditions = null) {
+		if (!$this->_useAlias) {
+			return parent::update($model, $fields, $values, $conditions);
+		}
+
+		if ($values == null) {
+			$combined = $fields;
+		} else {
+			$combined = array_combine($fields, $values);
+		}
+
+		$alias = $joins = false;
+		$fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions));
+		$fields = join(', ', $fields);
+		$table = $this->fullTableName($model);
+
+		if (!empty($conditions)) {
+			$alias = $this->name($model->alias);
+			if ($model->name == $model->alias) {
+				$joins = implode(' ', $this->_getJoins($model));
+			}
+		}
+		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
+
+		if ($conditions === false) {
+			return false;
+		}
+
+		if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) {
+			$model->onError();
+			return false;
+		}
+		return true;
+	}
+/**
+ * Generates and executes an SQL DELETE statement for given id/conditions on given model.
+ *
+ * @param Model $model
+ * @param mixed $conditions
+ * @return boolean Success
+ */
+	function delete(&$model, $conditions = null) {
+		if (!$this->_useAlias) {
+			return parent::delete($model, $conditions);
+		}
+		$alias = $this->name($model->alias);
+		$table = $this->fullTableName($model);
+		$joins = implode(' ', $this->_getJoins($model));
+
+		if (empty($conditions)) {
+			$alias = $joins = false;
+		}
+		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
+
+		if ($conditions === false) {
+			return false;
+		}
+
+		if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
+			$model->onError();
+			return false;
+		}
+		return true;
+	}
+/**
+ * Sets the database encoding
+ *
+ * @param string $enc Database encoding
+ */
+	function setEncoding($enc) {
+		return $this->_execute('SET NAMES ' . $enc) != false;
+	}
+/**
+ * Returns an array of the indexes in given datasource name.
+ *
+ * @param string $model Name of model to inspect
+ * @return array Fields in table. Keys are column and unique
+ */
+	function index($model) {
+		$index = array();
+		$table = $this->fullTableName($model);
+		if ($table) {
+			$indexes = $this->query('SHOW INDEX FROM ' . $table);
+			$keys = Set::extract($indexes, '{n}.STATISTICS');
+			foreach ($keys as $i => $key) {
+				if (!isset($index[$key['Key_name']])) {
+					$col = array();
+					$index[$key['Key_name']]['column'] = $key['Column_name'];
+					$index[$key['Key_name']]['unique'] = intval($key['Non_unique'] == 0);
+				} else {
+					if (!is_array($index[$key['Key_name']]['column'])) {
+						$col[] = $index[$key['Key_name']]['column'];
+					}
+					$col[] = $key['Column_name'];
+					$index[$key['Key_name']]['column'] = $col;
+				}
+			}
+		}
+		return $index;
+	}
+/**
+ * Generate a MySQL Alter Table syntax for the given Schema comparison
+ *
+ * @param array $compare Result of a CakeSchema::compare()
+ * @return array Array of alter statements to make.
+ */
+	function alterSchema($compare, $table = null) {
+		if (!is_array($compare)) {
+			return false;
+		}
+		$out = '';
+		$colList = array();
+		foreach ($compare as $curTable => $types) {
+			$indexes = array();
+			if (!$table || $table == $curTable) {
+				$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
+				foreach ($types as $type => $column) {
+					if (isset($column['indexes'])) {
+						$indexes[$type] = $column['indexes'];
+						unset($column['indexes']);
+					}
+					switch ($type) {
+						case 'add':
+							foreach ($column as $field => $col) {
+								$col['name'] = $field;
+								$alter = 'ADD '.$this->buildColumn($col);
+								if (isset($col['after'])) {
+									$alter .= ' AFTER '. $this->name($col['after']);
+								}
+								$colList[] = $alter;
+							}
+						break;
+						case 'drop':
+							foreach ($column as $field => $col) {
+								$col['name'] = $field;
+								$colList[] = 'DROP '.$this->name($field);
+							}
+						break;
+						case 'change':
+							foreach ($column as $field => $col) {
+								if (!isset($col['name'])) {
+									$col['name'] = $field;
+								}
+								$colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
+							}
+						break;
+					}
+				}
+				$colList = array_merge($colList, $this->_alterIndexes($curTable, $indexes));
+				$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
+			}
+		}
+		return $out;
+	}
+/**
+ * Generate a MySQL "drop table" statement for the given Schema object
+ *
+ * @param object $schema An instance of a subclass of CakeSchema
+ * @param string $table Optional.  If specified only the table name given will be generated.
+ *                      Otherwise, all tables defined in the schema are generated.
+ * @return string
+ */
+	function dropSchema($schema, $table = null) {
+		if (!is_a($schema, 'CakeSchema')) {
+			trigger_error(__('Invalid schema object', true), E_USER_WARNING);
+			return null;
+		}
+		$out = '';
+		foreach ($schema->tables as $curTable => $columns) {
+			if (!$table || $table == $curTable) {
+				$out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n";
+			}
+		}
+		return $out;
+	}
+/**
+ * Generate MySQL index alteration statements for a table.
+ *
+ * @param string $table Table to alter indexes for
+ * @param array $new Indexes to add and drop
+ * @return array Index alteration statements
+ */	
+	function _alterIndexes($table, $indexes) {
+		$alter = array();
+		if (isset($indexes['drop'])) {
+			foreach($indexes['drop'] as $name => $value) {
+				$out = 'DROP ';
+				if ($name == 'PRIMARY') {
+					$out .= 'PRIMARY KEY';
+				} else {
+					$out .= 'KEY ' . $name;
+				}
+				$alter[] = $out;
+			}
+		}
+		if (isset($indexes['add'])) {
+			foreach ($indexes['add'] as $name => $value) {
+				$out = 'ADD ';
+				if ($name == 'PRIMARY') {
+					$out .= 'PRIMARY ';
+					$name = null;
+				} else {
+					if (!empty($value['unique'])) {
+						$out .= 'UNIQUE ';
+					}
+				}
+				if (is_array($value['column'])) {
+					$out .= 'KEY '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
+				} else {
+					$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
+				}
+				$alter[] = $out;
+			}
+		}
+		return $alter;
+	}
+/**
+ * Inserts multiple values into a table
+ *
+ * @param string $table
+ * @param string $fields
+ * @param array $values
+ */
+	function insertMulti($table, $fields, $values) {
+		$table = $this->fullTableName($table);
+		if (is_array($fields)) {
+			$fields = join(', ', array_map(array(&$this, 'name'), $fields));
+		}
+		$values = implode(', ', $values);
+		$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
+	}
+}
+
+/**
+ * MySQL DBO driver object
+ *
+ * Provides connection and SQL generation for MySQL RDMS
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboMysql extends DboMysqlBase {
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $description = "MySQL DBO Driver";
+/**
+ * Base configuration settings for MySQL driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'root',
+		'password' => '',
+		'database' => 'cake',
+		'port' => '3306',
+		'connect' => 'mysql_pconnect'
+	);
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$connect = $config['connect'];
+		$this->connected = false;
+
+		if (!$config['persistent']) {
+			$this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
+		} else {
+			$this->connection = $connect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
+		}
+
+		if (mysql_select_db($config['database'], $this->connection)) {
+			$this->connected = true;
+		}
+
+		if (isset($config['encoding']) && !empty($config['encoding'])) {
+			$this->setEncoding($config['encoding']);
+		}
+
+		$this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
+
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		if (isset($this->results) && is_resource($this->results)) {
+			mysql_free_result($this->results);
+		}
+		$this->connected = !@mysql_close($this->connection);
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		return mysql_query($sql, $this->connection);
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+		if ($cache != null) {
+			return $cache;
+		}
+		$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
+
+		if (!$result) {
+			return array();
+		} else {
+			$tables = array();
+
+			while ($line = mysql_fetch_array($result)) {
+				$tables[] = $line[0];
+			}
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param string $tableName Name of database table to inspect
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+		$cache = parent::describe($model);
+		if ($cache != null) {
+			return $cache;
+		}
+		$fields = false;
+		$cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
+
+		foreach ($cols as $column) {
+			$colKey = array_keys($column);
+			if (isset($column[$colKey[0]]) && !isset($column[0])) {
+				$column[0] = $column[$colKey[0]];
+			}
+			if (isset($column[0])) {
+				$fields[$column[0]['Field']] = array(
+					'type'		=> $this->column($column[0]['Type']),
+					'null'		=> ($column[0]['Null'] == 'YES' ? true : false),
+					'default'	=> $column[0]['Default'],
+					'length'	=> $this->length($column[0]['Type']),
+				);
+				if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
+					$fields[$column[0]['Field']]['key']	= $this->index[$column[0]['Key']];
+				}
+			}
+		}
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		} elseif ($data === null || (is_array($data) && empty($data))) {
+			return 'NULL';
+		} elseif ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
+			return  "''";
+		}
+		if (empty($column)) {
+			$column = $this->introspectType($data);
+		}
+
+		switch ($column) {
+			case 'boolean':
+				return $this->boolean((bool)$data);
+			break;
+			case 'integer':
+			case 'float':
+				if ($data === '') {
+					return 'NULL';
+				}
+				if ((is_int($data) || is_float($data) || $data === '0') || (
+					is_numeric($data) && strpos($data, ',') === false &&
+					$data[0] != '0' && strpos($data, 'e') === false)) {
+						return $data;
+					}
+			default:
+				$data = "'" . mysql_real_escape_string($data, $this->connection) . "'";
+			break;
+		}
+		return $data;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		if (mysql_errno($this->connection)) {
+			return mysql_errno($this->connection).': '.mysql_error($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return mysql_affected_rows($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->hasResult()) {
+			return mysql_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
+		if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
+			return $id[0]['insertID'];
+		}
+
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+			if (isset($real['limit'])) {
+				$col .= '('.$real['limit'].')';
+			}
+			return $col;
+		}
+
+		$col = str_replace(')', '', $real);
+		$limit = $this->length($real);
+		if (strpos($col, '(') !== false) {
+			list($col, $vals) = explode('(', $col);
+		}
+
+		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
+			return $col;
+		}
+		if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
+			return 'boolean';
+		}
+		if (strpos($col, 'int') !== false) {
+			return 'integer';
+		}
+		if (strpos($col, 'char') !== false || $col == 'tinytext') {
+			return 'string';
+		}
+		if (strpos($col, 'text') !== false) {
+			return 'text';
+		}
+		if (strpos($col, 'blob') !== false || $col == 'binary') {
+			return 'binary';
+		}
+		if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
+			return 'float';
+		}
+		if (strpos($col, 'enum') !== false) {
+			return "enum($vals)";
+		}
+		return 'text';
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
+			mysql_free_result($this->results);
+		}
+		$this->results =& $results;
+		$this->map = array();
+		$numFields = mysql_num_fields($results);
+		$index = 0;
+		$j = 0;
+
+		while ($j < $numFields) {
+
+			$column = mysql_fetch_field($results,$j);
+			if (!empty($column->table)) {
+				$this->map[$index++] = array($column->table, $column->name);
+			} else {
+				$this->map[$index++] = array(0, $column->name);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = mysql_fetch_row($this->results)) {
+			$resultRow = array();
+			$i = 0;
+			foreach ($row as $index => $field) {
+				list($table, $column) = $this->map[$index];
+				$resultRow[$table][$column] = $row[$index];
+				$i++;
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Gets the database encoding
+ *
+ * @return string The database encoding
+ */
+	function getEncoding() {
+		return mysql_client_encoding($this->connection);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_oracle.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_oracle.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_oracle.php	(revision 0)
@@ -0,0 +1,1118 @@
+<?php
+/* SVN FILE: $Id: dbo_oracle.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Oracle layer for DBO.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP v 1.2.0.4041
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for class.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboOracle extends DboSource {
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access public
+ */
+	var $config = array();
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $alias = '';
+/**
+ * Sequence names as introspected from the database
+ */
+	var $_sequences = array();
+/**
+ * Transaction in progress flag
+ *
+ * @var boolean
+ */
+	var $__transactionStarted = false;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access public
+ */
+	var $columns = array(
+		'primary_key' => array('name' => ''),
+		'string' => array('name' => 'varchar2', 'limit' => '255'),
+		'text' => array('name' => 'varchar2'),
+		'integer' => array('name' => 'number'),
+		'float' => array('name' => 'float'),
+		'datetime' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
+		'timestamp' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
+		'time' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
+		'date' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'),
+		'binary' => array('name' => 'bytea'),
+		'boolean' => array('name' => 'boolean'),
+		'number' => array('name' => 'number'),
+		'inet' => array('name' => 'inet'));
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $connection;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_limit = -1;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_offset = 0;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_map;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_currentRow;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_numRows;
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_results;
+/**
+ * Last error issued by oci extension
+ *
+ * @var unknown_type
+ */
+	var $_error;
+/**
+ * Base configuration settings for MySQL driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'system',
+		'password' => '',
+		'database' => 'cake',
+		'nls_sort' => '',
+		'nls_sort' => ''
+	);
+/**
+ * Table-sequence map
+ *
+ * @var unknown_type
+ */
+	var $_sequenceMap = array();
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ * @access public
+ */
+	function connect() {
+		$config = $this->config;
+		$this->connected = false;
+		$config['charset'] = !empty($config['charset']) ? $config['charset'] : null;
+
+		if ($this->config['persistent']) {
+			$connect = 'ociplogon';
+		} else {
+			$connect = 'ocilogon';
+		}
+		$this->connection = @$connect($config['login'], $config['password'], $config['database'], $config['charset']);
+
+		if ($this->connection) {
+			$this->connected = true;
+			if (!empty($config['nls_sort'])) {
+				$this->execute('ALTER SESSION SET NLS_SORT='.$config['nls_sort']);
+			}
+
+			if (!empty($config['nls_comp'])) {
+				$this->execute('ALTER SESSION SET NLS_COMP='.$config['nls_comp']);
+			}
+			$this->execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
+		} else {
+			$this->connected = false;
+			$this->_setError();
+			return false;
+		}
+		return $this->connected;
+	}
+	/**
+	 * Keeps track of the most recent Oracle error
+	 *
+	 */
+	function _setError($source = null, $clear = false) {
+		if ($source) {
+			$e = ocierror($source);
+		} else {
+			$e = ocierror();
+		}
+		$this->_error = $e['message'];
+		if ($clear) {
+			$this->_error = null;
+		}
+	}
+/**
+ * Sets the encoding language of the session
+ *
+ * @param string $lang language constant
+ * @return bool
+ */
+	function setEncoding($lang) {
+		if (!$this->execute('ALTER SESSION SET NLS_LANGUAGE='.$lang)) {
+			return false;
+		}
+		return true;
+	}
+/**
+ * Gets the current encoding language
+ *
+ * @return string language constant
+ */
+	function getEncoding() {
+		$sql = 'SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER=\'NLS_LANGUAGE\'';
+		if (!$this->execute($sql)) {
+			return false;
+		}
+
+		if (!$row = $this->fetchRow()) {
+			return false;
+		}
+		return $row[0]['VALUE'];
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ * @access public
+ */
+	function disconnect() {
+		if ($this->connection) {
+			$this->connected = !ocilogoff($this->connection);
+			return !$this->connected;
+		}
+	}
+/**
+ * Scrape the incoming SQL to create the association map. This is an extremely
+ * experimental method that creates the association maps since Oracle will not tell us.
+ *
+ * @param string $sql
+ * @return false if sql is nor a SELECT
+ * @access protected
+ */
+	function _scrapeSQL($sql) {
+		$sql = str_replace("\"", '', $sql);
+		$preFrom = preg_split('/\bFROM\b/', $sql);
+		$preFrom = $preFrom[0];
+		$find = array('SELECT');
+		$replace = array('');
+		$fieldList = trim(str_replace($find, $replace, $preFrom));
+		$fields = preg_split('/,\s+/', $fieldList);//explode(', ', $fieldList);
+		$lastTableName	= '';
+
+		foreach($fields as $key => $value) {
+			if ($value != 'COUNT(*) AS count') {
+				if (preg_match('/\s+(\w+(\.\w+)*)$/', $value, $matches)) {
+					$fields[$key]	= $matches[1];
+
+					if (preg_match('/^(\w+\.)/', $value, $matches)) {
+						$fields[$key]	= $matches[1] . $fields[$key];
+						$lastTableName	= $matches[1];
+					}
+				}
+				/*
+				if (preg_match('/(([[:alnum:]_]+)\.[[:alnum:]_]+)(\s+AS\s+(\w+))?$/i', $value, $matches)) {
+					$fields[$key]	= isset($matches[4]) ? $matches[2] . '.' . $matches[4] : $matches[1];
+				}
+				*/
+			}
+		}
+		$this->_map = array();
+
+		foreach($fields as $f) {
+			$e = explode('.', $f);
+			if (count($e) > 1) {
+				$table = $e[0];
+				$field = strtolower($e[1]);
+			} else {
+				$table = 0;
+				$field = $e[0];
+			}
+			$this->_map[] = array($table, $field);
+		}
+	}
+/**
+ * Modify a SQL query to limit (and offset) the result set
+ *
+ * @param integer $limit Maximum number of rows to return
+ * @param integer $offset Row to begin returning
+ * @return modified SQL Query
+ * @access public
+ */
+	function limit($limit = -1, $offset = 0) {
+		$this->_limit = (int) $limit;
+		$this->_offset = (int) $offset;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ * @access public
+ */
+	function lastNumRows() {
+		return $this->_numRows;
+	}
+/**
+ * Executes given SQL statement. This is an overloaded method.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier or null
+ * @access protected
+ */
+	function _execute($sql) {
+		$this->_statementId = @ociparse($this->connection, $sql);
+		if (!$this->_statementId) {
+			$this->_setError($this->connection);
+			return false;
+		}
+
+		if ($this->__transactionStarted) {
+			$mode = OCI_DEFAULT;
+		} else {
+			$mode = OCI_COMMIT_ON_SUCCESS;
+		}
+
+		if (!@ociexecute($this->_statementId, $mode)) {
+			$this->_setError($this->_statementId);
+			return false;
+		}
+
+		$this->_setError(null, true);
+
+		switch(ocistatementtype($this->_statementId)) {
+			case 'DESCRIBE':
+			case 'SELECT':
+				$this->_scrapeSQL($sql);
+			break;
+			default:
+				return $this->_statementId;
+			break;
+		}
+
+		if ($this->_limit >= 1) {
+			ocisetprefetch($this->_statementId, $this->_limit);
+		} else {
+			ocisetprefetch($this->_statementId, 3000);
+		}
+		$this->_numRows = ocifetchstatement($this->_statementId, $this->_results, $this->_offset, $this->_limit, OCI_NUM | OCI_FETCHSTATEMENT_BY_ROW);
+		$this->_currentRow = 0;
+		$this->limit();
+		return $this->_statementId;
+	}
+/**
+ * Enter description here...
+ *
+ * @return unknown
+ * @access public
+ */
+	function fetchRow() {
+		if ($this->_currentRow >= $this->_numRows) {
+			ocifreestatement($this->_statementId);
+			$this->_map = null;
+			$this->_results = null;
+			$this->_currentRow = null;
+			$this->_numRows = null;
+			return false;
+		}
+		$resultRow = array();
+
+		foreach($this->_results[$this->_currentRow] as $index => $field) {
+			list($table, $column) = $this->_map[$index];
+
+			if (strpos($column, ' count')) {
+				$resultRow[0]['count'] = $field;
+			} else {
+				$resultRow[$table][$column] = $this->_results[$this->_currentRow][$index];
+			}
+		}
+		$this->_currentRow++;
+		return $resultRow;
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		return $this->fetchRow();
+	}
+/**
+ * Checks to see if a named sequence exists
+ *
+ * @param string $sequence
+ * @return bool
+ * @access public
+ */
+	function sequenceExists($sequence) {
+		$sql = "SELECT SEQUENCE_NAME FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '$sequence'";
+		if (!$this->execute($sql)) {
+			return false;
+		}
+		return $this->fetchRow();
+	}
+/**
+ * Creates a database sequence
+ *
+ * @param string $sequence
+ * @return bool
+ * @access public
+ */
+	function createSequence($sequence) {
+		$sql = "CREATE SEQUENCE $sequence";
+		return $this->execute($sql);
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $table
+ * @return unknown
+ * @access public
+ */
+	function createTrigger($table) {
+		$sql = "CREATE OR REPLACE TRIGGER pk_$table" . "_trigger BEFORE INSERT ON $table FOR EACH ROW BEGIN SELECT pk_$table.NEXTVAL INTO :NEW.ID FROM DUAL; END;";
+		return $this->execute($sql);
+	}
+/**
+ * Returns an array of tables in the database. If there are no tables, an error is
+ * raised and the application exits.
+ *
+ * @return array tablenames in the database
+ * @access public
+ */
+	function listSources() {
+		$cache = parent::listSources();
+		if ($cache != null) {
+			return $cache;
+		}
+		$sql = 'SELECT view_name AS name FROM all_views UNION SELECT table_name AS name FROM all_tables';
+
+		if (!$this->execute($sql)) {
+			return false;
+		}
+		$sources = array();
+
+		while($r = $this->fetchRow()) {
+			$sources[] = strtolower($r[0]['name']);
+		}
+		return $sources;
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param object instance of a model to inspect
+ * @return array Fields in table. Keys are name and type
+ * @access public
+ */
+	function describe(&$model) {
+
+		if (!empty($model->sequence)) {
+			$this->_sequenceMap[$model->table] = $model->sequence;
+		} elseif (!empty($model->table)) {
+			$this->_sequenceMap[$model->table] = $model->table . '_seq';
+		}
+
+		$cache = parent::describe($model);
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM all_tab_columns WHERE table_name = \'';
+		$sql .= strtoupper($this->fullTableName($model)) . '\'';
+
+		if (!$this->execute($sql)) {
+			return false;
+		}
+		$fields = array();
+
+		for ($i = 0; $row = $this->fetchRow(); $i++) {
+			$fields[strtolower($row[0]['COLUMN_NAME'])] = array(
+				'type'=> $this->column($row[0]['DATA_TYPE']),
+				'length'=> $row[0]['DATA_LENGTH']
+			);
+		}
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+
+		return $fields;
+	}
+/**
+ * Deletes all the records in a table and drops all associated auto-increment sequences.
+ * Using DELETE instead of TRUNCATE because it causes locking problems.
+ *
+ * @param mixed $table A string or model class representing the table to be truncated
+ * @param integer $reset If -1, sequences are dropped, if 0 (default), sequences are reset,
+ *						and if 1, sequences are not modified
+ * @return boolean	SQL TRUNCATE TABLE statement, false if not applicable.
+ * @access public
+ *
+ */
+	function truncate($table, $reset = 0) {
+
+		if (empty($this->_sequences)) {
+			$sql = "SELECT sequence_name FROM all_sequences";
+			$this->execute($sql);
+			while ($row = $this->fetchRow()) {
+				$this->_sequences[] = strtolower($row[0]['sequence_name']);
+			}
+		}
+
+		$this->execute('DELETE FROM ' . $this->fullTableName($table));
+		if (!isset($this->_sequenceMap[$table]) || !in_array($this->_sequenceMap[$table], $this->_sequences)) {
+			return true;
+		}
+		if ($reset === 0) {
+			$this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
+			$row = $this->fetchRow();
+			$currval = $row[$this->_sequenceMap[$table]]['nextval'];
+
+			$this->execute("SELECT min_value FROM all_sequences WHERE sequence_name = '{$this->_sequenceMap[$table]}'");
+			$row = $this->fetchRow();
+			$min_value = $row[0]['min_value'];
+
+			if ($min_value == 1) $min_value = 0;
+			$offset = -($currval - $min_value);
+
+			$this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY $offset MINVALUE $min_value");
+			$this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual");
+			$this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY 1");
+		} else {
+			//$this->execute("DROP SEQUENCE {$this->_sequenceMap[$table]}");
+		}
+		return true;
+	}
+/**
+ * Enables, disables, and lists table constraints
+ *
+ * Note: This method could have been written using a subselect for each table,
+ * however the effort Oracle expends to run the constraint introspection is very high.
+ * Therefore, this method caches the result once and loops through the arrays to find
+ * what it needs. It reduced my query time by 50%. YMMV.
+ *
+ * @param string $action
+ * @param string $table
+ * @return mixed boolean true or array of constraints
+ */
+	function constraint($action, $table) {
+		if (empty($table)) {
+			trigger_error(__('Must specify table to operate on constraints'));
+		}
+
+		$table = strtoupper($table);
+
+		if (empty($this->_keyConstraints)) {
+			$sql = "SELECT
+					  table_name,
+					  c.constraint_name
+					FROM all_cons_columns cc
+					LEFT JOIN all_indexes i ON (cc.constraint_name = i.index_name)
+					LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)";
+			$this->execute($sql);
+			while ($row = $this->fetchRow()) {
+				$this->_keyConstraints[] = array($row[0]['table_name'], $row['c']['constraint_name']);
+			}
+		}
+
+		$relatedKeys = array();
+		foreach ($this->_keyConstraints as $c) {
+			if ($c[0] == $table) {
+				$relatedKeys[] = $c[1];
+			}
+		}
+
+		if (empty($this->_constraints)) {
+			$sql = "SELECT
+					  table_name,
+					  constraint_name,
+					  r_constraint_name
+					FROM
+					  all_constraints";
+			$this->execute($sql);
+			while ($row = $this->fetchRow()) {
+				$this->_constraints[] = $row[0];
+			}
+		}
+
+		$constraints = array();
+		foreach ($this->_constraints as $c) {
+			if (in_array($c['r_constraint_name'], $relatedKeys)) {
+				$constraints[] = array($c['table_name'], $c['constraint_name']);
+			}
+		}
+
+		foreach ($constraints as $c) {
+			list($table, $constraint) = $c;
+			switch ($action) {
+				case 'enable':
+					$this->execute("ALTER TABLE $table ENABLE CONSTRAINT $constraint");
+					break;
+				case 'disable':
+					$this->execute("ALTER TABLE $table DISABLE CONSTRAINT $constraint");
+					break;
+				case 'list':
+					return $constraints;
+					break;
+				default:
+					trigger_error(__('DboOracle::constraint() accepts only enable, disable, or list'));
+			}
+		}
+		return true;
+	}
+/**
+ * Returns an array of the indexes in given table name.
+ *
+ * @param string $model Name of model to inspect
+ * @return array Fields in table. Keys are column and unique
+ */
+	function index($model) {
+		$index = array();
+		$table = $this->fullTableName($model, false);
+		if ($table) {
+			$indexes = $this->query('SELECT
+			  cc.table_name,
+			  cc.column_name,
+			  cc.constraint_name,
+			  c.constraint_type,
+			  i.index_name,
+			  i.uniqueness
+			FROM all_cons_columns cc
+			LEFT JOIN all_indexes i ON(cc.constraint_name = i.index_name)
+			LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)
+			WHERE cc.table_name = \'' . strtoupper($table) .'\'');
+			foreach ($indexes as $i => $idx) {
+				if ($idx['c']['constraint_type'] == 'P') {
+					$key = 'PRIMARY';
+				} else {
+					continue;
+				}
+				if (!isset($index[$key])) {
+					$index[$key]['column'] = strtolower($idx['cc']['column_name']);
+					$index[$key]['unique'] = intval($idx['i']['uniqueness'] == 'UNIQUE');
+				} else {
+					if (!is_array($index[$key]['column'])) {
+						$col[] = $index[$key]['column'];
+					}
+					$col[] = strtolower($idx['cc']['column_name']);
+					$index[$key]['column'] = $col;
+				}
+			}
+		}
+		return $index;
+	}
+/**
+ * Generate a Oracle Alter Table syntax for the given Schema comparison
+ *
+ * @param unknown_type $schema
+ * @return unknown
+ */
+	function alterSchema($compare, $table = null) {
+		if (!is_array($compare)) {
+			return false;
+		}
+		$out = '';
+		$colList = array();
+		foreach($compare as $curTable => $types) {
+			if (!$table || $table == $curTable) {
+				$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
+				foreach($types as $type => $column) {
+					switch($type) {
+						case 'add':
+							foreach($column as $field => $col) {
+								$col['name'] = $field;
+								$alter = 'ADD '.$this->buildColumn($col);
+								if (isset($col['after'])) {
+									$alter .= ' AFTER '. $this->name($col['after']);
+								}
+								$colList[] = $alter;
+							}
+						break;
+						case 'drop':
+							foreach($column as $field => $col) {
+								$col['name'] = $field;
+								$colList[] = 'DROP '.$this->name($field);
+							}
+						break;
+						case 'change':
+							foreach($column as $field => $col) {
+								if (!isset($col['name'])) {
+									$col['name'] = $field;
+								}
+								$colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
+							}
+						break;
+					}
+				}
+				$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
+			}
+		}
+		return $out;
+	}
+/**
+ * This method should quote Oracle identifiers. Well it doesn't.
+ * It would break all scaffolding and all of Cake's default assumptions.
+ *
+ * @param unknown_type $var
+ * @return unknown
+ * @access public
+ */
+	function name($name) {
+		if (strpos($name, '.') !== false && strpos($name, '"') === false) {
+			list($model, $field) = explode('.', $name);
+			if ($field[0] == "_") {
+				$name = "$model.\"$field\"";
+			}
+		} else {
+			if ($name[0] == "_") {
+				$name = "\"$name\"";
+			}
+		}
+		return $name;
+	}
+/**
+ * Begin a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions).
+ */
+	function begin() {
+		$this->__transactionStarted = true;
+		return true;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function rollback() {
+		return ocirollback($this->connection);
+	}
+/**
+ * Commit a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function commit() {
+		$this->__transactionStarted = false;
+		return ocicommit($this->connection);
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ * @access public
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+
+			if (isset($real['limit'])) {
+				$col .= '('.$real['limit'].')';
+			}
+			return $col;
+		} else {
+			$real = strtolower($real);
+		}
+		$col = str_replace(')', '', $real);
+		$limit = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('date', 'timestamp'))) {
+			return $col;
+		}
+		if (strpos($col, 'number') !== false) {
+			return 'integer';
+		}
+		if (strpos($col, 'integer') !== false) {
+			return 'integer';
+		}
+		if (strpos($col, 'char') !== false) {
+			return 'string';
+		}
+		if (strpos($col, 'text') !== false) {
+			return 'text';
+		}
+		if (strpos($col, 'blob') !== false) {
+			return 'binary';
+		}
+		if (in_array($col, array('float', 'double', 'decimal'))) {
+			return 'float';
+		}
+		if ($col == 'boolean') {
+			return $col;
+		}
+		return 'text';
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @return string Quoted and escaped
+ * @access public
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+
+		if ($data === '') {
+			return  "''";
+		}
+
+		switch($column) {
+			case 'date':
+				$date = new DateTime($data);
+				$data = $date->format('Y-m-d H:i:s');
+				$data = "TO_DATE('$data', 'YYYY-MM-DD HH24:MI:SS')";
+			break;
+			case 'integer' :
+			case 'float' :
+			case null :
+				if (is_numeric($data)) {
+					break;
+				}
+			default:
+				$data = str_replace("'", "''", $data);
+				$data = "'$data'";
+			break;
+		}
+		return $data;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param string
+ * @return integer
+ * @access public
+ */
+	function lastInsertId($source) {
+		$sequence = $this->_sequenceMap[$source];
+		$sql = "SELECT $sequence.currval FROM dual";
+
+		if (!$this->execute($sql)) {
+			return false;
+		}
+
+		while($row = $this->fetchRow()) {
+			return $row[$sequence]['currval'];
+		}
+		return false;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ * @access public
+ */
+	function lastError() {
+		return $this->_error;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
+ *
+ * @return int Number of affected rows
+ * @access public
+ */
+	function lastAffected() {
+		return $this->_statementId ? ocirowcount($this->_statementId): false;
+	}
+/**
+ * Renders a final SQL statement by putting together the component parts in the correct order
+ *
+ * @param string $type
+ * @param array $data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		extract($data);
+		$aliases = null;
+
+		switch (strtolower($type)) {
+			case 'select':
+				return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order} {$limit}";
+			break;
+			case 'create':
+				return "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
+			break;
+			case 'update':
+				if (!empty($alias)) {
+					$aliases = "{$this->alias}{$alias} ";
+				}
+				return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
+			break;
+			case 'delete':
+				if (!empty($alias)) {
+					$aliases = "{$this->alias}{$alias} ";
+				}
+				return "DELETE FROM {$table} {$aliases}{$conditions}";
+			break;
+			case 'schema':
+				foreach (array('columns', 'indexes') as $var) {
+					if (is_array(${$var})) {
+						${$var} = "\t" . join(",\n\t", array_filter(${$var}));
+					}
+				}
+				if (trim($indexes) != '') {
+					$columns .= ',';
+				}
+				return "CREATE TABLE {$table} (\n{$columns}{$indexes})";
+			break;
+			case 'alter':
+				break;
+		}
+	}
+/**
+ * Enter description here...
+ *
+ * @param Model $model
+ * @param unknown_type $linkModel
+ * @param string $type Association type
+ * @param unknown_type $association
+ * @param unknown_type $assocData
+ * @param unknown_type $queryData
+ * @param unknown_type $external
+ * @param unknown_type $resultSet
+ * @param integer $recursive Number of levels of association
+ * @param array $stack
+ */
+	function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
+		if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
+			if (!isset($resultSet) || !is_array($resultSet)) {
+				if (Configure::read() > 0) {
+					e('<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ');
+					if (isset($this->error) && $this->error != null) {
+						e($this->error);
+					}
+					e('</div>');
+				}
+				return null;
+			}
+			$count = count($resultSet);
+
+			if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) {
+				$ins = $fetch = array();
+				for ($i = 0; $i < $count; $i++) {
+					if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
+						$ins[] = $in;
+					}
+				}
+
+				if (!empty($ins)) {
+					$fetch = array();
+					$ins = array_chunk($ins, 1000);
+					foreach ($ins as $i) {
+						$q = str_replace('{$__cakeID__$}', join(', ', $i), $query);
+						$q = str_replace('= (', 'IN (', $q);
+						$res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
+						$fetch = array_merge($fetch, $res);
+					}
+				}
+
+				if (!empty($fetch) && is_array($fetch)) {
+					if ($recursive > 0) {
+
+						foreach ($linkModel->__associations as $type1) {
+							foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
+								$deepModel =& $linkModel->{$assoc1};
+								$tmpStack = $stack;
+								$tmpStack[] = $assoc1;
+
+								if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
+									$db =& $this;
+								} else {
+									$db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
+								}
+								$db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
+							}
+						}
+					}
+				}
+				return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
+			} elseif ($type === 'hasAndBelongsToMany') {
+				$ins = $fetch = array();
+				for ($i = 0; $i < $count; $i++) {
+					if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
+						$ins[] = $in;
+					}
+				}
+
+				$foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
+				$joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
+				list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
+				$habtmFieldsCount = count($habtmFields);
+
+				if (!empty($ins)) {
+					$fetch = array();
+					$ins = array_chunk($ins, 1000);
+					foreach ($ins as $i) {
+						$q = str_replace('{$__cakeID__$}', '(' .join(', ', $i) .')', $query);
+						$q = str_replace('= (', 'IN (', $q);
+						$q = str_replace('  WHERE 1 = 1', '', $q);
+
+
+						$q = $this->insertQueryData($q, null, $association, $assocData, $model, $linkModel, $stack);
+						if ($q != false) {
+							$res = $this->fetchAll($q, $model->cacheQueries, $model->alias);
+							$fetch = array_merge($fetch, $res);
+						}
+					}
+				}
+			}
+
+			for ($i = 0; $i < $count; $i++) {
+				$row =& $resultSet[$i];
+
+				if ($type !== 'hasAndBelongsToMany') {
+					$q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
+					if ($q != false) {
+						$fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
+					} else {
+						$fetch = null;
+					}
+				}
+
+				if (!empty($fetch) && is_array($fetch)) {
+					if ($recursive > 0) {
+
+						foreach ($linkModel->__associations as $type1) {
+							foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
+
+								$deepModel =& $linkModel->{$assoc1};
+								if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
+									$tmpStack = $stack;
+									$tmpStack[] = $assoc1;
+									if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
+										$db =& $this;
+									} else {
+										$db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
+									}
+									$db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
+								}
+							}
+						}
+					}
+					if ($type == 'hasAndBelongsToMany') {
+						$merge = array();
+						foreach($fetch as $j => $data) {
+							if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) {
+								if ($habtmFieldsCount > 2) {
+									$merge[] = $data;
+								} else {
+									$merge[] = Set::diff($data, array($with => $data[$with]));
+								}
+							}
+						}
+						if (empty($merge) && !isset($row[$association])) {
+							$row[$association] = $merge;
+						} else {
+							$this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
+						}
+					} else {
+						$this->__mergeAssociation($resultSet[$i], $fetch, $association, $type);
+					}
+					$resultSet[$i][$association] = $linkModel->afterfind($resultSet[$i][$association]);
+
+				} else {
+					$tempArray[0][$association] = false;
+					$this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type);
+				}
+			}
+		}
+	}
+	/**
+	 * Generate a "drop table" statement for the given Schema object
+	 *
+	 * @param object $schema An instance of a subclass of CakeSchema
+	 * @param string $table Optional.  If specified only the table name given will be generated.
+	 *						Otherwise, all tables defined in the schema are generated.
+	 * @return string
+	 */
+		function dropSchema($schema, $table = null) {
+			if (!is_a($schema, 'CakeSchema')) {
+				trigger_error(__('Invalid schema object', true), E_USER_WARNING);
+				return null;
+			}
+			$out = '';
+
+			foreach ($schema->tables as $curTable => $columns) {
+				if (!$table || $table == $curTable) {
+					$out .= 'DROP TABLE ' . $this->fullTableName($curTable) . "\n";
+				}
+			}
+			return $out;
+		}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_firebird.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_firebird.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_firebird.php	(revision 0)
@@ -0,0 +1,513 @@
+<?php
+/* SVN FILE: $Id: dbo_firebird.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Firebird/Interbase layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.dbo
+ * @since         CakePHP(tm) v 1.2.0.5152
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for class.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.dbo
+ */
+class DboFirebird extends DboSource {
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $description = "Firebird/Interbase DBO Driver";
+/**
+ * Saves the original table name
+ *
+ * @var unknown_type
+ */
+	var $modeltmp = array();
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $startQuote = "\'";
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $endQuote = "\'";
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $alias = ' ';
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $goofyLimit = true;
+/**
+ * Creates a map between field aliases and numeric indexes.
+ *
+ * @var array
+ */
+	var $__fieldMappings = array();
+/**
+ * Base configuration settings for Firebird driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'SYSDBA',
+		'password' => 'masterkey',
+		'database' => 'c:\\CAKE.FDB',
+		'port' => '3050',
+		'connect' => 'ibase_connect'
+	);
+/**
+ * Firebird column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
+		'string'	=> array('name'	 => 'varchar', 'limit' => '255'),
+		'text'		=> array('name' => 'BLOB SUB_TYPE 1 SEGMENT SIZE 100 CHARACTER SET NONE'),
+		'integer'	=> array('name' => 'integer'),
+		'float'		=> array('name' => 'float', 'formatter' => 'floatval'),
+		'datetime'	=> array('name' => 'timestamp', 'format'	=> 'd.m.Y H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name'	=> 'timestamp', 'format'	 => 'd.m.Y H:i:s', 'formatter' => 'date'),
+		'time'		=> array('name' => 'time', 'format'	   => 'H:i:s', 'formatter' => 'date'),
+		'date'		=> array('name' => 'date', 'format'	   => 'd.m.Y', 'formatter' => 'date'),
+		'binary'	=> array('name' => 'blob'),
+		'boolean'	=> array('name' => 'smallint')
+	);
+/**
+ * Firebird Transaction commands.
+ *
+ * @var array
+ **/
+	var $_commands = array(
+		'begin'	   => 'SET TRANSACTION',
+		'commit'   => 'COMMIT',
+		'rollback' => 'ROLLBACK'
+	);
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$connect = $config['connect'];
+
+		$this->connected = false;
+		$this->connection = $connect($config['host'] . ':' . $config['database'], $config['login'], $config['password']);
+		$this->connected = true;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		$this->connected = false;
+		return @ibase_close($this->connection);
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		return @ibase_query($this->connection,	$sql);
+	}
+/**
+ * Returns a row from given resultset as an array .
+ *
+ * @return array The fetched row as an array
+ */
+	function fetchRow() {
+		if ($this->hasResult()) {
+			$this->resultSet($this->_result);
+			$resultRow = $this->fetchResult();
+			return $resultRow;
+		} else {
+			return null;
+		}
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$sql = "select RDB" . "$" . "RELATION_NAME as name
+				FROM RDB" ."$" . "RELATIONS
+				Where RDB" . "$" . "SYSTEM_FLAG =0";
+
+		$result = @ibase_query($this->connection,$sql);
+		$tables = array();
+		while ($row = ibase_fetch_row ($result)) {
+			$tables[] = strtolower(trim($row[0]));
+		}
+		parent::listSources($tables);
+		return $tables;
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param Model $model Model object to describe
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+		$this->modeltmp[$model->table] = $model->alias;
+		$cache = parent::describe($model);
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$fields = false;
+		$sql = "SELECT * FROM " . $this->fullTableName($model, false);
+		$rs = ibase_query($sql);
+		$coln = ibase_num_fields($rs);
+		$fields = false;
+
+		for ($i = 0; $i < $coln; $i++) {
+			$col_info = ibase_field_info($rs, $i);
+			$fields[strtolower($col_info['name'])] = array(
+					'type' => $this->column($col_info['type']),
+					'null' => '',
+					'length' => $col_info['length']
+				);
+		}
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted name of $data for use in an SQL statement.
+ *
+ * @param string $data Name (table.field) to be prepared for use in an SQL statement
+ * @return string Quoted for Firebird
+ */
+	function name($data) {
+		if ($data == '*') {
+				return '*';
+		}
+		$pos = strpos($data, '"');
+
+		if ($pos === false) {
+			if (!strpos($data, ".")) {
+				$data = '"' . strtoupper($data) . '"';
+			} else {
+				$build = explode('.', $data);
+				$data = '"' . strtoupper($build[0]) . '"."' . strtoupper($build[1]) . '"';
+			}
+		}
+		return $data;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+		if ($data === null) {
+			return 'NULL';
+		}
+		if ($data === '') {
+			return "''";
+		}
+
+		switch($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			default:
+				if (get_magic_quotes_gpc()) {
+					$data = stripslashes(str_replace("'", "''", $data));
+				} else {
+					$data = str_replace("'", "''", $data);
+				}
+			break;
+		}
+		return "'" . $data . "'";
+	}
+/**
+ * Removes Identity (primary key) column from update data before returning to parent
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @return array
+ */
+	function update(&$model, $fields = array(), $values = array()) {
+		foreach ($fields as $i => $field) {
+			if ($field == $model->primaryKey) {
+				unset ($fields[$i]);
+				unset ($values[$i]);
+				break;
+			}
+		}
+		return parent::update($model, $fields, $values);
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		$error = ibase_errmsg();
+
+		if ($error !== false) {
+			return $error;
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return ibase_affected_rows($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		return $this->_result? /*ibase_affected_rows($this->_result)*/ 1: false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null, $field = 'id') {
+		$query = "SELECT RDB\$TRIGGER_SOURCE
+		FROM RDB\$TRIGGERS WHERE RDB\$RELATION_NAME = '".  strtoupper($source) .  "' AND
+		RDB\$SYSTEM_FLAG IS NULL AND  RDB\$TRIGGER_TYPE = 1 ";
+
+		$result = @ibase_query($this->connection,$query);
+		$generator = "";
+
+		while ($row = ibase_fetch_row($result, IBASE_TEXT)) {
+			if (strpos($row[0], "NEW." . strtoupper($field))) {
+				$pos = strpos($row[0], "GEN_ID(");
+
+				if ($pos > 0) {
+					$pos2 = strpos($row[0],",",$pos + 7);
+
+					if ($pos2 > 0) {
+						$generator = substr($row[0], $pos +7, $pos2 - $pos- 7);
+					}
+				}
+				break;
+			}
+		}
+
+		if (!empty($generator)) {
+			$sql = "SELECT GEN_ID(". $generator	 . ",0) AS maxi FROM RDB" . "$" . "DATABASE";
+			$res = $this->rawQuery($sql);
+			$data = $this->fetchRow($res);
+			return $data['maxi'];
+		} else {
+			return false;
+		}
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+
+			if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
+				$rt = ' FIRST';
+			}
+			$rt .= ' ' . $limit;
+
+			if (is_int($offset) && $offset > 0) {
+				$rt .= ' SKIP ' . $offset;
+			}
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+
+			if (isset($real['limit'])) {
+				$col .= '(' . $real['limit'] . ')';
+			}
+			return $col;
+		}
+
+		$col = str_replace(')', '', $real);
+		$limit = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('DATE', 'TIME'))) {
+			return strtolower($col);
+		}
+		if ($col == 'TIMESTAMP') {
+			return 'datetime';
+		}
+		if ($col == 'SMALLINT') {
+			return 'boolean';
+		}
+		if (strpos($col, 'int') !== false || $col == 'numeric' || $col == 'INTEGER') {
+			return 'integer';
+		}
+		if (strpos($col, 'char') !== false) {
+			return 'string';
+		}
+		if (strpos($col, 'text') !== false) {
+			return 'text';
+		}
+		if (strpos($col, 'VARCHAR') !== false) {
+			return 'string';
+		}
+		if (strpos($col, 'BLOB') !== false) {
+			return 'text';
+		}
+		if (in_array($col, array('FLOAT', 'NUMERIC', 'DECIMAL'))) {
+			return 'float';
+		}
+		return 'text';
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$this->map = array();
+		$num_fields = ibase_num_fields($results);
+		$index = 0;
+		$j = 0;
+
+		while ($j < $num_fields) {
+			$column = ibase_field_info($results, $j);
+			if (!empty($column[2])) {
+				$this->map[$index++] = array(ucfirst(strtolower($this->modeltmp[strtolower($column[2])])), strtolower($column[1]));
+			} else {
+				$this->map[$index++] = array(0, strtolower($column[1]));
+			}
+			$j++;
+		}
+	}
+/**
+ * Builds final SQL statement
+ *
+ * @param string $type Query type
+ * @param array $data Query data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		extract($data);
+
+		if (strtolower($type) == 'select') {
+			if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
+				$limit = preg_replace('/\s*offset.*$/i', '', $limit);
+				preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
+				$offset = intval($offset[1]) + intval($limitVal[1]);
+				$rOrder = $this->__switchSort($order);
+				list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
+				return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
+			} else {
+				return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}";
+			}
+		} else {
+			return parent::renderStatement($type, $data);
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = ibase_fetch_row($this->results, IBASE_TEXT)) {
+			$resultRow = array();
+			$i = 0;
+
+			foreach ($row as $index => $field) {
+				list($table, $column) = $this->map[$index];
+
+				if (trim($table) == "") {
+					$resultRow[0][$column] = $row[$index];
+				} else {
+					$resultRow[$table][$column] = $row[$index];
+					$i++;
+				}
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_postgres.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_postgres.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_postgres.php	(revision 0)
@@ -0,0 +1,864 @@
+<?php
+/* SVN FILE: $Id: dbo_postgres.php 7945 2008-12-19 02:16:01Z gwoo $ */
+
+/**
+ * PostgreSQL layer for DBO.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.9.1.114
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * PostgreSQL layer for DBO.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboPostgres extends DboSource {
+/**
+ * Driver description
+ *
+ * @var string
+ * @access public
+ */
+	var $description = "PostgreSQL DBO Driver";
+/**
+ * Index of basic SQL commands
+ *
+ * @var array
+ * @access protected
+ */
+	var $_commands = array(
+		'begin'    => 'BEGIN',
+		'commit'   => 'COMMIT',
+		'rollback' => 'ROLLBACK'
+	);
+/**
+ * Base driver configuration settings.  Merged with user settings.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_baseConfig = array(
+		'connect'	=> 'pg_pconnect',
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'root',
+		'password' => '',
+		'database' => 'cake',
+		'schema' => 'public',
+		'port' => 5432,
+		'encoding' => ''
+	);
+
+	var $columns = array(
+		'primary_key' => array('name' => 'serial NOT NULL'),
+		'string' => array('name'  => 'varchar', 'limit' => '255'),
+		'text' => array('name' => 'text'),
+		'integer' => array('name' => 'integer', 'formatter' => 'intval'),
+		'float' => array('name' => 'float', 'formatter' => 'floatval'),
+		'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
+		'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' => array('name' => 'bytea'),
+		'boolean' => array('name' => 'boolean'),
+		'number' => array('name' => 'numeric'),
+		'inet' => array('name'  => 'inet')
+	);
+
+	var $startQuote = '"';
+
+	var $endQuote = '"';
+/**
+ * Contains mappings of custom auto-increment sequences, if a table uses a sequence name
+ * other than what is dictated by convention.
+ *
+ * @var array
+ */
+	var $_sequenceMap = array();
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return True if successfully connected.
+ */
+	function connect() {
+		$config = $this->config;
+		$conn  = "host='{$config['host']}' port='{$config['port']}' dbname='{$config['database']}' ";
+		$conn .= "user='{$config['login']}' password='{$config['password']}'";
+
+		if (!$config['persistent']) {
+			$this->connection = pg_connect($conn, PGSQL_CONNECT_FORCE_NEW);
+		} else {
+			$this->connection = pg_pconnect($conn);
+		}
+		$this->connected = false;
+
+		if ($this->connection) {
+			$this->connected = true;
+			$this->_execute("SET search_path TO " . $config['schema']);
+		}
+		if (!empty($config['encoding'])) {
+			$this->setEncoding($config['encoding']);
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		if ($this->hasResult()) {
+			pg_free_result($this->_result);
+		}
+		if (is_resource($this->connection)) {
+			$this->connected = !pg_close($this->connection);
+		} else {
+			$this->connected = false;
+		}
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ */
+	function _execute($sql) {
+		return pg_query($this->connection, $sql);
+	}
+/**
+ * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$schema = $this->config['schema'];
+		$sql = "SELECT table_name as name FROM INFORMATION_SCHEMA.tables WHERE table_schema = '{$schema}';";
+		$result = $this->fetchAll($sql, false);
+
+		if (!$result) {
+			return array();
+		} else {
+			$tables = array();
+
+			foreach ($result as $item) {
+				$tables[] = $item[0]['name'];
+			}
+
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param string $tableName Name of database table to inspect
+ * @return array Fields in table. Keys are name and type
+ */
+	function &describe(&$model) {
+		$fields = parent::describe($model);
+		$table = $this->fullTableName($model, false);
+		$this->_sequenceMap[$table] = array();
+
+		if ($fields === null) {
+			$cols = $this->fetchAll(
+				"SELECT DISTINCT column_name AS name, data_type AS type, is_nullable AS null,
+					column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
+					character_octet_length AS oct_length FROM information_schema.columns
+				WHERE table_name = " . $this->value($table) . " AND table_schema = " .
+				$this->value($this->config['schema'])."  ORDER BY position",
+				false
+			);
+
+			foreach ($cols as $column) {
+				$colKey = array_keys($column);
+
+				if (isset($column[$colKey[0]]) && !isset($column[0])) {
+					$column[0] = $column[$colKey[0]];
+				}
+
+				if (isset($column[0])) {
+					$c = $column[0];
+
+					if (!empty($c['char_length'])) {
+						$length = intval($c['char_length']);
+					} elseif (!empty($c['oct_length'])) {
+						$length = intval($c['oct_length']);
+					} else {
+						$length = $this->length($c['type']);
+					}
+					$fields[$c['name']] = array(
+						'type'    => $this->column($c['type']),
+						'null'    => ($c['null'] == 'NO' ? false : true),
+						'default' => preg_replace(
+							"/^'(.*)'$/",
+							"$1",
+							preg_replace('/::.*/', '', $c['default'])
+						),
+						'length'  => $length
+					);
+					if ($c['name'] == $model->primaryKey) {
+						$fields[$c['name']]['key'] = 'primary';
+						if ($fields[$c['name']]['type'] !== 'string') {
+							$fields[$c['name']]['length'] = 11;
+						}
+					}
+					if (
+						$fields[$c['name']]['default'] == 'NULL' ||
+						preg_match('/nextval\([\'"]?([\w.]+)/', $c['default'], $seq)
+					) {
+						$fields[$c['name']]['default'] = null;
+						if (!empty($seq) && isset($seq[1])) {
+							$this->_sequenceMap[$table][$c['name']] = $seq[1];
+						}
+					}
+				}
+			}
+			$this->__cacheDescription($table, $fields);
+		}
+		if (isset($model->sequence)) {
+			$this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
+		}
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $read Value to be used in READ or WRITE context
+ * @return string Quoted and escaped
+ * @todo Add logic that formats/escapes data based on column type
+ */
+	function value($data, $column = null, $read = true) {
+
+		$parent = parent::value($data, $column);
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+		if (empty($column)) {
+			$column = $this->introspectType($data);
+		}
+
+		switch($column) {
+			case 'inet':
+			case 'float':
+			case 'integer':
+				if ($data === '') {
+					return $read ? 'NULL' : 'DEFAULT';
+				}
+			case 'binary':
+				$data = pg_escape_bytea($data);
+			break;
+			case 'boolean':
+				if ($data === true || $data === 't' || $data === 'true') {
+					return 'TRUE';
+				} elseif ($data === false || $data === 'f' || $data === 'false') {
+					return 'FALSE';
+				}
+				return (!empty($data) ? 'TRUE' : 'FALSE');
+			break;
+			default:
+				$data = pg_escape_string($data);
+			break;
+		}
+		return "'" . $data . "'";
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message
+ */
+	function lastError() {
+		$error = pg_last_error($this->connection);
+		return ($error) ? $error : null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		return ($this->_result) ? pg_affected_rows($this->_result) : false;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		return ($this->_result) ? pg_num_rows($this->_result) : false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param string $source Name of the database table
+ * @param string $field Name of the ID database field. Defaults to "id"
+ * @return integer
+ */
+	function lastInsertId($source, $field = 'id') {
+		$seq = $this->getSequence($source, $field);
+		$data = $this->fetchRow("SELECT currval('{$seq}') as max");
+		return $data[0]['max'];
+	}
+/**
+ * Gets the associated sequence for the given table/field
+ *
+ * @param mixed $table Either a full table name (with prefix) as a string, or a model object
+ * @param string $field Name of the ID database field. Defaults to "id"
+ * @return string The associated sequence name from the sequence map, defaults to "{$table}_{$field}_seq"
+ */
+	function getSequence($table, $field = 'id') {
+		if (is_object($table)) {
+			$table = $this->fullTableName($table, false);
+		}
+		if (isset($this->_sequenceMap[$table]) && isset($this->_sequenceMap[$table][$field])) {
+			return $this->_sequenceMap[$table][$field];
+		} else {
+			return "{$table}_{$field}_seq";
+		}
+	}
+/**
+ * Deletes all the records in a table and drops all associated auto-increment sequences
+ *
+ * @param mixed $table A string or model class representing the table to be truncated
+ * @param integer $reset If -1, sequences are dropped, if 0 (default), sequences are reset,
+ *						and if 1, sequences are not modified
+ * @return boolean	SQL TRUNCATE TABLE statement, false if not applicable.
+ * @access public
+ */
+	function truncate($table, $reset = 0) {
+		if (parent::truncate($table)) {
+			$table = $this->fullTableName($table, false);
+			if (isset($this->_sequenceMap[$table]) && $reset !== 1) {
+				foreach ($this->_sequenceMap[$table] as $field => $sequence) {
+					if ($reset === 0) {
+						$this->execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1");
+					} elseif ($reset === -1) {
+						$this->execute("DROP SEQUENCE IF EXISTS \"{$sequence}\"");
+					}
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+/**
+ * Prepares field names to be quoted by parent
+ *
+ * @param string $data
+ * @return string SQL field
+ */
+	function name($data) {
+		if (is_string($data)) {
+			$data = str_replace('"__"', '__', $data);
+		}
+		return parent::name($data);
+	}
+/**
+ * Generates the fields list of an SQL query.
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields
+ * @return array
+ */
+	function fields(&$model, $alias = null, $fields = array(), $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->alias;
+		}
+		$fields = parent::fields($model, $alias, $fields, false);
+
+		if (!$quote) {
+			return $fields;
+		}
+		$count = count($fields);
+
+		if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
+			for ($i = 0; $i < $count; $i++) {
+				if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
+					$prepend = '';
+					if (strpos($fields[$i], 'DISTINCT') !== false) {
+						$prepend = 'DISTINCT ';
+						$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
+					}
+
+					if (strrpos($fields[$i], '.') === false) {
+						$fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
+					} else {
+						$build = explode('.', $fields[$i]);
+						$fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
+					}
+				}
+			}
+		}
+		return $fields;
+	}
+/**
+ * Returns an array of the indexes in given datasource name.
+ *
+ * @param string $model Name of model to inspect
+ * @return array Fields in table. Keys are column and unique
+ */
+	function index($model) {
+		$index = array();
+		$table = $this->fullTableName($model, false);
+		if ($table) {
+			$indexes = $this->query("SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, i.indisvalid, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) as statement, c2.reltablespace
+			FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
+			WHERE c.oid  = ( 
+				SELECT c.oid 
+				FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 
+				WHERE c.relname ~ '^(" . $table . ")$' 
+					AND pg_catalog.pg_table_is_visible(c.oid) 
+					AND n.nspname ~ '^(" . $this->config['schema'] . ")$'
+			) 
+			AND c.oid = i.indrelid AND i.indexrelid = c2.oid
+			ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false);
+			foreach ($indexes as $i => $info) {
+				$key = array_pop($info);
+				if ($key['indisprimary']) {
+					$key['relname'] = 'PRIMARY';
+				}
+				$col = array();
+				preg_match('/\(([^\)]+)\)/', $key['statement'], $indexColumns);
+				$parsedColumn = $indexColumns[1];
+				if (strpos($indexColumns[1], ',') !== false) {
+					$parsedColumn = explode(', ', $indexColumns[1]);
+				}
+				$index[$key['relname']]['unique'] = $key['indisunique'];
+				$index[$key['relname']]['column'] = $parsedColumn;
+			}
+		}
+		return $index;
+	}
+/**
+ * Alter the Schema of a table.
+ *
+ * @param array $compare Results of CakeSchema::compare()
+ * @param string $table name of the table
+ * @access public
+ * @return array
+ */
+	function alterSchema($compare, $table = null) {
+		if (!is_array($compare)) {
+			return false;
+		}
+		$out = '';
+		$colList = array();
+		foreach ($compare as $curTable => $types) {
+			$indexes = array();
+			if (!$table || $table == $curTable) {
+				$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
+				foreach ($types as $type => $column) {
+					if (isset($column['indexes'])) {
+						$indexes[$type] = $column['indexes'];
+						unset($column['indexes']);
+					}
+					switch ($type) {
+						case 'add':
+							foreach ($column as $field => $col) {
+								$col['name'] = $field;
+								$alter = 'ADD COLUMN '.$this->buildColumn($col);
+								if (isset($col['after'])) {
+									$alter .= ' AFTER '. $this->name($col['after']);
+								}
+								$colList[] = $alter;
+							}
+						break;
+						case 'drop':
+							foreach ($column as $field => $col) {
+								$col['name'] = $field;
+								$colList[] = 'DROP COLUMN '.$this->name($field);
+							}
+						break;
+						case 'change':
+							foreach ($column as $field => $col) {
+								if (!isset($col['name'])) {
+									$col['name'] = $field;
+								}
+								$fieldName = $this->name($field);
+								$colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace($fieldName, '', $this->buildColumn($col));
+							}
+						break;
+					}
+				}
+				if (isset($indexes['drop']['PRIMARY'])) {
+					$colList[] = 'DROP CONSTRAINT ' . $curTable . '_pkey';
+				}
+				if (isset($indexes['add']['PRIMARY'])) {
+					$cols = $indexes['add']['PRIMARY']['column'];
+					if (is_array($cols)) {
+						$cols = implode(', ', $cols);
+					}
+					$colList[] = 'ADD PRIMARY KEY (' . $cols . ')';
+				}
+				
+				if (!empty($colList)) {
+					$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
+				} else {
+					$out = '';
+				}
+				$out .= join(";\n\t", $this->_alterIndexes($curTable, $indexes)) . ";";
+			}
+		}
+		return $out;
+	}
+/**
+ * Generate PostgreSQL index alteration statements for a table.
+ *
+ * @param string $table Table to alter indexes for
+ * @param array $new Indexes to add and drop
+ * @return array Index alteration statements
+ */	
+	function _alterIndexes($table, $indexes) {
+		$alter = array();
+		if (isset($indexes['drop'])) {
+			foreach($indexes['drop'] as $name => $value) {
+				$out = 'DROP ';
+				if ($name == 'PRIMARY') {
+					continue;
+				} else {
+					$out .= 'INDEX ' . $name;
+				}
+				$alter[] = $out;
+			}
+		}
+		if (isset($indexes['add'])) {
+			foreach ($indexes['add'] as $name => $value) {
+				$out = 'CREATE ';
+				if ($name == 'PRIMARY') {
+					continue;
+				} else {
+					if (!empty($value['unique'])) {
+						$out .= 'UNIQUE ';
+					}
+					$out .= 'INDEX ';
+				}
+				if (is_array($value['column'])) {
+					$out .= $name . ' ON ' . $table . ' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
+				} else {
+					$out .= $name . ' ON ' . $table . ' (' . $this->name($value['column']) . ')';
+				}
+				$alter[] = $out;
+			}
+		}
+		return $alter;
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+			if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
+				$rt = ' LIMIT';
+			}
+
+			$rt .= ' ' . $limit;
+			if ($offset) {
+				$rt .= ' OFFSET ' . $offset;
+			}
+
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+			if (isset($real['limit'])) {
+				$col .= '(' . $real['limit'] . ')';
+			}
+			return $col;
+		}
+
+		$col = str_replace(')', '', $real);
+		$limit = null;
+
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		$floats = array(
+			'float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'real', 'numeric'
+		);
+
+		switch (true) {
+			case (in_array($col, array('date', 'time', 'inet', 'boolean'))):
+				return $col;
+			case (strpos($col, 'timestamp') !== false):
+				return 'datetime';
+			case (strpos($col, 'time') === 0):
+				return 'time';
+			case (strpos($col, 'int') !== false && $col != 'interval'):
+				return 'integer';
+			case (strpos($col, 'char') !== false || $col == 'uuid'):
+				return 'string';
+			case (strpos($col, 'text') !== false):
+				return 'text';
+			case (strpos($col, 'bytea') !== false):
+				return 'binary';
+			case (in_array($col, $floats)):
+				return 'float';
+			default:
+				return 'text';
+			break;
+		}
+	}
+/**
+ * Gets the length of a database-native column description, or null if no length
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return int An integer representing the length of the column
+ */
+	function length($real) {
+		$col = str_replace(array(')', 'unsigned'), '', $real);
+		$limit = null;
+
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+		if ($col == 'uuid') {
+			return 36;
+		}
+		if ($limit != null) {
+			return intval($limit);
+		}
+		return null;
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$this->map = array();
+		$num_fields = pg_num_fields($results);
+		$index = 0;
+		$j = 0;
+
+		while ($j < $num_fields) {
+			$columnName = pg_field_name($results, $j);
+
+			if (strpos($columnName, '__')) {
+				$parts = explode('__', $columnName);
+				$this->map[$index++] = array($parts[0], $parts[1]);
+			} else {
+				$this->map[$index++] = array(0, $columnName);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = pg_fetch_row($this->results)) {
+			$resultRow = array();
+
+			foreach ($row as $index => $field) {
+				list($table, $column) = $this->map[$index];
+				$type = pg_field_type($this->results, $index);
+
+				switch ($type) {
+					case 'bool':
+						$resultRow[$table][$column] = $this->boolean($row[$index], false);
+					break;
+					case 'binary':
+					case 'bytea':
+						$resultRow[$table][$column] = pg_unescape_bytea($row[$index]);
+					break;
+					default:
+						$resultRow[$table][$column] = $row[$index];
+					break;
+				}
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Translates between PHP boolean values and PostgreSQL boolean values
+ *
+ * @param mixed $data Value to be translated
+ * @param boolean $quote	True to quote value, false otherwise
+ * @return mixed Converted boolean value
+ */
+	function boolean($data, $quote = true) {
+		switch (true) {
+			case ($data === true || $data === false):
+				return $data;
+			case ($data === 't' || $data === 'f'):
+				return ($data === 't');
+			case ($data === 'true' || $data === 'false'):
+				return ($data === 'true');
+			case ($data === 'TRUE' || $data === 'FALSE'):
+				return ($data === 'TRUE');
+			default:
+				return (bool)$data;
+			break;
+		}
+	}
+/**
+ * Sets the database encoding
+ *
+ * @param mixed $enc Database encoding
+ * @return boolean True on success, false on failure
+ */
+	function setEncoding($enc) {
+		return pg_set_client_encoding($this->connection, $enc) == 0;
+	}
+/**
+ * Gets the database encoding
+ *
+ * @return string The database encoding
+ */
+	function getEncoding() {
+		return pg_client_encoding($this->connection);
+	}
+/**
+ * Generate a Postgres-native column schema string
+ *
+ * @param array $column An array structured like the following:
+ *                      array('name'=>'value', 'type'=>'value'[, options]),
+ *                      where options can be 'default', 'length', or 'key'.
+ * @return string
+ */
+	function buildColumn($column) {
+		$col = $this->columns[$column['type']];
+		if (!isset($col['length']) && !isset($col['limit'])) {
+			unset($column['length']);
+		}
+		$out = preg_replace('/integer\([0-9]+\)/', 'integer', parent::buildColumn($column));
+		$out = str_replace('integer serial', 'serial', $out);
+		if (strpos($out, 'timestamp DEFAULT')) {
+			if (isset($column['null']) && $column['null']) {
+				$out = str_replace('DEFAULT NULL', '', $out);
+			} else {
+				$out = str_replace('DEFAULT NOT NULL', '', $out);
+			}
+		}
+		if (strpos($out, 'DEFAULT DEFAULT')) {
+			if (isset($column['null']) && $column['null']) {
+				$out = str_replace('DEFAULT DEFAULT', 'DEFAULT NULL', $out);
+			} elseif (in_array($column['type'], array('integer', 'float'))) {
+				$out = str_replace('DEFAULT DEFAULT', 'DEFAULT 0', $out);
+			} elseif ($column['type'] == 'boolean') {
+				$out = str_replace('DEFAULT DEFAULT', 'DEFAULT FALSE', $out);
+			}
+		}
+		return $out;
+	}
+/**
+ * Format indexes for create table
+ *
+ * @param array $indexes
+ * @param string $table
+ * @return string
+ */
+	function buildIndex($indexes, $table = null) {
+		$join = array();
+		if (!is_array($indexes)) {
+			return array();
+		}
+		foreach ($indexes as $name => $value) {
+			if ($name == 'PRIMARY') {
+				$out = 'PRIMARY KEY  (' . $this->name($value['column']) . ')';
+			} else {
+				$out = 'CREATE ';
+				if (!empty($value['unique'])) {
+					$out .= 'UNIQUE ';
+				}
+				if (is_array($value['column'])) {
+					$value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
+				} else {
+					$value['column'] = $this->name($value['column']);
+				}
+				$out .= "INDEX {$name} ON {$table}({$value['column']});";
+			}
+			$join[] = $out;
+		}
+		return $join;
+	}
+/**
+ * Overrides DboSource::renderStatement to handle schema generation with Postgres-style indexes
+ *
+ * @param string $type
+ * @param array $data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		switch (strtolower($type)) {
+			case 'schema':
+				extract($data);
+
+				foreach ($indexes as $i => $index) {
+					if (preg_match('/PRIMARY KEY/', $index)) {
+						unset($indexes[$i]);
+						$columns[] = $index;
+						break;
+					}
+				}
+				$join = array('columns' => ",\n\t", 'indexes' => "\n");
+
+				foreach (array('columns', 'indexes') as $var) {
+					if (is_array(${$var})) {
+						${$var} = join($join[$var], array_filter(${$var}));
+					}
+				}
+				return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}";
+			break;
+			default:
+				return parent::renderStatement($type, $data);
+			break;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_sybase.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_sybase.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_sybase.php	(revision 0)
@@ -0,0 +1,384 @@
+<?php
+/* SVN FILE: $Id: dbo_sybase.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Sybase layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 1.2.0.3097
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for class.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboSybase extends DboSource {
+/**
+ * Driver description
+ *
+ * @var string
+ */
+	var $description = "Sybase DBO Driver";
+/**
+ * Start quote for quoted identifiers
+ *
+ * @var string
+ */
+	var $startQuote = "";
+/**
+ * End quote for quoted identifiers
+ *
+ * @var string
+ */
+	var $endQuote = "";
+/**
+ * Base configuration settings for Sybase driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'sa',
+		'password' => '',
+		'database' => 'cake',
+		'port' => '4100'
+	);
+/**
+ * Sybase column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'numeric(9,0) IDENTITY PRIMARY KEY'),
+		'string' => array('name' => 'varchar', 'limit' => '255'),
+		'text' => array('name' => 'text'),
+		'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
+		'float' => array('name' => 'float', 'formatter' => 'floatval'),
+		'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
+		'date' => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' => array('name' => 'image'),
+		'boolean' => array('name' => 'bit')
+	);
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$this->connected = false;
+
+		if (!$config['persistent']) {
+			$this->connection = sybase_connect($config['host'], $config['login'], $config['password'], true);
+		} else {
+			$this->connection = sybase_pconnect($config['host'], $config['login'], $config['password']);
+		}
+
+		if (sybase_select_db($config['database'], $this->connection)) {
+			$this->connected = true;
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		$this->connected = !@sybase_close($this->connection);
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		return sybase_query($sql, $this->connection);
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$result = $this->_execute("select name from sysobjects where type='U'");
+		if (!$result) {
+			return array();
+		} else {
+
+			$tables = array();
+			while ($line = sybase_fetch_array($result)) {
+				$tables[] = $line[0];
+			}
+
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param string $tableName Name of database table to inspect
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+
+		$cache = parent::describe($model);
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$fields = false;
+		$cols = $this->query('DESC ' . $this->fullTableName($model));
+
+		foreach ($cols as $column) {
+			$colKey = array_keys($column);
+			if (isset($column[$colKey[0]]) && !isset($column[0])) {
+				$column[0] = $column[$colKey[0]];
+			}
+			if (isset($column[0])) {
+				$fields[$column[0]['Field']] = array('type' => $this->column($column[0]['Type']),
+													'null' => $column[0]['Null'],
+													'length' => $this->length($column[0]['Type']),
+													);
+			}
+		}
+
+		$this->__cacheDescription($model->tablePrefix.$model->table, $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+
+		if ($data === '') {
+			return  "''";
+		}
+
+		switch ($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			default:
+				$data = str_replace("'", "''", $data);
+			break;
+		}
+
+		return "'" . $data . "'";
+	}
+/**
+ * Begin a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions).
+ */
+	function begin(&$model) {
+		if (parent::begin($model)) {
+			if ($this->execute('BEGIN TRAN')) {
+				$this->_transactionStarted = true;
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Commit a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function commit(&$model) {
+		if (parent::commit($model)) {
+			$this->_transactionStarted = false;
+			return $this->execute('COMMIT TRAN');
+		}
+		return false;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function rollback(&$model) {
+		if (parent::rollback($model)) {
+			return $this->execute('ROLLBACK TRAN');
+		}
+		return false;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @todo not implemented
+ * @return string Error message with error number
+ */
+	function lastError() {
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return sybase_affected_rows($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->hasResult()) {
+			return @sybase_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		$result=$this->fetchRow('SELECT @@IDENTITY');
+		return $result[0];
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+			if (isset($real['limit']))
+			{
+				$col .= '('.$real['limit'].')';
+			}
+			return $col;
+		}
+
+		$col = str_replace(')', '', $real);
+		$limit = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('datetime', 'smalldatetime'))) {
+			return 'datetime';
+		} elseif (in_array($col, array('int', 'bigint', 'smallint', 'tinyint'))) {
+			return 'integer';
+		} elseif (in_array($col, array('float', 'double', 'real', 'decimal', 'money', 'numeric', 'smallmoney'))) {
+			return 'float';
+		} elseif (strpos($col, 'text') !== false) {
+			return 'text';
+		} elseif (in_array($col, array('char', 'nchar', 'nvarchar', 'string', 'varchar'))) {
+			return 'binary';
+		} elseif (in_array($col, array('binary', 'image', 'varbinary'))) {
+			return 'binary';
+		}
+
+		return 'text';
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$this->map = array();
+		$num_fields = sybase_num_fields($results);
+		$index = 0;
+		$j = 0;
+
+		while ($j < $num_fields) {
+
+			$column = sybase_fetch_field($results,$j);
+			if (!empty($column->table)) {
+				$this->map[$index++] = array($column->table, $column->name);
+			} else {
+				$this->map[$index++] = array(0, $column->name);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = sybase_fetch_row($this->results)) {
+			$resultRow = array();
+			$i = 0;
+			foreach ($row as $index => $field) {
+				list($table, $column) = $this->map[$index];
+				$resultRow[$table][$column] = $row[$index];
+				$i++;
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_db2.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_db2.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_db2.php	(revision 0)
@@ -0,0 +1,564 @@
+<?php
+/* SVN FILE: $Id: dbo_db2.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * IBM DB2 for DBO
+ *
+ * This file supports IBM DB2 and Cloudscape (aka Apache Derby,
+ * Sun Java DB) using the native ibm_db2 extension:
+ * http://pecl.php.net/package/ibm_db2
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2007, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * IBM DB2 for DBO
+ *
+ * This file supports IBM DB2 and Cloudscape (aka Apache Derby,
+ * Sun Java DB) using the native ibm_db2 extension:
+ * http://pecl.php.net/package/ibm_db2
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboDb2 extends DboSource {
+/**
+ * A short description of the type of driver.
+ *
+ * @var string
+ */
+	var $description = 'IBM DB2 DBO Driver';
+/**
+ * The start quote in which database column and table names should be wrapped.
+ *
+ * @var string
+ */
+	var $startQuote = '';
+/**
+ * The end quote in which database column and table names should be wrapped.
+ *
+ * @var string
+ */
+	var $endQuote = '';
+/**
+ * An array of base configuration settings to be used if settings are not
+ * provided, i.e. default host, port, and connection method.
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' 	=> true,
+		'login' 		=> 'db2inst1',
+		'password' 		=> '',
+		'database' 		=> 'cake',
+		'schema'		=> '',
+		'hostname'		=> '127.0.0.1',
+		'port'			=> '50001',
+		'encoding'		=> 'UTF-8',
+		'cataloged'		=> true,
+		'autocommit'	=> true
+	);
+/**
+ * An array that maps Cake column types to database native column types.
+ * The mapped information can include a reference to a function that should
+ * be used to format the data, as well as a string that defines the
+ * formatting according to that function.
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' 	=> array('name' => 'not null generated by default as identity (start with 1, increment by 1)'),
+		'string' 		=> array('name' => 'varchar', 'limit' => '255'),
+		'text' 			=> array('name' => 'clob'),
+		'integer' 		=> array('name' => 'integer', 'limit' => '10', 'formatter' => 'intval'),
+		'float' 		=> array('name' => 'double', 'formatter' => 'floatval'),
+		'datetime' 		=> array('name' => 'timestamp', 'format' => 'Y-m-d-H.i.s', 'formatter' => 'date'),
+		'timestamp' 	=> array('name' => 'timestamp', 'format' => 'Y-m-d-H.i.s', 'formatter' => 'date'),
+		'time' 			=> array('name' => 'time', 'format' => 'H.i.s', 'formatter' => 'date'),
+		'date' 			=> array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' 		=> array('name' => 'blob'),
+		'boolean' 		=> array('name' => 'smallint', 'limit' => '1')
+	);
+/**
+ * A map for every result mapping tables to columns
+ *
+ * @var array result -> ( table -> column )
+ */
+	var $_resultMap = array();
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$connect = 'db2_connect';
+		if ($config['persistent']) {
+			$connect = 'db2_pconnect';
+		}
+		$this->connected = false;
+
+		if ($config['cataloged']) {
+			$this->connection = $connect($config['database'], $config['login'], $config['password']);
+		} else {
+			$connString = sprintf(
+				"DRIVER={IBM DB2 ODBC DRIVER};DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;",
+				$config['database'],
+				$config['hostname'],
+				$config['port'],
+				$config['login'],
+				$config['password']
+			);
+			$this->connection = db2_connect($connString, '', '');
+		}
+
+		if ($this->connection) {
+			$this->connected = true;
+		}
+
+		if ($config['schema'] !== '') {
+			$this->_execute('SET CURRENT SCHEMA = ' . $config['schema']);
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		@db2_free_result($this->results);
+		$this->connected = !@db2_close($this->connection);
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.  We should use prepare / execute to allow the
+ * database server to reuse its access plan and increase the efficiency
+ * of your database access
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		// get result from db
+		$result = db2_exec($this->connection, $sql);
+
+		if (!is_bool($result)) {
+			// build table/column map for this result
+			$map = array();
+			$numFields = db2_num_fields($result);
+			$index = 0;
+			$j = 0;
+			$offset = 0;
+
+			while ($j < $numFields) {
+				$columnName = strtolower(db2_field_name($result, $j));
+				$tmp = strpos($sql, '.' . $columnName, $offset);
+				$tableName = substr($sql, $offset, ($tmp-$offset));
+				$tableName = substr($tableName, strrpos($tableName, ' ') + 1);
+				$map[$index++] = array($tableName, $columnName);
+				$j++;
+				$offset = strpos($sql, ' ', $tmp);
+			}
+
+			$this->_resultMap[$result] = $map;
+		}
+
+		return $result;
+	}
+/**
+ * Returns an array of all the tables in the database.
+ * Should call parent::listSources twice in the method:
+ * once to see if the list is cached, and once to cache
+ * the list if not.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$result = db2_tables($this->connection);
+		$tables = array();
+
+		while (db2_fetch_row($result)) {
+			$tables[] = strtolower(db2_result($result, 'TABLE_NAME'));
+		}
+		parent::listSources($tables);
+		return $tables;
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param Model $model Model object to describe
+ * @return array Fields in table. Keys are name and type
+ */
+	function &describe(&$model) {
+		$cache = parent::describe($model);
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$fields = array();
+		$result = db2_columns($this->connection, '', '', strtoupper($this->fullTableName($model)));
+
+		while (db2_fetch_row($result)) {
+			$fields[strtolower(db2_result($result, 'COLUMN_NAME'))] = array(
+				'type' => $this->column(strtolower(db2_result($result, 'TYPE_NAME'))),
+				'null' => db2_result($result, 'NULLABLE'),
+				'default' => db2_result($result, 'COLUMN_DEF'),
+				'length' => db2_result($result, 'COLUMN_SIZE')
+			);
+		}
+		$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted name of $data for use in an SQL statement.
+ *
+ * @param string $data Name (table.field) to be prepared for use in an SQL statement
+ * @return string Quoted for MySQL
+ */
+	function name($data) {
+		return $data;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @return string Quoted and escaped
+ * @todo Add logic that formats/escapes data based on column type
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+
+		if ($data === '') {
+			return  "''";
+		}
+
+		switch ($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			case 'integer':
+				$data = intval($data);
+			break;
+			default:
+				$data = str_replace("'", "''", $data);
+			break;
+		}
+
+		if ($column == 'integer' || $column == 'float') {
+			return $data;
+		}
+		return "'" . $data . "'";
+	}
+/**
+ * Not sure about this one, MySQL needs it but does ODBC?  Safer just to leave it
+ * Translates between PHP boolean values and MySQL (faked) boolean values
+ *
+ * @param mixed $data Value to be translated
+ * @return mixed Converted boolean value
+ */
+	function boolean($data) {
+		if ($data === true || $data === false) {
+			if ($data === true) {
+				return 1;
+			}
+			return 0;
+		} else {
+			if (intval($data !== 0)) {
+				return true;
+			}
+			return false;
+		}
+	}
+/**
+ * Begins a transaction.  Returns true if the transaction was
+ * started successfully, otherwise false.
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions).
+ */
+	function begin(&$model) {
+		if (parent::begin($model)) {
+			if (db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF)) {
+				$this->_transactionStarted = true;
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Commit a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function commit(&$model) {
+		if (parent::commit($model)) {
+			if (db2_commit($this->connection)) {
+				$this->_transactionStarted = false;
+				db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON);
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function rollback(&$model) {
+		if (parent::rollback($model)) {
+			$this->_transactionStarted = false;
+			db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON);
+			return db2_rollback($this->connection);
+		}
+		return false;
+	}
+/**
+ * Removes Identity (primary key) column from update data before returning to parent
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @return array
+ */
+	function update(&$model, $fields = array(), $values = array()) {
+		foreach ($fields as $i => $field) {
+			if ($field == $model->primaryKey) {
+				unset ($fields[$i]);
+				unset ($values[$i]);
+				break;
+			}
+		}
+		return parent::update($model, $fields, $values);
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ * DB2 distinguishes between statement and connnection errors so we
+ * must check for both.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		if (db2_stmt_error()) {
+			return db2_stmt_error() . ': ' . db2_stmt_errormsg();
+		} elseif (db2_conn_error()) {
+			return db2_conn_error() . ': ' . db2_conn_errormsg();
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return db2_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->_result) {
+			return db2_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		$data = $this->fetchRow(sprintf('SELECT SYSIBM.IDENTITY_VAL_LOCAL() AS ID FROM %s FETCH FIRST ROW ONLY', $source));
+
+		if ($data && isset($data[0]['id'])) {
+			return $data[0]['id'];
+		}
+		return null;
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+
+			// If limit is not in the passed value already, add a limit clause.
+			if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
+				$rt = sprintf('FETCH FIRST %d ROWS ONLY', $limit);
+			}
+
+			// TODO: Implement paging with the offset.  This could get hairy.
+			/*
+ 			WITH WHOLE AS
+			(SELECT FIRSTNME, MIDINIT, LASTNAME, SALARY,
+			ROW_NUMBER() OVER (ORDER BY SALARY DESC) AS RN
+			FROM EMPLOYEE)
+			SELECT FIRSTNME, MIDINIT, LASTNAME, SALARY, RN
+			FROM WHOLE
+			WHERE RN BETWEEN 10 AND 15
+			*/
+
+			/*
+			if ($offset) {
+				$rt .= ' ' . $offset . ',';
+			}
+
+			$rt .= ' ' . $limit;
+			*/
+
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+
+			if (isset($real['limit'])) {
+				$col .= '(' . $real['limit'] . ')';
+			}
+			return $col;
+		}
+		$col                = str_replace(')', '', $real);
+		$limit              = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
+			return $col;
+		}
+
+		if ($col == 'smallint') {
+			return 'boolean';
+		}
+
+		if (strpos($col, 'char') !== false) {
+			return 'string';
+		}
+
+		if (strpos($col, 'clob') !== false) {
+			return 'text';
+		}
+
+		if (strpos($col, 'blob') !== false || $col == 'image') {
+			return 'binary';
+		}
+
+		if (in_array($col, array('double', 'real', 'decimal'))) {
+			return 'float';
+		}
+		return 'text';
+	}
+/**
+ * Maps a result set to an array so that returned fields are
+ * grouped by model.  Any calculated fields, or fields that
+ * do not correspond to a particular model belong under array
+ * key 0.
+ *
+ * 1.  Gets the column headers
+ *
+ * Post.id
+ * Post.title
+ *
+ *  [0] => Array
+ *       (
+ *           [0] => Post
+ *           [1] => id
+ *       )
+ *
+ *  [1] => Array
+ *      (
+ *          [0] => Post
+ *          [1] => title
+ *      )
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results, $sql = null) {
+		$this->results =& $results;
+		$this->map = $this->_resultMap[$this->results];
+	}
+/**
+ * Fetches the next row from the current result set
+ * Maps the records in the $result property to the map
+ * created in resultSet().
+ *
+ * 2. Gets the actual values.
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = db2_fetch_array($this->results)) {
+			$resultRow = array();
+			$i = 0;
+
+			foreach ($row as $index => $field) {
+				$table = $this->map[$index][0];
+				$column = strtolower($this->map[$index][1]);
+				$resultRow[$table][$column] = $row[$index];
+				$i++;
+			}
+			return $resultRow;
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_odbc.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_odbc.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_odbc.php	(revision 0)
@@ -0,0 +1,360 @@
+<?php
+/* SVN FILE: $Id: dbo_odbc.php 7945 2008-12-19 02:16:01Z gwoo $ */
+
+/**
+ * ODBC for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+
+/**
+ * Short description for class.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboOdbc extends DboSource {
+/**
+ * Driver description
+ *
+ * @var string
+ */
+	var $description = "ODBC DBO Driver";
+/**
+ * Table/column starting quote
+ *
+ * @var string
+ */
+	var $startQuote = "`";
+/**
+ * Table/column end quote
+ *
+ * @var string
+ */
+	var $endQuote = "`";
+/**
+ * Driver base configuration
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'login' => 'root',
+		'password' => '',
+		'database' => 'cake',
+		'connect'  => 'odbc_pconnect'
+	);
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $columns = array();
+
+	//	var $columns = array('primary_key' => array('name' => 'int(11) DEFAULT NULL auto_increment'),
+	//						'string' => array('name' => 'varchar', 'limit' => '255'),
+	//						'text' => array('name' => 'text'),
+	//						'integer' => array('name' => 'int', 'limit' => '11'),
+	//						'float' => array('name' => 'float'),
+	//						'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d h:i:s', 'formatter' => 'date'),
+	//						'timestamp' => array('name' => 'datetime', 'format' => 'Y-m-d h:i:s', 'formatter' => 'date'),
+	//						'time' => array('name' => 'time', 'format' => 'h:i:s', 'formatter' => 'date'),
+	//						'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
+	//						'binary' => array('name' => 'blob'),
+	//						'boolean' => array('name' => 'tinyint', 'limit' => '1'));
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$connect = $config['connect'];
+		if (!$config['persistent']) {
+			$connect = 'odbc_connect';
+		}
+		if (!function_exists($connect)) {
+			die('no odbc?');
+		}
+		$this->connected = false;
+		$this->connection = $connect($config['database'], $config['login'], $config['password'],  SQL_CUR_USE_ODBC);
+		if ($this->connection) {
+			$this->connected = true;
+		}
+
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		return @odbc_close($this->connection);
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		switch ($sql) {
+			case 'BEGIN':
+				return odbc_autocommit($this->connection, false);
+			case 'COMMIT':
+				return odbc_commit($this->connection);
+			case 'ROLLBACK':
+				return odbc_rollback($this->connection);
+		}
+		// TODO: should flags be set? possible requirement:  SQL_CURSOR_STATIC
+		return odbc_exec($this->connection, $sql);
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$result = odbc_tables($this->connection);
+
+		$tables = array();
+		while (odbc_fetch_row($result)) {
+			array_push($tables, odbc_result($result, 'TABLE_NAME'));
+		}
+
+		parent::listSources($tables);
+		return $tables;
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param Model $model Model object to describe
+ * @return array Fields in table. Keys are name and type
+ */
+	function &describe(&$model) {
+		$cache=parent::describe($model);
+
+		if ($cache != null) {
+				return $cache;
+		}
+
+		$fields = array();
+		$sql = 'SELECT * FROM ' . $this->fullTableName($model);
+		$result = odbc_exec($this->connection, $sql);
+
+		$count = odbc_num_fields($result);
+
+		for ($i = 1; $i <= $count; $i++) {
+				$cols[$i - 1] = odbc_field_name($result, $i);
+		}
+
+		foreach ($cols as $column) {
+			$type = odbc_field_type(odbc_exec($this->connection, 'SELECT ' . $column . ' FROM ' . $this->fullTableName($model)), 1);
+			$fields[$column] = array('type' => $type);
+		}
+
+		$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @return string Quoted and escaped
+ * @todo Add logic that formats/escapes data based on column type
+ */
+	function value($data, $column = null) {
+		$parent=parent::value($data, $column);
+
+		if ($parent != null) {
+				return $parent;
+		}
+
+		if ($data === null) {
+				return 'NULL';
+		}
+
+		if (!is_numeric($data)) {
+				return "'" . $data . "'";
+		}
+
+		return $data;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		if ($error = odbc_errormsg($this->connection)) {
+			return odbc_error($this->connection) . ': ' . $error;
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->hasResult()) {
+			return odbc_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return int Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->hasResult()) {
+			return odbc_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return int
+ */
+	function lastInsertId($source = null) {
+		$result = $this->fetchRow('SELECT @@IDENTITY');
+		return $result[0];
+	}
+/**
+ * Enter description here...
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col=$real['name'];
+			if (isset($real['limit'])) {
+				$col .= '(' . $real['limit'] . ')';
+			}
+			return $col;
+		}
+		return $real;
+	}
+/**
+* Enter description here...
+*
+* @param unknown_type $results
+*/
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$num_fields = odbc_num_fields($results);
+		$this->map = array();
+		$index = 0;
+		$j = 0;
+		while ($j < $num_fields) {
+			$column = odbc_field_name($results, $j+1);
+
+			if (strpos($column, '_dot_') !== false) {
+				list($table, $column) = explode('_dot_', $column);
+				$this->map[$index++] = array($table, $column);
+			} else {
+				$this->map[$index++] = array(0, $column);
+			}
+			$j++;
+		}
+	}
+/**
+* Generates the fields list of an SQL query.
+*
+* @param Model $model
+* @param string $alias Alias tablename
+* @param mixed $fields
+* @return array
+*/
+	function fields(&$model, $alias = null, $fields = null, $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->name;
+		}
+		if (!is_array($fields)) {
+			if ($fields != null) {
+				$fields = array_map('trim', explode(',', $fields));
+			} else {
+				foreach($model->tableToModel as $tableName => $modelName) {
+					foreach($this->__descriptions[$model->tablePrefix .$tableName] as $field => $type) {
+						$fields[] = $modelName .'.' .$field;
+					}
+				}
+			}
+		}
+
+		$count = count($fields);
+
+		if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
+			for ($i = 0; $i < $count; $i++) {
+				if (!preg_match('/^.+\\(.*\\)/', $fields[$i])) {
+					$prepend = '';
+					if (strpos($fields[$i], 'DISTINCT') !== false) {
+						$prepend = 'DISTINCT ';
+						$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
+					}
+
+					if (strrpos($fields[$i], '.') === false) {
+						$fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '_dot_' . $fields[$i]);
+					} else {
+						$build = explode('.', $fields[$i]);
+						$fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '_dot_' . $build[1]);
+					}
+				}
+			}
+		}
+		return $fields;
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = odbc_fetch_row($this->results)) {
+			$resultRow = array();
+			$numFields = odbc_num_fields($this->results);
+			$i = 0;
+			for($i = 0; $i < $numFields; $i++) {
+				list($table, $column) = $this->map[$i];
+				$resultRow[$table][$column] = odbc_result($this->results, $i + 1);
+			}
+			return $resultRow;
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_adodb.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_adodb.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_adodb.php	(revision 0)
@@ -0,0 +1,516 @@
+<?php
+/* SVN FILE: $Id: dbo_adodb.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * AdoDB layer for DBO.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Include AdoDB files.
+ */
+App::import('Vendor', 'NewADOConnection', array('file' => 'adodb' . DS . 'adodb.inc.php'));
+/**
+ * AdoDB DBO implementation.
+ *
+ * Database abstraction implementation for the AdoDB library.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboAdodb extends DboSource {
+/**
+ * Enter description here...
+ *
+ * @var string
+ */
+	var $description = "ADOdb DBO Driver";
+/**
+ * ADOConnection object with which we connect.
+ *
+ * @var ADOConnection The connection object.
+ * @access private
+ */
+	var $_adodb = null;
+/**
+ * Array translating ADOdb column MetaTypes to cake-supported metatypes
+ *
+ * @var array
+ * @access private
+ */
+	var $_adodbColumnTypes = array(
+		'string' => 'C',
+		'text' => 'X',
+		'date' => 'D',
+		'timestamp' => 'T',
+		'time' => 'T',
+		'datetime' => 'T',
+		'boolean' => 'L',
+		'float' => 'N',
+		'integer' => 'I',
+		'binary' => 'R',
+	);
+/**
+ * ADOdb column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'R', 'limit' => 11),
+		'string' => array('name' => 'C', 'limit' => '255'),
+		'text' => array('name' => 'X'),
+		'integer' => array('name' => 'I', 'limit' => '11', 'formatter' => 'intval'),
+		'float' => array('name' => 'N', 'formatter' => 'floatval'),
+		'timestamp' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time' => array('name' => 'T',  'format' => 'H:i:s', 'formatter' => 'date'),
+		'datetime' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'date' => array('name' => 'D', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' => array('name' => 'B'),
+		'boolean' => array('name' => 'L', 'limit' => '1')
+	);
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @param array $config Configuration array for connecting
+ */
+	function connect() {
+		$config = $this->config;
+		$persistent = strrpos($config['connect'], '|p');
+
+		if ($persistent === false) {
+			$adodb_driver = $config['connect'];
+			$connect = 'Connect';
+		} else {
+			$adodb_driver = substr($config['connect'], 0, $persistent);
+			$connect = 'PConnect';
+		}
+
+		$this->_adodb = NewADOConnection($adodb_driver);
+
+		$this->_adodbDataDict = NewDataDictionary($this->_adodb, $adodb_driver);
+
+		$this->startQuote = $this->_adodb->nameQuote;
+		$this->endQuote = $this->_adodb->nameQuote;
+
+		$this->connected = $this->_adodb->$connect($config['host'], $config['login'], $config['password'], $config['database']);
+		$this->_adodbMetatyper = &$this->_adodb->execute('Select 1');
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		return $this->_adodb->Close();
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ */
+	function _execute($sql) {
+		global $ADODB_FETCH_MODE;
+		$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+		return $this->_adodb->execute($sql);
+	}
+/**
+ * Returns a row from current resultset as an array .
+ *
+ * @return array The fetched row as an array
+ */
+	function fetchRow($sql = null) {
+		if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
+			if (!$this->execute($sql)) {
+				return null;
+			}
+		}
+
+		if (!$this->hasResult()) {
+			return null;
+		} else {
+			$resultRow = $this->_result->FetchRow();
+			$this->resultSet($resultRow);
+			return $this->fetchResult();
+		}
+	}
+/**
+ * Begin a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions).
+ */
+	function begin(&$model) {
+		if (parent::begin($model)) {
+			if ($this->_adodb->BeginTrans()) {
+				$this->_transactionStarted = true;
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Commit a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function commit(&$model) {
+		if (parent::commit($model)) {
+			$this->_transactionStarted = false;
+			return $this->_adodb->CommitTrans();
+		}
+		return false;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function rollback(&$model) {
+		if (parent::rollback($model)) {
+			return $this->_adodb->RollbackTrans();
+		}
+		return false;
+	}
+/**
+ * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$tables = $this->_adodb->MetaTables('TABLES');
+
+		if (!sizeof($tables) > 0) {
+			trigger_error(ERROR_NO_TABLE_LIST, E_USER_NOTICE);
+			exit;
+		}
+		return $tables;
+	}
+/**
+ * Returns an array of the fields in the table used by the given model.
+ *
+ * @param AppModel $model Model object
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+		$cache = parent::describe($model);
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$fields = false;
+		$cols = $this->_adodb->MetaColumns($this->fullTableName($model, false));
+
+		foreach ($cols as $column) {
+			$fields[$column->name] = array(
+										'type' => $this->column($column->type),
+										'null' => !$column->not_null,
+										'length' => $column->max_length,
+									);
+			if ($column->has_default) {
+				$fields[$column->name]['default'] = $column->default_value;
+			}
+			if ($column->primary_key == 1) {
+				$fields[$column->name]['key'] = 'primary';
+			}
+		}
+
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message
+ */
+	function lastError() {
+		return $this->_adodb->ErrorMsg();
+	}
+/**
+ * Returns number of affected rows in previous database operation, or false if no previous operation exists.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		return $this->_adodb->Affected_Rows();
+	}
+/**
+ * Returns number of rows in previous resultset, or false if no previous resultset exists.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		return $this->_result ? $this->_result->RecordCount() : false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @return int
+ *
+ * @Returns the last autonumbering ID inserted. Returns false if function not supported.
+ */
+	function lastInsertId() {
+		return $this->_adodb->Insert_ID();
+	}
+/**
+ * Returns a LIMIT statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ * @todo Please change output string to whatever select your database accepts. adodb doesn't allow us to get the correct limit string out of it.
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+			if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
+				$rt = ' LIMIT';
+			}
+
+			if ($offset) {
+				$rt .= ' ' . $offset . ',';
+			}
+
+			$rt .= ' ' . $limit;
+			return $rt;
+		}
+		return null;
+		// please change to whatever select your database accepts
+		// adodb doesn't allow us to get the correct limit string out of it
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		$metaTypes = array_flip($this->_adodbColumnTypes);
+
+		$interpreted_type = $this->_adodbMetatyper->MetaType($real);
+
+		if (!isset($metaTypes[$interpreted_type])) {
+			return 'text';
+		}
+		return $metaTypes[$interpreted_type];
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column_type The type of the column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+
+		if ($data === '') {
+			return "''";
+		}
+		return $this->_adodb->qstr($data);
+	}
+
+/**
+ * Generates the fields list of an SQL query.
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields
+ * @return array
+ */
+	function fields(&$model, $alias = null, $fields = array(), $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->alias;
+		}
+		$fields = parent::fields($model, $alias, $fields, false);
+
+		if (!$quote) {
+			return $fields;
+		}
+		$count = count($fields);
+
+		if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
+			for ($i = 0; $i < $count; $i++) {
+				if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
+					$prepend = '';
+					if (strpos($fields[$i], 'DISTINCT') !== false) {
+						$prepend = 'DISTINCT ';
+						$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
+					}
+
+					if (strrpos($fields[$i], '.') === false) {
+						$fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
+					} else {
+						$build = explode('.', $fields[$i]);
+						$fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
+					}
+				}
+			}
+		}
+		return $fields;
+	}
+/**
+ * Build ResultSets and map data
+ *
+ * @param array $results
+ */
+	function resultSet(&$results) {
+		$num_fields = count($results);
+		$fields = array_keys($results);
+		$this->results =& $results;
+		$this->map = array();
+		$index = 0;
+		$j = 0;
+
+		while ($j < $num_fields) {
+			$columnName = $fields[$j];
+
+			if (strpos($columnName, '__')) {
+				$parts = explode('__', $columnName);
+				$this->map[$index++] = array($parts[0], $parts[1]);
+			} else {
+				$this->map[$index++] = array(0, $columnName);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if (!empty($this->results)) {
+			$row = $this->results;
+			$this->results = null;
+		} else {
+			$row = $this->_result->FetchRow();
+		}
+
+		if (empty($row)) {
+			return false;
+		}
+
+		$resultRow = array();
+		$fields = array_keys($row);
+		$count = count($fields);
+		$i = 0;
+		for ($i = 0; $i < $count; $i++) { //$row as $index => $field) {
+			list($table, $column) = $this->map[$i];
+			$resultRow[$table][$column] = $row[$fields[$i]];
+		}
+		return $resultRow;
+	}
+/**
+ * Generate a database-native column schema string
+ *
+ * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
+ *                      where options can be 'default', 'length', or 'key'.
+ * @return string
+ */
+	function buildColumn($column) {
+		$name = $type = null;
+		extract(array_merge(array('null' => true), $column));
+
+		if (empty($name) || empty($type)) {
+			trigger_error('Column name or type not defined in schema', E_USER_WARNING);
+			return null;
+		}
+
+		//$metaTypes = array_flip($this->_adodbColumnTypes);
+		if (!isset($this->_adodbColumnTypes[$type])) {
+			trigger_error("Column type {$type} does not exist", E_USER_WARNING);
+			return null;
+		}
+		$metaType = $this->_adodbColumnTypes[$type];
+		$concreteType = $this->_adodbDataDict->ActualType($metaType);
+		$real = $this->columns[$type];
+
+		//UUIDs are broken so fix them.
+		if ($type == 'string' && isset($real['length']) && $real['length'] == 36) {
+			$concreteType = 'CHAR';
+		}
+
+		$out = $this->name($name) . ' ' . $concreteType;
+
+		if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
+			if (isset($column['length'])) {
+				$length = $column['length'];
+			} elseif (isset($column['limit'])) {
+				$length = $column['limit'];
+			} elseif (isset($real['length'])) {
+				$length = $real['length'];
+			} else {
+				$length = $real['limit'];
+			}
+			$out .= '(' . $length . ')';
+		}
+		$_notNull = $_default = $_autoInc = $_constraint = $_unsigned = false;
+
+		if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
+			$_constraint = '';
+			$_autoInc = true;
+		} elseif (isset($column['key']) && $column['key'] == 'primary') {
+			$_notNull = '';
+		} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
+			$_notNull = true;
+			$_default = $column['default'];
+		} elseif ( isset($column['null']) && $column['null'] == true) {
+			$_notNull = false;
+			$_default = 'NULL';
+		}
+		if (isset($column['default']) && $_default == false) {
+			$_default = $this->value($column['default']);
+		}
+		if (isset($column['null']) && $column['null'] == false) {
+			$_notNull = true;
+		}
+		//use concrete instance of DataDict to make the suffixes for us.
+		$out .=	$this->_adodbDataDict->_CreateSuffix($out, $metaType, $_notNull, $_default, $_autoInc, $_constraint, $_unsigned);
+		return $out;
+
+	}
+/**
+ * Checks if the result is valid
+ *
+ * @return boolean True if the result is valid, else false
+ */
+	function hasResult() {
+		return is_object($this->_result) && !$this->_result->EOF;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_mysqli.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_mysqli.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_mysqli.php	(revision 0)
@@ -0,0 +1,427 @@
+<?php
+/* SVN FILE: $Id: dbo_mysqli.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * MySQLi layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 1.1.4.2974
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'DboMysql');
+/**
+ * MySQLi DBO driver object
+ *
+ * Provides connection and SQL generation for MySQL RDMS using PHP's MySQLi Interface
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboMysqli extends DboMysqlBase {
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $description = "Mysqli DBO Driver";
+/**
+ * Base configuration settings for Mysqli driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'root',
+		'password' => '',
+		'database' => 'cake',
+		'port' => '3306',
+		'connect' => 'mysqli_connect'
+	);
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+		$this->connected = false;
+
+		if (is_numeric($config['port'])) {
+			$config['socket'] = null;
+		} else {
+			$config['socket'] = $config['port'];
+			$config['port'] = null;
+		}
+
+		$this->connection = mysqli_connect($config['host'], $config['login'], $config['password'], $config['database'], $config['port'], $config['socket']);
+
+		if ($this->connection !== false) {
+			$this->connected = true;
+		}
+		
+		$this->_useAlias = (bool)version_compare(mysqli_get_server_info($this->connection), "4.1", ">=");
+		
+		if (!empty($config['encoding'])) {
+			$this->setEncoding($config['encoding']);
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		if (isset($this->results) && is_resource($this->results)) {
+			mysqli_free_result($this->results);
+		}
+		$this->connected = !@mysqli_close($this->connection);
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		if (preg_match('/^\s*call/i', $sql)) {
+			return $this->_executeProcedure($sql);
+		} else {
+			return mysqli_query($this->connection, $sql);
+		}
+	}
+/**
+ * Executes given SQL statement (procedure call).
+ *
+ * @param string $sql SQL statement (procedure call)
+ * @return resource Result resource identifier for first recordset
+ * @access protected
+ */
+	function _executeProcedure($sql) {
+		$answer = mysqli_multi_query($this->connection, $sql);
+
+		$firstResult = mysqli_store_result($this->connection);
+
+		if (mysqli_more_results($this->connection)) {
+			while ($lastResult = mysqli_next_result($this->connection));
+		}
+		return $firstResult;
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+		if ($cache != null) {
+			return $cache;
+		}
+		$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
+
+		if (!$result) {
+			return array();
+		} else {
+			$tables = array();
+
+			while ($line = mysqli_fetch_array($result)) {
+				$tables[] = $line[0];
+			}
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param string $tableName Name of database table to inspect
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+
+		$cache = parent::describe($model);
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$fields = false;
+		$cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
+
+		foreach ($cols as $column) {
+			$colKey = array_keys($column);
+			if (isset($column[$colKey[0]]) && !isset($column[0])) {
+				$column[0] = $column[$colKey[0]];
+			}
+			if (isset($column[0])) {
+				$fields[$column[0]['Field']] = array(
+					'type'		=> $this->column($column[0]['Type']),
+					'null'		=> ($column[0]['Null'] == 'YES' ? true : false),
+					'default'	=> $column[0]['Default'],
+					'length'	=> $this->length($column[0]['Type'])
+				);
+				if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
+					$fields[$column[0]['Field']]['key']	= $this->index[$column[0]['Key']];
+				}
+			}
+		}
+
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+
+		if ($data === null) {
+			return 'NULL';
+		}
+
+		if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
+			return  "''";
+		}
+
+		switch ($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			case 'integer' :
+			case 'float' :
+			case null :
+				if ($data === '') {
+					return 'NULL';
+				}
+				if ((is_int($data) || is_float($data) || $data === '0') || (
+					is_numeric($data) && strpos($data, ',') === false &&
+					$data[0] != '0' && strpos($data, 'e') === false)) {
+						return $data;
+					}
+			default:
+				$data = "'" . mysqli_real_escape_string($this->connection, $data) . "'";
+			break;
+		}
+
+		return $data;
+	}
+/**
+ * Begin a transaction
+ *
+ * @param unknown_type $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions).
+ */
+	function begin(&$model) {
+		if (parent::begin($model) && $this->execute('START TRANSACTION')) {
+			$this->_transactionStarted = true;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		if (mysqli_errno($this->connection)) {
+			return mysqli_errno($this->connection).': '.mysqli_error($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return mysqli_affected_rows($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->hasResult()) {
+			return mysqli_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
+		if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
+			return $id[0]['insertID'];
+		}
+
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+			if (isset($real['limit'])) {
+				$col .= '('.$real['limit'].')';
+			}
+			return $col;
+		}
+
+		$col = str_replace(')', '', $real);
+		$limit = $this->length($real);
+		if (strpos($col, '(') !== false) {
+			list($col, $vals) = explode('(', $col);
+		}
+
+		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
+			return $col;
+		}
+		if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
+			return 'boolean';
+		}
+		if (strpos($col, 'int') !== false) {
+			return 'integer';
+		}
+		if (strpos($col, 'char') !== false || $col == 'tinytext') {
+			return 'string';
+		}
+		if (strpos($col, 'text') !== false) {
+			return 'text';
+		}
+		if (strpos($col, 'blob') !== false || $col == 'binary') {
+			return 'binary';
+		}
+		if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
+			return 'float';
+		}
+		if (strpos($col, 'enum') !== false) {
+			return "enum($vals)";
+		}
+		return 'text';
+	}
+/**
+ * Gets the length of a database-native column description, or null if no length
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return integer An integer representing the length of the column
+ */
+	function length($real) {
+		$col = str_replace(array(')', 'unsigned'), '', $real);
+		$limit = null;
+
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if ($limit != null) {
+			return intval($limit);
+		}
+		return null;
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
+			mysqli_free_result($this->results);
+		}
+		$this->results =& $results;
+		$this->map = array();
+		$numFields = mysqli_num_fields($results);
+		$index = 0;
+		$j = 0;
+		while ($j < $numFields) {
+			$column = mysqli_fetch_field_direct($results, $j);
+			if (!empty($column->table)) {
+				$this->map[$index++] = array($column->table, $column->name);
+			} else {
+				$this->map[$index++] = array(0, $column->name);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = mysqli_fetch_row($this->results)) {
+			$resultRow = array();
+			$i = 0;
+			foreach ($row as $index => $field) {
+				$table = $column = null;
+				if (count($this->map[$index]) == 2) {
+					list($table, $column) = $this->map[$index];
+				}
+				$resultRow[$table][$column] = $row[$index];
+				$i++;
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Gets the database encoding
+ *
+ * @return string The database encoding
+ */
+	function getEncoding() {
+		return mysqli_client_encoding($this->connection);
+	}
+/**
+ * Checks if the result is valid
+ *
+ * @return boolean True if the result is valid, else false
+ */
+	function hasResult() {
+		return is_object($this->_result);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_mssql.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_mssql.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_mssql.php	(revision 0)
@@ -0,0 +1,684 @@
+<?php
+/* SVN FILE: $Id: dbo_mssql.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * MS SQL layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for class.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboMssql extends DboSource {
+/**
+ * Driver description
+ *
+ * @var string
+ */
+	var $description = "MS SQL DBO Driver";
+/**
+ * Starting quote character for quoted identifiers
+ *
+ * @var string
+ */
+	var $startQuote = "[";
+/**
+ * Ending quote character for quoted identifiers
+ *
+ * @var string
+ */
+	var $endQuote = "]";
+/**
+ * Creates a map between field aliases and numeric indexes.  Workaround for the
+ * SQL Server driver's 30-character column name limitation.
+ *
+ * @var array
+ */
+	var $__fieldMappings = array();
+/**
+ * Base configuration settings for MS SQL driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'host' => 'localhost',
+		'login' => 'root',
+		'password' => '',
+		'database' => 'cake',
+		'port' => '1433',
+	);
+/**
+ * MS SQL column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
+		'string'	=> array('name' => 'varchar', 'limit' => '255'),
+		'text'		=> array('name' => 'text'),
+		'integer'	=> array('name' => 'int', 'formatter' => 'intval'),
+		'float'		=> array('name' => 'numeric', 'formatter' => 'floatval'),
+		'datetime'	=> array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time'		=> array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
+		'date'		=> array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary'	=> array('name' => 'image'),
+		'boolean'	=> array('name' => 'bit')
+	);
+/**
+ * Index of basic SQL commands
+ *
+ * @var array
+ * @access protected
+ */
+	var $_commands = array(
+		'begin'    => 'BEGIN TRANSACTION',
+		'commit'   => 'COMMIT',
+		'rollback' => 'ROLLBACK'
+	);
+/**
+ * MS SQL DBO driver constructor; sets SQL Server error reporting defaults
+ *
+ * @param array $config Configuration data from app/config/databases.php
+ * @return boolean True if connected successfully, false on error
+ */
+	function __construct($config, $autoConnect = true) {
+		if ($autoConnect) {
+			if (!function_exists('mssql_min_message_severity')) {
+				trigger_error("PHP SQL Server interface is not installed, cannot continue.  For troubleshooting information, see http://php.net/mssql/", E_USER_WARNING);
+			}
+			mssql_min_message_severity(15);
+			mssql_min_error_severity(2);
+		}
+		return parent::__construct($config, $autoConnect);
+	}
+/**
+ * Connects to the database using options in the given configuration array.
+ *
+ * @return boolean True if the database could be connected, else false
+ */
+	function connect() {
+		$config = $this->config;
+
+		$os = env('OS');
+		if (!empty($os) && strpos($os, 'Windows') !== false) {
+			$sep = ',';
+		} else {
+			$sep = ':';
+		}
+		$this->connected = false;
+
+		if (is_numeric($config['port'])) {
+			$port = $sep . $config['port'];	// Port number
+		} elseif ($config['port'] === null) {
+			$port = '';						// No port - SQL Server 2005
+		} else {
+			$port = '\\' . $config['port'];	// Named pipe
+		}
+
+		if (!$config['persistent']) {
+			$this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true);
+		} else {
+			$this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']);
+		}
+
+		if (mssql_select_db($config['database'], $this->connection)) {
+			$this->_execute("SET DATEFORMAT ymd");
+			$this->connected = true;
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		@mssql_free_result($this->results);
+		$this->connected = !@mssql_close($this->connection);
+		return !$this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ * @access protected
+ */
+	function _execute($sql) {
+		return mssql_query($sql, $this->connection);
+	}
+/**
+ * Returns an array of sources (tables) in the database.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES', false);
+
+		if (!$result || empty($result)) {
+			return array();
+		} else {
+			$tables = array();
+
+			foreach ($result as $table) {
+				$tables[] = $table[0]['TABLE_NAME'];
+			}
+
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param Model $model Model object to describe
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+		$cache = parent::describe($model);
+
+		if ($cache != null) {
+			return $cache;
+		}
+
+		$fields = false;
+		$cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $this->fullTableName($model, false) . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $this->fullTableName($model, false) . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $this->fullTableName($model, false) . "'", false);
+
+		foreach ($cols as $column) {
+			$field = $column[0]['Field'];
+			$fields[$field] = array(
+				'type' => $this->column($column[0]['Type']),
+				'null' => (strtoupper($column[0]['Null']) == 'YES'),
+				'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column[0]['Default']),
+				'length' => intval($column[0]['Length']),
+				'key'	=> ($column[0]['Key'] == '1')
+			);
+			if ($fields[$field]['default'] === 'null') {
+				$fields[$field]['default'] = null;
+			} else {
+				$this->value($fields[$field]['default'], $fields[$field]['type']);
+			}
+
+			if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') {
+				$fields[$field]['length'] = 11;
+			} elseif (!$fields[$field]['key']) {
+				unset($fields[$field]['key']);
+			}
+			if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) {
+				$fields[$field]['length'] = null;
+			}
+		}
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
+ * @return string Quoted and escaped data
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+		if ($data === null) {
+			return 'NULL';
+		}
+		if ($data === '') {
+			return "''";
+		}
+
+		switch ($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			default:
+				if (get_magic_quotes_gpc()) {
+					$data = stripslashes(str_replace("'", "''", $data));
+				} else {
+					$data = str_replace("'", "''", $data);
+				}
+			break;
+		}
+
+		if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) {
+			return $data;
+		}
+		return "'" . $data . "'";
+	}
+/**
+ * Generates the fields list of an SQL query.
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields
+ * @return array
+ */
+	function fields(&$model, $alias = null, $fields = array(), $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->alias;
+		}
+		$fields = parent::fields($model, $alias, $fields, false);
+		$count = count($fields);
+
+		if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
+			for ($i = 0; $i < $count; $i++) {
+				$prepend = '';
+
+				if (strpos($fields[$i], 'DISTINCT') !== false) {
+					$prepend = 'DISTINCT ';
+					$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
+				}
+				$fieldAlias = count($this->__fieldMappings);
+
+				if (!preg_match('/\s+AS\s+/i', $fields[$i])) {
+					if (strpos($fields[$i], '.') === false) {
+						$this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i];
+						$fieldName  = $this->name($alias . '.' . $fields[$i]);
+						$fieldAlias = $this->name($alias . '__' . $fieldAlias);
+					} else {
+						$build = explode('.', $fields[$i]);
+						$this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i];
+						$fieldName  = $this->name($build[0] . '.' . $build[1]);
+						$fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias);
+					}
+					if ($model->getColumnType($fields[$i]) == 'datetime') {
+						$fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)";
+					}
+					$fields[$i] =  "{$fieldName} AS {$fieldAlias}";
+				}
+				$fields[$i] = $prepend . $fields[$i];
+			}
+		}
+		return $fields;
+	}
+/**
+ * Generates and executes an SQL INSERT statement for given model, fields, and values.
+ * Removes Identity (primary key) column from update data before returning to parent, if
+ * value is empty.
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @param mixed $conditions
+ * @return array
+ */
+	function create(&$model, $fields = null, $values = null) {
+		if (!empty($values)) {
+			$fields = array_combine($fields, $values);
+		}
+
+		if (array_key_exists($model->primaryKey, $fields)) {
+			if (empty($fields[$model->primaryKey])) {
+				unset($fields[$model->primaryKey]);
+			} else {
+				$this->_execute("SET IDENTITY_INSERT " . $this->fullTableName($model) . " ON");
+			}
+		}
+		$result = parent::create($model, array_keys($fields), array_values($fields));
+		if (array_key_exists($model->primaryKey, $fields) && !empty($fields[$model->primaryKey])) {
+			$this->_execute("SET IDENTITY_INSERT " . $this->fullTableName($model) . " OFF");
+		}
+		return $result;
+	}
+/**
+ * Generates and executes an SQL UPDATE statement for given model, fields, and values.
+ * Removes Identity (primary key) column from update data before returning to parent.
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @param mixed $conditions
+ * @return array
+ */
+	function update(&$model, $fields = array(), $values = null, $conditions = null) {
+		if (!empty($values)) {
+			$fields = array_combine($fields, $values);
+		}
+		if (isset($fields[$model->primaryKey])) {
+			unset($fields[$model->primaryKey]);
+		}
+		return parent::update($model, array_keys($fields), array_values($fields), $conditions);
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message with error number
+ */
+	function lastError() {
+		$error = mssql_get_last_message($this->connection);
+
+		if ($error) {
+			if (!preg_match('/contexto de la base de datos a|contesto di database|changed database/i', $error)) {
+				return $error;
+			}
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists,
+ * this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if ($this->_result) {
+			return mssql_rows_affected($this->connection);
+		}
+		return null;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->_result) {
+			return @mssql_num_rows($this->_result);
+		}
+		return null;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return in
+ */
+	function lastInsertId($source = null) {
+		$id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false);
+		return $id[0]['insertID'];
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+			if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
+				$rt = ' TOP';
+			}
+			$rt .= ' ' . $limit;
+			if (is_int($offset) && $offset > 0) {
+				$rt .= ' OFFSET ' . $offset;
+			}
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+
+			if (isset($real['limit'])) {
+				$col .= '(' . $real['limit'] . ')';
+			}
+			return $col;
+		}
+		$col                = str_replace(')', '', $real);
+		$limit              = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
+			return $col;
+		}
+		if ($col == 'bit') {
+			return 'boolean';
+		}
+		if (strpos($col, 'int') !== false) {
+			return 'integer';
+		}
+		if (strpos($col, 'char') !== false) {
+			return 'string';
+		}
+		if (strpos($col, 'text') !== false) {
+			return 'text';
+		}
+		if (strpos($col, 'binary') !== false || $col == 'image') {
+			return 'binary';
+		}
+		if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) {
+			return 'float';
+		}
+		return 'text';
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$this->map = array();
+		$numFields = mssql_num_fields($results);
+		$index = 0;
+		$j = 0;
+
+		while ($j < $numFields) {
+			$column = mssql_field_name($results, $j);
+
+			if (strpos($column, '__')) {
+				if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) {
+					$map = explode('.', $this->__fieldMappings[$column]);
+				} elseif (isset($this->__fieldMappings[$column])) {
+					$map = array(0, $this->__fieldMappings[$column]);
+				} else {
+					$map = array(0, $column);
+				}
+				$this->map[$index++] = $map;
+			} else {
+				$this->map[$index++] = array(0, $column);
+			}
+			$j++;
+		}
+	}
+/**
+ * Builds final SQL statement
+ *
+ * @param string $type Query type
+ * @param array $data Query data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		switch (strtolower($type)) {
+			case 'select':
+				extract($data);
+				$fields = trim($fields);
+
+				if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) {
+					$limit = 'DISTINCT ' . trim($limit);
+					$fields = substr($fields, 9);
+				}
+
+				if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
+					$limit = preg_replace('/\s*offset.*$/i', '', $limit);
+					preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
+					$offset = intval($offset[1]) + intval($limitVal[1]);
+					$rOrder = $this->__switchSort($order);
+					list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
+					return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
+				} else {
+					return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}";
+				}
+			break;
+			case "schema":
+				extract($data);
+
+				foreach ($indexes as $i => $index) {
+					if (preg_match('/PRIMARY KEY/', $index)) {
+						unset($indexes[$i]);
+						break;
+					}
+				}
+
+				foreach (array('columns', 'indexes') as $var) {
+					if (is_array(${$var})) {
+						${$var} = "\t" . join(",\n\t", array_filter(${$var}));
+					}
+				}
+				return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
+			break;
+			default:
+				return parent::renderStatement($type, $data);
+			break;
+		}
+	}
+/**
+ * Reverses the sort direction of ORDER statements to get paging offsets to work correctly
+ *
+ * @param string $order
+ * @return string
+ * @access private
+ */
+	function __switchSort($order) {
+		$order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order);
+		$order = preg_replace('/\s+DESC/i', ' ASC', $order);
+		return preg_replace('/__tmp_asc__/', ' DESC', $order);
+	}
+/**
+ * Translates field names used for filtering and sorting to shortened names using the field map
+ *
+ * @param string $sql A snippet of SQL representing an ORDER or WHERE statement
+ * @return string The value of $sql with field names replaced
+ * @access private
+ */
+	function __mapFields($sql) {
+		if (empty($sql) || empty($this->__fieldMappings)) {
+			return $sql;
+		}
+		foreach ($this->__fieldMappings as $key => $val) {
+			$sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql);
+			$sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql);
+		}
+		return $sql;
+	}
+/**
+ * Returns an array of all result rows for a given SQL query.
+ * Returns false if no rows matched.
+ *
+ * @param string $sql SQL statement
+ * @param boolean $cache Enables returning/storing cached query results
+ * @return array Array of resultset rows, or false if no rows matched
+ */
+	function read(&$model, $queryData = array(), $recursive = null) {
+		$results = parent::read($model, $queryData, $recursive);
+		$this->__fieldMappings = array();
+		return $results;
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = mssql_fetch_row($this->results)) {
+			$resultRow = array();
+			$i = 0;
+
+			foreach ($row as $index => $field) {
+				list($table, $column) = $this->map[$index];
+				$resultRow[$table][$column] = $row[$index];
+				$i++;
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Generate a database-native column schema string
+ *
+ * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
+ *                      where options can be 'default', 'length', or 'key'.
+ * @return string
+ */
+	function buildColumn($column) {
+		$result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column));
+		$null = (
+			(isset($column['null']) && $column['null'] == true) ||
+			(array_key_exists('default', $column) && $column['default'] === null) ||
+			(array_keys($column) == array('type', 'name'))
+		);
+		$primaryKey = (isset($column['key']) && $column['key'] == 'primary');
+		$stringKey =  ($primaryKey && $column['type'] != 'integer');
+
+		if ($null && !$primaryKey) {
+			$result .= " NULL";
+		}
+		return $result;
+	}
+/**
+ * Format indexes for create table
+ *
+ * @param array $indexes
+ * @param string $table
+ * @return string
+ */
+	function buildIndex($indexes, $table = null) {
+		$join = array();
+
+		foreach ($indexes as $name => $value) {
+			if ($name == 'PRIMARY') {
+				$out = 'PRIMARY KEY  (' . $this->name($value['column']) . ')';
+			} else {
+				$out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
+
+				if (is_array($value['column'])) {
+					$value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
+				} else {
+					$value['column'] = $this->name($value['column']);
+				}
+				$out .= "({$value['column']});";
+			}
+			$join[] = $out;
+		}
+		return $join;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo/dbo_sqlite.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_sqlite.php	(revision 0)
+++ cake/libs/model/datasources/dbo/dbo_sqlite.php	(revision 0)
@@ -0,0 +1,594 @@
+<?php
+/* SVN FILE: $Id: dbo_sqlite.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * SQLite layer for DBO
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.9.0
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * DBO implementation for the SQLite DBMS.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboSqlite extends DboSource {
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ */
+	var $description = "SQLite DBO Driver";
+/**
+ * Opening quote for quoted identifiers
+ *
+ * @var string
+ */
+	var $startQuote = '"';
+/**
+ * Closing quote for quoted identifiers
+ *
+ * @var string
+ */
+	var $endQuote = '"';
+/**
+ * Keeps the transaction statistics of CREATE/UPDATE/DELETE queries
+ *
+ * @var array
+ * @access protected
+ */
+	var $_queryStats = array();
+/**
+ * Base configuration settings for SQLite driver
+ *
+ * @var array
+ */
+	var $_baseConfig = array(
+		'persistent' => true,
+		'database' => null,
+		'connect' => 'sqlite_popen'
+	);
+/**
+ * Index of basic SQL commands
+ *
+ * @var array
+ * @access protected
+ */
+	var $_commands = array(
+		'begin'    => 'BEGIN TRANSACTION',
+		'commit'   => 'COMMIT TRANSACTION',
+		'rollback' => 'ROLLBACK TRANSACTION'
+	);
+/**
+ * SQLite column definition
+ *
+ * @var array
+ */
+	var $columns = array(
+		'primary_key' => array('name' => 'integer primary key'),
+		'string' => array('name' => 'varchar', 'limit' => '255'),
+		'text' => array('name' => 'text'),
+		'integer' => array('name' => 'integer', 'limit' => null, 'formatter' => 'intval'),
+		'float' => array('name' => 'float', 'formatter' => 'floatval'),
+		'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
+		'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
+		'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
+		'binary' => array('name' => 'blob'),
+		'boolean' => array('name' => 'boolean')
+	);
+/**
+ * Connects to the database using config['database'] as a filename.
+ *
+ * @param array $config Configuration array for connecting
+ * @return mixed
+ */
+	function connect() {
+		$config = $this->config;
+		$this->connection = $config['connect']($config['database']);
+		$this->connected = is_resource($this->connection);
+
+		if ($this->connected) {
+			$this->_execute('PRAGMA count_changes = 1;');
+		}
+		return $this->connected;
+	}
+/**
+ * Disconnects from database.
+ *
+ * @return boolean True if the database could be disconnected, else false
+ */
+	function disconnect() {
+		@sqlite_close($this->connection);
+		$this->connected = false;
+		return $this->connected;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return resource Result resource identifier
+ */
+	function _execute($sql) {
+		$result = sqlite_query($this->connection, $sql);
+
+		if (preg_match('/^(INSERT|UPDATE|DELETE)/', $sql)) {
+			$this->resultSet($result);
+			list($this->_queryStats) = $this->fetchResult();
+		}
+		return $result;
+	}
+/**
+ * Overrides DboSource::execute() to correctly handle query statistics
+ *
+ * @param string $sql
+ * @return unknown
+ */
+	function execute($sql) {
+		$result = parent::execute($sql);
+		$this->_queryStats = array();
+		return $result;
+	}
+/**
+ * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
+ *
+ * @return array Array of tablenames in the database
+ */
+	function listSources() {
+		$cache = parent::listSources();
+
+		if ($cache != null) {
+			return $cache;
+		}
+		$result = $this->fetchAll("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", false);
+
+		if (empty($result)) {
+			return array();
+		} else {
+			$tables = array();
+			foreach ($result as $table) {
+				$tables[] = $table[0]['name'];
+			}
+			parent::listSources($tables);
+			return $tables;
+		}
+		return array();
+	}
+/**
+ * Returns an array of the fields in given table name.
+ *
+ * @param string $tableName Name of database table to inspect
+ * @return array Fields in table. Keys are name and type
+ */
+	function describe(&$model) {
+		$cache = parent::describe($model);
+		if ($cache != null) {
+			return $cache;
+		}
+		$fields = array();
+		$result = $this->fetchAll('PRAGMA table_info(' . $this->fullTableName($model) . ')');
+
+		foreach ($result as $column) {
+			$fields[$column[0]['name']] = array(
+				'type'		=> $this->column($column[0]['type']),
+				'null'		=> !$column[0]['notnull'],
+				'default'	=> $column[0]['dflt_value'],
+				'length'	=> $this->length($column[0]['type'])
+			);
+			if ($column[0]['pk'] == 1) {
+				$fields[$column[0]['name']] = array(
+					'type'		=> $fields[$column[0]['name']]['type'],
+					'null'		=> false,
+					'default'	=> $column[0]['dflt_value'],
+					'key'		=> $this->index['PRI'],
+					'length'	=> 11
+				);
+			}
+		}
+
+		$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
+		return $fields;
+	}
+/**
+ * Returns a quoted and escaped string of $data for use in an SQL statement.
+ *
+ * @param string $data String to be prepared for use in an SQL statement
+ * @return string Quoted and escaped
+ */
+	function value($data, $column = null, $safe = false) {
+		$parent = parent::value($data, $column, $safe);
+
+		if ($parent != null) {
+			return $parent;
+		}
+		if ($data === null) {
+			return 'NULL';
+		}
+		if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
+			return  "''";
+		}
+		switch ($column) {
+			case 'boolean':
+				$data = $this->boolean((bool)$data);
+			break;
+			case 'integer':
+			case 'float':
+				if ($data === '') {
+					return 'NULL';
+				}
+			default:
+				$data = sqlite_escape_string($data);
+			break;
+		}
+		return "'" . $data . "'";
+	}
+/**
+ * Generates and executes an SQL UPDATE statement for given model, fields, and values.
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @param mixed $conditions
+ * @return array
+ */
+	function update(&$model, $fields = array(), $values = null, $conditions = null) {
+		if (empty($values) && !empty($fields)) {
+			foreach ($fields as $field => $value) {
+				if (strpos($field, $model->alias . '.') !== false) {
+					unset($fields[$field]);
+					$field = str_replace($model->alias . '.', "", $field);
+					$field = str_replace($model->alias . '.', "", $field);
+					$fields[$field] = $value;
+				}
+			}
+		}
+		$result = parent::update($model, $fields, $values, $conditions);
+		return $result;
+	}
+/**
+ * Deletes all the records in a table and resets the count of the auto-incrementing
+ * primary key, where applicable.
+ *
+ * @param mixed $table A string or model class representing the table to be truncated
+ * @return boolean	SQL TRUNCATE TABLE statement, false if not applicable.
+ * @access public
+ */
+	function truncate($table) {
+		return $this->execute('DELETE From ' . $this->fullTableName($table));
+	}
+/**
+ * Returns a formatted error message from previous database operation.
+ *
+ * @return string Error message
+ */
+	function lastError() {
+		$error = sqlite_last_error($this->connection);
+		if ($error) {
+			return $error.': '.sqlite_error_string($error);
+		}
+		return null;
+	}
+/**
+ * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
+ *
+ * @return integer Number of affected rows
+ */
+	function lastAffected() {
+		if (!empty($this->_queryStats)) {
+			foreach (array('rows inserted', 'rows updated', 'rows deleted') as $key) {
+				if (array_key_exists($key, $this->_queryStats)) {
+					return $this->_queryStats[$key];
+				}
+			}
+		}
+		return false;
+	}
+/**
+ * Returns number of rows in previous resultset. If no previous resultset exists,
+ * this returns false.
+ *
+ * @return integer Number of rows in resultset
+ */
+	function lastNumRows() {
+		if ($this->hasResult()) {
+			sqlite_num_rows($this->_result);
+		}
+		return false;
+	}
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @return int
+ */
+	function lastInsertId() {
+		return sqlite_last_insert_rowid($this->connection);
+	}
+/**
+ * Converts database-layer column types to basic types
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string Abstract column type (i.e. "string")
+ */
+	function column($real) {
+		if (is_array($real)) {
+			$col = $real['name'];
+			if (isset($real['limit'])) {
+				$col .= '('.$real['limit'].')';
+			}
+			return $col;
+		}
+
+		$col = strtolower(str_replace(')', '', $real));
+		$limit = null;
+		if (strpos($col, '(') !== false) {
+			list($col, $limit) = explode('(', $col);
+		}
+
+		if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) {
+			return $col;
+		}
+		if (strpos($col, 'varchar') !== false) {
+			return 'string';
+		}
+		if (in_array($col, array('blob', 'clob'))) {
+			return 'binary';
+		}
+		if (strpos($col, 'numeric') !== false) {
+			return 'float';
+		}
+		return 'text';
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $results
+ */
+	function resultSet(&$results) {
+		$this->results =& $results;
+		$this->map = array();
+		$fieldCount = sqlite_num_fields($results);
+		$index = $j = 0;
+
+		while ($j < $fieldCount) {
+			$columnName = str_replace('"', '', sqlite_field_name($results, $j));
+
+			if (strpos($columnName, '.')) {
+				$parts = explode('.', $columnName);
+				$this->map[$index++] = array($parts[0], $parts[1]);
+			} else {
+				$this->map[$index++] = array(0, $columnName);
+			}
+			$j++;
+		}
+	}
+/**
+ * Fetches the next row from the current result set
+ *
+ * @return unknown
+ */
+	function fetchResult() {
+		if ($row = sqlite_fetch_array($this->results, SQLITE_ASSOC)) {
+			$resultRow = array();
+			$i = 0;
+
+			foreach ($row as $index => $field) {
+				if (strpos($index, '.')) {
+					list($table, $column) = explode('.', str_replace('"', '', $index));
+					$resultRow[$table][$column] = $row[$index];
+				} else {
+					$resultRow[0][str_replace('"', '', $index)] = $row[$index];
+				}
+				$i++;
+			}
+			return $resultRow;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+			if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
+				$rt = ' LIMIT';
+			}
+			$rt .= ' ' . $limit;
+			if ($offset) {
+				$rt .= ' OFFSET ' . $offset;
+			}
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Generate a database-native column schema string
+ *
+ * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
+ *                      where options can be 'default', 'length', or 'key'.
+ * @return string
+ */
+	function buildColumn($column) {
+		$name = $type = null;
+		$column = array_merge(array('null' => true), $column);
+		extract($column);
+
+		if (empty($name) || empty($type)) {
+			trigger_error('Column name or type not defined in schema', E_USER_WARNING);
+			return null;
+		}
+
+		if (!isset($this->columns[$type])) {
+			trigger_error("Column type {$type} does not exist", E_USER_WARNING);
+			return null;
+		}
+
+		$real = $this->columns[$type];
+		if (isset($column['key']) && $column['key'] == 'primary') {
+			$out = $this->name($name) . ' ' . $this->columns['primary_key']['name'];
+		} else {
+			$out = $this->name($name) . ' ' . $real['name'];
+
+			if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
+				if (isset($column['length'])) {
+					$length = $column['length'];
+				} elseif (isset($column['limit'])) {
+					$length = $column['limit'];
+				} elseif (isset($real['length'])) {
+					$length = $real['length'];
+				} else {
+					$length = $real['limit'];
+				}
+				$out .= '(' . $length . ')';
+			}
+			if (isset($column['key']) && $column['key'] == 'primary') {
+				$out .= ' NOT NULL';
+			} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
+				$out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
+			} elseif (isset($column['default'])) {
+				$out .= ' DEFAULT ' . $this->value($column['default'], $type);
+			} elseif (isset($column['null']) && $column['null'] == true) {
+				$out .= ' DEFAULT NULL';
+			} elseif (isset($column['null']) && $column['null'] == false) {
+				$out .= ' NOT NULL';
+			}
+		}
+		return $out;
+	}
+/**
+ * Sets the database encoding
+ *
+ * @param string $enc Database encoding
+ */
+	function setEncoding($enc) {
+		if (!in_array($enc, array("UTF-8", "UTF-16", "UTF-16le", "UTF-16be"))) {
+			return false;
+		}
+		return $this->_execute("PRAGMA encoding = \"{$enc}\"") !== false;
+	}
+/**
+ * Gets the database encoding
+ *
+ * @return string The database encoding
+ */
+	function getEncoding() {
+		return $this->fetchRow('PRAGMA encoding');
+	}
+/**
+ * Removes redundant primary key indexes, as they are handled in the column def of the key.
+ *
+ * @param array $indexes
+ * @param string $table
+ * @return string
+ */
+	function buildIndex($indexes, $table = null) {
+		$join = array();
+
+		foreach ($indexes as $name => $value) {
+
+			if ($name == 'PRIMARY') {
+				continue;
+			}
+			$out = 'CREATE ';
+
+			if (!empty($value['unique'])) {
+				$out .= 'UNIQUE ';
+			}
+			if (is_array($value['column'])) {
+				$value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
+			} else {
+				$value['column'] = $this->name($value['column']);
+			}
+			$out .= "INDEX {$name} ON {$table}({$value['column']});";
+			$join[] = $out;
+		}
+		return $join;
+	}
+/**
+ * Overrides DboSource::index to handle SQLite indexe introspection
+ * Returns an array of the indexes in given table name.
+ *
+ * @param string $model Name of model to inspect
+ * @return array Fields in table. Keys are column and unique
+ */
+	function index(&$model) {
+		$index = array();
+		$table = $this->fullTableName($model);
+		if ($table) {
+			$indexes = $this->query('PRAGMA index_list(' . $table . ')');
+			$tableInfo = $this->query('PRAGMA table_info(' . $table . ')');
+			foreach ($indexes as $i => $info) {
+				$key = array_pop($info);
+				$keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")');
+				foreach ($keyInfo as $keyCol) {
+					if (!isset($index[$key['name']])) {
+						$col = array();
+						if (preg_match('/autoindex/', $key['name'])) {
+							$key['name'] = 'PRIMARY';
+						}
+						$index[$key['name']]['column'] = $keyCol[0]['name'];
+						$index[$key['name']]['unique'] = intval($key['unique'] == 1);
+					} else {
+						if (!is_array($index[$key['name']]['column'])) {
+							$col[] = $index[$key['name']]['column'];
+						}
+						$col[] = $keyCol[0]['name'];
+						$index[$key['name']]['column'] = $col;
+					}
+				}
+			}
+		}
+		return $index;
+	}
+	
+/**
+ * Overrides DboSource::renderStatement to handle schema generation with SQLite-style indexes
+ *
+ * @param string $type
+ * @param array $data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		switch (strtolower($type)) {
+			case 'schema':
+				extract($data);
+
+				foreach (array('columns', 'indexes') as $var) {
+					if (is_array(${$var})) {
+						${$var} = "\t" . join(",\n\t", array_filter(${$var}));
+					}
+				}
+				return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
+			break;
+			default:
+				return parent::renderStatement($type, $data);
+			break;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/model/datasources/dbo_source.php
===================================================================
--- cake/libs/model/datasources/dbo_source.php	(revision 0)
+++ cake/libs/model/datasources/dbo_source.php	(revision 0)
@@ -0,0 +1,2436 @@
+<?php
+/* SVN FILE: $Id: dbo_source.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', array('Set', 'String'));
+
+/**
+ * DboSource
+ *
+ * Creates DBO-descendant objects from a given db connection configuration
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ */
+class DboSource extends DataSource {
+/**
+ * Description string for this Database Data Source.
+ *
+ * @var unknown_type
+ */
+	var $description = "Database Data Source";
+/**
+ * index definition, standard cake, primary, index, unique
+ *
+ * @var array
+ */
+	var $index = array('PRI' => 'primary', 'MUL' => 'index', 'UNI' => 'unique');
+/**
+ * Database keyword used to assign aliases to identifiers.
+ *
+ * @var string
+ */
+	var $alias = 'AS ';
+/**
+ * Caches fields quoted in DboSource::name()
+ *
+ * @var array
+ */
+	var $fieldCache = array();
+/**
+ * Bypass automatic adding of joined fields/associations.
+ *
+ * @var boolean
+ */
+	var $__bypass = false;
+/**
+ * The set of valid SQL operations usable in a WHERE statement
+ *
+ * @var array
+ */
+	var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
+/**
+ * Index of basic SQL commands
+ *
+ * @var array
+ * @access protected
+ */
+	var $_commands = array(
+		'begin'	   => 'BEGIN',
+		'commit'   => 'COMMIT',
+		'rollback' => 'ROLLBACK'
+	);
+/**
+ * Constructor
+ */
+	function __construct($config = null, $autoConnect = true) {
+		if (!isset($config['prefix'])) {
+			$config['prefix'] = '';
+		}
+		parent::__construct($config);
+		$this->fullDebug = Configure::read() > 1;
+
+		if ($autoConnect) {
+			return $this->connect();
+		} else {
+			return true;
+		}
+	}
+/**
+ * Reconnects to database server with optional new settings
+ *
+ * @param array $config An array defining the new configuration settings
+ * @return boolean True on success, false on failure
+ */
+	function reconnect($config = null) {
+		$this->disconnect();
+		$this->setConfig($config);
+		$this->_sources = null;
+
+		return $this->connect();
+	}
+/**
+ * Prepares a value, or an array of values for database queries by quoting and escaping them.
+ *
+ * @param mixed $data A value or an array of values to prepare.
+ * @param string $column The column into which this data will be inserted
+ * @param boolean $read Value to be used in READ or WRITE context
+ * @return mixed Prepared value or array of values.
+ */
+	function value($data, $column = null, $read = true) {
+		if (is_array($data) && !empty($data)) {
+			return array_map(
+				array(&$this, 'value'),
+				$data, array_fill(0, count($data), $column), array_fill(0, count($data), $read)
+			);
+		} elseif (is_object($data) && isset($data->type)) {
+			if ($data->type == 'identifier') {
+				return $this->name($data->value);
+			} elseif ($data->type == 'expression') {
+				return $data->value;
+			}
+		} elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) {
+			return $data;
+		} else {
+			return null;
+		}
+	}
+/**
+ * Returns an object to represent a database identifier in a query
+ *
+ * @param string $identifier
+ * @return object An object representing a database identifier to be used in a query
+ */
+	function identifier($identifier) {
+		$obj = new stdClass();
+		$obj->type = 'identifier';
+		$obj->value = $identifier;
+		return $obj;
+	}
+/**
+ * Returns an object to represent a database expression in a query
+ *
+ * @param string $expression
+ * @return object An object representing a database expression to be used in a query
+ */
+	function expression($expression) {
+		$obj = new stdClass();
+		$obj->type = 'expression';
+		$obj->value = $expression;
+		return $obj;
+	}
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return unknown
+ */
+	function rawQuery($sql) {
+		$this->took = $this->error = $this->numRows = false;
+		return $this->execute($sql);
+	}
+/**
+ * Queries the database with given SQL statement, and obtains some metadata about the result
+ * (rows affected, timing, any errors, number of rows in resultset). The query is also logged.
+ * If DEBUG is set, the log is shown all the time, else it is only shown on errors.
+ *
+ * @param string $sql
+ * @param array $options
+ * @return mixed Resource or object representing the result set, or false on failure
+ */
+	function execute($sql, $options = array()) {
+		$defaults = array('stats' => true, 'log' => $this->fullDebug);
+		$options = array_merge($defaults, $options);
+
+		if ($options['stats']) {
+			$t = getMicrotime();
+			$this->_result = $this->_execute($sql);
+			$this->took = round((getMicrotime() - $t) * 1000, 0);
+			$this->affected = $this->lastAffected();
+			$this->error = $this->lastError();
+			$this->numRows = $this->lastNumRows();
+		}
+
+		if ($options['log']) {
+			$this->logQuery($sql);
+		}
+
+		if ($this->error) {
+			$this->showQuery($sql);
+			return false;
+		} else {
+			return $this->_result;
+		}
+	}
+/**
+ * DataSource Query abstraction
+ *
+ * @return resource Result resource identifier
+ */
+	function query() {
+		$args	  = func_get_args();
+		$fields	  = null;
+		$order	  = null;
+		$limit	  = null;
+		$page	  = null;
+		$recursive = null;
+
+		if (count($args) == 1) {
+			return $this->fetchAll($args[0]);
+
+		} elseif (count($args) > 1 && (strpos(strtolower($args[0]), 'findby') === 0 || strpos(strtolower($args[0]), 'findallby') === 0)) {
+			$params = $args[1];
+
+			if (strpos(strtolower($args[0]), 'findby') === 0) {
+				$all  = false;
+				$field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
+			} else {
+				$all  = true;
+				$field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
+			}
+
+			$or = (strpos($field, '_or_') !== false);
+			if ($or) {
+				$field = explode('_or_', $field);
+			} else {
+				$field = explode('_and_', $field);
+			}
+			$off = count($field) - 1;
+
+			if (isset($params[1 + $off])) {
+				$fields = $params[1 + $off];
+			}
+
+			if (isset($params[2 + $off])) {
+				$order = $params[2 + $off];
+			}
+
+			if (!array_key_exists(0, $params)) {
+				return false;
+			}
+
+			$c = 0;
+			$conditions = array();
+
+			foreach ($field as $f) {
+				$conditions[$args[2]->alias . '.' . $f] = $params[$c];
+				$c++;
+			}
+
+			if ($or) {
+				$conditions = array('OR' => $conditions);
+			}
+
+			if ($all) {
+				if (isset($params[3 + $off])) {
+					$limit = $params[3 + $off];
+				}
+
+				if (isset($params[4 + $off])) {
+					$page = $params[4 + $off];
+				}
+
+				if (isset($params[5 + $off])) {
+					$recursive = $params[5 + $off];
+				}
+				return $args[2]->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
+			} else {
+				if (isset($params[3 + $off])) {
+					$recursive = $params[3 + $off];
+				}
+				return $args[2]->find('first', compact('conditions', 'fields', 'order', 'recursive'));
+			}
+		} else {
+			if (isset($args[1]) && $args[1] === true) {
+				return $this->fetchAll($args[0], true);
+			} else if (isset($args[1]) && !is_array($args[1]) ) {
+				return $this->fetchAll($args[0], false);
+			} else if (isset($args[1]) && is_array($args[1])) {
+				$offset = 0;
+				if (isset($args[2])) {
+					$cache = $args[2];
+				} else {
+					$cache = true;
+				}
+				$args[1] = array_map(array(&$this, 'value'), $args[1]);
+				return $this->fetchAll(String::insert($args[0], $args[1]), $cache);
+			}
+		}
+	}
+/**
+ * Returns a row from current resultset as an array
+ *
+ * @return array The fetched row as an array
+ */
+	function fetchRow($sql = null) {
+		if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
+			if (!$this->execute($sql)) {
+				return null;
+			}
+		}
+
+		if ($this->hasResult()) {
+			$this->resultSet($this->_result);
+			$resultRow = $this->fetchResult();
+			return $resultRow;
+		} else {
+			return null;
+		}
+	}
+/**
+ * Returns an array of all result rows for a given SQL query.
+ * Returns false if no rows matched.
+ *
+ * @param string $sql SQL statement
+ * @param boolean $cache Enables returning/storing cached query results
+ * @return array Array of resultset rows, or false if no rows matched
+ */
+	function fetchAll($sql, $cache = true, $modelName = null) {
+		if ($cache && isset($this->_queryCache[$sql])) {
+			if (preg_match('/^\s*select/i', $sql)) {
+				return $this->_queryCache[$sql];
+			}
+		}
+
+		if ($this->execute($sql)) {
+			$out = array();
+
+			$first = $this->fetchRow();
+			if ($first != null) {
+				$out[] = $first;
+			}
+			while ($this->hasResult() && $item = $this->fetchResult()) {
+				$out[] = $item;
+			}
+
+			if ($cache) {
+				if (strpos(trim(strtolower($sql)), 'select') !== false) {
+					$this->_queryCache[$sql] = $out;
+				}
+			}
+			return $out;
+
+		} else {
+			return false;
+		}
+	}
+/**
+ * Returns a single field of the first of query results for a given SQL query, or false if empty.
+ *
+ * @param string $name Name of the field
+ * @param string $sql SQL query
+ * @return unknown
+ */
+	function field($name, $sql) {
+		$data = $this->fetchRow($sql);
+
+		if (!isset($data[$name]) || empty($data[$name])) {
+			return false;
+		} else {
+			return $data[$name];
+		}
+	}
+/**
+ * Returns a quoted name of $data for use in an SQL statement.
+ * Strips fields out of SQL functions before quoting.
+ *
+ * @param string $data
+ * @return string SQL field
+ */
+	function name($data) {
+		if ($data == '*') {
+			return '*';
+		}
+		if (is_object($data) && isset($data->type)) {
+			return $data->value;
+		}
+		$array = is_array($data);
+		$data = (array)$data;
+		$count = count($data);
+
+		for ($i = 0; $i < $count; $i++) {
+			if ($data[$i] == '*') {
+				continue;
+			}
+			if (strpos($data[$i], '(') !== false && preg_match_all('/([^(]*)\((.*)\)(.*)/', $data[$i], $fields)) {
+				$fields = Set::extract($fields, '{n}.0');
+
+				if (!empty($fields[1])) {
+					if (!empty($fields[2])) {
+						$data[$i] = $fields[1] . '(' . $this->name($fields[2]) . ')' . $fields[3];
+					} else {
+						$data[$i] = $fields[1] . '()' . $fields[3];
+					}
+				}
+			}
+			$data[$i] = str_replace('.', $this->endQuote . '.' . $this->startQuote, $data[$i]);
+			$data[$i] = $this->startQuote . $data[$i] . $this->endQuote;
+			$data[$i] = str_replace($this->startQuote . $this->startQuote, $this->startQuote, $data[$i]);
+			$data[$i] = str_replace($this->startQuote . '(', '(', $data[$i]);
+			$data[$i] = str_replace(')' . $this->startQuote, ')', $data[$i]);
+
+			if (strpos($data[$i], ' AS ')) {
+				$data[$i] = str_replace(' AS ', $this->endQuote . ' AS ' . $this->startQuote, $data[$i]);
+			}
+			if (!empty($this->endQuote) && $this->endQuote == $this->startQuote) {
+				if (substr_count($data[$i], $this->endQuote) % 2 == 1) {
+					$data[$i] = trim($data[$i], $this->endQuote);
+				}
+			}
+			if (strpos($data[$i], '*')) {
+				$data[$i] = str_replace($this->endQuote . '*' . $this->endQuote, '*', $data[$i]);
+			}
+			$data[$i] = str_replace($this->endQuote . $this->endQuote, $this->endQuote, $data[$i]);
+		}
+		return (!$array) ? $data[0] : $data;
+	}
+/**
+ * Checks if it's connected to the database
+ *
+ * @return boolean True if the database is connected, else false
+ */
+	function isConnected() {
+		return $this->connected;
+	}
+/**
+ * Checks if the result is valid
+ *
+ * @return boolean True if the result is valid else false
+ */
+	function hasResult() {
+		return is_resource($this->_result);
+	}
+/**
+ * Outputs the contents of the queries log.
+ *
+ * @param boolean $sorted
+ */
+	function showLog($sorted = false) {
+		if ($sorted) {
+			$log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
+		} else {
+			$log = $this->_queriesLog;
+		}
+
+		if ($this->_queriesCnt > 1) {
+			$text = 'queries';
+		} else {
+			$text = 'query';
+		}
+
+		if (PHP_SAPI != 'cli') {
+			print ("<table class=\"cake-sql-log\" id=\"cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true)) . "\" summary=\"Cake SQL Log\" cellspacing=\"0\" border = \"0\">\n<caption>({$this->configKeyName}) {$this->_queriesCnt} {$text} took {$this->_queriesTime} ms</caption>\n");
+			print ("<thead>\n<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>\n</thead>\n<tbody>\n");
+
+			foreach ($log as $k => $i) {
+				print ("<tr><td>" . ($k + 1) . "</td><td>" . h($i['query']) . "</td><td>{$i['error']}</td><td style = \"text-align: right\">{$i['affected']}</td><td style = \"text-align: right\">{$i['numRows']}</td><td style = \"text-align: right\">{$i['took']}</td></tr>\n");
+			}
+			print ("</tbody></table>\n");
+		} else {
+			foreach ($log as $k => $i) {
+				print (($k + 1) . ". {$i['query']} {$i['error']}\n");
+			}
+		}
+	}
+/**
+ * Log given SQL query.
+ *
+ * @param string $sql SQL statement
+ * @todo: Add hook to log errors instead of returning false
+ */
+	function logQuery($sql) {
+		$this->_queriesCnt++;
+		$this->_queriesTime += $this->took;
+		$this->_queriesLog[] = array(
+			'query' => $sql,
+			'error'		=> $this->error,
+			'affected'	=> $this->affected,
+			'numRows'	=> $this->numRows,
+			'took'		=> $this->took
+		);
+		if (count($this->_queriesLog) > $this->_queriesLogMax) {
+			array_pop($this->_queriesLog);
+		}
+		if ($this->error) {
+			return false;
+		}
+	}
+/**
+ * Output information about an SQL query. The SQL statement, number of rows in resultset,
+ * and execution time in microseconds. If the query fails, an error is output instead.
+ *
+ * @param string $sql Query to show information on.
+ */
+	function showQuery($sql) {
+		$error = $this->error;
+		if (strlen($sql) > 200 && !$this->fullDebug && Configure::read() > 1) {
+			$sql = substr($sql, 0, 200) . '[...]';
+		}
+		if (Configure::read() > 0) {
+			$out = null;
+			if ($error) {
+				trigger_error("<span style = \"color:Red;text-align:left\"><b>SQL Error:</b> {$this->error}</span>", E_USER_WARNING);
+			} else {
+				$out = ("<small>[Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]</small>");
+			}
+			pr(sprintf("<p style = \"text-align:left\"><b>Query:</b> %s %s</p>", $sql, $out));
+		}
+	}
+/**
+ * Gets full table name including prefix
+ *
+ * @param mixed $model
+ * @param boolean $quote
+ * @return string Full quoted table name
+ */
+	function fullTableName($model, $quote = true) {
+		if (is_object($model)) {
+			$table = $model->tablePrefix . $model->table;
+		} elseif (isset($this->config['prefix'])) {
+			$table = $this->config['prefix'] . strval($model);
+		} else {
+			$table = strval($model);
+		}
+		if ($quote) {
+			return $this->name($table);
+		}
+		return $table;
+	}
+/**
+ * The "C" in CRUD
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @return boolean Success
+ */
+	function create(&$model, $fields = null, $values = null) {
+		$id = null;
+
+		if ($fields == null) {
+			unset($fields, $values);
+			$fields = array_keys($model->data);
+			$values = array_values($model->data);
+		}
+		$count = count($fields);
+
+		for ($i = 0; $i < $count; $i++) {
+			$valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i]), false);
+		}
+		for ($i = 0; $i < $count; $i++) {
+			$fieldInsert[] = $this->name($fields[$i]);
+			if ($fields[$i] == $model->primaryKey) {
+				$id = $values[$i];
+			}
+		}
+		$query = array(
+			'table' => $this->fullTableName($model),
+			'fields' => join(', ', $fieldInsert),
+			'values' => join(', ', $valueInsert)
+		);
+
+		if ($this->execute($this->renderStatement('create', $query))) {
+			if (empty($id)) {
+				$id = $this->lastInsertId($this->fullTableName($model, false), $model->primaryKey);
+			}
+			$model->setInsertID($id);
+			$model->id = $id;
+			return true;
+		} else {
+			$model->onError();
+			return false;
+		}
+	}
+/**
+ * The "R" in CRUD
+ *
+ * @param Model $model
+ * @param array $queryData
+ * @param integer $recursive Number of levels of association
+ * @return unknown
+ */
+	function read(&$model, $queryData = array(), $recursive = null) {
+		$queryData = $this->__scrubQueryData($queryData);
+
+		$null = null;
+		$array = array();
+		$linkedModels = array();
+		$this->__bypass = false;
+		$this->__booleans = array();
+
+		if ($recursive === null && isset($queryData['recursive'])) {
+			$recursive = $queryData['recursive'];
+		}
+
+		if (!is_null($recursive)) {
+			$_recursive = $model->recursive;
+			$model->recursive = $recursive;
+		}
+
+		if (!empty($queryData['fields'])) {
+			$this->__bypass = true;
+			$queryData['fields'] = $this->fields($model, null, $queryData['fields']);
+		} else {
+			$queryData['fields'] = $this->fields($model);
+		}
+
+		foreach ($model->__associations as $type) {
+			foreach ($model->{$type} as $assoc => $assocData) {
+				if ($model->recursive > -1) {
+					$linkModel =& $model->{$assoc};
+					$external = isset($assocData['external']);
+
+					if ($model->useDbConfig == $linkModel->useDbConfig) {
+						if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
+							$linkedModels[] = $type . '/' . $assoc;
+						}
+					}
+				}
+			}
+		}
+
+		$query = $this->generateAssociationQuery($model, $null, null, null, null, $queryData, false, $null);
+
+		$resultSet = $this->fetchAll($query, $model->cacheQueries, $model->alias);
+
+		if ($resultSet === false) {
+			$model->onError();
+			return false;
+		}
+
+		$filtered = $this->__filterResults($resultSet, $model);
+
+		if ($model->recursive > 0) {
+			foreach ($model->__associations as $type) {
+				foreach ($model->{$type} as $assoc => $assocData) {
+					$linkModel =& $model->{$assoc};
+
+					if (!in_array($type . '/' . $assoc, $linkedModels)) {
+						if ($model->useDbConfig == $linkModel->useDbConfig) {
+							$db =& $this;
+						} else {
+							$db =& ConnectionManager::getDataSource($linkModel->useDbConfig);
+						}
+					} elseif ($model->recursive > 1 && ($type == 'belongsTo' || $type == 'hasOne')) {
+						$db =& $this;
+					}
+
+					if (isset($db)) {
+						$stack = array($assoc);
+						$db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack);
+						unset($db);
+					}
+				}
+			}
+			$this->__filterResults($resultSet, $model, $filtered);
+		}
+
+		if (!is_null($recursive)) {
+			$model->recursive = $_recursive;
+		}
+		return $resultSet;
+	}
+/**
+ * Private method.	Passes association results thru afterFind filters of corresponding model
+ *
+ * @param array $results Reference of resultset to be filtered
+ * @param object $model Instance of model to operate against
+ * @param array $filtered List of classes already filtered, to be skipped
+ * @return return
+ */
+	function __filterResults(&$results, &$model, $filtered = array()) {
+		$filtering = array();
+		$count = count($results);
+
+		for ($i = 0; $i < $count; $i++) {
+			if (is_array($results[$i])) {
+				$classNames = array_keys($results[$i]);
+				$count2 = count($classNames);
+
+				for ($j = 0; $j < $count2; $j++) {
+					$className = $classNames[$j];
+					if ($model->alias != $className && !in_array($className, $filtered)) {
+						if (!in_array($className, $filtering)) {
+							$filtering[] = $className;
+						}
+
+						if (isset($model->{$className}) && is_object($model->{$className})) {
+							$data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false);
+						}
+						if (isset($data[0][$className])) {
+							$results[$i][$className] = $data[0][$className];
+						}
+					}
+				}
+			}
+		}
+		return $filtering;
+	}
+/**
+ * Enter description here...
+ *
+ * @param Model $model
+ * @param unknown_type $linkModel
+ * @param string $type Association type
+ * @param unknown_type $association
+ * @param unknown_type $assocData
+ * @param unknown_type $queryData
+ * @param unknown_type $external
+ * @param unknown_type $resultSet
+ * @param integer $recursive Number of levels of association
+ * @param array $stack
+ */
+	function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
+		if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
+			if (!isset($resultSet) || !is_array($resultSet)) {
+				if (Configure::read() > 0) {
+					e('<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ');
+					if (isset($this->error) && $this->error != null) {
+						e($this->error);
+					}
+					e('</div>');
+				}
+				return null;
+			}
+			$count = count($resultSet);
+
+			if ($type === 'hasMany' && empty($assocData['limit']) && !empty($assocData['foreignKey'])) {
+				$ins = $fetch = array();
+				for ($i = 0; $i < $count; $i++) {
+					if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
+						$ins[] = $in;
+					}
+				}
+
+				if (!empty($ins)) {
+					$fetch = $this->fetchAssociated($model, $query, $ins);
+				}
+
+				if (!empty($fetch) && is_array($fetch)) {
+					if ($recursive > 0) {
+						foreach ($linkModel->__associations as $type1) {
+							foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
+								$deepModel =& $linkModel->{$assoc1};
+								$tmpStack = $stack;
+								$tmpStack[] = $assoc1;
+
+								if ($linkModel->useDbConfig === $deepModel->useDbConfig) {
+									$db =& $this;
+								} else {
+									$db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
+								}
+								$db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
+							}
+						}
+					}
+				}
+				$this->__filterResults($fetch, $model);
+				return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive);
+			} elseif ($type === 'hasAndBelongsToMany') {
+				$ins = $fetch = array();
+				for ($i = 0; $i < $count; $i++) {
+					if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
+						$ins[] = $in;
+					}
+				}
+				if (!empty($ins)) {
+					if (count($ins) > 1) {
+						$query = str_replace('{$__cakeID__$}', '(' .join(', ', $ins) .')', $query);
+						$query = str_replace('= (', 'IN (', $query);
+						$query = str_replace('=	 (', 'IN (', $query);
+					} else {
+						$query = str_replace('{$__cakeID__$}',$ins[0], $query);
+					}
+
+					$query = str_replace('	WHERE 1 = 1', '', $query);
+				}
+
+				$foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey'];
+				$joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']);
+				list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys);
+				$habtmFieldsCount = count($habtmFields);
+				$q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack);
+
+				if ($q != false) {
+					$fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
+				} else {
+					$fetch = null;
+				}
+			}
+
+			for ($i = 0; $i < $count; $i++) {
+				$row =& $resultSet[$i];
+
+				if ($type !== 'hasAndBelongsToMany') {
+					$q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack);
+					if ($q != false) {
+						$fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias);
+					} else {
+						$fetch = null;
+					}
+				}
+				$selfJoin = false;
+
+				if ($linkModel->name === $model->name) {
+					$selfJoin = true;
+				}
+
+				if (!empty($fetch) && is_array($fetch)) {
+					if ($recursive > 0) {
+						foreach ($linkModel->__associations as $type1) {
+							foreach ($linkModel->{$type1} as $assoc1 => $assocData1) {
+								$deepModel =& $linkModel->{$assoc1};
+
+								if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) {
+									$tmpStack = $stack;
+									$tmpStack[] = $assoc1;
+									if ($linkModel->useDbConfig == $deepModel->useDbConfig) {
+										$db =& $this;
+									} else {
+										$db =& ConnectionManager::getDataSource($deepModel->useDbConfig);
+									}
+									$db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack);
+								}
+							}
+						}
+					}
+					if ($type == 'hasAndBelongsToMany') {
+						$uniqueIds = $merge = array();
+
+						foreach ($fetch as $j => $data) {
+							if (
+								(isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) &&
+								(!in_array($data[$with][$joinKeys[1]], $uniqueIds))
+							) {
+								$uniqueIds[] = $data[$with][$joinKeys[1]];
+
+								if ($habtmFieldsCount <= 2) {
+									unset($data[$with]);
+								}
+								$merge[] = $data;
+							}
+						}
+						if (empty($merge) && !isset($row[$association])) {
+							$row[$association] = $merge;
+						} else {
+							$this->__mergeAssociation($resultSet[$i], $merge, $association, $type);
+						}
+					} else {
+						$this->__mergeAssociation($resultSet[$i], $fetch, $association, $type, $selfJoin);
+					}
+					if (isset($resultSet[$i][$association])) {
+						$resultSet[$i][$association] = $linkModel->afterFind($resultSet[$i][$association]);
+					}
+				} else {
+					$tempArray[0][$association] = false;
+					$this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type, $selfJoin);
+				}
+			}
+		}
+	}
+/**
+ * A more efficient way to fetch associations.	Woohoo!
+ *
+ * @param model $model		Primary model object
+ * @param string $query		Association query
+ * @param array $ids		Array of IDs of associated records
+ * @return array Association results
+ */
+	function fetchAssociated($model, $query, $ids) {
+		$query = str_replace('{$__cakeID__$}', join(', ', $ids), $query);
+		if (count($ids) > 1) {
+			$query = str_replace('= (', 'IN (', $query);
+			$query = str_replace('=	 (', 'IN (', $query);
+		}
+		return $this->fetchAll($query, $model->cacheQueries, $model->alias);
+	}
+/**
+ * mergeHasMany - Merge the results of hasMany relations.
+ *
+ *
+ * @param array $resultSet Data to merge into
+ * @param array $merge Data to merge
+ * @param string $association Name of Model being Merged
+ * @param object $model Model being merged onto
+ * @param object $linkModel Model being merged
+ * @return void
+ **/
+	function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) {
+		foreach ($resultSet as $i => $value) {
+			$count = 0;
+			$merged[$association] = array();
+			foreach ($merge as $j => $data) {
+				if (isset($value[$model->alias]) && $value[$model->alias][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) {
+					if (count($data) > 1) {
+						$data = array_merge($data[$association], $data);
+						unset($data[$association]);
+						foreach ($data as $key => $name) {
+							if (is_numeric($key)) {
+								$data[$association][] = $name;
+								unset($data[$key]);
+							}
+						}
+						$merged[$association][] = $data;
+					} else {
+						$merged[$association][] = $data[$association];
+					}
+				}
+				$count++;
+			}
+			if (isset($value[$model->alias])) {
+				$resultSet[$i] = Set::pushDiff($resultSet[$i], $merged);
+				unset($merged);
+			}
+		}
+	}
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $data
+ * @param unknown_type $merge
+ * @param unknown_type $association
+ * @param unknown_type $type
+ * @param boolean $selfJoin
+ */
+	function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = false) {
+		if (isset($merge[0]) && !isset($merge[0][$association])) {
+			$association = Inflector::pluralize($association);
+		}
+
+		if ($type == 'belongsTo' || $type == 'hasOne') {
+			if (isset($merge[$association])) {
+				$data[$association] = $merge[$association][0];
+			} else {
+				if (count($merge[0][$association]) > 1) {
+					foreach ($merge[0] as $assoc => $data2) {
+						if ($assoc != $association) {
+							$merge[0][$association][$assoc] = $data2;
+						}
+					}
+				}
+				if (!isset($data[$association])) {
+					if ($merge[0][$association] != null) {
+						$data[$association] = $merge[0][$association];
+					} else {
+						$data[$association] = array();
+					}
+				} else {
+					if (is_array($merge[0][$association])) {
+						foreach ($data[$association] as $k => $v) {
+							if (!is_array($v)) {
+								$dataAssocTmp[$k] = $v;
+							}
+						}
+
+						foreach ($merge[0][$association] as $k => $v) {
+							if (!is_array($v)) {
+								$mergeAssocTmp[$k] = $v;
+							}
+						}
+						$dataKeys = array_keys($data);
+						$mergeKeys = array_keys($merge[0]);
+
+						if ($mergeKeys[0] === $dataKeys[0] || $mergeKeys === $dataKeys) {
+							$data[$association][$association] = $merge[0][$association];
+						} else {
+							$diff = Set::diff($dataAssocTmp, $mergeAssocTmp);
+							$data[$association] = array_merge($merge[0][$association], $diff);
+						}
+					} elseif ($selfJoin && array_key_exists($association, $merge[0])) {
+						$data[$association] = array_merge($data[$association], array($association => array()));
+					}
+				}
+			}
+		} else {
+			if (isset($merge[0][$association]) && $merge[0][$association] === false) {
+				if (!isset($data[$association])) {
+					$data[$association] = array();
+				}
+			} else {
+				foreach ($merge as $i => $row) {
+					if (count($row) == 1) {
+						if (empty($data[$association]) || (isset($data[$association]) && !in_array($row[$association], $data[$association]))) {
+							$data[$association][] = $row[$association];
+						}
+					} else if (!empty($row)) {
+						$tmp = array_merge($row[$association], $row);
+						unset($tmp[$association]);
+						$data[$association][] = $tmp;
+					}
+				}
+			}
+		}
+	}
+/**
+ * Generates an array representing a query or part of a query from a single model or two associated models
+ *
+ * @param Model $model
+ * @param Model $linkModel
+ * @param string $type
+ * @param string $association
+ * @param array $assocData
+ * @param array $queryData
+ * @param boolean $external
+ * @param array $resultSet
+ * @return mixed
+ */
+	function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
+		$queryData = $this->__scrubQueryData($queryData);
+		$assocData = $this->__scrubQueryData($assocData);
+
+		if (empty($queryData['fields'])) {
+			$queryData['fields'] = $this->fields($model, $model->alias);
+		} elseif (!empty($model->hasMany) && $model->recursive > -1) {
+			$assocFields = $this->fields($model, $model->alias, array("{$model->alias}.{$model->primaryKey}"));
+			$passedFields = $this->fields($model, $model->alias, $queryData['fields']);
+
+			if (count($passedFields) === 1) {
+				$match = strpos($passedFields[0], $assocFields[0]);
+				$match1 = strpos($passedFields[0], 'COUNT(');
+				if ($match === false && $match1 === false) {
+					$queryData['fields'] = array_merge($passedFields, $assocFields);
+				} else {
+					$queryData['fields'] = $passedFields;
+				}
+			} else {
+				$queryData['fields'] = array_merge($passedFields, $assocFields);
+			}
+			unset($assocFields, $passedFields);
+		}
+
+		if ($linkModel == null) {
+			return $this->buildStatement(
+				array(
+					'fields' => array_unique($queryData['fields']),
+					'table' => $this->fullTableName($model),
+					'alias' => $model->alias,
+					'limit' => $queryData['limit'],
+					'offset' => $queryData['offset'],
+					'joins' => $queryData['joins'],
+					'conditions' => $queryData['conditions'],
+					'order' => $queryData['order'],
+					'group' => $queryData['group']
+				),
+				$model
+			);
+		}
+		if ($external && !empty($assocData['finderQuery'])) {
+			return $assocData['finderQuery'];
+		}
+
+		$alias = $association;
+		$self = ($model->name == $linkModel->name);
+		$fields = array();
+
+		if ((!$external && in_array($type, array('hasOne', 'belongsTo')) && $this->__bypass === false) || $external) {
+			$fields = $this->fields($linkModel, $alias, $assocData['fields']);
+		}
+		if (empty($assocData['offset']) && !empty($assocData['page'])) {
+			$assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit'];
+		}
+		$assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']);
+
+		switch ($type) {
+			case 'hasOne':
+			case 'belongsTo':
+				$conditions = $this->__mergeConditions(
+					$assocData['conditions'],
+					$this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external', 'self')))
+				);
+
+				if (!$self && $external) {
+					foreach ($conditions as $key => $condition) {
+						if (is_numeric($key) && strpos($condition, $model->alias . '.') !== false) {
+							unset($conditions[$key]);
+						}
+					}
+				}
+
+				if ($external) {
+					$query = array_merge($assocData, array(
+						'conditions' => $conditions,
+						'table' => $this->fullTableName($linkModel),
+						'fields' => $fields,
+						'alias' => $alias,
+						'group' => null
+					));
+					$query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query);
+				} else {
+					$join = array(
+						'table' => $this->fullTableName($linkModel),
+						'alias' => $alias,
+						'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
+						'conditions' => trim($this->conditions($conditions, true, false, $model))
+					);
+					$queryData['fields'] = array_merge($queryData['fields'], $fields);
+
+					if (!empty($assocData['order'])) {
+						$queryData['order'][] = $assocData['order'];
+					}
+					if (!in_array($join, $queryData['joins'])) {
+						$queryData['joins'][] = $join;
+					}
+					return true;
+				}
+			break;
+			case 'hasMany':
+				$assocData['fields'] = $this->fields($linkModel, $alias, $assocData['fields']);
+				if (!empty($assocData['foreignKey'])) {
+					$assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}")));
+				}
+				$query = array(
+					'conditions' => $this->__mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $alias, $assocData), $assocData['conditions']),
+					'fields' => array_unique($assocData['fields']),
+					'table' => $this->fullTableName($linkModel),
+					'alias' => $alias,
+					'order' => $assocData['order'],
+					'limit' => $assocData['limit'],
+					'group' => null
+				);
+			break;
+			case 'hasAndBelongsToMany':
+				$joinFields = array();
+				$joinAssoc = null;
+
+				if (isset($assocData['with']) && !empty($assocData['with'])) {
+					$joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']);
+					list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys);
+
+					$joinTbl = $this->fullTableName($model->{$with});
+					$joinAlias = $joinTbl;
+
+					if (is_array($joinFields) && !empty($joinFields)) {
+						$joinFields = $this->fields($model->{$with}, $model->{$with}->alias, $joinFields);
+						$joinAssoc = $joinAlias = $model->{$with}->alias;
+					} else {
+						$joinFields = array();
+					}
+				} else {
+					$joinTbl = $this->fullTableName($assocData['joinTable']);
+					$joinAlias = $joinTbl;
+				}
+				$query = array(
+					'conditions' => $assocData['conditions'],
+					'limit' => $assocData['limit'],
+					'table' => $this->fullTableName($linkModel),
+					'alias' => $alias,
+					'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields),
+					'order' => $assocData['order'],
+					'group' => null,
+					'joins' => array(array(
+						'table' => $joinTbl,
+						'alias' => $joinAssoc,
+						'conditions' => $this->getConstraint('hasAndBelongsToMany', $model, $linkModel, $joinAlias, $assocData, $alias)
+					))
+				);
+			break;
+		}
+		if (isset($query)) {
+			return $this->buildStatement($query, $model);
+		}
+		return null;
+	}
+/**
+ * Returns a conditions array for the constraint between two models
+ *
+ * @param string $type Association type
+ * @param object $model Model object
+ * @param array $association Association array
+ * @return array Conditions array defining the constraint between $model and $association
+ */
+	function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) {
+		$assoc = array_merge(array('external' => false, 'self' => false), $assoc);
+
+		if (array_key_exists('foreignKey', $assoc) && empty($assoc['foreignKey'])) {
+			return array();
+		}
+
+		switch (true) {
+			case ($assoc['external'] && $type == 'hasOne'):
+				return array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}');
+			break;
+			case ($assoc['external'] && $type == 'belongsTo'):
+				return array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}');
+			break;
+			case (!$assoc['external'] && $type == 'hasOne'):
+				return array("{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}"));
+			break;
+			case (!$assoc['external'] && $type == 'belongsTo'):
+				return array("{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}"));
+			break;
+			case ($type == 'hasMany'):
+				return array("{$alias}.{$assoc['foreignKey']}" => array('{$__cakeID__$}'));
+			break;
+			case ($type == 'hasAndBelongsToMany'):
+				return array(
+					array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'),
+					array("{$alias}.{$assoc['associationForeignKey']}" => $this->identifier("{$alias2}.{$linkModel->primaryKey}"))
+				);
+			break;
+		}
+		return array();
+	}
+/**
+ * Builds and generates a JOIN statement from an array.	 Handles final clean-up before conversion.
+ *
+ * @param array $join An array defining a JOIN statement in a query
+ * @return string An SQL JOIN statement to be used in a query
+ * @see DboSource::renderJoinStatement()
+ * @see DboSource::buildStatement()
+ */
+	function buildJoinStatement($join) {
+		$data = array_merge(array(
+			'type' => null,
+			'alias' => null,
+			'table' => 'join_table',
+			'conditions' => array()
+		), $join);
+
+		if (!empty($data['alias'])) {
+			$data['alias'] = $this->alias . $this->name($data['alias']);
+		}
+		if (!empty($data['conditions'])) {
+			$data['conditions'] = trim($this->conditions($data['conditions'], true, false));
+		}
+		return $this->renderJoinStatement($data);
+	}
+/**
+ * Builds and generates an SQL statement from an array.	 Handles final clean-up before conversion.
+ *
+ * @param array $query An array defining an SQL query
+ * @param object $model The model object which initiated the query
+ * @return string An executable SQL statement
+ * @see DboSource::renderStatement()
+ */
+	function buildStatement($query, $model) {
+		$query = array_merge(array('offset' => null, 'joins' => array()), $query);
+		if (!empty($query['joins'])) {
+			$count = count($query['joins']);
+			for ($i = 0; $i < $count; $i++) {
+				if (is_array($query['joins'][$i])) {
+					$query['joins'][$i] = $this->buildJoinStatement($query['joins'][$i]);
+				}
+			}
+		}
+		return $this->renderStatement('select', array(
+			'conditions' => $this->conditions($query['conditions'], true, true, $model),
+			'fields' => join(', ', $query['fields']),
+			'table' => $query['table'],
+			'alias' => $this->alias . $this->name($query['alias']),
+			'order' => $this->order($query['order']),
+			'limit' => $this->limit($query['limit'], $query['offset']),
+			'joins' => join(' ', $query['joins']),
+			'group' => $this->group($query['group'])
+		));
+	}
+/**
+ * Renders a final SQL JOIN statement
+ *
+ * @param array $data
+ * @return string
+ */
+	function renderJoinStatement($data) {
+		extract($data);
+		return trim("{$type} JOIN {$table} {$alias} ON ({$conditions})");
+	}
+/**
+ * Renders a final SQL statement by putting together the component parts in the correct order
+ *
+ * @param string $type
+ * @param array $data
+ * @return string
+ */
+	function renderStatement($type, $data) {
+		extract($data);
+		$aliases = null;
+
+		switch (strtolower($type)) {
+			case 'select':
+				return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}";
+			break;
+			case 'create':
+				return "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
+			break;
+			case 'update':
+				if (!empty($alias)) {
+					$aliases = "{$this->alias}{$alias} {$joins} ";
+				}
+				return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}";
+			break;
+			case 'delete':
+				if (!empty($alias)) {
+					$aliases = "{$this->alias}{$alias} {$joins} ";
+				}
+				return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}";
+			break;
+			case 'schema':
+				foreach (array('columns', 'indexes') as $var) {
+					if (is_array(${$var})) {
+						${$var} = "\t" . join(",\n\t", array_filter(${$var}));
+					}
+				}
+				if (trim($indexes) != '') {
+					$columns .= ',';
+				}
+				return "CREATE TABLE {$table} (\n{$columns}{$indexes});";
+			break;
+			case 'alter':
+			break;
+		}
+	}
+/**
+ * Merges a mixed set of string/array conditions
+ *
+ * @return array
+ */
+	function __mergeConditions($query, $assoc) {
+		if (empty($assoc)) {
+			return $query;
+		}
+
+		if (is_array($query)) {
+			return array_merge((array)$assoc, $query);
+		}
+
+		if (!empty($query)) {
+			$query = array($query);
+			if (is_array($assoc)) {
+				$query = array_merge($query, $assoc);
+			} else {
+				$query[] = $assoc;
+			}
+			return $query;
+		}
+
+		return $assoc;
+	}
+/**
+ * Generates and executes an SQL UPDATE statement for given model, fields, and values.
+ * For databases that do not support aliases in UPDATE queries.
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param array $values
+ * @param mixed $conditions
+ * @return boolean Success
+ */
+	function update(&$model, $fields = array(), $values = null, $conditions = null) {
+		if ($values == null) {
+			$combined = $fields;
+		} else {
+			$combined = array_combine($fields, $values);
+		}
+		$fields = join(', ', $this->_prepareUpdateFields($model, $combined, empty($conditions)));
+
+		$alias = $joins = null;
+		$table = $this->fullTableName($model);
+		$conditions = $this->_matchRecords($model, $conditions);
+
+		if ($conditions === false) {
+			return false;
+		}
+		$query = compact('table', 'alias', 'joins', 'fields', 'conditions');
+
+		if (!$this->execute($this->renderStatement('update', $query))) {
+			$model->onError();
+			return false;
+		}
+		return true;
+	}
+/**
+ * Quotes and prepares fields and values for an SQL UPDATE statement
+ *
+ * @param Model $model
+ * @param array $fields
+ * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets
+ * @param boolean $alias Include the model alias in the field name
+ * @return array Fields and values, quoted and preparted
+ * @access protected
+ */
+	function _prepareUpdateFields(&$model, $fields, $quoteValues = true, $alias = false) {
+		$quotedAlias = $this->startQuote . $model->alias . $this->endQuote;
+
+		foreach ($fields as $field => $value) {
+			if ($alias && strpos($field, '.') === false) {
+				$quoted = $model->escapeField($field);
+			} elseif (!$alias && strpos($field, '.') !== false) {
+				$quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace(
+					$model->alias . '.', '', $field
+				)));
+			} else {
+				$quoted = $this->name($field);
+			}
+
+			if ($value === null) {
+				$updates[] = $quoted . ' = NULL';
+				continue;
+			}
+			$update = $quoted . ' = ';
+
+			if ($quoteValues) {
+				$update .= $this->value($value, $model->getColumnType($field), false);
+			} elseif (!$alias) {
+				$update .= str_replace($quotedAlias . '.', '', str_replace(
+					$model->alias . '.', '', $value
+				));
+			} else {
+				$update .= $value;
+			}
+			$updates[] =  $update;
+		}
+		return $updates;
+	}
+/**
+ * Generates and executes an SQL DELETE statement.
+ * For databases that do not support aliases in UPDATE queries.
+ *
+ * @param Model $model
+ * @param mixed $conditions
+ * @return boolean Success
+ */
+	function delete(&$model, $conditions = null) {
+		$alias = $joins = null;
+		$table = $this->fullTableName($model);
+		$conditions = $this->_matchRecords($model, $conditions);
+
+		if ($conditions === false) {
+			return false;
+		}
+
+		if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
+			$model->onError();
+			return false;
+		}
+		return true;
+	}
+/**
+ * Gets a list of record IDs for the given conditions.	Used for multi-record updates and deletes
+ * in databases that do not support aliases in UPDATE/DELETE queries.
+ *
+ * @param Model $model
+ * @param mixed $conditions
+ * @return array List of record IDs
+ * @access protected
+ */
+	function _matchRecords(&$model, $conditions = null) {
+		if ($conditions === true) {
+			$conditions = $this->conditions(true);
+		} elseif ($conditions === null) {
+			$conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model);
+		} else {
+			$idList = $model->find('all', array(
+				'fields' => "{$model->alias}.{$model->primaryKey}",
+				'conditions' => $conditions
+			));
+
+			if (empty($idList)) {
+				return false;
+			}
+			$conditions = $this->conditions(array(
+				$model->primaryKey => Set::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}")
+			));
+		}
+		return $conditions;
+	}
+/**
+ * Returns an array of SQL JOIN fragments from a model's associations
+ *
+ * @param object $model
+ * @return array
+ */
+	function _getJoins($model) {
+		$join = array();
+		$joins = array_merge($model->getAssociated('hasOne'), $model->getAssociated('belongsTo'));
+
+		foreach ($joins as $assoc) {
+			if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig) {
+				$assocData = $model->getAssociated($assoc);
+				$join[] = $this->buildJoinStatement(array(
+					'table' => $this->fullTableName($model->{$assoc}),
+					'alias' => $assoc,
+					'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
+					'conditions' => trim($this->conditions(
+						$this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData),
+						true, false, $model
+					))
+				));
+			}
+		}
+		return $join;
+	}
+/**
+ * Returns the an SQL calculation, i.e. COUNT() or MAX()
+ *
+ * @param model $model
+ * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'
+ * @param array $params Function parameters (any values must be quoted manually)
+ * @return string	An SQL calculation function
+ * @access public
+ */
+	function calculate(&$model, $func, $params = array()) {
+		$params = (array)$params;
+
+		switch (strtolower($func)) {
+			case 'count':
+				if (!isset($params[0])) {
+					$params[0] = '*';
+				}
+				if (!isset($params[1])) {
+					$params[1] = 'count';
+				}
+				return 'COUNT(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
+			case 'max':
+			case 'min':
+				if (!isset($params[1])) {
+					$params[1] = $params[0];
+				}
+				return strtoupper($func) . '(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
+			break;
+		}
+	}
+/**
+ * Deletes all the records in a table and resets the count of the auto-incrementing
+ * primary key, where applicable.
+ *
+ * @param mixed $table A string or model class representing the table to be truncated
+ * @return boolean	SQL TRUNCATE TABLE statement, false if not applicable.
+ * @access public
+ */
+	function truncate($table) {
+		return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table));
+	}
+/**
+ * Begin a transaction
+ *
+ * @param model $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function begin(&$model) {
+		if (parent::begin($model) && $this->execute($this->_commands['begin'])) {
+			$this->_transactionStarted = true;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Commit a transaction
+ *
+ * @param model $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function commit(&$model) {
+		if (parent::commit($model) && $this->execute($this->_commands['commit'])) {
+			$this->_transactionStarted = false;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Rollback a transaction
+ *
+ * @param model $model
+ * @return boolean True on success, false on fail
+ * (i.e. if the database/model does not support transactions,
+ * or a transaction has not started).
+ */
+	function rollback(&$model) {
+		if (parent::rollback($model) && $this->execute($this->_commands['rollback'])) {
+			$this->_transactionStarted = false;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Creates a default set of conditions from the model if $conditions is null/empty.
+ *
+ * @param object $model
+ * @param mixed	 $conditions
+ * @param boolean $useAlias Use model aliases rather than table names when generating conditions
+ * @return mixed
+ */
+	function defaultConditions(&$model, $conditions, $useAlias = true) {
+		if (!empty($conditions)) {
+			return $conditions;
+		}
+		if (!$model->exists()) {
+			return false;
+		}
+		$alias = $model->alias;
+
+		if (!$useAlias) {
+			$alias = $this->fullTableName($model, false);
+		}
+		return array("{$alias}.{$model->primaryKey}" => $model->getID());
+	}
+/**
+ * Returns a key formatted like a string Model.fieldname(i.e. Post.title, or Country.name)
+ *
+ * @param unknown_type $model
+ * @param unknown_type $key
+ * @param unknown_type $assoc
+ * @return string
+ */
+	function resolveKey($model, $key, $assoc = null) {
+		if (empty($assoc)) {
+			$assoc = $model->alias;
+		}
+		if (!strpos('.', $key)) {
+			return $this->name($model->alias) . '.' . $this->name($key);
+		}
+		return $key;
+	}
+/**
+ * Private helper method to remove query metadata in given data array.
+ *
+ * @param array $data
+ * @return array
+ */
+	function __scrubQueryData($data) {
+		foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) {
+			if (!isset($data[$key]) || empty($data[$key])) {
+				$data[$key] = array();
+			}
+		}
+		return $data;
+	}
+/**
+ * Generates the fields list of an SQL query.
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields
+ * @param boolean $quote If false, returns fields array unquoted
+ * @return array
+ */
+	function fields(&$model, $alias = null, $fields = array(), $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->alias;
+		}
+		if (empty($fields)) {
+			$fields = array_keys($model->schema());
+		} elseif (!is_array($fields)) {
+			$fields = String::tokenize($fields);
+		}
+		$fields = array_values(array_filter($fields));
+
+		if (!$quote) {
+			return $fields;
+		}
+		$count = count($fields);
+
+		if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) {
+			for ($i = 0; $i < $count; $i++) {
+				if (!preg_match('/^.+\\(.*\\)/', $fields[$i])) {
+					$prepend = '';
+
+					if (strpos($fields[$i], 'DISTINCT') !== false) {
+						$prepend = 'DISTINCT ';
+						$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
+					}
+					$dot = strpos($fields[$i], '.');
+
+					if ($dot === false) {
+						$prefix = !(
+							strpos($fields[$i], ' ') !== false ||
+							strpos($fields[$i], '(') !== false
+						);
+						$fields[$i] = $this->name(($prefix ? '' : '') . $alias . '.' . $fields[$i]);
+					} else {
+						$value = array();
+						$comma = strpos($fields[$i], ',');
+						if ($comma === false) {
+							$build = explode('.', $fields[$i]);
+							if (!Set::numeric($build)) {
+								$fields[$i] = $this->name($build[0] . '.' . $build[1]);
+							}
+							$comma = String::tokenize($fields[$i]);
+							foreach ($comma as $string) {
+								if (preg_match('/^[0-9]+\.[0-9]+$/', $string)) {
+									$value[] = $string;
+								} else {
+									$build = explode('.', $string);
+									$value[] = $this->name(trim($build[0]) . '.' . trim($build[1]));
+								}
+							}
+							$fields[$i] = implode(', ', $value);
+						}
+					}
+					$fields[$i] = $prepend . $fields[$i];
+				} elseif (preg_match('/\(([\.\w]+)\)/', $fields[$i], $field)) {
+					if (isset($field[1])) {
+						if (strpos($field[1], '.') === false) {
+							$field[1] = $this->name($alias . '.' . $field[1]);
+						} else {
+							$field[0] = explode('.', $field[1]);
+							if (!Set::numeric($field[0])) {
+								$field[0] = join('.', array_map(array($this, 'name'), $field[0]));
+								$fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1);
+							}
+						}
+					}
+				}
+			}
+		}
+		return array_unique($fields);
+	}
+/**
+ * Creates a WHERE clause by parsing given conditions data.
+ *
+ * @param mixed $conditions Array or string of conditions
+ * @param boolean $quoteValues If true, values should be quoted
+ * @param boolean $where If true, "WHERE " will be prepended to the return value
+ * @param Model $model A reference to the Model instance making the query
+ * @return string SQL fragment
+ */
+	function conditions($conditions, $quoteValues = true, $where = true, $model = null) {
+		$clause = $out = '';
+
+		if ($where) {
+			$clause = ' WHERE ';
+		}
+
+		if (is_array($conditions) && !empty($conditions)) {
+			$out = $this->conditionKeysToString($conditions, $quoteValues, $model);
+
+			if (empty($out)) {
+				return $clause . ' 1 = 1';
+			}
+			return $clause . join(' AND ', $out);
+		}
+
+		if (empty($conditions) || trim($conditions) == '' || $conditions === true) {
+			return $clause . '1 = 1';
+		}
+		$clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i';
+
+		if (preg_match($clauses, $conditions, $match)) {
+			$clause = '';
+		}
+		if (trim($conditions) == '') {
+			$conditions = ' 1 = 1';
+		} else {
+			$conditions = $this->__quoteFields($conditions);
+		}
+		return $clause . $conditions;
+	}
+/**
+ * Creates a WHERE clause by parsing given conditions array.  Used by DboSource::conditions().
+ *
+ * @param array $conditions Array or string of conditions
+ * @param boolean $quoteValues If true, values should be quoted
+ * @param Model $model A reference to the Model instance making the query
+ * @return string SQL fragment
+ */
+	function conditionKeysToString($conditions, $quoteValues = true, $model = null) {
+		$c = 0;
+		$out = array();
+		$data = $columnType = null;
+		$bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&');
+
+		foreach ($conditions as $key => $value) {
+			$join = ' AND ';
+			$not = null;
+
+			if (is_array($value)) {
+				$valueInsert = (
+					!empty($value) &&
+					(substr_count($key, '?') == count($value) || substr_count($key, ':') == count($value))
+				);
+			}
+
+			if (is_numeric($key) && empty($value)) {
+				continue;
+			} elseif (is_numeric($key) && is_string($value)) {
+				$out[] = $not . $this->__quoteFields($value);
+			} elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) {
+				if (in_array(strtolower(trim($key)), $bool)) {
+					$join = ' ' . strtoupper($key) . ' ';
+				} else {
+					$key = $join;
+				}
+				$value = $this->conditionKeysToString($value, $quoteValues, $model);
+
+				if (strpos($join, 'NOT') !== false) {
+					if (strtoupper(trim($key)) == 'NOT') {
+						$key = 'AND ' . trim($key);
+					}
+					$not = 'NOT ';
+				}
+
+				if (empty($value[1])) {
+					if ($not) {
+						$out[] = $not . '(' . $value[0] . ')';
+					} else {
+						$out[] = $value[0] ;
+					}
+				} else {
+					$out[] = '(' . $not . '(' . join(') ' . strtoupper($key) . ' (', $value) . '))';
+				}
+
+			} else {
+				if (is_object($value) && isset($value->type)) {
+					if ($value->type == 'identifier') {
+						$data .= $this->name($key) . ' = ' . $this->name($value->value);
+					} elseif ($value->type == 'expression') {
+						if (is_numeric($key)) {
+							$data .= $value->value;
+						} else {
+							$data .= $this->name($key) . ' = ' . $value->value;
+						}
+					}
+				} elseif (is_array($value) && !empty($value) && !$valueInsert) {
+					$keys = array_keys($value);
+					if (array_keys($value) === array_values(array_keys($value))) {
+						$count = count($value);
+						if ($count === 1) {
+							$data = $this->name($key) . ' = (';
+						} else {
+							$data = $this->name($key) . ' IN (';
+						}
+						if ($quoteValues || strpos($value[0], '-!') !== 0) {
+							if (is_object($model)) {
+								$columnType = $model->getColumnType($key);
+							}
+							$data .= join(', ', $this->value($value, $columnType));
+						}
+						$data .= ')';
+					} else {
+						$ret = $this->conditionKeysToString($value, $quoteValues, $model);
+						if (count($ret) > 1) {
+							$data = '(' . join(') AND (', $ret) . ')';
+						} elseif (isset($ret[0])) {
+							$data = $ret[0];
+						}
+					}
+				} elseif (is_numeric($key) && !empty($value)) {
+					$data = $this->__quoteFields($value);
+				} else {
+					$data = $this->__parseKey($model, trim($key), $value);
+				}
+
+				if ($data != null) {
+					if (preg_match('/^\(\(\((.+)\)\)\)$/', $data)) {
+						$data = substr($data, 1, strlen($data) - 2);
+					}
+					$out[] = $data;
+					$data = null;
+				}
+			}
+			$c++;
+		}
+		return $out;
+	}
+/**
+ * Extracts a Model.field identifier and an SQL condition operator from a string, formats
+ * and inserts values, and composes them into an SQL snippet.
+ *
+ * @param Model $model Model object initiating the query
+ * @param string $key An SQL key snippet containing a field and optional SQL operator
+ * @param mixed $value The value(s) to be inserted in the string
+ * @return string
+ * @access private
+ */
+	function __parseKey($model, $key, $value) {
+		$operatorMatch = '/^((' . join(')|(', $this->__sqlOps);
+		$operatorMatch .= '\\x20)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is';
+		$bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false));
+
+		if (!strpos($key, ' ')) {
+			$operator = '=';
+		} else {
+			list($key, $operator) = explode(' ', trim($key), 2);
+
+			if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) {
+				$key = $key . ' ' . $operator;
+				$split = strrpos($key, ' ');
+				$operator = substr($key, $split);
+				$key = substr($key, 0, $split);
+			}
+		}
+		$type = (is_object($model) ? $model->getColumnType($key) : null);
+		$null = ($value === null || (is_array($value) && empty($value)));
+
+		if (strtolower($operator) === 'not') {
+			$data = $this->conditionKeysToString(
+				array($operator => array($key => $value)), true, $model
+			);
+			return $data[0];
+		}
+		$value = $this->value($value, $type);
+
+		$key = (strpos($key, '(') !== false || strpos($key, ')') !== false) ?
+			$this->__quoteFields($key) :
+			$key = $this->name($key);
+
+		if ($bound) {
+			return String::insert($key . ' ' . trim($operator), $value);
+		}
+
+		if (!preg_match($operatorMatch, trim($operator))) {
+			$operator .= ' =';
+		}
+		$operator = trim($operator);
+
+		if (is_array($value)) {
+			$value = join(', ', $value);
+
+			switch ($operator) {
+				case '=':
+					$operator = 'IN';
+				break;
+				case '!=':
+				case '<>':
+					$operator = 'NOT IN';
+				break;
+			}
+			$value = "({$value})";
+		} elseif ($null) {
+			switch ($operator) {
+				case '=':
+					$operator = 'IS';
+				break;
+				case '!=':
+				case '<>':
+					$operator = 'IS NOT';
+				break;
+			}
+		}
+		return "{$key} {$operator} {$value}";
+	}
+/**
+ * Quotes Model.fields
+ *
+ * @param string $conditions
+ * @return string or false if no match
+ * @access private
+ */
+	function __quoteFields($conditions) {
+		$start = $end  = null;
+		$original = $conditions;
+
+		if (!empty($this->startQuote)) {
+			$start = preg_quote($this->startQuote);
+		}
+		if (!empty($this->endQuote)) {
+			$end = preg_quote($this->endQuote);
+		}
+		$conditions = str_replace(array($start, $end), '', $conditions);
+		preg_match_all('/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_' . $start . $end . ']*\\.[a-z0-9_' . $start . $end . ']*)/i', $conditions, $replace, PREG_PATTERN_ORDER);
+
+		if (isset($replace['1']['0'])) {
+			$pregCount = count($replace['1']);
+
+			for ($i = 0; $i < $pregCount; $i++) {
+				if (!empty($replace['1'][$i]) && !is_numeric($replace['1'][$i])) {
+					$conditions = preg_replace('/\b' . preg_quote($replace['1'][$i]) . '\b/', $this->name($replace['1'][$i]), $conditions);
+				}
+			}
+			return $conditions;
+		}
+		return $original;
+	}
+/**
+ * Returns a limit statement in the correct format for the particular database.
+ *
+ * @param integer $limit Limit of results returned
+ * @param integer $offset Offset from which to start results
+ * @return string SQL limit/offset statement
+ */
+	function limit($limit, $offset = null) {
+		if ($limit) {
+			$rt = '';
+			if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
+				$rt = ' LIMIT';
+			}
+
+			if ($offset) {
+				$rt .= ' ' . $offset . ',';
+			}
+
+			$rt .= ' ' . $limit;
+			return $rt;
+		}
+		return null;
+	}
+/**
+ * Returns an ORDER BY clause as a string.
+ *
+ * @param string $key Field reference, as a key (i.e. Post.title)
+ * @param string $direction Direction (ASC or DESC)
+ * @return string ORDER BY clause
+ */
+	function order($keys, $direction = 'ASC') {
+		if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) {
+			$keys = array_map('trim', explode(',', $keys));
+		}
+
+		if (is_array($keys)) {
+			$keys = array_filter($keys);
+		}
+
+		if (empty($keys) || (is_array($keys) && count($keys) && isset($keys[0]) && empty($keys[0]))) {
+			return '';
+		}
+
+		if (is_array($keys)) {
+			$keys = (Set::countDim($keys) > 1) ? array_map(array(&$this, 'order'), $keys) : $keys;
+
+			foreach ($keys as $key => $value) {
+				if (is_numeric($key)) {
+					$key = $value = ltrim(str_replace('ORDER BY ', '', $this->order($value)));
+					$value = (!preg_match('/\\x20ASC|\\x20DESC/i', $key) ? ' ' . $direction : '');
+				} else {
+					$value = ' ' . $value;
+				}
+
+				if (!preg_match('/^.+\\(.*\\)/', $key) && !strpos($key, ',')) {
+					if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $dir)) {
+						$dir = $dir[0];
+						$key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
+					} else {
+						$dir = '';
+					}
+					$key = trim($key);
+					if (!preg_match('/\s/', $key)) {
+						$key = $this->name($key);
+					}
+					$key .= ' ' . trim($dir);
+				}
+				$order[] = $this->order($key . $value);
+			}
+			return ' ORDER BY ' . trim(str_replace('ORDER BY', '', join(',', $order)));
+		}
+		$keys = preg_replace('/ORDER\\x20BY/i', '', $keys);
+
+		if (strpos($keys, '.')) {
+			preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $keys, $result, PREG_PATTERN_ORDER);
+			$pregCount = count($result[0]);
+
+			for ($i = 0; $i < $pregCount; $i++) {
+				if (!is_numeric($result[0][$i])) {
+					$keys = preg_replace('/' . $result[0][$i] . '/', $this->name($result[0][$i]), $keys);
+				}
+			}
+			$result = ' ORDER BY ' . $keys;
+			return $result . (!preg_match('/\\x20ASC|\\x20DESC/i', $keys) ? ' ' . $direction : '');
+
+		} elseif (preg_match('/(\\x20ASC|\\x20DESC)/i', $keys, $match)) {
+			$direction = $match[1];
+			return ' ORDER BY ' . preg_replace('/' . $match[1] . '/', '', $keys) . $direction;
+		}
+		return ' ORDER BY ' . $keys . ' ' . $direction;
+	}
+/**
+ * Create a GROUP BY SQL clause
+ *
+ * @param string $group Group By Condition
+ * @return mixed string condition or null
+ */
+	function group($group) {
+		if ($group) {
+			if (is_array($group)) {
+				$group = join(', ', $group);
+			}
+			return ' GROUP BY ' . $this->__quoteFields($group);
+		}
+		return null;
+	}
+/**
+ * Disconnects database, kills the connection and says the connection is closed,
+ * and if DEBUG is turned on, the log for this object is shown.
+ *
+ */
+	function close() {
+		if (Configure::read() > 1) {
+			$this->showLog();
+		}
+		$this->disconnect();
+	}
+/**
+ * Checks if the specified table contains any record matching specified SQL
+ *
+ * @param Model $model Model to search
+ * @param string $sql SQL WHERE clause (condition only, not the "WHERE" part)
+ * @return boolean True if the table has a matching record, else false
+ */
+	function hasAny(&$Model, $sql) {
+		$sql = $this->conditions($sql);
+		$table = $this->fullTableName($Model);
+		$where = $sql ? "WHERE {$sql}" : 'WHERE 1 = 1';
+		$id = $Model->primaryKey;
+
+		$out = $this->fetchRow("SELECT COUNT({$id}) {$this->alias}count FROM {$table} {$where}");
+
+		if (is_array($out)) {
+			return $out[0]['count'];
+		}
+		return false;
+	}
+/**
+ * Gets the length of a database-native column description, or null if no length
+ *
+ * @param string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return mixed An integer or string representing the length of the column
+ */
+	function length($real) {
+		if (!preg_match_all('/([\w\s]+)(?:\((\d+)(?:,(\d+))?\))?(\sunsigned)?(\szerofill)?/', $real, $result)) {
+			trigger_error(__('FIXME: Can\'t parse field: ' . $real, true), E_USER_WARNING);
+			$col = str_replace(array(')', 'unsigned'), '', $real);
+			$limit = null;
+
+			if (strpos($col, '(') !== false) {
+				list($col, $limit) = explode('(', $col);
+			}
+			if ($limit != null) {
+				return intval($limit);
+			}
+			return null;
+		}
+
+		$types = array(
+			'int' => 1, 'tinyint' => 1, 'smallint' => 1, 'mediumint' => 1, 'integer' => 1, 'bigint' => 1
+		);
+
+		list($real, $type, $length, $offset, $sign, $zerofill) = $result;
+		$typeArr = $type;
+		$type = $type[0];
+		$length = $length[0];
+		$offset = $offset[0];
+
+		$isFloat = in_array($type, array('dec', 'decimal', 'float', 'numeric', 'double'));
+		if ($isFloat && $offset) {
+			return $length.','.$offset;
+		}
+
+		if (($real[0] == $type) && (count($real) == 1)) {
+			return null;
+		}
+
+		if (isset($types[$type])) {
+			$length += $types[$type];
+			if (!empty($sign)) {
+				$length--;
+			}
+		} elseif (in_array($type, array('enum', 'set'))) {
+			$length = 0;
+			foreach ($typeArr as $key => $enumValue) {
+				if ($key == 0) {
+					continue;
+				}
+				$tmpLength = strlen($enumValue);
+				if ($tmpLength > $length) {
+					$length = $tmpLength;
+				}
+			}
+		}
+		return intval($length);
+	}
+/**
+ * Translates between PHP boolean values and Database (faked) boolean values
+ *
+ * @param mixed $data Value to be translated
+ * @return mixed Converted boolean value
+ */
+	function boolean($data) {
+		if ($data === true || $data === false) {
+			if ($data === true) {
+				return 1;
+			}
+			return 0;
+		} else {
+			return !empty($data);
+		}
+	}
+/**
+ * Inserts multiple values into a table
+ *
+ * @param string $table
+ * @param string $fields
+ * @param array $values
+ * @access protected
+ */
+	function insertMulti($table, $fields, $values) {
+		$table = $this->fullTableName($table);
+		if (is_array($fields)) {
+			$fields = join(', ', array_map(array(&$this, 'name'), $fields));
+		}
+		$count = count($values);
+		for ($x = 0; $x < $count; $x++) {
+			$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}");
+		}
+	}
+/**
+ * Returns an array of the indexes in given datasource name.
+ *
+ * @param string $model Name of model to inspect
+ * @return array Fields in table. Keys are column and unique
+ */
+	function index($model) {
+		return false;
+	}
+/**
+ * Generate a database-native schema for the given Schema object
+ *
+ * @param object $schema An instance of a subclass of CakeSchema
+ * @param string $tableName Optional.  If specified only the table name given will be generated.
+ *						Otherwise, all tables defined in the schema are generated.
+ * @return string
+ */
+	function createSchema($schema, $tableName = null) {
+		if (!is_a($schema, 'CakeSchema')) {
+			trigger_error(__('Invalid schema object', true), E_USER_WARNING);
+			return null;
+		}
+		$out = '';
+
+		foreach ($schema->tables as $curTable => $columns) {
+			if (!$tableName || $tableName == $curTable) {
+				$cols = $colList = $indexes = array();
+				$primary = null;
+				$table = $this->fullTableName($curTable);
+
+				foreach ($columns as $name => $col) {
+					if (is_string($col)) {
+						$col = array('type' => $col);
+					}
+					if (isset($col['key']) && $col['key'] == 'primary') {
+						$primary = $name;
+					}
+					if ($name !== 'indexes') {
+						$col['name'] = $name;
+						if (!isset($col['type'])) {
+							$col['type'] = 'string';
+						}
+						$cols[] = $this->buildColumn($col);
+					} else {
+						$indexes = array_merge($indexes, $this->buildIndex($col, $table));
+					}
+				}
+				if (empty($indexes) && !empty($primary)) {
+					$col = array('PRIMARY' => array('column' => $primary, 'unique' => 1));
+					$indexes = array_merge($indexes, $this->buildIndex($col, $table));
+				}
+				$columns = $cols;
+				$out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes')) . "\n\n";
+			}
+		}
+		return $out;
+	}
+/**
+ * Generate a alter syntax from	 CakeSchema::compare()
+ *
+ * @param unknown_type $schema
+ * @return unknown
+ */
+	function alterSchema($compare, $table = null) {
+		return false;
+	}
+/**
+ * Generate a "drop table" statement for the given Schema object
+ *
+ * @param object $schema An instance of a subclass of CakeSchema
+ * @param string $table Optional.  If specified only the table name given will be generated.
+ *						Otherwise, all tables defined in the schema are generated.
+ * @return string
+ */
+	function dropSchema($schema, $table = null) {
+		if (!is_a($schema, 'CakeSchema')) {
+			trigger_error(__('Invalid schema object', true), E_USER_WARNING);
+			return null;
+		}
+		$out = '';
+
+		foreach ($schema->tables as $curTable => $columns) {
+			if (!$table || $table == $curTable) {
+				$out .= 'DROP TABLE ' . $this->fullTableName($curTable) . ";\n";
+			}
+		}
+		return $out;
+	}
+/**
+ * Generate a database-native column schema string
+ *
+ * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
+ *						where options can be 'default', 'length', or 'key'.
+ * @return string
+ */
+	function buildColumn($column) {
+		$name = $type = null;
+		extract(array_merge(array('null' => true), $column));
+
+		if (empty($name) || empty($type)) {
+			trigger_error('Column name or type not defined in schema', E_USER_WARNING);
+			return null;
+		}
+
+		if (!isset($this->columns[$type])) {
+			trigger_error("Column type {$type} does not exist", E_USER_WARNING);
+			return null;
+		}
+
+		$real = $this->columns[$type];
+		$out = $this->name($name) . ' ' . $real['name'];
+
+		if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
+			if (isset($column['length'])) {
+				$length = $column['length'];
+			} elseif (isset($column['limit'])) {
+				$length = $column['limit'];
+			} elseif (isset($real['length'])) {
+				$length = $real['length'];
+			} else {
+				$length = $real['limit'];
+			}
+			$out .= '(' . $length . ')';
+		}
+
+		if (($column['type'] == 'integer' || $column['type'] == 'float' ) && isset($column['default']) && $column['default'] === '') {
+			$column['default'] = null;
+		}
+
+		if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
+			$out .= ' ' . $this->columns['primary_key']['name'];
+		} elseif (isset($column['key']) && $column['key'] == 'primary') {
+			$out .= ' NOT NULL';
+		} elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
+			$out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL';
+		} elseif (isset($column['default'])) {
+			$out .= ' DEFAULT ' . $this->value($column['default'], $type);
+		} elseif (isset($column['null']) && $column['null'] == true) {
+			$out .= ' DEFAULT NULL';
+		} elseif (isset($column['null']) && $column['null'] == false) {
+			$out .= ' NOT NULL';
+		}
+		return $out;
+	}
+/**
+ * Format indexes for create table
+ *
+ * @param array $indexes
+ * @param string $table
+ * @return array
+ */
+	function buildIndex($indexes, $table = null) {
+		$join = array();
+		foreach ($indexes as $name => $value) {
+			$out = '';
+			if ($name == 'PRIMARY') {
+				$out .= 'PRIMARY ';
+				$name = null;
+			} else {
+				if (!empty($value['unique'])) {
+					$out .= 'UNIQUE ';
+				}
+			}
+			if (is_array($value['column'])) {
+				$out .= 'KEY '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
+			} else {
+				$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
+			}
+			$join[] = $out;
+		}
+		return $join;
+	}
+/**
+ * Guesses the data type of an array
+ *
+ * @param string $value
+ * @return void
+ * @access public
+ */
+	function introspectType($value) {
+		if (!is_array($value)) {
+			if ($value === true || $value === false) {
+				return 'boolean';
+			}
+			if (is_float($value) && floatval($value) === $value) {
+				return 'float';
+			}
+			if (is_int($value) && intval($value) === $value) {
+				return 'integer';
+			}
+			if (is_string($value) && strlen($value) > 255) {
+				return 'text';
+			}
+			return 'string';
+		}
+
+		$isAllFloat = $isAllInt = true;
+		$containsFloat = $containsInt = $containsString = false;
+		foreach ($value as $key => $valElement) {
+			$valElement = trim($valElement);
+			if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) {
+				$isAllFloat = false;
+			} else {
+				$containsFloat = true;
+				continue;
+			}
+			if (!is_int($valElement) && !preg_match('/^[\d]+$/', $valElement)) {
+				$isAllInt = false;
+			} else {
+				$containsInt = true;
+				continue;
+			}
+			$containsString = true;
+		}
+
+		if ($isAllFloat) {
+			return 'float';
+		}
+		if ($isAllInt) {
+			return 'integer';
+		}
+
+		if ($containsInt && !$containsString) {
+			return 'integer';
+		}
+		return 'string';
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/folder.php
===================================================================
--- cake/libs/folder.php	(revision 0)
+++ cake/libs/folder.php	(revision 0)
@@ -0,0 +1,768 @@
+<?php
+/* SVN FILE: $Id: folder.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Convenience class for handling directories.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	uses('object');
+}
+/**
+ * Folder structure browser, lists folders and files.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Folder extends Object {
+/**
+ * Path to Folder.
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+/**
+ * Sortedness.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $sort = false;
+/**
+ * mode to be used on create.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $mode = 0755;
+/**
+ * holds messages from last method.
+ *
+ * @var array
+ * @access private
+ */
+	var $__messages = array();
+/**
+ * holds errors from last method.
+ *
+ * @var array
+ * @access private
+ */
+	var $__errors = false;
+/**
+ * holds array of complete directory paths.
+ *
+ * @var array
+ * @access private
+ */
+	var $__directories;
+/**
+ * holds array of complete file paths.
+ *
+ * @var array
+ * @access private
+ */
+	var $__files;
+/**
+ * Constructor.
+ *
+ * @param string $path Path to folder
+ * @param boolean $create Create folder if not found
+ * @param mixed $mode Mode (CHMOD) to apply to created folder, false to ignore
+ */
+	function __construct($path = false, $create = false, $mode = false) {
+		parent::__construct();
+		if (empty($path)) {
+			$path = TMP;
+		}
+		if ($mode) {
+			$this->mode = $mode;
+		}
+
+		if (!file_exists($path) && $create === true) {
+			$this->create($path, $this->mode);
+		}
+		if (!Folder::isAbsolute($path)) {
+			$path = realpath($path);
+		}
+		if (!empty($path)) {
+			$this->cd($path);
+		}
+	}
+/**
+ * Return current path.
+ *
+ * @return string Current path
+ * @access public
+ */
+	function pwd() {
+		return $this->path;
+	}
+/**
+ * Change directory to $path.
+ *
+ * @param string $path Path to the directory to change to
+ * @return string The new path. Returns false on failure
+ * @access public
+ */
+	function cd($path) {
+		$path = $this->realpath($path);
+		if (is_dir($path)) {
+			return $this->path = $path;
+		}
+		return false;
+	}
+/**
+ * Returns an array of the contents of the current directory.
+ * The returned array holds two arrays: One of directories and one of files.
+ *
+ * @param boolean $sort
+ * @param mixed $exceptions Either an array or boolean true will not grab dot files
+ * @param boolean $fullPath True returns the full path
+ * @return mixed Contents of current directory as an array, an empty array on failure
+ * @access public
+ */
+	function read($sort = true, $exceptions = false, $fullPath = false) {
+		$dirs = $files = array();
+
+		if (is_array($exceptions)) {
+			$exceptions = array_flip($exceptions);
+		}
+		$skipHidden = isset($exceptions['.']) || $exceptions === true;
+
+		if (false === ($dir = @opendir($this->path))) {
+			return array($dirs, $files);
+		}
+
+		while (false !== ($item = readdir($dir))) {
+			if ($item === '.' || $item === '..' || ($skipHidden && $item[0] === '.') || isset($exceptions[$item])) {
+				continue;
+			}
+
+			$path = Folder::addPathElement($this->path, $item);
+			if (is_dir($path)) {
+				$dirs[] = $fullPath ? $path : $item;
+			} else {
+				$files[] = $fullPath ? $path : $item;
+			}
+		}
+
+		if ($sort || $this->sort) {
+			sort($dirs);
+			sort($files);
+		}
+
+		closedir($dir);
+		return array($dirs, $files);
+	}
+/**
+ * Returns an array of all matching files in current directory.
+ *
+ * @param string $pattern Preg_match pattern (Defaults to: .*)
+ * @return array Files that match given pattern
+ * @access public
+ */
+	function find($regexpPattern = '.*', $sort = false) {
+		list($dirs, $files) = $this->read($sort);
+		return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files)); ;
+	}
+/**
+ * Returns an array of all matching files in and below current directory.
+ *
+ * @param string $pattern Preg_match pattern (Defaults to: .*)
+ * @return array Files matching $pattern
+ * @access public
+ */
+	function findRecursive($pattern = '.*', $sort = false) {
+		$startsOn = $this->path;
+		$out = $this->_findRecursive($pattern, $sort);
+		$this->cd($startsOn);
+		return $out;
+	}
+/**
+ * Private helper function for findRecursive.
+ *
+ * @param string $pattern Pattern to match against
+ * @return array Files matching pattern
+ * @access private
+ */
+	function _findRecursive($pattern, $sort = false) {
+		list($dirs, $files) = $this->read($sort);
+
+		$found = array();
+		foreach ($files as $file) {
+			if (preg_match('/^' . $pattern . '$/i', $file)) {
+				$found[] = Folder::addPathElement($this->path, $file);
+			}
+		}
+		$start = $this->path;
+		foreach ($dirs as $dir) {
+			$this->cd(Folder::addPathElement($start, $dir));
+			$found = array_merge($found, $this->findRecursive($pattern));
+		}
+		return $found;
+	}
+/**
+ * Returns true if given $path is a Windows path.
+ *
+ * @param string $path Path to check
+ * @return boolean true if windows path, false otherwise
+ * @access public
+ * @static
+ */
+	function isWindowsPath($path) {
+		if (preg_match('/^[A-Z]:\\\\/i', $path)) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns true if given $path is an absolute path.
+ *
+ * @param string $path Path to check
+ * @return bool
+ * @access public
+ * @static
+ */
+	function isAbsolute($path) {
+		$match = preg_match('/^\\//', $path) || preg_match('/^[A-Z]:\\\\/i', $path);
+		return $match;
+	}
+/**
+ * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
+ *
+ * @param string $path Path to check
+ * @return string Set of slashes ("\\" or "/")
+ * @access public
+ * @static
+ */
+	function normalizePath($path) {
+		return Folder::correctSlashFor($path);
+	}
+/**
+ * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
+ *
+ * @param string $path Path to check
+ * @return string Set of slashes ("\\" or "/")
+ * @access public
+ * @static
+ */
+	function correctSlashFor($path) {
+		if (Folder::isWindowsPath($path)) {
+			return '\\';
+		}
+		return '/';
+	}
+/**
+ * Returns $path with added terminating slash (corrected for Windows or other OS).
+ *
+ * @param string $path Path to check
+ * @return string Path with ending slash
+ * @access public
+ * @static
+ */
+	function slashTerm($path) {
+		if (Folder::isSlashTerm($path)) {
+			return $path;
+		}
+		return $path . Folder::correctSlashFor($path);
+	}
+/**
+ * Returns $path with $element added, with correct slash in-between.
+ *
+ * @param string $path Path
+ * @param string $element Element to and at end of path
+ * @return string Combined path
+ * @access public
+ * @static
+ */
+	function addPathElement($path, $element) {
+		return Folder::slashTerm($path) . $element;
+	}
+/**
+ * Returns true if the File is in a given CakePath.
+ *
+ * @return bool
+ * @access public
+ */
+	function inCakePath($path = '') {
+		$dir = substr(Folder::slashTerm(ROOT), 0, -1);
+		$newdir = $dir . $path;
+
+		return $this->inPath($newdir);
+	}
+/**
+ * Returns true if the File is in given path.
+ *
+ * @return bool
+ * @access public
+ */
+	function inPath($path = '', $reverse = false) {
+		$dir = Folder::slashTerm($path);
+		$current = Folder::slashTerm($this->pwd());
+
+		if (!$reverse) {
+			$return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
+		} else {
+			$return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
+		}
+		if ($return == 1) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Change the mode on a directory structure recursively. This includes changing the mode on files as well.
+ *
+ * @param string $path The path to chmod
+ * @param integer $mode octal value 0755
+ * @param boolean $recursive chmod recursively
+ * @param array $exceptions array of files, directories to skip
+ * @return boolean Returns TRUE on success, FALSE on failure
+ * @access public
+ */
+	function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
+		if (!$mode) {
+			$mode = $this->mode;
+		}
+
+		if ($recursive === false && is_dir($path)) {
+			if (@chmod($path, intval($mode, 8))) {
+				$this->__messages[] = sprintf(__('%s changed to %s', true), $path, $mode);
+				return true;
+			}
+
+			$this->__errors[] = sprintf(__('%s NOT changed to %s', true), $path, $mode);
+			return false;
+		}
+
+		if (is_dir($path)) {
+			$paths = $this->tree($path);
+
+			foreach ($paths as $type) {
+				foreach ($type as $key => $fullpath) {
+					$check = explode(DS, $fullpath);
+					$count = count($check);
+
+					if (in_array($check[$count - 1], $exceptions)) {
+						continue;
+					}
+
+					if (@chmod($fullpath, intval($mode, 8))) {
+						$this->__messages[] = sprintf(__('%s changed to %s', true), $fullpath, $mode);
+					} else {
+						$this->__errors[] = sprintf(__('%s NOT changed to %s', true), $fullpath, $mode);
+					}
+				}
+			}
+
+			if (empty($this->__errors)) {
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Returns an array of nested directories and files in each directory
+ *
+ * @param string $path the directory path to build the tree from
+ * @param boolean $hidden return hidden files and directories
+ * @param string $type either file or dir. null returns both files and directories
+ * @return mixed array of nested directories and files in each directory
+ * @access public
+ */
+	function tree($path, $exceptions = true, $type = null) {
+		$original = $this->path;
+		$path = rtrim($path, DS);
+		$this->__files = array();
+		$this->__directories = array($path);
+		$directories = array();
+
+		if ($exceptions === false) {
+			$exceptions = true;
+		}
+		while (count($this->__directories)) {
+			$dir = array_pop($this->__directories);
+			$this->__tree($dir, $exceptions);
+			$directories[] = $dir;
+		}
+
+		if ($type === null) {
+			return array($directories, $this->__files);
+		}
+		if ($type === 'dir') {
+			return $directories;
+		}
+		$this->cd($original);
+
+		return $this->__files;
+	}
+/**
+ * Private method to list directories and files in each directory
+ *
+ * @param string $path
+ * @param = boolean $hidden
+ * @access private
+ */
+	function __tree($path, $exceptions) {
+		if ($this->cd($path)) {
+			list($dirs, $files) = $this->read(false, $exceptions, true);
+			$this->__directories = array_merge($this->__directories, $dirs);
+			$this->__files = array_merge($this->__files, $files);
+		}
+	}
+/**
+ * Create a directory structure recursively.
+ *
+ * @param string $pathname The directory structure to create
+ * @param integer $mode octal value 0755
+ * @return boolean Returns TRUE on success, FALSE on failure
+ * @access public
+ */
+	function create($pathname, $mode = false) {
+		if (is_dir($pathname) || empty($pathname)) {
+			return true;
+		}
+
+		if (!$mode) {
+			$mode = $this->mode;
+		}
+
+		if (is_file($pathname)) {
+			$this->__errors[] = sprintf(__('%s is a file', true), $pathname);
+			return false;
+		}
+		$nextPathname = substr($pathname, 0, strrpos($pathname, DS));
+
+		if ($this->create($nextPathname, $mode)) {
+			if (!file_exists($pathname)) {
+				$old = umask(0);
+				if (mkdir($pathname, $mode)) {
+					umask($old);
+					$this->__messages[] = sprintf(__('%s created', true), $pathname);
+					return true;
+				} else {
+					umask($old);
+					$this->__errors[] = sprintf(__('%s NOT created', true), $pathname);
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Returns the size in bytes of this Folder.
+ *
+ * @param string $directory Path to directory
+ * @return int size in bytes of current folder
+ * @access public
+ */
+	function dirsize() {
+		$size = 0;
+		$directory = Folder::slashTerm($this->path);
+		$stack = array($directory);
+		$count = count($stack);
+		for ($i = 0, $j = $count; $i < $j; ++$i) {
+			if (is_file($stack[$i])) {
+				$size += filesize($stack[$i]);
+			} elseif (is_dir($stack[$i])) {
+				$dir = dir($stack[$i]);
+				if ($dir) {
+					while (false !== ($entry = $dir->read())) {
+						if ($entry === '.' || $entry === '..') {
+							continue;
+						}
+						$add = $stack[$i] . $entry;
+
+						if (is_dir($stack[$i] . $entry)) {
+							$add = Folder::slashTerm($add);
+						}
+						$stack[] = $add;
+					}
+					$dir->close();
+				}
+			}
+			$j = count($stack);
+		}
+		return $size;
+	}
+/**
+ * Recursively Remove directories if system allow.
+ *
+ * @param string $path Path of directory to delete
+ * @return boolean Success
+ * @access public
+ */
+	function delete($path = null) {
+		if (!$path) {
+			$path = $this->pwd();
+		}
+		$path = Folder::slashTerm($path);
+		if (is_dir($path) === true) {
+			$normalFiles = glob($path . '*');
+			$hiddenFiles = glob($path . '\.?*');
+			$files = array_merge($normalFiles, $hiddenFiles);
+			if (is_array($files)) {
+				foreach ($files as $file) {
+					if (preg_match('/(\.|\.\.)$/', $file)) {
+						continue;
+					}
+					if (is_file($file) === true) {
+						if (@unlink($file)) {
+							$this->__messages[] = sprintf(__('%s removed', true), $file);
+						} else {
+							$this->__errors[] = sprintf(__('%s NOT removed', true), $file);
+						}
+					} elseif (is_dir($file) === true && $this->delete($file) === false) {
+						return false;
+					}
+				}
+			}
+			$path = substr($path, 0, strlen($path) - 1);
+			if (rmdir($path) === false) {
+				$this->__errors[] = sprintf(__('%s NOT removed', true), $path);
+				return false;
+			} else {
+				$this->__messages[] = sprintf(__('%s removed', true), $path);
+			}
+		}
+		return true;
+	}
+/**
+ * Recursive directory copy.
+ *
+ * @param array $options (to, from, chmod, skip)
+ * @return bool
+ * @access public
+ */
+	function copy($options = array()) {
+		$to = null;
+		if (is_string($options)) {
+			$to = $options;
+			$options = array();
+		}
+		$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
+
+		$fromDir = $options['from'];
+		$toDir = $options['to'];
+		$mode = $options['mode'];
+
+		if (!$this->cd($fromDir)) {
+			$this->__errors[] = sprintf(__('%s not found', true), $fromDir);
+			return false;
+		}
+
+		if (!is_dir($toDir)) {
+			$this->mkdir($toDir, $mode);
+		}
+
+		if (!is_writable($toDir)) {
+			$this->__errors[] = sprintf(__('%s not writable', true), $toDir);
+			return false;
+		}
+
+		$exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
+		if ($handle = @opendir($fromDir)) {
+			while (false !== ($item = readdir($handle))) {
+				if (!in_array($item, $exceptions)) {
+					$from = Folder::addPathElement($fromDir, $item);
+					$to = Folder::addPathElement($toDir, $item);
+					if (is_file($from)) {
+						if (copy($from, $to)) {
+							chmod($to, intval($mode, 8));
+							touch($to, filemtime($from));
+							$this->__messages[] = sprintf(__('%s copied to %s', true), $from, $to);
+						} else {
+							$this->__errors[] = sprintf(__('%s NOT copied to %s', true), $from, $to);
+						}
+					}
+
+					if (is_dir($from) && !file_exists($to)) {
+						$old = umask(0);
+						if (mkdir($to, $mode)) {
+							umask($old);
+							$old = umask(0);
+							chmod($to, $mode);
+							umask($old);
+							$this->__messages[] = sprintf(__('%s created', true), $to);
+							$options = array_merge($options, array('to'=> $to, 'from'=> $from));
+							$this->copy($options);
+						} else {
+							$this->__errors[] = sprintf(__('%s not created', true), $to);
+						}
+					}
+				}
+			}
+			closedir($handle);
+		} else {
+			return false;
+		}
+
+		if (!empty($this->__errors)) {
+			return false;
+		}
+		return true;
+	}
+/**
+ * Recursive directory move.
+ *
+ * @param array $options (to, from, chmod, skip)
+ * @return boolean Success
+ * @access public
+ */
+	function move($options) {
+		$to = null;
+		if (is_string($options)) {
+			$to = $options;
+			$options = (array)$options;
+		}
+		$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
+
+		if ($this->copy($options)) {
+			if ($this->delete($options['from'])) {
+				return $this->cd($options['to']);
+			}
+		}
+		return false;
+	}
+/**
+ * get messages from latest method
+ *
+ * @return array
+ * @access public
+ */
+	function messages() {
+		return $this->__messages;
+	}
+/**
+ * get error from latest method
+ *
+ * @return array
+ * @access public
+ */
+	function errors() {
+		return $this->__errors;
+	}
+/**
+ * nix flavored alias
+ *
+ * @see read
+ * @access public
+ */
+	function ls($sort = true, $exceptions = false) {
+		return $this->read($sort, $exceptions);
+	}
+/**
+ * nix flavored alias
+ *
+ * @see create
+ * @access public
+ */
+	function mkdir($pathname, $mode = 0755) {
+		return $this->create($pathname, $mode);
+	}
+/**
+ * nix flavored alias
+ *
+ * @see copy
+ * @access public
+ */
+	function cp($options) {
+		return $this->copy($options);
+	}
+/**
+ * nix flavored alias
+ *
+ * @see move
+ * @access public
+ */
+	function mv($options) {
+		return $this->move($options);
+	}
+/**
+ * nix flavored alias
+ *
+ * @see delete
+ * @access public
+ */
+	function rm($path) {
+		return $this->delete($path);
+	}
+/**
+ * Get the real path (taking ".." and such into account)
+ *
+ * @param string $path Path to resolve
+ * @return string The resolved path
+ */
+	function realpath($path) {
+		$path = str_replace('/', DS, trim($path));
+		if (strpos($path, '..') === false) {
+			if (!Folder::isAbsolute($path)) {
+				$path = Folder::addPathElement($this->path, $path);
+			}
+			return $path;
+		}
+		$parts = explode(DS, $path);
+		$newparts = array();
+		$newpath = '';
+		if ($path[0] === DS) {
+			$newpath = DS;
+		}
+
+		while (($part = array_shift($parts)) !== NULL) {
+			if ($part === '.' || $part === '') {
+				continue;
+			}
+			if ($part === '..') {
+				if (count($newparts) > 0) {
+					array_pop($newparts);
+					continue;
+				} else {
+					return false;
+				}
+			}
+			$newparts[] = $part;
+		}
+		$newpath .= implode(DS, $newparts);
+
+		return Folder::slashTerm($newpath);
+	}
+/**
+ * Returns true if given $path ends in a slash (i.e. is slash-terminated).
+ *
+ * @param string $path Path to check
+ * @return boolean true if path ends with slash, false otherwise
+ * @access public
+ * @static
+ */
+	function isSlashTerm($path) {
+		$lastChar = $path[strlen($path) - 1];
+		return $lastChar === '/' || $lastChar === '\\';
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/cache/xcache.php
===================================================================
--- cake/libs/cache/xcache.php	(revision 0)
+++ cake/libs/cache/xcache.php	(revision 0)
@@ -0,0 +1,153 @@
+<?php
+/* SVN FILE: $Id: xcache.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Xcache storage engine for cache.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4947
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Xcache storage engine for cache
+ *
+ * @link          http://trac.lighttpd.net/xcache/ Xcache
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ */
+class XcacheEngine extends CacheEngine {
+/**
+ * settings
+ * 		PHP_AUTH_USER = xcache.admin.user, default cake
+ * 		PHP_AUTH_PW = xcache.admin.password, default cake
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array();
+/**
+ * Initialize the Cache Engine
+ *
+ * Called automatically by the cache frontend
+ * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
+ *
+ * @param array $setting array of setting for the engine
+ * @return boolean True if the engine has been successfully initialized, false if not
+ * @access public
+ */
+	function init($settings) {
+		parent::init(array_merge(array(
+			'engine' => 'Xcache', 'prefix' => Inflector::slug(APP_DIR) . '_', 'PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password'
+			), $settings)
+		);
+		return function_exists('xcache_info');
+	}
+/**
+ * Write data for key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached
+ * @param integer $duration How long to cache the data, in seconds
+ * @return boolean True if the data was succesfully cached, false on failure
+ * @access public
+ */
+	function write($key, &$value, $duration) {
+		$expires = time() + $duration;
+		xcache_set($key.'_expires', $expires, $duration);
+		return xcache_set($key, $value, $duration);
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ */
+	function read($key) {
+		if (xcache_isset($key)) {
+			$time = time();
+			$cachetime = intval(xcache_get($key.'_expires'));
+			if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
+				return false;
+			}
+			return xcache_get($key);
+		}
+		return false;
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ */
+	function delete($key) {
+		return xcache_unset($key);
+	}
+/**
+ * Delete all keys from the cache
+ *
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ */
+	function clear() {
+		$this->__auth();
+		for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++) {
+			xcache_clear_cache(XC_TYPE_VAR, $i);
+		}
+		$this->__auth(true);
+		return xcache_count(XC_TYPE_VAR) == 0;
+	}
+/**
+ * Populates and reverses $_SERVER authentication values
+ * Makes necessary changes (and reverting them back) in $_SERVER
+ *
+ * This has to be done because xcache_clear_cache() needs to pass Basic Http Auth
+ * (see xcache.admin configuration settings)
+ *
+ * @param boolean Revert changes
+ * @access private
+ */
+	function __auth($reverse = false) {
+		static $backup = array();
+		$keys = array('PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password');
+		foreach ($keys as $key => $setting) {
+			if ($reverse) {
+				if (isset($backup[$key])) {
+					$_SERVER[$key] = $backup[$key];
+					unset($backup[$key]);
+				} else {
+					unset($_SERVER[$key]);
+				}
+			} else {
+				$value = env($key);
+				if (!empty($value)) {
+					$backup[$key] = $value;
+				}
+				if (!empty($this->settings[$setting])) {
+					$_SERVER[$key] = $this->settings[$setting];
+				} else if (!empty($this->settings[$key])) {
+					$_SERVER[$key] = $this->settings[$key];
+				} else {
+					$_SERVER[$key] = $value;
+				}
+			}
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/cache/file.php
===================================================================
--- cake/libs/cache/file.php	(revision 0)
+++ cake/libs/cache/file.php	(revision 0)
@@ -0,0 +1,270 @@
+<?php
+/* SVN FILE: $Id: file.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * File Storage engine for cache
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * File Storage engine for cache
+ *
+ * @todo use the File and Folder classes (if it's not a too big performance hit)
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ */
+class FileEngine extends CacheEngine {
+/**
+ * Instance of File class
+ *
+ * @var object
+ * @access private
+ */
+	var $__File = null;
+/**
+ * settings
+ * 		path = absolute path to cache directory, default => CACHE
+ * 		prefix = string prefix for filename, default => cake_
+ * 		lock = enable file locking on write, default => false
+ * 		serialize = serialize the data, default => true
+ *
+ * @var array
+ * @see CacheEngine::__defaults
+ * @access public
+ */
+	var $settings = array();
+/**
+ * Set to true if FileEngine::init(); and FileEngine::__active(); do not fail.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__active = false;
+/**
+ * True unless FileEngine::__active(); fails
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__init = true;
+/**
+ * Initialize the Cache Engine
+ *
+ * Called automatically by the cache frontend
+ * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
+ *
+ * @param array $setting array of setting for the engine
+ * @return boolean True if the engine has been successfully initialized, false if not
+ * @access public
+ */
+	function init($settings = array()) {
+		parent::init(array_merge(
+			array(
+				'engine' => 'File', 'path' => CACHE, 'prefix'=> 'cake_', 'lock'=> false,
+				'serialize'=> true, 'isWindows' => false
+			),
+			$settings
+		));
+		if (!isset($this->__File)) {
+			if (!class_exists('File')) {
+				require LIBS . 'file.php';
+			}
+			$this->__File =& new File($this->settings['path'] . DS . 'cake');
+		}
+
+		if (DIRECTORY_SEPARATOR === '\\') {
+			$this->settings['isWindows'] = true;
+		}
+
+		$this->settings['path'] = $this->__File->Folder->cd($this->settings['path']);
+		if (empty($this->settings['path'])) {
+			return false;
+		}
+		return $this->__active();
+	}
+/**
+ * Garbage collection. Permanently remove all expired and deleted data
+ *
+ * @return boolean True if garbage collection was succesful, false on failure
+ * @access public
+ */
+	function gc() {
+		return $this->clear(true);
+	}
+/**
+ * Write data for key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $data Data to be cached
+ * @param mixed $duration How long to cache the data, in seconds
+ * @return boolean True if the data was succesfully cached, false on failure
+ * @access public
+ */
+	function write($key, &$data, $duration) {
+		if ($data === '' || !$this->__init) {
+			return false;
+		}
+
+		if ($this->__setKey($key) === false) {
+			return false;
+		}
+
+		$lineBreak = "\n";
+
+		if ($this->settings['isWindows']) {
+			$lineBreak = "\r\n";
+		}
+
+		if (!empty($this->settings['serialize'])) {
+			if ($this->settings['isWindows']) {
+				$data = str_replace('\\', '\\\\\\\\', serialize($data));
+			} else {
+				$data = serialize($data);
+			}
+		}
+
+		if ($this->settings['lock']) {
+			$this->__File->lock = true;
+		}
+		$expires = time() + $duration;
+		$contents = $expires . $lineBreak . $data . $lineBreak;
+		$success = $this->__File->write($contents);
+		$this->__File->close();
+		return $success;
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ */
+	function read($key) {
+		if ($this->__setKey($key) === false || !$this->__init || !$this->__File->exists()) {
+			return false;
+		}
+		if ($this->settings['lock']) {
+			$this->__File->lock = true;
+		}
+		$time = time();
+		$cachetime = intval($this->__File->read(11));
+
+		if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
+			$this->__File->close();
+			$this->__File->delete();
+			return false;
+		}
+		$data = $this->__File->read(true);
+
+		if ($data !== '' && !empty($this->settings['serialize'])) {
+			if ($this->settings['isWindows']) {
+				$data = str_replace('\\\\\\\\', '\\', $data);
+			}
+			$data = unserialize((string)$data);
+		}
+		$this->__File->close();
+		return $data;
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ */
+	function delete($key) {
+		if ($this->__setKey($key) === false || !$this->__init) {
+			return false;
+		}
+		return $this->__File->delete();
+	}
+/**
+ * Delete all values from the cache
+ *
+ * @param boolean $check Optional - only delete expired cache items
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ */
+	function clear($check) {
+		if (!$this->__init) {
+			return false;
+		}
+		$dir = dir($this->settings['path']);
+		if ($check) {
+			$now = time();
+			$threshold = $now - $this->settings['duration'];
+		}
+		while (($entry = $dir->read()) !== false) {
+			if ($this->__setKey($entry) === false) {
+				continue;
+			}
+			if ($check) {
+				$mtime = $this->__File->lastChange();
+
+				if ($mtime === false || $mtime > $threshold) {
+					continue;
+				}
+
+				$expires = $this->__File->read(11);
+				$this->__File->close();
+
+				if ($expires > $now) {
+					continue;
+				}
+			}
+			$this->__File->delete();
+		}
+		$dir->close();
+		return true;
+	}
+/**
+ * Get absolute file for a given key
+ *
+ * @param string $key The key
+ * @return mixed Absolute cache file for the given key or false if erroneous
+ * @access private
+ */
+	function __setKey($key) {
+		$this->__File->Folder->cd($this->settings['path']);
+		if ($key !== $this->__File->name) {
+			$this->__File->name = $key;
+			$this->__File->path = null;
+		}
+		if (!$this->__File->Folder->inPath($this->__File->pwd(), true)) {
+			return false;
+		}
+	}
+/**
+ * Determine is cache directory is writable
+ *
+ * @return boolean
+ * @access private
+ */
+	function __active() {
+		if (!$this->__active && $this->__init && !is_writable($this->settings['path'])) {
+			$this->__init = false;
+			trigger_error(sprintf(__('%s is not writable', true), $this->settings['path']), E_USER_WARNING);
+		} else {
+			$this->__active = true;
+		}
+		return true;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/cache/memcache.php
===================================================================
--- cake/libs/cache/memcache.php	(revision 0)
+++ cake/libs/cache/memcache.php	(revision 0)
@@ -0,0 +1,158 @@
+<?php
+/* SVN FILE: $Id: memcache.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Memcache storage engine for cache
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Memcache storage engine for cache
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ */
+class MemcacheEngine extends CacheEngine {
+/**
+ * Memcache wrapper.
+ *
+ * @var object
+ * @access private
+ */
+	var $__Memcache = null;
+/**
+ * settings
+ * 		servers = string or array of memcache servers, default => 127.0.0.1
+ * 		compress = boolean, default => false
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array();
+/**
+ * Initialize the Cache Engine
+ *
+ * Called automatically by the cache frontend
+ * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
+ *
+ * @param array $setting array of setting for the engine
+ * @return boolean True if the engine has been successfully initialized, false if not
+ * @access public
+ */
+	function init($settings = array()) {
+		if (!class_exists('Memcache')) {
+			return false;
+		}
+		parent::init(array_merge(array(
+			'engine'=> 'Memcache', 'prefix' => Inflector::slug(APP_DIR) . '_', 'servers' => array('127.0.0.1'), 'compress'=> false
+			), $settings)
+		);
+
+		if ($this->settings['compress']) {
+			$this->settings['compress'] = MEMCACHE_COMPRESSED;
+		}
+		if (!is_array($this->settings['servers'])) {
+			$this->settings['servers'] = array($this->settings['servers']);
+		}
+		if (!isset($this->__Memcache)) {
+			$return = false;
+			$this->__Memcache =& new Memcache();
+			foreach ($this->settings['servers'] as $server) {
+				$parts = explode(':', $server);
+				$host = $parts[0];
+				$port = 11211;
+				if (isset($parts[1])) {
+					$port = $parts[1];
+				}
+				if ($this->__Memcache->addServer($host, $port)) {
+					$return = true;
+				}
+			}
+			return $return;
+		}
+		return true;
+	}
+/**
+ * Write data for key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached
+ * @param integer $duration How long to cache the data, in seconds
+ * @return boolean True if the data was succesfully cached, false on failure
+ * @access public
+ */
+	function write($key, &$value, $duration) {
+		$expires = time() + $duration;
+		$this->__Memcache->set($key.'_expires', $expires, $this->settings['compress'], $duration);
+		return $this->__Memcache->set($key, $value, $this->settings['compress'], $duration);
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ */
+	function read($key) {
+		$time = time();
+		$cachetime = intval($this->__Memcache->get($key.'_expires'));
+		if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
+			return false;
+		}
+		return $this->__Memcache->get($key);
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ */
+	function delete($key) {
+		return $this->__Memcache->delete($key);
+	}
+/**
+ * Delete all keys from the cache
+ *
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ */
+	function clear() {
+		return $this->__Memcache->flush();
+	}
+/**
+ * Connects to a server in connection pool
+ *
+ * @param string $host host ip address or name
+ * @param integer $port Server port
+ * @return boolean True if memcache server was connected
+ * @access public
+ */
+	function connect($host, $port = 11211) {
+		if ($this->__Memcache->getServerStatus($host, $port) === 0) {
+			if ($this->__Memcache->connect($host, $port)) {
+				return true;
+			}
+			return false;
+		}
+		return true;
+	}
+}
+?>
Index: cake/libs/cache/apc.php
===================================================================
--- cake/libs/cache/apc.php	(revision 0)
+++ cake/libs/cache/apc.php	(revision 0)
@@ -0,0 +1,97 @@
+<?php
+/* SVN FILE: $Id: apc.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * APC storage engine for cache.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * APC storage engine for cache
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ */
+class ApcEngine extends CacheEngine {
+/**
+ * Initialize the Cache Engine
+ *
+ * Called automatically by the cache frontend
+ * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
+ *
+ * @param array $setting array of setting for the engine
+ * @return boolean True if the engine has been successfully initialized, false if not
+ * @see CacheEngine::__defaults
+ * @access public
+ */
+	function init($settings = array()) {
+		parent::init(array_merge(array('engine' => 'Apc', 'prefix' => Inflector::slug(APP_DIR) . '_'), $settings));
+		return function_exists('apc_cache_info');
+	}
+/**
+ * Write data for key into cache
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached
+ * @param integer $duration How long to cache the data, in seconds
+ * @return boolean True if the data was succesfully cached, false on failure
+ * @access public
+ */
+	function write($key, &$value, $duration) {
+		$expires = time() + $duration;
+		apc_store($key.'_expires', $expires, $duration);
+		return apc_store($key, $value, $duration);
+	}
+/**
+ * Read a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
+ * @access public
+ */
+	function read($key) {
+		$time = time();
+		$cachetime = intval(apc_fetch($key.'_expires'));
+		if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
+			return false;
+		}
+		return apc_fetch($key);
+	}
+/**
+ * Delete a key from the cache
+ *
+ * @param string $key Identifier for the data
+ * @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
+ * @access public
+ */
+	function delete($key) {
+		return apc_delete($key);
+	}
+/**
+ * Delete all keys from the cache
+ *
+ * @return boolean True if the cache was succesfully cleared, false otherwise
+ * @access public
+ */
+	function clear() {
+		return apc_clear_cache('user');
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/file.php
===================================================================
--- cake/libs/file.php	(revision 0)
+++ cake/libs/file.php	(revision 0)
@@ -0,0 +1,507 @@
+<?php
+/* SVN FILE: $Id: file.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Convenience class for reading, writing and appending to files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	uses('object');
+}
+if (!class_exists('Folder')) {
+	require LIBS . 'folder.php';
+}
+/**
+ * Convenience class for reading, writing and appending to files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class File extends Object {
+/**
+ * Folder object of the File
+ *
+ * @var object
+ * @access public
+ */
+	var $Folder = null;
+/**
+ * Filename
+ *
+ * @var string
+ * @access public
+ */
+	var $name = null;
+/**
+ * file info
+ *
+ * @var string
+ * @access public
+ */
+	var $info = array();
+/**
+ * Holds the file handler resource if the file is opened
+ *
+ * @var resource
+ * @access public
+ */
+	var $handle = null;
+/**
+ * enable locking for file reading and writing
+ *
+ * @var boolean
+ * @access public
+ */
+	var $lock = null;
+/**
+ * path property
+ *
+ * Current file's absolute path
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $path = null;
+/**
+ * Constructor
+ *
+ * @param string $path Path to file
+ * @param boolean $create Create file if it does not exist (if true)
+ * @param integer $mode Mode to apply to the folder holding the file
+ * @access private
+ */
+	function __construct($path, $create = false, $mode = 0755) {
+		parent::__construct();
+		$this->Folder =& new Folder(dirname($path), $create, $mode);
+		if (!is_dir($path)) {
+			$this->name = basename($path);
+		}
+		$this->pwd();
+
+		if (!$this->exists()) {
+			if ($create === true) {
+				if ($this->safe($path) && $this->create() === false) {
+					return false;
+				}
+			} else {
+				return false;
+			}
+		}
+	}
+/**
+ * Closes the current file if it is opened
+ *
+ * @access private
+ */
+	function __destruct() {
+		$this->close();
+	}
+/**
+ * Creates the File.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function create() {
+		$dir = $this->Folder->pwd();
+		if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
+			$old = umask(0);
+			if (touch($this->path)) {
+				umask($old);
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Opens the current file with a given $mode
+ *
+ * @param string $mode A valid 'fopen' mode string (r|w|a ...)
+ * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
+ * @return boolean True on success, false on failure
+ * @access public
+ */
+	function open($mode = 'r', $force = false) {
+		if (!$force && is_resource($this->handle)) {
+			return true;
+		}
+		clearstatcache();
+		if ($this->exists() === false) {
+			if ($this->create() === false) {
+				return false;
+			}
+		}
+
+		$this->handle = fopen($this->path, $mode);
+		if (is_resource($this->handle)) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Return the contents of this File as a string.
+ *
+ * @param string $bytes where to start
+ * @param string $mode
+ * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
+ * @return mixed string on success, false on failure
+ * @access public
+ */
+	function read($bytes = false, $mode = 'rb', $force = false) {
+		if ($bytes === false && $this->lock === null) {
+			return file_get_contents($this->path);
+		}
+		if ($this->open($mode, $force) === false) {
+			return false;
+		}
+		if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
+			return false;
+		}
+		if (is_int($bytes)) {
+			return fread($this->handle, $bytes);
+		}
+
+		$data = '';
+		while (!feof($this->handle)) {
+			$data .= fgets($this->handle, 4096);
+		}
+		$data = trim($data);
+
+		if ($this->lock !== null) {
+			flock($this->handle, LOCK_UN);
+		}
+		if ($bytes === false) {
+			$this->close();
+		}
+		return $data;
+	}
+/**
+ * Sets or gets the offset for the currently opened file.
+ *
+ * @param mixed $offset The $offset in bytes to seek. If set to false then the current offset is returned.
+ * @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
+ * @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
+ * @access public
+ */
+	function offset($offset = false, $seek = SEEK_SET) {
+		if ($offset === false) {
+			if (is_resource($this->handle)) {
+				return ftell($this->handle);
+			}
+		} elseif ($this->open() === true) {
+			return fseek($this->handle, $offset, $seek) === 0;
+		}
+		return false;
+	}
+/**
+ * Prepares a ascii string for writing
+ * fixes line endings
+ *
+ * @param string $data Data to prepare for writing.
+ * @return string
+ * @access public
+ */
+	function prepare($data, $forceWindows = false) {
+		$lineBreak = "\n";
+		if (DIRECTORY_SEPARATOR == '\\' || $forceWindows === true) {
+			$lineBreak = "\r\n";
+		}
+		return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
+	}
+
+/**
+ * Write given data to this File.
+ *
+ * @param string $data	Data to write to this File.
+ * @param string $mode	Mode of writing. {@link http://php.net/fwrite See fwrite()}.
+ * @param string $force	force the file to open
+ * @return boolean Success
+ * @access public
+ */
+	function write($data, $mode = 'w', $force = false) {
+		$success = false;
+		if ($this->open($mode, $force) === true) {
+			if ($this->lock !== null) {
+				if (flock($this->handle, LOCK_EX) === false) {
+					return false;
+				}
+			}
+
+			if (fwrite($this->handle, $data) !== false) {
+				$success = true;
+			}
+			if ($this->lock !== null) {
+				flock($this->handle, LOCK_UN);
+			}
+		}
+		return $success;
+	}
+/**
+ * Append given data string to this File.
+ *
+ * @param string $data Data to write
+ * @param string $force	force the file to open
+ * @return boolean Success
+ * @access public
+ */
+	function append($data, $force = false) {
+		return $this->write($data, 'a', $force);
+	}
+/**
+ * Closes the current file if it is opened.
+ *
+ * @return boolean True if closing was successful or file was already closed, otherwise false
+ * @access public
+ */
+	function close() {
+		if (!is_resource($this->handle)) {
+			return true;
+		}
+		return fclose($this->handle);
+	}
+/**
+ * Deletes the File.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function delete() {
+		clearstatcache();
+		if ($this->exists()) {
+			return unlink($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns the File extension.
+ *
+ * @return string The File extension
+ * @access public
+ */
+	function info() {
+		if ($this->info == null) {
+			$this->info = pathinfo($this->path);
+		}
+		if (!isset($this->info['filename'])) {
+			$this->info['filename'] = $this->name();
+		}
+		return $this->info;
+	}
+/**
+ * Returns the File extension.
+ *
+ * @return string The File extension
+ * @access public
+ */
+	function ext() {
+		if ($this->info == null) {
+			$this->info();
+		}
+		if (isset($this->info['extension'])) {
+			return $this->info['extension'];
+		}
+		return false;
+	}
+/**
+ * Returns the File name without extension.
+ *
+ * @return string The File name without extension.
+ * @access public
+ */
+	function name() {
+		if ($this->info == null) {
+			$this->info();
+		}
+		if (isset($this->info['extension'])) {
+			return basename($this->name, '.'.$this->info['extension']);
+		} elseif ($this->name) {
+			return $this->name;
+		}
+		return false;
+	}
+/**
+ * makes filename safe for saving
+ *
+ * @param string $name the name of the file to make safe if different from $this->name
+ * @return string $ext the extension of the file
+ * @access public
+ */
+	function safe($name = null, $ext = null) {
+		if (!$name) {
+			$name = $this->name;
+		}
+		if (!$ext) {
+			$ext = $this->ext();
+		}
+		return preg_replace( "/(?:[^\w\.-]+)/", "_", basename($name, $ext));
+	}
+/**
+ * Get md5 Checksum of file with previous check of Filesize
+ *
+ * @param mixed $maxsize in MB or true to force
+ * @return string md5 Checksum {@link http://php.net/md5_file See md5_file()}
+ * @access public
+ */
+	function md5($maxsize = 5) {
+		if ($maxsize === true) {
+			return md5_file($this->path);
+		} else {
+			$size = $this->size();
+			if ($size && $size < ($maxsize * 1024) * 1024) {
+				return md5_file($this->path);
+			}
+		}
+		return false;
+	}
+/**
+* Returns the full path of the File.
+*
+* @return string Full path to file
+* @access public
+*/
+	function pwd() {
+		if (is_null($this->path)) {
+			$this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name;
+		}
+		return $this->path;
+	}
+/**
+ * Returns true if the File exists.
+ *
+ * @return boolean true if it exists, false otherwise
+ * @access public
+ */
+	function exists() {
+		return (file_exists($this->path) && is_file($this->path));
+	}
+/**
+ * Returns the "chmod" (permissions) of the File.
+ *
+ * @return string Permissions for the file
+ * @access public
+ */
+	function perms() {
+		if ($this->exists()) {
+			return substr(sprintf('%o', fileperms($this->path)), -4);
+		}
+		return false;
+	}
+/**
+ * Returns the Filesize
+ *
+ * @return integer size of the file in bytes, or false in case of an error
+ * @access public
+ */
+	function size() {
+		if ($this->exists()) {
+			return filesize($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns true if the File is writable.
+ *
+ * @return boolean true if its writable, false otherwise
+ * @access public
+ */
+	function writable() {
+		return is_writable($this->path);
+	}
+/**
+ * Returns true if the File is executable.
+ *
+ * @return boolean true if its executable, false otherwise
+ * @access public
+ */
+	function executable() {
+		return is_executable($this->path);
+	}
+/**
+ * Returns true if the File is readable.
+ *
+ * @return boolean true if file is readable, false otherwise
+ * @access public
+ */
+	function readable() {
+		return is_readable($this->path);
+	}
+/**
+ * Returns the File's owner.
+ *
+ * @return integer the Fileowner
+ * @access public
+ */
+	function owner() {
+		if ($this->exists()) {
+			return fileowner($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns the File group.
+ *
+ * @return integer the Filegroup
+ * @access public
+ */
+	function group() {
+		if ($this->exists()) {
+			return filegroup($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns last access time.
+ *
+ * @return integer timestamp Timestamp of last access time
+ * @access public
+ */
+	function lastAccess() {
+		if ($this->exists()) {
+			return fileatime($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns last modified time.
+ *
+ * @return integer timestamp Timestamp of last modification
+ * @access public
+ */
+	function lastChange() {
+		if ($this->exists()) {
+			return filemtime($this->path);
+		}
+		return false;
+	}
+/**
+ * Returns the current folder.
+ *
+ * @return Folder Current folder
+ * @access public
+ */
+	function &Folder() {
+		return $this->Folder;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/i18n.php
===================================================================
--- cake/libs/i18n.php	(revision 0)
+++ cake/libs/i18n.php	(revision 0)
@@ -0,0 +1,449 @@
+<?php
+/* SVN FILE: $Id: i18n.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4116
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ */
+App::import('Core', 'l10n');
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class I18n extends Object {
+/**
+ * Instance of the I10n class for localization
+ *
+ * @var object
+ * @access public
+ */
+	var $l10n = null;
+/**
+ * Current domain of translation
+ *
+ * @var string
+ * @access public
+ */
+	var $domain = null;
+/**
+ * Current category of translation
+ *
+ * @var string
+ * @access public
+ */
+	var $category = 'LC_MESSAGES';
+/**
+ * Current language used for translations
+ *
+ * @var string
+ * @access private;
+ */
+	var $__lang = null;
+/**
+ * Translation strings for a specific domain read from the .mo or .po files
+ *
+ * @var array
+ * @access private
+ */
+	var $__domains = array();
+/**
+ * Set to true when I18N::__bindTextDomain() is called for the first time.
+ * If a translation file is found it is set to false again
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__noLocale = false;
+/**
+ * Determine if $__domains cache should be wrote
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__cache = false;
+/**
+ * Set to true when I18N::__bindTextDomain() is called for the first time.
+ * If a translation file is found it is set to false again
+ *
+ * @var array
+ * @access private
+ */
+	var $__categories = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES', 'LC_ALL');
+/**
+ * Return a static instance of the I18n class
+ *
+ * @return object I18n
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new I18n();
+			$instance[0]->l10n =& new L10n();
+		}
+		return $instance[0];
+	}
+/**
+ * Used by the translation functions in basics.php
+ * Can also be used like I18n::translate(); but only if the uses('i18n'); has been used to load the class.
+ *
+ * @param string $singular String to translate
+ * @param string $plural Plural string (if any)
+ * @param string $domain Domain
+ * @param string $category Category
+ * @param integer $count Count
+ * @return string translated strings.
+ * @access public
+ */
+	function translate($singular, $plural = null, $domain = null, $category = null, $count = null) {
+		$_this =& I18n::getInstance();
+
+		if (strpos($singular, "\r\n") !== false) {
+			$singular = str_replace("\r\n", "\n", $singular);
+		}
+		if ($plural !== null && strpos($plural, "\r\n") !== false) {
+			$plural = str_replace("\r\n", "\n", $plural);
+		}
+
+		if (is_numeric($category)) {
+			$_this->category = $_this->__categories[$category];
+		}
+		$language = Configure::read('Config.language');
+
+		if (!empty($_SESSION['Config']['language'])) {
+			$language = $_SESSION['Config']['language'];
+		}
+
+		if (($_this->__lang && $_this->__lang !== $language) || !$_this->__lang) {
+			$lang = $_this->l10n->get($language);
+			$_this->__lang = $lang;
+		}
+
+		if (is_null($domain)) {
+			$domain = 'default';
+		}
+		$_this->domain = $domain . '_' . $_this->l10n->locale;
+
+		if (empty($_this->__domains)) {
+			$_this->__domains = Cache::read($_this->domain, '_cake_core_');
+		}
+
+		if (!isset($_this->__domains[$_this->category][$_this->__lang][$domain])) {
+			$_this->__bindTextDomain($domain);
+			$_this->__cache = true;
+		}
+
+		if (!isset($count)) {
+			$plurals = 0;
+		} elseif (!empty($_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"]) && $_this->__noLocale === false) {
+			$header = $_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"];
+			$plurals = $_this->__pluralGuess($header, $count);
+		} else {
+			if ($count != 1) {
+				$plurals = 1;
+			} else {
+				$plurals = 0;
+			}
+		}
+
+		if (!empty($_this->__domains[$_this->category][$_this->__lang][$domain][$singular])) {
+			if (($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$singular]) || ($plurals) && ($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$plural])) {
+				if (is_array($trans)) {
+					if (isset($trans[$plurals])) {
+						$trans = $trans[$plurals];
+					}
+				}
+				if (strlen($trans)) {
+					$singular = $trans;
+					return $singular;
+				}
+			}
+		}
+
+		if (!empty($plurals)) {
+			return($plural);
+		}
+		return($singular);
+	}
+/**
+ * Attempts to find the plural form of a string.
+ *
+ * @param string $header Type
+ * @param integrer $n Number
+ * @return integer plural match
+ * @access private
+ */
+	function __pluralGuess($header, $n) {
+		if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) {
+			return 0;
+		}
+
+		if ($header === "nplurals=2;plural=n!=1;") {
+			return $n != 1 ? 1 : 0;
+		} elseif ($header === "nplurals=2;plural=n>1;") {
+			return $n > 1 ? 1 : 0;
+		}
+
+		if (strpos($header, "plurals=3")) {
+			if (strpos($header, "100!=11")) {
+				if (strpos($header, "10<=4")) {
+					return $n % 10 === 1 && $n % 100 !== 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
+				} elseif (strpos($header, "100<10")) {
+					return $n % 10 === 1 && $n % 100 !== 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
+				}
+				return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2);
+			} elseif (strpos($header, "n==2")) {
+				return $n === 1 ? 0 : ($n === 2 ? 1 : 2);
+			} elseif (strpos($header, "n==0")) {
+				return $n === 1 ? 0 : ($n === 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2);
+			} elseif (strpos($header, "n>=2")) {
+				return $n === 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
+			} elseif (strpos($header, "10>=2")) {
+				return $n === 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
+			}
+			return $n % 10 === 1 ? 0 : ($n % 10 === 2 ? 1 : 2);
+		} elseif (strpos($header, "plurals=4")) {
+			if (strpos($header, "100==2")) {
+				return $n % 100 === 1 ? 0 : ($n % 100 === 2 ? 1 : ($n % 100 === 3 || $n % 100 === 4 ? 2 : 3));
+			} elseif (strpos($header, "n>=3")) {
+				return $n === 1 ? 0 : ($n === 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3));
+			} elseif (strpos($header, "100>=1")) {
+				return $n === 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3));
+			}
+		} elseif (strpos($header, "plurals=5")) {
+			return $n === 1 ? 0 : ($n === 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4)));
+		}
+	}
+/**
+ * Binds the given domain to a file in the specified directory.
+ *
+ * @param string $domain Domain to bind
+ * @return string Domain binded
+ * @access private
+ */
+	function __bindTextDomain($domain) {
+		$this->__noLocale = true;
+		$core = true;
+		$merge = array();
+		$searchPaths = Configure::read('localePaths');
+		$plugins = Configure::listObjects('plugin');
+
+		if (!empty($plugins)) {
+			$pluginPaths = Configure::read('pluginPaths');
+
+			foreach ($plugins as $plugin) {
+				$plugin = Inflector::underscore($plugin);
+				if ($plugin === $domain) {
+					foreach ($pluginPaths as $pluginPath) {
+						$searchPaths[] = $pluginPath . DS . $plugin . DS . 'locale';
+					}
+					$searchPaths = array_reverse($searchPaths);
+					break;
+				}
+			}
+		}
+
+		foreach ($searchPaths as $directory) {
+			foreach ($this->l10n->languagePath as $lang) {
+				$file = $directory . DS . $lang . DS . $this->category . DS . $domain;
+
+				if ($core) {
+					$app = $directory . DS . $lang . DS . $this->category . DS . 'core';
+					if (file_exists($fn = "$app.mo")) {
+						$this->__loadMo($fn, $domain);
+						$this->__noLocale = false;
+						$merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
+						$core = null;
+					} elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) {
+						$this->__loadPo($f, $domain);
+						$this->__noLocale = false;
+						$merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
+						$core = null;
+					}
+				}
+
+				if (file_exists($fn = "$file.mo")) {
+					$this->__loadMo($fn, $domain);
+					$this->__noLocale = false;
+					break 2;
+				} elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) {
+					$this->__loadPo($f, $domain);
+					$this->__noLocale = false;
+					break 2;
+				}
+			}
+		}
+
+		if (empty($this->__domains[$this->category][$this->__lang][$domain])) {
+			$this->__domains[$this->category][$this->__lang][$domain] = array();
+			return($domain);
+		}
+
+		if ($head = $this->__domains[$this->category][$this->__lang][$domain][""]) {
+			foreach (explode("\n", $head) as $line) {
+				$header = strtok($line,":");
+				$line = trim(strtok("\n"));
+				$this->__domains[$this->category][$this->__lang][$domain]["%po-header"][strtolower($header)] = $line;
+			}
+
+			if (isset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"])) {
+				$switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"]);
+				$this->__domains[$this->category][$this->__lang][$domain]["%plural-c"] = $switch;
+				unset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]);
+			}
+			$this->__domains = Set::pushDiff($this->__domains, $merge);
+
+			if (isset($this->__domains[$this->category][$this->__lang][$domain][null])) {
+				unset($this->__domains[$this->category][$this->__lang][$domain][null]);
+			}
+		}
+		return($domain);
+	}
+/**
+ * Loads the binary .mo file for translation and sets the values for this translation in the var I18n::__domains
+ *
+ * @param resource $file Binary .mo file to load
+ * @param string $domain Domain where to load file in
+ * @access private
+ */
+	function __loadMo($file, $domain) {
+		$data = file_get_contents($file);
+
+		if ($data) {
+			$header = substr($data, 0, 20);
+			$header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header);
+			extract($header);
+
+			if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && $version == 0) {
+				for ($n = 0; $n < $count; $n++) {
+					$r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8));
+					$msgid = substr($data, $r["offs"], $r["len"]);
+					unset($msgid_plural);
+
+					if (strpos($msgid, "\000")) {
+						list($msgid, $msgid_plural) = explode("\000", $msgid);
+					}
+					$r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8));
+					$msgstr = substr($data, $r["offs"], $r["len"]);
+
+					if (strpos($msgstr, "\000")) {
+						$msgstr = explode("\000", $msgstr);
+					}
+					$this->__domains[$this->category][$this->__lang][$domain][$msgid] = $msgstr;
+
+					if (isset($msgid_plural)) {
+						$this->__domains[$this->category][$this->__lang][$domain][$msgid_plural] =& $this->__domains[$this->category][$this->__lang][$domain][$msgid];
+					}
+				}
+			}
+		}
+	}
+/**
+ * Loads the text .po file for translation and sets the values for this translation in the var I18n::__domains
+ *
+ * @param resource $file Text .po file to load
+ * @param string $domain Domain to load file in
+ * @return array Binded domain elements
+ * @access private
+ */
+	function __loadPo($file, $domain) {
+		$type = 0;
+		$translations = array();
+		$translationKey = "";
+		$plural = 0;
+		$header = "";
+
+		do {
+			$line = trim(fgets($file, 1024));
+			if ($line == "" || $line[0] == "#") {
+				continue;
+			}
+			if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
+				$type = 1;
+				$translationKey = stripcslashes($regs[1]);
+			} elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
+				$type = 2;
+				$translationKey = "";
+			} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
+				$type = 3;
+				$translationKey .= stripcslashes($regs[1]);
+			} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
+				$translations[$translationKey] = stripcslashes($regs[1]);
+				$type = 4;
+			} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
+				$type = 4;
+				$translations[$translationKey] = "";
+			} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
+				$translations[$translationKey] .= stripcslashes($regs[1]);
+			} elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
+				$type = 6;
+			} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
+				$type = 6;
+			} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
+				$plural = $regs[1];
+				$translations[$translationKey][$plural] = stripcslashes($regs[2]);
+				$type = 7;
+			} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
+				$plural = $regs[1];
+				$translations[$translationKey][$plural] = "";
+				$type = 7;
+			} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
+				$translations[$translationKey][$plural] .= stripcslashes($regs[1]);
+			} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
+				$header .= stripcslashes($regs[1]);
+				$type = 5;
+			} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) {
+				$header = "";
+				$type = 5;
+			} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
+				$header .= stripcslashes($regs[1]);
+			} else {
+				unset($translations[$translationKey]);
+				$type = 0;
+				$translationKey = "";
+				$plural = 0;
+			}
+		} while (!feof($file));
+		fclose($file);
+		$merge[""] = $header;
+		return $this->__domains[$this->category][$this->__lang][$domain] = array_merge($merge ,$translations);
+	}
+/**
+ * Object destructor
+ *
+ * Write cache file if changes have been made to the $__map or $__paths
+ * @access private
+ */
+	function __destruct() {
+		if ($this->__cache) {
+			Cache::write($this->domain, array_filter($this->__domains), '_cake_core_');
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/router.php
===================================================================
--- cake/libs/router.php	(revision 0)
+++ cake/libs/router.php	(revision 0)
@@ -0,0 +1,1361 @@
+<?php
+/* SVN FILE: $Id: router.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Parses the request URL into controller, action, and parameters.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	App::import('Core', 'Object');
+}
+/**
+ * Parses the request URL into controller, action, and parameters.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Router extends Object {
+/**
+ * Array of routes
+ *
+ * @var array
+ * @access public
+ */
+	var $routes = array();
+/**
+ * Caches admin setting from Configure class
+ *
+ * @var array
+ * @access private
+ */
+	var $__admin = null;
+/**
+ * List of action prefixes used in connected routes
+ *
+ * @var array
+ * @access private
+ */
+	var $__prefixes = array();
+/**
+ * Directive for Router to parse out file extensions for mapping to Content-types.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__parseExtensions = false;
+/**
+ * List of valid extensions to parse from a URL.  If null, any extension is allowed.
+ *
+ * @var array
+ * @access private
+ */
+	var $__validExtensions = null;
+/**
+ * 'Constant' regular expression definitions for named route elements
+ *
+ * @var array
+ * @access private
+ */
+	var $__named = array(
+		'Action'	=> 'index|show|add|create|edit|update|remove|del|delete|view|item',
+		'Year'		=> '[12][0-9]{3}',
+		'Month'		=> '0[1-9]|1[012]',
+		'Day'		=> '0[1-9]|[12][0-9]|3[01]',
+		'ID'		=> '[0-9]+',
+		'UUID'		=> '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}'
+	);
+/**
+ * Stores all information necessary to decide what named arguments are parsed under what conditions.
+ *
+ * @var string
+ * @access public
+ */
+	var $named = array(
+		'default' => array('page', 'fields', 'order', 'limit', 'recursive', 'sort', 'direction', 'step'),
+		'greedy' => true,
+		'separator' => ':',
+		'rules' => false,
+	);
+/**
+ * The route matching the URL of the current request
+ *
+ * @var array
+ * @access private
+ */
+	var $__currentRoute = array();
+/**
+ * HTTP header shortcut map.  Used for evaluating header-based route expressions.
+ *
+ * @var array
+ * @access private
+ */
+	var $__headerMap = array(
+		'type'		=> 'content_type',
+		'method'	=> 'request_method',
+		'server'	=> 'server_name'
+	);
+/**
+ * Default HTTP request method => controller action map.
+ *
+ * @var array
+ * @access private
+ */
+	var $__resourceMap = array(
+		array('action' => 'index',	'method' => 'GET',		'id' => false),
+		array('action' => 'view',	'method' => 'GET',		'id' => true),
+		array('action' => 'add',	'method' => 'POST',		'id' => false),
+		array('action' => 'edit',	'method' => 'PUT', 		'id' => true),
+		array('action' => 'delete',	'method' => 'DELETE',	'id' => true),
+		array('action' => 'edit',	'method' => 'POST', 	'id' => true)
+	);
+/**
+ * List of resource-mapped controllers
+ *
+ * @var array
+ * @access private
+ */
+	var $__resourceMapped = array();
+/**
+ * Maintains the parameter stack for the current request
+ *
+ * @var array
+ * @access private
+ */
+	var $__params = array();
+/**
+ * Maintains the path stack for the current request
+ *
+ * @var array
+ * @access private
+ */
+	var $__paths = array();
+/**
+ * Keeps Router state to determine if default routes have already been connected
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__defaultsMapped = false;
+/**
+ * Gets a reference to the Router object instance
+ *
+ * @return object Object instance
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Router();
+			$instance[0]->__admin = Configure::read('Routing.admin');
+		}
+		return $instance[0];
+	}
+/**
+ * Gets the named route elements for use in app/config/routes.php
+ *
+ * @return array Named route elements
+ * @access public
+ * @see Router::$__named
+ * @static
+ */
+	function getNamedExpressions() {
+		$_this =& Router::getInstance();
+		return $_this->__named;
+	}
+/**
+ * Returns this object's routes array. Returns false if there are no routes available.
+ *
+ * @param string $route			An empty string, or a route string "/"
+ * @param array $default		NULL or an array describing the default route
+ * @param array $params			An array matching the named elements in the route to regular expressions which that element should match.
+ * @see routes
+ * @return array			Array of routes
+ * @access public
+ * @static
+ */
+	function connect($route, $default = array(), $params = array()) {
+		$_this =& Router::getInstance();
+
+		if (!isset($default['action'])) {
+			$default['action'] = 'index';
+		}
+		if (isset($default[$_this->__admin])) {
+			$default['prefix'] = $_this->__admin;
+		}
+		if (isset($default['prefix'])) {
+			$_this->__prefixes[] = $default['prefix'];
+			$_this->__prefixes = array_keys(array_flip($_this->__prefixes));
+		}
+		$_this->routes[] = array($route, $default, $params);
+		return $_this->routes;
+	}
+/**
+ *Specifies what named parameters CakePHP should be parsing. The most common setups are:
+ *
+ * Do not parse any named parameters:
+ * 	Router::connectNamed(false);
+ *
+ * Parse only default parameters used for CakePHP's pagination:
+ * 	Router::connectNamed(false, array('default' => true));
+ *
+ * Parse only the page parameter if its value is a number:
+ * 	Router::connectNamed(array('page' => '[\d]+'), array('default' => false, 'greedy' => false));
+ *
+ * Parse only the page parameter no mater what.
+ * 	Router::connectNamed(array('page'), array('default' => false, 'greedy' => false));
+ *
+ * Parse only the page parameter if the current action is 'index'.
+ * 	Router::connectNamed(array('page' => array('action' => 'index')), array('default' => false, 'greedy' => false));
+ *
+ * Parse only the page parameter if the current action is 'index' and the controller is 'pages'.
+ * 	Router::connectNamed(array('page' => array('action' => 'index', 'controller' => 'pages')), array('default' => false, 'greedy' => false));
+ *
+ * @param array $named A list of named parameters. Key value pairs are accepted where values are either regex strings to match, or arrays as seen above.
+ * @param array $options Allows to control all settings: separator, greedy, reset, default
+ * @return array
+ * @access public
+ * @static
+ */
+	function connectNamed($named, $options = array()) {
+		$_this =& Router::getInstance();
+
+		if (isset($options['argSeparator'])) {
+			$_this->named['separator'] = $options['argSeparator'];
+			unset($options['argSeparator']);
+		}
+
+		if ($named === true || $named === false) {
+			$options = array_merge(array('default' => $named, 'reset' => true, 'greedy' => $named), $options);
+			$named = array();
+		}
+		$options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options);
+
+		if ($options['reset'] == true || $_this->named['rules'] === false) {
+			$_this->named['rules'] = array();
+		}
+
+		if ($options['default']) {
+			$named = array_merge($named, $_this->named['default']);
+		}
+
+		foreach ($named as $key => $val) {
+			if (is_numeric($key)) {
+				$_this->named['rules'][$val] = true;
+			} else {
+				$_this->named['rules'][$key] = $val;
+			}
+		}
+		$_this->named['greedy'] = $options['greedy'];
+		return $_this->named;
+	}
+/**
+ * Creates REST resource routes for the given controller(s)
+ *
+ * @param mixed $controller		A controller name or array of controller names (i.e. "Posts" or "ListItems")
+ * @param array $options		Options to use when generating REST routes
+ *					'id' -		The regular expression fragment to use when matching IDs.  By default, matches
+ *								integer values and UUIDs.
+ *					'prefix' -	URL prefix to use for the generated routes.  Defaults to '/'.
+ * @return void
+ * @access public
+ * @static
+ */
+	function mapResources($controller, $options = array()) {
+		$_this =& Router::getInstance();
+		$options = array_merge(array('prefix' => '/', 'id' => $_this->__named['ID'] . '|' . $_this->__named['UUID']), $options);
+		$prefix = $options['prefix'];
+
+		foreach ((array)$controller as $ctlName) {
+			$urlName = Inflector::underscore($ctlName);
+
+			foreach ($_this->__resourceMap as $params) {
+				extract($params);
+				$url = $prefix . $urlName . (($id) ? '/:id' : '');
+
+				Router::connect($url,
+					array('controller' => $urlName, 'action' => $action, '[method]' => $params['method']),
+					array('id' => $options['id'], 'pass' => array('id'))
+				);
+			}
+			$_this->__resourceMapped[] = $urlName;
+		}
+	}
+/**
+ * Builds a route regular expression
+ *
+ * @param string $route			An empty string, or a route string "/"
+ * @param array $default		NULL or an array describing the default route
+ * @param array $params			An array matching the named elements in the route to regular expressions which that element should match.
+ * @return array
+ * @see routes
+ * @access public
+ * @static
+ */
+	function writeRoute($route, $default, $params) {
+		if (empty($route) || ($route === '/')) {
+			return array('/^[\/]*$/', array());
+		}
+		$names = array();
+		$elements = explode('/', $route);
+
+		foreach ($elements as $element) {
+			if (empty($element)) {
+				continue;
+			}
+			$q = null;
+			$element = trim($element);
+			$namedParam = strpos($element, ':') !== false;
+
+			if ($namedParam && preg_match('/^:([^:]+)$/', $element, $r)) {
+				if (isset($params[$r[1]])) {
+					if ($r[1] != 'plugin' && array_key_exists($r[1], $default)) {
+						$q = '?';
+					}
+					$parsed[] = '(?:/(' . $params[$r[1]] . ')' . $q . ')' . $q;
+				} else {
+					$parsed[] = '(?:/([^\/]+))?';
+				}
+				$names[] = $r[1];
+			} elseif ($element === '*') {
+				$parsed[] = '(?:/(.*))?';
+			} else if ($namedParam && preg_match_all('/(?!\\\\):([a-z_0-9]+)/i', $element, $matches)) {
+				$matchCount = count($matches[1]);
+
+				foreach ($matches[1] as $i => $name) {
+					$pos = strpos($element, ':' . $name);
+					$before = substr($element, 0, $pos);
+					$element = substr($element, $pos + strlen($name) + 1);
+					$after = null;
+
+					if ($i + 1 === $matchCount && $element) {
+						$after = preg_quote($element);
+					}
+
+					if ($i === 0) {
+						$before = '/' . $before;
+					}
+					$before = preg_quote($before, '#');
+
+					if (isset($params[$name])) {
+						if (isset($default[$name]) && $name != 'plugin') {
+							$q = '?';
+						}
+						$parsed[] = '(?:' . $before . '(' . $params[$name] . ')' . $q . $after . ')' . $q;
+					} else {
+						$parsed[] = '(?:' . $before . '([^\/]+)' . $after . ')?';
+					}
+					$names[] = $name;
+				}
+			} else {
+				$parsed[] = '/' . $element;
+			}
+		}
+		return array('#^' . join('', $parsed) . '[\/]*$#', $names);
+	}
+/**
+ * Returns the list of prefixes used in connected routes
+ *
+ * @return array A list of prefixes used in connected routes
+ * @access public
+ * @static
+ */
+	function prefixes() {
+		$_this =& Router::getInstance();
+		return $_this->__prefixes;
+	}
+/**
+ * Parses given URL and returns an array of controllers, action and parameters
+ * taken from that URL.
+ *
+ * @param string $url URL to be parsed
+ * @return array Parsed elements from URL
+ * @access public
+ * @static
+ */
+	function parse($url) {
+		$_this =& Router::getInstance();
+		if (!$_this->__defaultsMapped) {
+			$_this->__connectDefaultRoutes();
+		}
+		$out = array('pass' => array(), 'named' => array());
+		$r = $ext = null;
+
+		if (ini_get('magic_quotes_gpc') === '1') {
+			$url = stripslashes_deep($url);
+		}
+
+		if ($url && strpos($url, '/') !== 0) {
+			$url = '/' . $url;
+		}
+		if (strpos($url, '?') !== false) {
+			$url = substr($url, 0, strpos($url, '?'));
+		}
+		extract($_this->__parseExtension($url));
+
+		foreach ($_this->routes as $i => $route) {
+			if (count($route) === 3) {
+				$route = $_this->compile($i);
+			}
+
+			if (($r = $_this->__matchRoute($route, $url)) !== false) {
+				$_this->__currentRoute[] = $route;
+				list($route, $regexp, $names, $defaults, $params) = $route;
+				$argOptions = array();
+
+				if (array_key_exists('named', $params)) {
+					$argOptions['named'] = $params['named'];
+					unset($params['named']);
+				}
+				if (array_key_exists('greedy', $params)) {
+					$argOptions['greedy'] = $params['greedy'];
+					unset($params['greedy']);
+				}
+				array_shift($r);
+
+				foreach ($names as $name) {
+					$out[$name] = null;
+				}
+				if (is_array($defaults)) {
+					foreach ($defaults as $name => $value) {
+						if (preg_match('#[a-zA-Z_\-]#i', $name)) {
+							$out[$name] = $value;
+						} else {
+							$out['pass'][] = $value;
+						}
+					}
+				}
+
+				foreach ($r as $key => $found) {
+					if (empty($found) && $found != 0) {
+						continue;
+					}
+
+					if (isset($names[$key])) {
+						$out[$names[$key]] = $_this->stripEscape($found);
+					} elseif (isset($names[$key]) && empty($names[$key]) && empty($out[$names[$key]])) {
+						break;
+					} else {
+						$argOptions['context'] = array('action' => $out['action'], 'controller' => $out['controller']);
+						extract($_this->getArgs($found, $argOptions));
+						$out['pass'] = array_merge($out['pass'], $pass);
+						$out['named'] = $named;
+					}
+				}
+
+				if (isset($params['pass'])) {
+					for ($j = count($params['pass']) - 1; $j > -1; $j--) {
+						if (isset($out[$params['pass'][$j]])) {
+							array_unshift($out['pass'], $out[$params['pass'][$j]]);
+						}
+					}
+				}
+				break;
+			}
+		}
+
+		if (!empty($ext)) {
+			$out['url']['ext'] = $ext;
+		}
+		return $out;
+	}
+/**
+ * Checks to see if the given URL matches the given route
+ *
+ * @param array $route
+ * @param string $url
+ * @return mixed Boolean false on failure, otherwise array
+ * @access private
+ */
+	function __matchRoute($route, $url) {
+		list($route, $regexp, $names, $defaults) = $route;
+
+		if (!preg_match($regexp, $url, $r)) {
+			return false;
+		} else {
+			foreach ($defaults as $key => $val) {
+				if ($key{0} === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) {
+					if (isset($this->__headerMap[$header[1]])) {
+						$header = $this->__headerMap[$header[1]];
+					} else {
+						$header = 'http_' . $header[1];
+					}
+
+					$val = (array)$val;
+					$h = false;
+
+					foreach ($val as $v) {
+						if (env(strtoupper($header)) === $v) {
+							$h = true;
+						}
+					}
+					if (!$h) {
+						return false;
+					}
+				}
+			}
+		}
+		return $r;
+	}
+/**
+ * Compiles a route by numeric key and returns the compiled expression, replacing
+ * the existing uncompiled route.  Do not call statically.
+ *
+ * @param integer $i
+ * @return array Returns an array containing the compiled route
+ * @access public
+ */
+	function compile($i) {
+		$route = $this->routes[$i];
+
+		if (!list($pattern, $names) = $this->writeRoute($route[0], $route[1], $route[2])) {
+			unset($this->routes[$i]);
+			return array();
+		}
+		$this->routes[$i] = array(
+			$route[0], $pattern, $names,
+			array_merge(array('plugin' => null, 'controller' => null), (array)$route[1]),
+			$route[2]
+		);
+		return $this->routes[$i];
+	}
+/**
+ * Parses a file extension out of a URL, if Router::parseExtensions() is enabled.
+ *
+ * @param string $url
+ * @return array Returns an array containing the altered URL and the parsed extension.
+ * @access private
+ */
+	function __parseExtension($url) {
+		$ext = null;
+
+		if ($this->__parseExtensions) {
+			if (preg_match('/\.[0-9a-zA-Z]*$/', $url, $match) === 1) {
+				$match = substr($match[0], 1);
+				if (empty($this->__validExtensions)) {
+					$url = substr($url, 0, strpos($url, '.' . $match));
+					$ext = $match;
+				} else {
+					foreach ($this->__validExtensions as $name) {
+						if (strcasecmp($name, $match) === 0) {
+							$url = substr($url, 0, strpos($url, '.' . $name));
+							$ext = $match;
+						}
+					}
+				}
+			}
+			if (empty($ext)) {
+				$ext = 'html';
+			}
+		}
+		return compact('ext', 'url');
+	}
+/**
+ * Connects the default, built-in routes, including admin routes, and (deprecated) web services
+ * routes.
+ *
+ * @return void
+ * @access private
+ */
+	function __connectDefaultRoutes() {
+		if ($this->__defaultsMapped) {
+			return;
+		}
+
+		if ($this->__admin) {
+			$params = array('prefix' => $this->__admin, $this->__admin => true);
+		}
+
+		if ($plugins = Configure::listObjects('plugin')) {
+			foreach ($plugins as $key => $value) {
+				$plugins[$key] = Inflector::underscore($value);
+			}
+
+			$match = array('plugin' => implode('|', $plugins));
+			$this->connect('/:plugin/:controller/:action/*', array(), $match);
+
+			if ($this->__admin) {
+				$this->connect("/{$this->__admin}/:plugin/:controller", $params, $match);
+				$this->connect("/{$this->__admin}/:plugin/:controller/:action/*", $params, $match);
+			}
+		}
+
+		if ($this->__admin) {
+			$this->connect("/{$this->__admin}/:controller", $params);
+			$this->connect("/{$this->__admin}/:controller/:action/*", $params);
+		}
+		$this->connect('/:controller', array('action' => 'index'));
+		$this->connect('/:controller/:action/*');
+
+		if ($this->named['rules'] === false) {
+			$this->connectNamed(true);
+		}
+		$this->__defaultsMapped = true;
+	}
+/**
+ * Takes parameter and path information back from the Dispatcher
+ *
+ * @param array $params Parameters and path information
+ * @return void
+ * @access public
+ * @static
+ */
+	function setRequestInfo($params) {
+		$_this =& Router::getInstance();
+		$defaults = array('plugin' => null, 'controller' => null, 'action' => null);
+		$params[0] = array_merge($defaults, (array)$params[0]);
+		$params[1] = array_merge($defaults, (array)$params[1]);
+		list($_this->__params[], $_this->__paths[]) = $params;
+
+		if (count($_this->__paths)) {
+			if (isset($_this->__paths[0]['namedArgs'])) {
+				foreach ($_this->__paths[0]['namedArgs'] as $arg => $value) {
+					$_this->named['rules'][$arg] = true;
+				}
+			}
+		}
+	}
+/**
+ * Gets parameter information
+ *
+ * @param boolean $current Get current parameter (true)
+ * @return array Parameter information
+ * @access public
+ * @static
+ */
+	function getParams($current = false) {
+		$_this =& Router::getInstance();
+		if ($current) {
+			return $_this->__params[count($_this->__params) - 1];
+		}
+		if (isset($_this->__params[0])) {
+			return $_this->__params[0];
+		}
+		return array();
+	}
+/**
+ * Gets URL parameter by name
+ *
+ * @param string $name Parameter name
+ * @param boolean $current Current parameter
+ * @return string Parameter value
+ * @access public
+ * @static
+ */
+	function getParam($name = 'controller', $current = false) {
+		$params = Router::getParams($current);
+		if (isset($params[$name])) {
+			return $params[$name];
+		}
+		return null;
+	}
+/**
+ * Gets path information
+ *
+ * @param boolean $current Current parameter
+ * @return array
+ * @access public
+ * @static
+ */
+	function getPaths($current = false) {
+		$_this =& Router::getInstance();
+		if ($current) {
+			return $_this->__paths[count($_this->__paths) - 1];
+		}
+		if (!isset($_this->__paths[0])) {
+			return array('base' => null);
+		}
+		return $_this->__paths[0];
+	}
+/**
+ * Reloads default Router settings
+ *
+ * @access public
+ * @return void
+ * @static
+ */
+	function reload() {
+		$_this =& Router::getInstance();
+		foreach (get_class_vars('Router') as $key => $val) {
+			$_this->{$key} = $val;
+		}
+		$_this->__admin = Configure::read('Routing.admin');
+	}
+/**
+ * Promote a route (by default, the last one added) to the beginning of the list
+ *
+ * @param $which A zero-based array index representing the route to move. For example,
+ *               if 3 routes have been added, the last route would be 2.
+ * @return boolean Retuns false if no route exists at the position specified by $which.
+ * @access public
+ * @static
+ */
+	function promote($which = null) {
+		$_this =& Router::getInstance();
+		if ($which === null) {
+			$which = count($_this->routes) - 1;
+		}
+		if (!isset($_this->routes[$which])) {
+			return false;
+		}
+		$route = $_this->routes[$which];
+		unset($_this->routes[$which]);
+		array_unshift($_this->routes, $route);
+		return true;
+	}
+/**
+ * Finds URL for specified action.
+ *
+ * Returns an URL pointing to a combination of controller and action. Param
+ * $url can be:
+ *	+ Empty - the method will find adress to actuall controller/action.
+ *	+ '/' - the method will find base URL of application.
+ *	+ A combination of controller/action - the method will find url for it.
+ *
+ * @param  mixed  $url    Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4"
+ *                        or an array specifying any of the following: 'controller', 'action',
+ *                        and/or 'plugin', in addition to named arguments (keyed array elements),
+ *                        and standard URL arguments (indexed array elements)
+ * @param mixed $options If (bool)true, the full base URL will be prepended to the result. 
+ *                       If an array accepts the following keys
+ *                           escape - used when making urls embedded in html escapes query string '&'
+ *                           full - if true the full base URL will be prepended.
+ * @return string  Full translated URL with base path.
+ * @access public
+ * @static
+ */
+	function url($url = null, $full = false) {
+		$_this =& Router::getInstance();
+		$defaults = $params = array('plugin' => null, 'controller' => null, 'action' => 'index');
+		
+		if (is_bool($full)) {
+			$escape = false;
+		} else {
+			extract(array_merge(array('escape' => false, 'full' => false), $full));
+		}
+		
+		if (!empty($_this->__params)) {
+			if (isset($this) && !isset($this->params['requested'])) {
+				$params = $_this->__params[0];
+			} else {
+				$params = end($_this->__params);
+			}
+		}
+		$path = array('base' => null);
+
+		if (!empty($_this->__paths)) {
+			if (isset($this) && !isset($this->params['requested'])) {
+				$path = $_this->__paths[0];
+			} else {
+				$path = end($_this->__paths);
+			}
+		}
+		$base = $path['base'];
+		$extension = $output = $mapped = $q = $frag = null;
+
+		if (is_array($url)) {
+			if (isset($url['base']) && $url['base'] === false) {
+				$base = null;
+				unset($url['base']);
+			}
+			if (isset($url['full_base']) && $url['full_base'] === true) {
+				$full = true;
+				unset($url['full_base']);
+			}
+			if (isset($url['?'])) {
+				$q = $url['?'];
+				unset($url['?']);
+			}
+			if (isset($url['#'])) {
+				$frag = '#' . urlencode($url['#']);
+				unset($url['#']);
+			}
+			if (empty($url['action'])) {
+				if (empty($url['controller']) || $params['controller'] === $url['controller']) {
+					$url['action'] = $params['action'];
+				} else {
+					$url['action'] = 'index';
+				}
+			}
+			if ($_this->__admin) {
+				if (!isset($url[$_this->__admin]) && !empty($params[$_this->__admin])) {
+					$url[$_this->__admin] = true;
+				} elseif ($_this->__admin && isset($url[$_this->__admin]) && !$url[$_this->__admin]) {
+					unset($url[$_this->__admin]);
+				}
+			}
+			$plugin = false;
+
+			if (array_key_exists('plugin', $url)) {
+				$plugin = $url['plugin'];
+			}
+
+			$url = array_merge(array('controller' => $params['controller'], 'plugin' => $params['plugin']), Set::filter($url, true));
+
+			if ($plugin !== false) {
+				$url['plugin'] = $plugin;
+			}
+
+			if (isset($url['ext'])) {
+				$extension = '.' . $url['ext'];
+				unset($url['ext']);
+			}
+			$match = false;
+
+			foreach ($_this->routes as $i => $route) {
+				if (count($route) === 3) {
+					$route = $_this->compile($i);
+				}
+				$originalUrl = $url;
+
+				if (isset($route[4]['persist'], $_this->__params[0])) {
+					$url = array_merge(array_intersect_key($params, Set::combine($route[4]['persist'], '/')), $url);
+				}
+				if ($match = $_this->mapRouteElements($route, $url)) {
+					$output = trim($match, '/');
+					$url = array();
+					break;
+				}
+				$url = $originalUrl;
+			}
+			$named = $args = array();
+			$skip = array(
+				'bare', 'action', 'controller', 'plugin', 'ext', '?', '#', 'prefix', $_this->__admin
+			);
+
+			$keys = array_values(array_diff(array_keys($url), $skip));
+			$count = count($keys);
+
+			// Remove this once parsed URL parameters can be inserted into 'pass'
+			for ($i = 0; $i < $count; $i++) {
+				if ($i === 0 && is_numeric($keys[$i]) && in_array('id', $keys)) {
+					$args[0] = $url[$keys[$i]];
+				} elseif (is_numeric($keys[$i]) || $keys[$i] === 'id') {
+					$args[] = $url[$keys[$i]];
+				} else {
+					$named[$keys[$i]] = $url[$keys[$i]];
+				}
+			}
+
+			if ($match === false) {
+				list($args, $named)  = array(Set::filter($args, true), Set::filter($named));
+				if (!empty($url[$_this->__admin])) {
+					$url['action'] = str_replace($_this->__admin . '_', '', $url['action']);
+				}
+
+				if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) {
+					$url['action'] = null;
+				}
+
+				$urlOut = Set::filter(array($url['controller'], $url['action']));
+
+				if (isset($url['plugin']) && $url['plugin'] != $url['controller']) {
+					array_unshift($urlOut, $url['plugin']);
+				}
+
+				if ($_this->__admin && isset($url[$_this->__admin])) {
+					array_unshift($urlOut, $_this->__admin);
+				}
+				$output = join('/', $urlOut) . '/';
+			}
+
+			if (!empty($args)) {
+				$args = join('/', $args);
+				if ($output{strlen($output) - 1} != '/') {
+					$args = '/'. $args;
+				}
+				$output .= $args;
+			}
+
+			if (!empty($named)) {
+				foreach ($named as $name => $value) {
+					$output .= '/' . $name . $_this->named['separator'] . $value;
+				}
+			}
+
+			$output = str_replace('//', '/', $base . '/' . $output);
+		} else {
+			if (((strpos($url, '://')) || (strpos($url, 'javascript:') === 0) || (strpos($url, 'mailto:') === 0)) || (!strncmp($url, '#', 1))) {
+				return $url;
+			}
+			if (empty($url)) {
+				if (!isset($path['here'])) {
+					$path['here'] = '/';
+				}
+				$output = $path['here'];
+			} elseif (substr($url, 0, 1) === '/') {
+				$output = $base . $url;
+			} else {
+				$output = $base . '/';
+				if ($_this->__admin && isset($params[$_this->__admin])) {
+					$output .= $_this->__admin . '/';
+				}
+				if (!empty($params['plugin']) && $params['plugin'] !== $params['controller']) {
+					$output .= Inflector::underscore($params['plugin']) . '/';
+				}
+				$output .= Inflector::underscore($params['controller']) . '/' . $url;
+			}
+			$output = str_replace('//', '/', $output);
+		}
+		if ($full && defined('FULL_BASE_URL')) {
+			$output = FULL_BASE_URL . $output;
+		}
+		if (!empty($extension) && substr($output, -1) === '/') {
+			$output = substr($output, 0, -1);
+		}
+
+		return $output . $extension . $_this->queryString($q, array(), $escape) . $frag;
+	}
+/**
+ * Maps a URL array onto a route and returns the string result, or false if no match
+ *
+ * @param array $route Route Route
+ * @param array $url URL URL to map
+ * @return mixed Result (as string) or false if no match
+ * @access public
+ * @static
+ */
+	function mapRouteElements($route, $url) {
+		if (isset($route[3]['prefix'])) {
+			$prefix = $route[3]['prefix'];
+			unset($route[3]['prefix']);
+		}
+
+		$pass = array();
+		$defaults = $route[3];
+		$routeParams = $route[2];
+		$params = Set::diff($url, $defaults);
+		$urlInv = array_combine(array_values($url), array_keys($url));
+
+		$i = 0;
+		while (isset($defaults[$i])) {
+			if (isset($urlInv[$defaults[$i]])) {
+				if (!in_array($defaults[$i], $url) && is_int($urlInv[$defaults[$i]])) {
+					return false;
+				}
+				unset($urlInv[$defaults[$i]], $defaults[$i]);
+			} else {
+				return false;
+			}
+			$i++;
+		}
+
+		foreach ($params as $key => $value) {
+			if (is_int($key)) {
+				$pass[] = $value;
+				unset($params[$key]);
+			}
+		}
+		list($named, $params) = Router::getNamedElements($params);
+
+		if (!strpos($route[0], '*') && (!empty($pass) || !empty($named))) {
+			return false;
+		}
+
+		$urlKeys = array_keys($url);
+		$paramsKeys = array_keys($params);
+		$defaultsKeys = array_keys($defaults);
+
+		if (!empty($params)) {
+			if (array_diff($paramsKeys, $routeParams) != array()) {
+				return false;
+			}
+			$required = array_values(array_diff($routeParams, $urlKeys));
+			$reqCount = count($required);
+
+			for ($i = 0; $i < $reqCount; $i++) {
+				if (array_key_exists($required[$i], $defaults) && $defaults[$required[$i]] === null) {
+					unset($required[$i]);
+				}
+			}
+		}
+		$isFilled = true;
+
+		if (!empty($routeParams)) {
+			$filled = array_intersect_key($url, array_combine($routeParams, array_keys($routeParams)));
+			$isFilled = (array_diff($routeParams, array_keys($filled)) === array());
+			if (!$isFilled && empty($params)) {
+				return false;
+			}
+		}
+
+		if (empty($params)) {
+			return Router::__mapRoute($route, array_merge($url, compact('pass', 'named', 'prefix')));
+		} elseif (!empty($routeParams) && !empty($route[3])) {
+
+			if (!empty($required)) {
+				return false;
+			}
+			foreach ($params as $key => $val) {
+				if ((!isset($url[$key]) || $url[$key] != $val) || (!isset($defaults[$key]) || $defaults[$key] != $val) && !in_array($key, $routeParams)) {
+					if (!isset($defaults[$key])) {
+						continue;
+					}
+					return false;
+				}
+			}
+		} else {
+			if (empty($required) && $defaults['plugin'] === $url['plugin'] && $defaults['controller'] === $url['controller'] && $defaults['action'] === $url['action']) {
+				return Router::__mapRoute($route, array_merge($url, compact('pass', 'named', 'prefix')));
+			}
+			return false;
+		}
+
+		if (!empty($route[4])) {
+			foreach ($route[4] as $key => $reg) {
+				if (array_key_exists($key, $url) && !preg_match('#' . $reg . '#', $url[$key])) {
+					return false;
+				}
+			}
+		}
+		return Router::__mapRoute($route, array_merge($filled, compact('pass', 'named', 'prefix')));
+	}
+/**
+ * Merges URL parameters into a route string
+ *
+ * @param array $route Route
+ * @param array $params Parameters
+ * @return string Merged URL with parameters
+ * @access private
+ */
+	function __mapRoute($route, $params = array()) {
+		if (isset($params['plugin']) && isset($params['controller']) && $params['plugin'] === $params['controller']) {
+			unset($params['controller']);
+		}
+
+		if (isset($params['prefix']) && isset($params['action'])) {
+			$params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
+			unset($params['prefix']);
+		}
+
+		if (isset($params['pass']) && is_array($params['pass'])) {
+			$params['pass'] = implode('/', Set::filter($params['pass'], true));
+		} elseif (!isset($params['pass'])) {
+			$params['pass'] = '';
+		}
+
+		if (isset($params['named'])) {
+			if (is_array($params['named'])) {
+				$count = count($params['named']);
+				$keys = array_keys($params['named']);
+				$named = array();
+
+				for ($i = 0; $i < $count; $i++) {
+					$named[] = $keys[$i] . $this->named['separator'] . $params['named'][$keys[$i]];
+				}
+				$params['named'] = join('/', $named);
+			}
+			$params['pass'] = str_replace('//', '/', $params['pass'] . '/' . $params['named']);
+		}
+		$out = $route[0];
+
+		foreach ($route[2] as $key) {
+			$string = null;
+			if (isset($params[$key])) {
+				$string = $params[$key];
+				unset($params[$key]);
+			}
+			$out = str_replace(':' . $key, $string, $out);
+		}
+
+		if (strpos($route[0], '*')) {
+			$out = str_replace('*', $params['pass'], $out);
+		}
+		return $out;
+	}
+/**
+ * Takes an array of URL parameters and separates the ones that can be used as named arguments
+ *
+ * @param array $params			Associative array of URL parameters.
+ * @param string $controller	Name of controller being routed.  Used in scoping.
+ * @param string $action	 	Name of action being routed.  Used in scoping.
+ * @return array
+ * @access public
+ * @static
+ */
+	function getNamedElements($params, $controller = null, $action = null) {
+		$_this =& Router::getInstance();
+		$named = array();
+
+		foreach ($params as $param => $val) {
+			if (isset($_this->named['rules'][$param])) {
+				$rule = $_this->named['rules'][$param];
+				if (Router::matchNamed($param, $val, $rule, compact('controller', 'action'))) {
+					$named[$param] = $val;
+					unset($params[$param]);
+				}
+			}
+		}
+		return array($named, $params);
+	}
+/**
+ * Return true if a given named $param's $val matches a given $rule depending on $context. Currently implemented
+ * rule types are controller, action and match that can be combined with each other.
+ *
+ * @param string $param The name of the named parameter
+ * @param string $val The value of the named parameter
+ * @param array $rule The rule(s) to apply, can also be a match string
+ * @param string $context An array with additional context information (controller / action)
+ * @return boolean
+ * @access public
+ */
+	function matchNamed($param, $val, $rule, $context = array()) {
+		if ($rule === true || $rule === false) {
+			return $rule;
+		}
+		if (is_string($rule)) {
+			$rule = array('match' => $rule);
+		}
+		if (!is_array($rule)) {
+			return false;
+		}
+
+		$controllerMatches = !isset($rule['controller'], $context['controller']) || in_array($context['controller'], (array)$rule['controller']);
+		if (!$controllerMatches) {
+			return false;
+		}
+		$actionMatches = !isset($rule['action'], $context['action']) || in_array($context['action'], (array)$rule['action']);
+		if (!$actionMatches) {
+			return false;
+		}
+		$valueMatches = !isset($rule['match']) || preg_match(sprintf('/%s/', $rule['match']), $val);
+		return $valueMatches;
+	}
+/**
+ * Generates a well-formed querystring from $q
+ *
+ * @param mixed $q Query string
+ * @param array $extra Extra querystring parameters.
+ * @param bool $escape Whether or not to use escaped &
+ * @return array
+ * @access public
+ * @static
+ */
+	function queryString($q, $extra = array(), $escape = false) {
+		if (empty($q) && empty($extra)) {
+			return null;
+		}
+		$join = '&';
+		if ($escape === true) {
+			$join = '&amp;';
+		}
+		$out = '';
+
+		if (is_array($q)) {
+			$q = array_merge($extra, $q);
+		} else {
+			$out = $q;
+			$q = $extra;
+		}
+		$out .= http_build_query($q, null, $join);
+		if (isset($out[0]) && $out[0] != '?') {
+			$out = '?' . $out;
+		}
+		return $out;
+	}
+/**
+ * Normalizes a URL for purposes of comparison
+ *
+ * @param mixed $url URL to normalize
+ * @return string Normalized URL
+ * @access public
+ */
+	function normalize($url = '/') {
+		if (is_array($url)) {
+			$url = Router::url($url);
+		} elseif (preg_match('/^[a-z\-]+:\/\//', $url)) {
+			return $url;
+		}
+		$paths = Router::getPaths();
+
+		if (!empty($paths['base']) && stristr($url, $paths['base'])) {
+			$url = preg_replace('/' . preg_quote($paths['base'], '/') . '/', '', $url, 1);
+		}
+		$url = '/' . $url;
+
+		while (strpos($url, '//') !== false) {
+			$url = str_replace('//', '/', $url);
+		}
+		$url = preg_replace('/(?:(\/$))/', '', $url);
+
+		if (empty($url)) {
+			return '/';
+		}
+		return $url;
+	}
+/**
+ * Returns the route matching the current request URL.
+ *
+ * @return array Matching route
+ * @access public
+ * @static
+ */
+	function requestRoute() {
+		$_this =& Router::getInstance();
+		return $_this->__currentRoute[0];
+	}
+/**
+ * Returns the route matching the current request (useful for requestAction traces)
+ *
+ * @return array Matching route
+ * @access public
+ * @static
+ */
+	function currentRoute() {
+		$_this =& Router::getInstance();
+		return $_this->__currentRoute[count($_this->__currentRoute) - 1];
+	}
+/**
+ * Removes the plugin name from the base URL.
+ *
+ * @param string $base Base URL
+ * @param string $plugin Plugin name
+ * @return base url with plugin name removed if present
+ * @access public
+ * @static
+ */
+	function stripPlugin($base, $plugin) {
+		if ($plugin != null) {
+			$base = preg_replace('/(?:' . $plugin . ')/', '', $base);
+			$base = str_replace('//', '', $base);
+			$pos1 = strrpos($base, '/');
+			$char = strlen($base) - 1;
+
+			if ($pos1 === $char) {
+				$base = substr($base, 0, $char);
+			}
+		}
+		return $base;
+	}
+
+/**
+ * Strip escape characters from parameter values.
+ *
+ * @param mixed $param Either an array, or a string
+ * @return mixed Array or string escaped
+ * @access public
+ * @static
+ */
+	function stripEscape($param) {
+		$_this =& Router::getInstance();
+		if (!is_array($param) || empty($param)) {
+			if (is_bool($param)) {
+				return $param;
+			}
+
+			$return = preg_replace('/^(?:[\\t ]*(?:-!)+)/', '', $param);
+			return $return;
+		}
+		foreach ($param as $key => $value) {
+			if (is_string($value)) {
+				$return[$key] = preg_replace('/^(?:[\\t ]*(?:-!)+)/', '', $value);
+			} else {
+				foreach ($value as $array => $string) {
+					$return[$key][$array] = $_this->stripEscape($string);
+				}
+			}
+		}
+		return $return;
+	}
+/**
+ * Instructs the router to parse out file extensions from the URL. For example,
+ * http://example.com/posts.rss would yield an file extension of "rss".
+ * The file extension itself is made available in the controller as
+ * $this->params['url']['ext'], and is used by the RequestHandler component to
+ * automatically switch to alternate layouts and templates, and load helpers
+ * corresponding to the given content, i.e. RssHelper.
+ *
+ * A list of valid extension can be passed to this method, i.e. Router::parseExtensions('rss', 'xml');
+ * If no parameters are given, anything after the first . (dot) after the last / in the URL will be
+ * parsed, excluding querystring parameters (i.e. ?q=...).
+ *
+ * @access public
+ * @return void
+ * @static
+ */
+	function parseExtensions() {
+		$_this =& Router::getInstance();
+		$_this->__parseExtensions = true;
+		if (func_num_args() > 0) {
+			$_this->__validExtensions = func_get_args();
+		}
+	}
+/**
+ * Takes an passed params and converts it to args
+ *
+ * @param array $params
+ * @return array Array containing passed and named parameters
+ * @access public
+ * @static
+ */
+	function getArgs($args, $options = array()) {
+		$_this =& Router::getInstance();
+		$pass = $named = array();
+		$args = explode('/', $args);
+
+		$greedy = $_this->named['greedy'];
+		if (isset($options['greedy'])) {
+			$greedy = $options['greedy'];
+		}
+		$context = array();
+		if (isset($options['context'])) {
+			$context = $options['context'];
+		}
+		$rules = $_this->named['rules'];
+		if (isset($options['named'])) {
+			$greedy = isset($options['greedy']) && $options['greedy'] === true;
+			foreach ((array)$options['named'] as $key => $val) {
+				if (is_numeric($key)) {
+					$rules[$val] = true;
+					continue;
+				}
+				$rules[$key] = $val;
+			}
+		}
+
+		foreach ($args as $param) {
+			if (empty($param) && $param !== '0' && $param !== 0) {
+				continue;
+			}
+			$param = $_this->stripEscape($param);
+			if ((!isset($options['named']) || !empty($options['named'])) && strpos($param, $_this->named['separator']) !== false) {
+				list($key, $val) = explode($_this->named['separator'], $param, 2);
+				$hasRule = isset($rules[$key]);
+				$passIt = (!$hasRule && !$greedy) || ($hasRule && !Router::matchNamed($key, $val, $rules[$key], $context));
+				if ($passIt) {
+					$pass[] = $param;
+				} else {
+					$named[$key] = $val;
+				}
+			} else {
+				$pass[] = $param;
+			}
+		}
+		return compact('pass', 'named');
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/session.php
===================================================================
--- cake/libs/session.php	(revision 0)
+++ cake/libs/session.php	(revision 0)
@@ -0,0 +1,778 @@
+<?php
+/* SVN FILE: $Id: session.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Session class for Cake.
+ *
+ * Cake abstracts the handling of sessions.
+ * There are several convenient methods to access session information.
+ * This class is the implementation of those methods.
+ * They are mostly used by the Session Component.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v .0.10.0.1222
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Database name for cake sessions.
+ *
+ */
+if (!class_exists('Set')) {
+	require LIBS . 'set.php';
+}
+if (!class_exists('Security')) {
+	require LIBS . 'security.php';
+}
+/**
+ * Session class for Cake.
+ *
+ * Cake abstracts the handling of sessions. There are several convenient methods to access session information.
+ * This class is the implementation of those methods. They are mostly used by the Session Component.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CakeSession extends Object {
+/**
+ * True if the Session is still valid
+ *
+ * @var boolean
+ * @access public
+ */
+	var $valid = false;
+/**
+ * Error messages for this session
+ *
+ * @var array
+ * @access public
+ */
+	var $error = false;
+/**
+ * User agent string
+ *
+ * @var string
+ * @access protected
+ */
+	var $_userAgent = '';
+/**
+ * Path to where the session is active.
+ *
+ * @var string
+ * @access public
+ */
+	var $path = '/';
+/**
+ * Error number of last occurred error
+ *
+ * @var integer
+ * @access public
+ */
+	var $lastError = null;
+/**
+ * 'Security.level' setting, "high", "medium", or "low".
+ *
+ * @var string
+ * @access public
+ */
+	var $security = null;
+/**
+ * Start time for this session.
+ *
+ * @var integer
+ * @access public
+ */
+	var $time = false;
+/**
+ * Time when this session becomes invalid.
+ *
+ * @var integer
+ * @access public
+ */
+	var $sessionTime = false;
+/**
+ * Keeps track of keys to watch for writes on
+ *
+ * @var array
+ * @access public
+ */
+	var $watchKeys = array();
+/**
+ * Current Session id
+ *
+ * @var string
+ * @access public
+ */
+	var $id = null;
+/**
+ * Constructor.
+ *
+ * @param string $base The base path for the Session
+ * @param boolean $start Should session be started right now
+ * @access public
+ */
+	function __construct($base = null, $start = true) {
+		if (Configure::read('Session.save') === 'database' && !class_exists('ConnectionManager')) {
+			App::import('Core', 'ConnectionManager');
+		}
+
+		if (Configure::read('Session.checkAgent') === true || Configure::read('Session.checkAgent') === null) {
+			if (env('HTTP_USER_AGENT') != null) {
+				$this->_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
+			}
+		}
+		$this->time = time();
+
+		if ($start === true) {
+			$this->host = env('HTTP_HOST');
+
+			if (empty($base) || strpos($base, '?') === 0 || strpos($base, 'index.php') === 0) {
+				$this->path = '/';
+			} else {
+				$this->path = $base;
+			}
+
+			if (strpos($this->host, ':') !== false) {
+				$this->host = substr($this->host, 0, strpos($this->host, ':'));
+			}
+
+			if (!class_exists('Security')) {
+				App::import('Core', 'Security');
+			}
+
+			$this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout'));
+			$this->security = Configure::read('Security.level');
+		}
+		parent::__construct();
+	}
+/**
+ * Starts the Session.
+ *
+ * @param string $name Variable name to check for
+ * @return boolean True if variable is there
+ * @access public
+ */
+	function start() {
+		if (function_exists('session_write_close')) {
+			session_write_close();
+		}
+		$this->__initSession();
+		return $this->__startSession();
+	}
+/**
+ * Determine if Session has been started.
+ *
+ * @access public
+ * @return boolean True if session has been started.
+ */
+	function started() {
+		if (isset($_SESSION)) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns true if given variable is set in session.
+ *
+ * @param string $name Variable name to check for
+ * @return boolean True if variable is there
+ * @access public
+ */
+	function check($name) {
+		$var = $this->__validateKeys($name);
+		if (empty($var)) {
+			return false;
+		}
+		$result = Set::extract($_SESSION, $var);
+		return isset($result);
+	}
+/**
+ * Returns the Session id
+ *
+ * @param id $name string
+ * @return string Session id
+ * @access public
+ */
+	function id($id = null) {
+		if ($id) {
+			$this->id = $id;
+			session_id($this->id);
+		}
+		if (isset($_SESSION)) {
+			return session_id();
+		} else {
+			return $this->id;
+		}
+	}
+/**
+ * Removes a variable from session.
+ *
+ * @param string $name Session variable to remove
+ * @return boolean Success
+ * @access public
+ */
+	function del($name) {
+		if ($this->check($name)) {
+			if ($var = $this->__validateKeys($name)) {
+				if (in_array($var, $this->watchKeys)) {
+					trigger_error('Deleting session key {' . $var . '}', E_USER_NOTICE);
+				}
+				$this->__overwrite($_SESSION, Set::remove($_SESSION, $var));
+				return ($this->check($var) == false);
+			}
+		}
+		$this->__setError(2, "$name doesn't exist");
+		return false;
+	}
+/**
+ * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself
+ *
+ * @param array $old Set of old variables => values
+ * @param array $new New set of variable => value
+ * @access private
+ */
+	function __overwrite(&$old, $new) {
+		if (!empty($old)) {
+			foreach ($old as $key => $var) {
+				if (!isset($new[$key])) {
+					unset($old[$key]);
+				}
+			}
+		}
+		foreach ($new as $key => $var) {
+			$old[$key] = $var;
+		}
+	}
+/**
+ * Return error description for given error number.
+ *
+ * @param integer $errorNumber Error to set
+ * @return string Error as string
+ * @access private
+ */
+	function __error($errorNumber) {
+		if (!is_array($this->error) || !array_key_exists($errorNumber, $this->error)) {
+			return false;
+		} else {
+			return $this->error[$errorNumber];
+		}
+	}
+/**
+ * Returns last occurred error as a string, if any.
+ *
+ * @return mixed Error description as a string, or false.
+ * @access public
+ */
+	function error() {
+		if ($this->lastError) {
+			return $this->__error($this->lastError);
+		} else {
+			return false;
+		}
+	}
+/**
+ * Returns true if session is valid.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function valid() {
+		if ($this->read('Config')) {
+			if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
+				if ($this->error === false) {
+					$this->valid = true;
+				}
+			} else {
+				$this->valid = false;
+				$this->__setError(1, 'Session Highjacking Attempted !!!');
+			}
+		}
+		return $this->valid;
+	}
+/**
+ * Returns given session variable, or all of them, if no parameters given.
+ *
+ * @param mixed $name The name of the session variable (or a path as sent to Set.extract)
+ * @return mixed The value of the session variable
+ * @access public
+ */
+	function read($name = null) {
+		if (is_null($name)) {
+			return $this->__returnSessionVars();
+		}
+		if (empty($name)) {
+			return false;
+		}
+		$result = Set::extract($_SESSION, $name);
+
+		if (!is_null($result)) {
+			return $result;
+		}
+		$this->__setError(2, "$name doesn't exist");
+		return null;
+	}
+/**
+ * Returns all session variables.
+ *
+ * @return mixed Full $_SESSION array, or false on error.
+ * @access private
+ */
+	function __returnSessionVars() {
+		if (!empty($_SESSION)) {
+			return $_SESSION;
+		}
+		$this->__setError(2, "No Session vars set");
+		return false;
+	}
+/**
+ * Tells Session to write a notification when a certain session path or subpath is written to
+ *
+ * @param mixed $var The variable path to watch
+ * @return void
+ * @access public
+ */
+	function watch($var) {
+		$var = $this->__validateKeys($var);
+		if (empty($var)) {
+			return false;
+		}
+		$this->watchKeys[] = $var;
+	}
+/**
+ * Tells Session to stop watching a given key path
+ *
+ * @param mixed $var The variable path to watch
+ * @return void
+ * @access public
+ */
+	function ignore($var) {
+		$var = $this->__validateKeys($var);
+		if (!in_array($var, $this->watchKeys)) {
+			return;
+		}
+		foreach ($this->watchKeys as $i => $key) {
+			if ($key == $var) {
+				unset($this->watchKeys[$i]);
+				$this->watchKeys = array_values($this->watchKeys);
+				return;
+			}
+		}
+	}
+/**
+ * Writes value to given session variable name.
+ *
+ * @param mixed $name Name of variable
+ * @param string $value Value to write
+ * @return boolean True if the write was successful, false if the write failed
+ * @access public
+ */
+	function write($name, $value) {
+		$var = $this->__validateKeys($name);
+
+		if (empty($var)) {
+			return false;
+		}
+		if (in_array($var, $this->watchKeys)) {
+			trigger_error('Writing session key {' . $var . '}: ' . Debugger::exportVar($value), E_USER_NOTICE);
+		}
+		$this->__overwrite($_SESSION, Set::insert($_SESSION, $var, $value));
+		return (Set::extract($_SESSION, $var) === $value);
+	}
+/**
+ * Helper method to destroy invalid sessions.
+ *
+ * @return void
+ * @access public
+ */
+	function destroy() {
+		$_SESSION = array();
+		$this->__construct($this->path);
+		$this->start();
+		$this->renew();
+		$this->_checkValid();
+	}
+/**
+ * Helper method to initialize a session, based on Cake core settings.
+ *
+ * @access private
+ */
+	function __initSession() {
+		$iniSet = function_exists('ini_set');
+
+		if ($iniSet && env('HTTPS')) {
+			ini_set('session.cookie_secure', 1);
+		}
+
+		switch ($this->security) {
+			case 'high':
+				$this->cookieLifeTime = 0;
+				if ($iniSet) {
+					ini_set('session.referer_check', $this->host);
+				}
+			break;
+			case 'medium':
+				$this->cookieLifeTime = 7 * 86400;
+				if ($iniSet) {
+					ini_set('session.referer_check', $this->host);
+				}
+			break;
+			case 'low':
+			default:
+				$this->cookieLifeTime = 788940000;
+			break;
+		}
+
+		switch (Configure::read('Session.save')) {
+			case 'cake':
+				if (empty($_SESSION)) {
+					if ($iniSet) {
+						ini_set('session.use_trans_sid', 0);
+						ini_set('url_rewriter.tags', '');
+						ini_set('session.serialize_handler', 'php');
+						ini_set('session.use_cookies', 1);
+						ini_set('session.name', Configure::read('Session.cookie'));
+						ini_set('session.cookie_lifetime', $this->cookieLifeTime);
+						ini_set('session.cookie_path', $this->path);
+						ini_set('session.auto_start', 0);
+						ini_set('session.save_path', TMP . 'sessions');
+					}
+				}
+			break;
+			case 'database':
+				if (empty($_SESSION)) {
+					if (Configure::read('Session.table') === null) {
+						trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING);
+						exit();
+					} elseif (Configure::read('Session.database') === null) {
+						Configure::write('Session.database', 'default');
+					}
+					if ($iniSet) {
+						ini_set('session.use_trans_sid', 0);
+						ini_set('url_rewriter.tags', '');
+						ini_set('session.save_handler', 'user');
+						ini_set('session.serialize_handler', 'php');
+						ini_set('session.use_cookies', 1);
+						ini_set('session.name', Configure::read('Session.cookie'));
+						ini_set('session.cookie_lifetime', $this->cookieLifeTime);
+						ini_set('session.cookie_path', $this->path);
+						ini_set('session.auto_start', 0);
+					}
+				}
+				session_set_save_handler(array('CakeSession','__open'),
+													array('CakeSession', '__close'),
+													array('CakeSession', '__read'),
+													array('CakeSession', '__write'),
+													array('CakeSession', '__destroy'),
+													array('CakeSession', '__gc'));
+			break;
+			case 'php':
+				if (empty($_SESSION)) {
+					if ($iniSet) {
+						ini_set('session.use_trans_sid', 0);
+						ini_set('session.name', Configure::read('Session.cookie'));
+						ini_set('session.cookie_lifetime', $this->cookieLifeTime);
+						ini_set('session.cookie_path', $this->path);
+					}
+				}
+			break;
+			case 'cache':
+				if (empty($_SESSION)) {
+					if (!class_exists('Cache')) {
+						uses('Cache');
+					}
+					if ($iniSet) {
+						ini_set('session.use_trans_sid', 0);
+						ini_set('url_rewriter.tags', '');
+						ini_set('session.save_handler', 'user');
+						ini_set('session.use_cookies', 1);
+						ini_set('session.name', Configure::read('Session.cookie'));
+						ini_set('session.cookie_lifetime', $this->cookieLifeTime);
+						ini_set('session.cookie_path', $this->path);
+					}
+				}
+				session_set_save_handler(array('CakeSession','__open'),
+													array('CakeSession', '__close'),
+													array('Cache', 'read'),
+													array('Cache', 'write'),
+													array('Cache', 'delete'),
+													array('CakeSession', '__gc'));
+			break;
+			default:
+				if (empty($_SESSION)) {
+					$config = CONFIGS . Configure::read('Session.save') . '.php';
+
+					if (is_file($config)) {
+						require_once ($config);
+					}
+				}
+			break;
+		}
+	}
+/**
+ * Helper method to start a session
+ *
+ * @access private
+ */
+	function __startSession() {
+		if (headers_sent()) {
+			if (empty($_SESSION)) {
+				$_SESSION = array();
+			}
+			return false;
+		} elseif (!isset($_SESSION)) {
+			session_cache_limiter ("must-revalidate");
+			session_start();
+			header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
+			return true;
+		} else {
+			session_start();
+			return true;
+		}
+	}
+/**
+ * Helper method to create a new session.
+ *
+ * @return void
+ * @access protected
+ */
+	function _checkValid() {
+		if ($this->read('Config')) {
+			if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
+				$time = $this->read('Config.time');
+				$this->write('Config.time', $this->sessionTime);
+
+				if (Configure::read('Security.level') === 'high') {
+					$check = $this->read('Config.timeout');
+					$check = $check - 1;
+					$this->write('Config.timeout', $check);
+
+					if (time() > ($time - (Security::inactiveMins() * Configure::read('Session.timeout')) + 2) || $check < 1) {
+						$this->renew();
+						$this->write('Config.timeout', 10);
+					}
+				}
+				$this->valid = true;
+			} else {
+				$this->destroy();
+				$this->valid = false;
+				$this->__setError(1, 'Session Highjacking Attempted !!!');
+			}
+		} else {
+			srand ((double)microtime() * 1000000);
+			$this->write('Config.userAgent', $this->_userAgent);
+			$this->write('Config.time', $this->sessionTime);
+			$this->write('Config.rand', mt_rand());
+			$this->write('Config.timeout', 10);
+			$this->valid = true;
+			$this->__setError(1, 'Session is valid');
+		}
+	}
+/**
+ * Helper method to restart a session.
+ *
+ * @return void
+ * @access private
+ */
+	function __regenerateId() {
+		$oldSessionId = session_id();
+		if ($oldSessionId) {
+			$sessionpath = session_save_path();
+			if (empty($sessionpath)) {
+				$sessionpath = "/tmp";
+			}
+			if (session_id() != "" || isset($_COOKIE[session_name()])) {
+				setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->path);
+			}
+			session_regenerate_id(true);
+			if (PHP_VERSION < 5.1) {
+				$newSessid = session_id();
+
+				if (function_exists('session_write_close')) {
+					session_write_close();
+				}
+				$this->__initSession();
+				session_id($oldSessionId);
+				session_start();
+				session_destroy();
+				$file = $sessionpath . DS . "sess_$oldSessionId";
+				@unlink($file);
+				$this->__initSession();
+				session_id($newSessid);
+				session_start();
+			}
+		}
+	}
+/**
+ * Restarts this session.
+ *
+ * @access public
+ */
+	function renew() {
+		$this->__regenerateId();
+	}
+/**
+ * Validate that the $name is in correct dot notation
+ * example: $name = 'ControllerName.key';
+ *
+ * @param string $name Session key names as string.
+ * @return mixed false is $name is not correct format, or $name if it is correct
+ * @access private
+ */
+	function __validateKeys($name) {
+		if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $name)) {
+			return $name;
+		}
+		$this->__setError(3, "$name is not a string");
+		return false;
+	}
+/**
+ * Helper method to set an internal error message.
+ *
+ * @param integer $errorNumber Number of the error
+ * @param string $errorMessage Description of the error
+ * @return void
+ * @access private
+ */
+	function __setError($errorNumber, $errorMessage) {
+		if ($this->error === false) {
+			$this->error = array();
+		}
+		$this->error[$errorNumber] = $errorMessage;
+		$this->lastError = $errorNumber;
+	}
+/**
+ * Method called on open of a database session.
+ *
+ * @return boolean Success
+ * @access private
+ */
+	function __open() {
+		return true;
+	}
+/**
+ * Method called on close of a database session.
+ *
+ * @return boolean Success
+ * @access private
+ */
+	function __close() {
+		$probability = mt_rand(1, 150);
+		if ($probability <= 3) {
+			switch (Configure::read('Session.save')) {
+				case 'cache':
+					Cache::gc();
+				break;
+				default:
+					CakeSession::__gc();
+				break;
+			}
+		}
+		return true;
+	}
+/**
+ * Method used to read from a database session.
+ *
+ * @param mixed $key The key of the value to read
+ * @return mixed The value of the key or false if it does not exist
+ * @access private
+ */
+	function __read($key) {
+		$db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
+		$table = $db->fullTableName(Configure::read('Session.table'), false);
+		$row = $db->query("SELECT " . $db->name($table.'.data') . " FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key), false);
+
+		if ($row && !isset($row[0][$table]) && isset($row[0][0])) {
+			$table = 0;
+		}
+
+		if ($row && $row[0][$table]['data']) {
+			return $row[0][$table]['data'];
+		} else {
+			return false;
+		}
+	}
+/**
+ * Helper function called on write for database sessions.
+ *
+ * @param mixed $key The name of the var
+ * @param mixed $value The value of the var
+ * @return boolean Success
+ * @access private
+ */
+	function __write($key, $value) {
+		$db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
+		$table = $db->fullTableName(Configure::read('Session.table'));
+
+		switch (Configure::read('Security.level')) {
+			case 'high':
+				$factor = 10;
+			break;
+			case 'medium':
+				$factor = 100;
+			break;
+			case 'low':
+				$factor = 300;
+			break;
+			default:
+				$factor = 10;
+			break;
+		}
+		$expires = time() +  Configure::read('Session.timeout') * $factor;
+		$row = $db->query("SELECT COUNT(id) AS count FROM " . $db->name($table) . " WHERE "
+								 . $db->name('id') . " = "
+								 . $db->value($key), false);
+
+		if ($row[0][0]['count'] > 0) {
+			$db->execute("UPDATE " . $db->name($table) . " SET " . $db->name('data') . " = "
+								. $db->value($value) . ", " . $db->name('expires') . " = "
+								. $db->value($expires) . " WHERE " . $db->name('id') . " = "
+								. $db->value($key));
+		} else {
+			$db->execute("INSERT INTO " . $db->name($table) . " (" . $db->name('data') . ","
+							  	. $db->name('expires') . "," . $db->name('id')
+							  	. ") VALUES (" . $db->value($value) . ", " . $db->value($expires) . ", "
+							  	. $db->value($key) . ")");
+		}
+		return true;
+	}
+/**
+ * Method called on the destruction of a database session.
+ *
+ * @param integer $key Key that uniquely identifies session in database
+ * @return boolean Success
+ * @access private
+ */
+	function __destroy($key) {
+		$db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
+		$table = $db->fullTableName(Configure::read('Session.table'));
+		$db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key));
+		return true;
+	}
+/**
+ * Helper function called on gc for database sessions.
+ *
+ * @param integer $expires Timestamp (defaults to current time)
+ * @return boolean Success
+ * @access private
+ */
+	function __gc($expires = null) {
+		$db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
+		$table = $db->fullTableName(Configure::read('Session.table'));
+		$db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.expires') . " < ". $db->value(time()));
+		return true;
+	 }
+}
+?>
\ No newline at end of file
Index: cake/libs/view/pages/home.ctp
===================================================================
--- cake/libs/view/pages/home.ctp	(revision 0)
+++ cake/libs/view/pages/home.ctp	(revision 0)
@@ -0,0 +1,144 @@
+<?php
+/* SVN FILE: $Id: home.ctp 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.pages
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php echo sprintf(__('Release Notes for CakePHP %s.', true), Configure::version()); ?></h2>
+<a href="https://trac.cakephp.org/wiki/changelog/1.2.x.x"><?php __('Read the changelog'); ?> </a>
+<?php
+if (Configure::read() > 0):
+	Debugger::checkSessionKey();
+endif;
+?>
+<p>
+	<?php
+		if (is_writable(TMP)):
+			echo '<span class="notice success">';
+				__('Your tmp directory is writable.');
+			echo '</span>';
+		else:
+			echo '<span class="notice">';
+				__('Your tmp directory is NOT writable.');
+			echo '</span>';
+		endif;
+	?>
+</p>
+<p>
+	<?php
+		$settings = Cache::settings();
+		if (!empty($settings)):
+			echo '<span class="notice success">';
+					echo sprintf(__('The %s is being used for caching. To change the config edit APP/config/core.php ', true), '<em>'. $settings['engine'] . 'Engine</em>');
+			echo '</span>';
+		else:
+			echo '<span class="notice">';
+					__('Your cache is NOT working. Please check the settings in APP/config/core.php');
+			echo '</span>';
+		endif;
+	?>
+</p>
+<p>
+	<?php
+		$filePresent = null;
+		if (file_exists(CONFIGS.'database.php')):
+			echo '<span class="notice success">';
+				__('Your database configuration file is present.');
+				$filePresent = true;
+			echo '</span>';
+		else:
+			echo '<span class="notice">';
+				__('Your database configuration file is NOT present.');
+				echo '<br/>';
+				__('Rename config/database.php.default to config/database.php');
+			echo '</span>';
+		endif;
+	?>
+</p>
+<?php
+if (isset($filePresent)):
+	uses('model' . DS . 'connection_manager');
+	$db = ConnectionManager::getInstance();
+	@$connected = $db->getDataSource('default');
+?>
+<p>
+	<?php
+		if ($connected->isConnected()):
+			echo '<span class="notice success">';
+	 			__('Cake is able to connect to the database.');
+			echo '</span>';
+		else:
+			echo '<span class="notice">';
+				__('Cake is NOT able to connect to the database.');
+			echo '</span>';
+		endif;
+	?>
+</p>
+<?php endif;?>
+<h3><?php __('Editing this Page'); ?></h3>
+<p>
+<?php
+__('To change the content of this page, create: APP/views/pages/home.ctp.<br />
+To change its layout, create: APP/views/layouts/default.ctp.<br />
+You can also add some CSS styles for your pages at: APP/webroot/css.');
+?>
+</p>
+<h3><?php __('Getting Started'); ?></h3>
+<p>
+	<a href="http://book.cakephp.org"><strong>new</strong> CakePHP 1.2 Docs</a>
+</p>
+<p>
+	<a href="http://book.cakephp.org/view/219/the-cakephp-blog-tutorial"><?php __('The 15 min Blog Tutorial'); ?></a><br />
+</p>
+<h3><?php __('More about Cake'); ?></h3>
+<p>
+<?php __('CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC.'); ?>
+</p>
+<p>
+<?php __('Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility.'); ?>
+</p>
+<br />
+<ul>
+	<li><a href="http://www.cakefoundation.org/"><?php __('Cake Software Foundation'); ?> </a>
+	<ul><li><?php __('Promoting development related to CakePHP'); ?></li></ul></li>
+	<li><a href="http://www.cakephp.org"><?php __('CakePHP'); ?> </a>
+	<ul><li><?php __('The Rapid Development Framework'); ?></li></ul></li>
+	<li><a href="http://book.cakephp.org"><?php __('CakePHP Documentation'); ?> </a>
+	<ul><li><?php __('Your Rapid Development Cookbook'); ?></li></ul></li>
+	<li><a href="http://api.cakephp.org"><?php __('CakePHP API'); ?> </a>
+	<ul><li><?php __('Quick Reference'); ?></li></ul></li>
+	<li><a href="http://bakery.cakephp.org"><?php __('The Bakery'); ?> </a>
+	<ul><li><?php __('Everything CakePHP'); ?></li></ul></li>
+	<li><a href="http://live.cakephp.org"><?php __('The Show'); ?> </a>
+	<ul><li><?php __('The Show is a live and archived internet radio broadcast CakePHP-related topics and answer questions live via IRC, Skype, and telephone.'); ?></li></ul></li>
+	<li><a href="http://groups.google.com/group/cake-php"><?php __('CakePHP Google Group'); ?> </a>
+	<ul><li><?php __('Community mailing list'); ?></li></ul></li>
+	<li><a href="irc://irc.freenode.net/cakephp">irc.freenode.net #cakephp</a>
+	<ul><li><?php __('Live chat about CakePHP'); ?></li></ul></li>
+	<li><a href="https://trac.cakephp.org/"><?php __('CakePHP Trac'); ?> </a>
+	<ul><li><?php __('For the Development of CakePHP (Tickets, SVN browser, Roadmap, Changelogs)'); ?></li></ul></li>
+	<li><a href="http://www.cakeforge.org"><?php __('CakeForge'); ?> </a>
+	<ul><li><?php __('Open Development for CakePHP'); ?></li></ul></li>
+	<li><a href="http://astore.amazon.com/cakesoftwaref-20/"><?php __('Book Store'); ?> </a>
+	<ul><li><?php __('Recommended Software Books'); ?></li></ul></li>
+	<li><a href="http://www.cafepress.com/cakefoundation"><?php __('CakePHP gear'); ?> </a>
+	<ul><li><?php __('Get your own CakePHP gear - Doughnate to Cake'); ?></li></ul></li>
+</ul>
\ No newline at end of file
Index: cake/libs/view/layouts/xml/default.ctp
===================================================================
--- cake/libs/view/layouts/xml/default.ctp	(revision 0)
+++ cake/libs/view/layouts/xml/default.ctp	(revision 0)
@@ -0,0 +1,2 @@
+<?php e($xml->header()); ?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file
Index: cake/libs/view/layouts/flash.ctp
===================================================================
--- cake/libs/view/layouts/flash.ctp	(revision 0)
+++ cake/libs/view/layouts/flash.ctp	(revision 0)
@@ -0,0 +1,43 @@
+<?php
+/* SVN FILE: $Id: flash.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<?php echo $html->charset(); ?>
+<title><?php echo $page_title; ?></title>
+
+<?php if (Configure::read() == 0) { ?>
+<meta http-equiv="Refresh" content="<?php echo $pause; ?>;url=<?php echo $url; ?>"/>
+<?php } ?>
+<style><!--
+P { text-align:center; font:bold 1.1em sans-serif }
+A { color:#444; text-decoration:none }
+A:HOVER { text-decoration: underline; color:#44E }
+--></style>
+</head>
+<body>
+<p><a href="<?php echo $url; ?>"><?php echo $message; ?></a></p>
+</body>
+</html>
\ No newline at end of file
Index: cake/libs/view/layouts/email/html/default.ctp
===================================================================
--- cake/libs/view/layouts/email/html/default.ctp	(revision 0)
+++ cake/libs/view/layouts/email/html/default.ctp	(revision 0)
@@ -0,0 +1,35 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+<head>
+	<title><?php echo $title_for_layout;?></title>
+</head>
+<body>
+	<?php echo $content_for_layout;?>
+
+	<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>
+</body>
+</html>
\ No newline at end of file
Index: cake/libs/view/layouts/email/text/default.ctp
===================================================================
--- cake/libs/view/layouts/email/text/default.ctp	(revision 0)
+++ cake/libs/view/layouts/email/text/default.ctp	(revision 0)
@@ -0,0 +1,28 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php echo $content_for_layout;?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.
+
Index: cake/libs/view/layouts/rss/default.ctp
===================================================================
--- cake/libs/view/layouts/rss/default.ctp	(revision 0)
+++ cake/libs/view/layouts/rss/default.ctp	(revision 0)
@@ -0,0 +1,16 @@
+<?php
+echo $rss->header();
+
+if (!isset($channel)) {
+	$channel = array();
+}
+if (!isset($channel['title'])) {
+	$channel['title'] = $title_for_layout;
+}
+
+echo $rss->document(
+	$rss->channel(
+		array(), $channel, $content_for_layout
+	)
+);
+?>
\ No newline at end of file
Index: cake/libs/view/layouts/ajax.ctp
===================================================================
--- cake/libs/view/layouts/ajax.ctp	(revision 0)
+++ cake/libs/view/layouts/ajax.ctp	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+/* SVN FILE: $Id: ajax.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file
Index: cake/libs/view/layouts/default.ctp
===================================================================
--- cake/libs/view/layouts/default.ctp	(revision 0)
+++ cake/libs/view/layouts/default.ctp	(revision 0)
@@ -0,0 +1,64 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<?php echo $html->charset(); ?>
+	<title>
+		<?php __('CakePHP: the rapid development php framework:'); ?>
+		<?php echo $title_for_layout; ?>
+	</title>
+	<?php
+		echo $html->meta('icon');
+
+		echo $html->css('cake.generic');
+
+		echo $scripts_for_layout;
+	?>
+</head>
+<body>
+	<div id="container">
+		<div id="header">
+			<h1><?php echo $html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
+		</div>
+		<div id="content">
+
+			<?php $session->flash(); ?>
+
+			<?php echo $content_for_layout; ?>
+
+		</div>
+		<div id="footer">
+			<?php echo $html->link(
+					$html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true), 'border'=>"0")),
+					'http://www.cakephp.org/',
+					array('target'=>'_blank'), null, false
+				);
+			?>
+		</div>
+	</div>
+	<?php echo $cakeDebug; ?>
+</body>
+</html>
\ No newline at end of file
Index: cake/libs/view/layouts/js/default.ctp
===================================================================
--- cake/libs/view/layouts/js/default.ctp	(revision 0)
+++ cake/libs/view/layouts/js/default.ctp	(revision 0)
@@ -0,0 +1,2 @@
+<?php echo $scripts_for_layout; ?>
+<script type="text/javascript"><?php echo $content_for_layout; ?></script>
\ No newline at end of file
Index: cake/libs/view/view.php
===================================================================
--- cake/libs/view/view.php	(revision 0)
+++ cake/libs/view/view.php	(revision 0)
@@ -0,0 +1,923 @@
+<?php
+/* SVN FILE: $Id: view.php 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ * Methods for displaying presentation data in the view.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ */
+App::import('Core', array('Helper', 'ClassRegistry'));
+/**
+ * View, the V in the MVC triad.
+ *
+ * Class holding methods for displaying presentation data.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ */
+class View extends Object {
+/**
+ * Path parts for creating links in views.
+ *
+ * @var string Base URL
+ * @access public
+ */
+	var $base = null;
+/**
+ * Stores the current URL (for links etc.)
+ *
+ * @var string Current URL
+ */
+	var $here = null;
+/**
+ * Name of the plugin.
+ *
+ * @link          http://manual.cakephp.org/chapter/plugins
+ * @var string
+ */
+	var $plugin = null;
+/**
+ * Name of the controller.
+ *
+ * @var string Name of controller
+ * @access public
+ */
+	var $name = null;
+/**
+ * Action to be performed.
+ *
+ * @var string Name of action
+ * @access public
+ */
+	var $action = null;
+/**
+ * Array of parameter data
+ *
+ * @var array Parameter data
+ */
+	var $params = array();
+/**
+ * Current passed params
+ *
+ * @var mixed
+ */
+	var $passedArgs = array();
+/**
+ * Array of data
+ *
+ * @var array Parameter data
+ */
+	var $data = array();
+/**
+ * An array of names of built-in helpers to include.
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @access public
+ */
+	var $helpers = array('Html');
+/**
+ * Path to View.
+ *
+ * @var string Path to View
+ */
+	var $viewPath = null;
+/**
+ * Variables for the view
+ *
+ * @var array
+ * @access public
+ */
+	var $viewVars = array();
+/**
+ * Name of layout to use with this View.
+ *
+ * @var string
+ * @access public
+ */
+	var $layout = 'default';
+/**
+ * Path to Layout.
+ *
+ * @var string Path to Layout
+ */
+	var $layoutPath = null;
+/**
+ * Title HTML element of this View.
+ *
+ * @var string
+ * @access public
+ */
+	var $pageTitle = false;
+/**
+ * Turns on or off Cake's conventional mode of rendering views. On by default.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoRender = true;
+/**
+ * Turns on or off Cake's conventional mode of finding layout files. On by default.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoLayout = true;
+/**
+ * File extension. Defaults to Cake's template ".ctp".
+ *
+ * @var string
+ */
+	var $ext = '.ctp';
+/**
+ * Sub-directory for this view file.
+ *
+ * @var string
+ */
+	var $subDir = null;
+/**
+ * Theme name.
+ *
+ * @var string
+ */
+	var $themeWeb = null;
+/**
+ * Used to define methods a controller that will be cached.
+ *
+ * @see Controller::$cacheAction
+ * @var mixed
+ * @access public
+ */
+	var $cacheAction = false;
+/**
+ * holds current errors for the model validation
+ *
+ * @var array
+ */
+	var $validationErrors = array();
+/**
+ * True when the view has been rendered.
+ *
+ * @var boolean
+ */
+	var $hasRendered = false;
+/**
+ * Array of loaded view helpers.
+ *
+ * @var array
+ */
+	var $loaded = array();
+/**
+ * True if in scope of model-specific region
+ *
+ * @var boolean
+ */
+	var $modelScope = false;
+/**
+ * Name of current model this view context is attached to
+ *
+ * @var string
+ */
+	var $model = null;
+/**
+ * Name of association model this view context is attached to
+ *
+ * @var string
+ */
+	var $association = null;
+/**
+ * Name of current model field this view context is attached to
+ *
+ * @var string
+ */
+	var $field = null;
+/**
+ * Suffix of current field this view context is attached to
+ *
+ * @var string
+ */
+	var $fieldSuffix = null;
+/**
+ * The current model ID this view context is attached to
+ *
+ * @var mixed
+ */
+	var $modelId = null;
+/**
+ * List of generated DOM UUIDs
+ *
+ * @var array
+ */
+	var $uuids = array();
+/**
+ * Holds View output.
+ *
+ * @var string
+ **/
+	var $output = false;
+/**
+ * List of variables to collect from the associated controller
+ *
+ * @var array
+ * @access protected
+ */
+	var $__passedVars = array(
+		'viewVars', 'action', 'autoLayout', 'autoRender', 'ext', 'base', 'webroot',
+		'helpers', 'here', 'layout', 'name', 'pageTitle', 'layoutPath', 'viewPath',
+		'params', 'data', 'plugin', 'passedArgs', 'cacheAction'
+	);
+/**
+ * Scripts (and/or other <head /> tags) for the layout
+ *
+ * @var array
+ * @access private
+ */
+	var $__scripts = array();
+/**
+ * Holds an array of paths.
+ *
+ * @var array
+ */
+	var $__paths = array();
+/**
+ * Constructor
+ *
+ * @return View
+ */
+	function __construct(&$controller, $register = true) {
+		if (is_object($controller)) {
+			$count = count($this->__passedVars);
+			for ($j = 0; $j < $count; $j++) {
+				$var = $this->__passedVars[$j];
+				$this->{$var} = $controller->{$var};
+			}
+		}
+		parent::__construct();
+
+		if ($register) {
+			ClassRegistry::addObject('view', $this);
+		}
+	}
+/**
+ * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string.
+ *
+ * This realizes the concept of Elements, (or "partial layouts")
+ * and the $params array is used to send data to be used in the
+ * Element.  Elements can be cached through use of the cache key.
+ *
+ * @param string $name Name of template file in the/app/views/elements/ folder
+ * @param array $params Array of data to be made available to the for rendered
+ *                      view (i.e. the Element)
+ *    Special params:
+ *		cache - enable caching for this element accepts boolean or strtotime compatible string.
+ *      Can also be an array
+ *				if an array,'time' is used to specify duration of cache.  'key' can be used to
+ *              create unique cache files.
+ *
+ * @return string Rendered Element
+ * @access public
+ */
+	function element($name, $params = array(), $loadHelpers = false) {
+		$file = $plugin = $key = null;
+
+		if (isset($params['plugin'])) {
+			$plugin = $params['plugin'];
+		}
+
+		if (isset($this->plugin) && !$plugin) {
+			$plugin = $this->plugin;
+		}
+
+		if (isset($params['cache'])) {
+			$expires = '+1 day';
+
+			if (is_array($params['cache'])) {
+				$expires = $params['cache']['time'];
+				$key = Inflector::slug($params['cache']['key']);
+			} elseif ($params['cache'] !== true) {
+				$expires = $params['cache'];
+				$key = implode('_', array_keys($params));
+			}
+
+			if ($expires) {
+				$cacheFile = 'element_' . $key . '_' . $plugin . Inflector::slug($name);
+				$cache = cache('views' . DS . $cacheFile, null, $expires);
+
+				if (is_string($cache)) {
+					return $cache;
+				}
+			}
+		}
+		$paths = $this->_paths($plugin);
+
+		foreach ($paths as $path) {
+			if (file_exists($path . 'elements' . DS . $name . $this->ext)) {
+				$file = $path . 'elements' . DS . $name . $this->ext;
+				break;
+			} elseif (file_exists($path . 'elements' . DS . $name . '.thtml')) {
+				$file = $path . 'elements' . DS . $name . '.thtml';
+				break;
+			}
+		}
+
+		if (is_file($file)) {
+			$params = array_merge_recursive($params, $this->loaded);
+			$element = $this->_render($file, array_merge($this->viewVars, $params), $loadHelpers);
+			if (isset($params['cache']) && isset($cacheFile) && isset($expires)) {
+				cache('views' . DS . $cacheFile, $element, $expires);
+			}
+			return $element;
+		}
+		$file = $paths[0] . 'elements' . DS . $name . $this->ext;
+
+		if (Configure::read() > 0) {
+			return "Not Found: " . $file;
+		}
+	}
+/**
+ * Renders view for given action and layout. If $file is given, that is used
+ * for a view filename (e.g. customFunkyView.ctp).
+ *
+ * @param string $action Name of action to render for
+ * @param string $layout Layout to use
+ * @param string $file Custom filename for view
+ * @return string Rendered Element
+ */
+	function render($action = null, $layout = null, $file = null) {
+		if ($this->hasRendered) {
+			return true;
+		}
+		$out = null;
+
+		if ($file != null) {
+			$action = $file;
+		}
+
+		if ($action !== false && $viewFileName = $this->_getViewFileName($action)) {
+			if (substr($viewFileName, -3) === 'ctp' || substr($viewFileName, -5) === 'thtml') {
+				$out = View::_render($viewFileName, $this->viewVars);
+			} else {
+				$out = $this->_render($viewFileName, $this->viewVars);
+			}
+		}
+
+		if ($layout === null) {
+			$layout = $this->layout;
+		}
+
+		if ($out !== false) {
+			if ($layout && $this->autoLayout) {
+				$out = $this->renderLayout($out, $layout);
+				$isCached = (
+					isset($this->loaded['cache']) &&
+					(($this->cacheAction != false)) && (Configure::read('Cache.check') === true)
+				);
+
+				if ($isCached) {
+					$replace = array('<cake:nocache>', '</cake:nocache>');
+					$out = str_replace($replace, '', $out);
+				}
+			}
+			$this->hasRendered = true;
+		} else {
+			$out = $this->_render($viewFileName, $this->viewVars);
+			$msg = __("Error in view %s, got: <blockquote>%s</blockquote>", true);
+			trigger_error(sprintf($msg, $viewFileName, $out), E_USER_ERROR);
+		}
+		return $out;
+	}
+/**
+ * Renders a layout. Returns output from _render(). Returns false on error.
+ * Several variables are created for use in layout.
+ *	title_for_layout - contains page title
+ *	content_for_layout - contains rendered view file
+ *	scripts_for_layout - contains scripts added to header
+ *  cakeDebug - if debug is on, cake debug information is added.
+ *
+ * @param string $content_for_layout Content to render in a view, wrapped by the surrounding layout.
+ * @return mixed Rendered output, or false on error
+ */
+	function renderLayout($content_for_layout, $layout = null) {
+		$layoutFileName = $this->_getLayoutFileName($layout);
+		$debug = '';
+
+		if (isset($this->viewVars['cakeDebug']) && Configure::read() > 2) {
+			$params = array('controller' => $this->viewVars['cakeDebug']);
+			$debug = View::element('dump', $params, false);
+			unset($this->viewVars['cakeDebug']);
+		}
+
+		if ($this->pageTitle !== false) {
+			$pageTitle = $this->pageTitle;
+		} else {
+			$pageTitle = Inflector::humanize($this->viewPath);
+		}
+		$data_for_layout = array_merge($this->viewVars, array(
+			'title_for_layout' => $pageTitle,
+			'content_for_layout' => $content_for_layout,
+			'scripts_for_layout' => join("\n\t", $this->__scripts),
+			'cakeDebug' => $debug
+		));
+
+		if (empty($this->loaded) && !empty($this->helpers)) {
+			$loadHelpers = true;
+		} else {
+			$loadHelpers = false;
+			$data_for_layout = array_merge($data_for_layout, $this->loaded);
+		}
+
+		$this->_triggerHelpers('beforeLayout');
+
+		if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml') {
+			$this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
+		} else {
+			$this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
+		}
+
+		if ($this->output === false) {
+			$this->output = $this->_render($layoutFileName, $data_for_layout);
+			$msg = __("Error in layout %s, got: <blockquote>%s</blockquote>", true);
+			trigger_error(sprintf($msg, $layoutFileName, $this->output), E_USER_ERROR);
+			return false;
+		}
+
+		$this->_triggerHelpers('afterLayout');
+
+		return $this->output;
+	}
+/**
+ * Fire a callback on all loaded Helpers
+ *
+ * @param string $callback name of callback fire.
+ * @access protected
+ * @return void
+ */
+	function _triggerHelpers($callback) {
+		if (empty($this->loaded)) {
+			return false;
+		}
+		$helpers = array_keys($this->loaded);
+		foreach ($helpers as $helperName) {
+			$helper =& $this->loaded[$helperName];
+			if (is_object($helper)) {
+				if (is_subclass_of($helper, 'Helper')) {
+					$helper->{$callback}();
+				}
+			}
+		}
+	}
+/**
+ * Render cached view
+ *
+ * @param string $filename the cache file to include
+ * @param string $timeStart the page render start time
+ */
+	function renderCache($filename, $timeStart) {
+		ob_start();
+		include ($filename);
+
+		if (Configure::read() > 0 && $this->layout != 'xml') {
+			echo "<!-- Cached Render Time: " . round(getMicrotime() - $timeStart, 4) . "s -->";
+		}
+		$out = ob_get_clean();
+
+		if (preg_match('/^<!--cachetime:(\\d+)-->/', $out, $match)) {
+			if (time() >= $match['1']) {
+				@unlink($filename);
+				unset ($out);
+				return false;
+			} else {
+				if ($this->layout === 'xml') {
+					header('Content-type: text/xml');
+				}
+				echo str_replace('<!--cachetime:' . $match['1'] . '-->', '', $out);
+				return true;
+			}
+		}
+	}
+/**
+ * Returns a list of variables available in the current View context
+ *
+ * @return array
+ * @access public
+ */
+	function getVars() {
+		return array_keys($this->viewVars);
+	}
+/**
+ * Returns the contents of the given View variable(s)
+ *
+ * @return array
+ * @access public
+ */
+	function getVar($var) {
+		if (!isset($this->viewVars[$var])) {
+			return null;
+		} else {
+			return $this->viewVars[$var];
+		}
+	}
+/**
+ * Adds a script block or other element to be inserted in $scripts_for_layout in
+ * the <head /> of a document layout
+ *
+ * @param string $name
+ * @param string $content
+ * @return void
+ * @access public
+ */
+	function addScript($name, $content = null) {
+		if (empty($content)) {
+			if (!in_array($name, array_values($this->__scripts))) {
+				$this->__scripts[] = $name;
+			}
+		} else {
+			$this->__scripts[$name] = $content;
+		}
+	}
+/**
+ * Generates a unique, non-random DOM ID for an object, based on the object type and the target URL.
+ *
+ * @param string $object Type of object, i.e. 'form' or 'link'
+ * @param string $url The object's target URL
+ * @return string
+ * @access public
+ */
+	function uuid($object, $url) {
+		$c = 1;
+		$url = Router::url($url);
+		$hash = $object . substr(md5($object . $url), 0, 10);
+		while (in_array($hash, $this->uuids)) {
+			$hash = $object . substr(md5($object . $url . $c), 0, 10);
+			$c++;
+		}
+		$this->uuids[] = $hash;
+		return $hash;
+	}
+/**
+ * Returns the entity reference of the current context as an array of identity parts
+ *
+ * @return array An array containing the identity elements of an entity
+ */
+	function entity() {
+		$assoc = ($this->association) ? $this->association : $this->model;
+		return array_values(Set::filter(
+			array($assoc, $this->modelId, $this->field, $this->fieldSuffix)
+		));
+	}
+/**
+ * Allows a template or element to set a variable that will be available in
+ * a layout or other element. Analagous to Controller::set.
+ *
+ * @param mixed $one A string or an array of data.
+ * @param mixed $two Value in case $one is a string (which then works as the key).
+ *              Unused if $one is an associative array, otherwise serves as the
+ *              values to $one's keys.
+ * @return unknown
+ */
+	function set($one, $two = null) {
+		$data = null;
+		if (is_array($one)) {
+			if (is_array($two)) {
+				$data = array_combine($one, $two);
+			} else {
+				$data = $one;
+			}
+		} else {
+			$data = array($one => $two);
+		}
+
+		if ($data == null) {
+			return false;
+		}
+
+		foreach ($data as $name => $value) {
+			if ($name == 'title') {
+				$this->pageTitle = $value;
+			} else {
+				$this->viewVars[$name] = $value;
+			}
+		}
+	}
+/**
+ * Displays an error page to the user. Uses layouts/error.ctp to render the page.
+ *
+ * @param integer $code HTTP Error code (for instance: 404)
+ * @param string $name Name of the error (for instance: Not Found)
+ * @param string $message Error message as a web page
+ */
+	function error($code, $name, $message) {
+		header ("HTTP/1.1 {$code} {$name}");
+		print ($this->_render(
+			$this->_getLayoutFileName('error'),
+			array('code' => $code, 'name' => $name, 'message' => $message)
+		));
+	}
+/**
+ * Renders and returns output for given view filename with its
+ * array of data.
+ *
+ * @param string $___viewFn Filename of the view
+ * @param array $___dataForView Data to include in rendered view
+ * @return string Rendered output
+ * @access protected
+ */
+	function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false) {
+		$loadedHelpers = array();
+
+		if ($this->helpers != false && $loadHelpers === true) {
+			$loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
+
+			foreach (array_keys($loadedHelpers) as $helper) {
+				$camelBackedHelper = Inflector::variable($helper);
+				${$camelBackedHelper} =& $loadedHelpers[$helper];
+				$this->loaded[$camelBackedHelper] =& ${$camelBackedHelper};
+			}
+
+			$this->_triggerHelpers('beforeRender');
+		}
+
+		extract($___dataForView, EXTR_SKIP);
+		ob_start();
+
+		if (Configure::read() > 0) {
+			include ($___viewFn);
+		} else {
+			@include ($___viewFn);
+		}
+
+		if ($loadHelpers === true) {
+			$this->_triggerHelpers('afterRender');
+		}
+
+		$out = ob_get_clean();
+		$caching = (
+			isset($this->loaded['cache']) &&
+			(($this->cacheAction != false)) && (Configure::read('Cache.check') === true)
+		);
+
+		if ($caching) {
+			if (is_a($this->loaded['cache'], 'CacheHelper')) {
+				$cache =& $this->loaded['cache'];
+				$cache->base = $this->base;
+				$cache->here = $this->here;
+				$cache->helpers = $this->helpers;
+				$cache->action = $this->action;
+				$cache->controllerName = $this->name;
+				$cache->layout	= $this->layout;
+				$cache->cacheAction = $this->cacheAction;
+				$cache->cache($___viewFn, $out, $cached);
+			}
+		}
+		return $out;
+	}
+/**
+ * Loads helpers, with their dependencies.
+ *
+ * @param array $loaded List of helpers that are already loaded.
+ * @param array $helpers List of helpers to load.
+ * @param string $parent holds name of helper, if loaded helper has helpers
+ * @return array
+ */
+	function &_loadHelpers(&$loaded, $helpers, $parent = null) {
+		if (empty($loaded)) {
+			$helpers[] = 'Session';
+		}
+
+		foreach ($helpers as $i => $helper) {
+			$options = array();
+
+			if (!is_int($i)) {
+				$options = $helper;
+				$helper = $i;
+			}
+			$plugin = $this->plugin;
+
+			if (strpos($helper, '.') !== false) {
+				list($plugin, $helper) = explode('.', $helper);
+			}
+			$helperCn = $helper . 'Helper';
+
+			if (!isset($loaded[$helper])) {
+				if (!class_exists($helperCn)) {
+					$isLoaded = false;
+					if (!is_null($plugin)) {
+						$isLoaded = App::import('Helper', $plugin . '.' . $helper);
+					}
+					if (!$isLoaded) {
+						if (!App::import('Helper', $helper)) {
+							$this->cakeError('missingHelperFile', array(array(
+								'helper' => $helper,
+								'file' => Inflector::underscore($helper) . '.php',
+								'base' => $this->base
+							)));
+							return false;
+						}
+					}
+					if (!class_exists($helperCn)) {
+						$this->cakeError('missingHelperClass', array(array(
+							'helper' => $helper,
+							'file' => Inflector::underscore($helper) . '.php',
+							'base' => $this->base
+						)));
+						return false;
+					}
+				}
+				$loaded[$helper] =& new $helperCn($options);
+				$vars = array(
+					'base', 'webroot', 'here', 'params', 'action', 'data', 'themeWeb', 'plugin'
+				);
+				$c = count($vars);
+
+				for ($j = 0; $j < $c; $j++) {
+					$loaded[$helper]->{$vars[$j]} = $this->{$vars[$j]};
+				}
+
+				if (!empty($this->validationErrors)) {
+					$loaded[$helper]->validationErrors = $this->validationErrors;
+				}
+				if (is_array($loaded[$helper]->helpers) && !empty($loaded[$helper]->helpers)) {
+					$loaded =& $this->_loadHelpers($loaded, $loaded[$helper]->helpers, $helper);
+				}
+			}
+			if (isset($loaded[$parent])) {
+				$loaded[$parent]->{$helper} =& $loaded[$helper];
+			}
+		}
+		return $loaded;
+	}
+/**
+ * Returns filename of given action's template file (.ctp) as a string.
+ * CamelCased action names will be under_scored! This means that you can have
+ * LongActionNames that refer to long_action_names.ctp views.
+ *
+ * @param string $action Controller action to find template filename for
+ * @return string Template filename
+ * @access protected
+ */
+	function _getViewFileName($name = null) {
+		$subDir = null;
+
+		if (!is_null($this->subDir)) {
+			$subDir = $this->subDir . DS;
+		}
+
+		if ($name === null) {
+			$name = $this->action;
+		}
+		$name = str_replace('/', DS, $name);
+
+		if (strpos($name, DS) === false && $name[0] !== '.') {
+			$name = $this->viewPath . DS . $subDir . Inflector::underscore($name);
+		} elseif (strpos($name, DS) !== false) {
+			if ($name{0} === DS || $name{1} === ':') {
+				if (is_file($name)) {
+					return $name;
+				}
+				$name = trim($name, DS);
+			} else if ($name[0] === '.') {
+				$name = substr($name, 3);
+			} else {
+				$name = $this->viewPath . DS . $subDir . $name;
+			}
+		}
+
+		$paths = $this->_paths($this->plugin);
+
+		foreach ($paths as $path) {
+			if (file_exists($path . $name . $this->ext)) {
+				return $path . $name . $this->ext;
+			} elseif (file_exists($path . $name . '.ctp')) {
+				return $path . $name . '.ctp';
+			} elseif (file_exists($path . $name . '.thtml')) {
+				return $path . $name . '.thtml';
+			}
+		}
+		$defaultPath = $paths[0];
+
+		if ($this->plugin) {
+			$pluginPaths = Configure::read('pluginPaths');
+			foreach ($paths as $path) {
+				if (strpos($path, $pluginPaths[0]) === 0) {
+					$defaultPath = $path;
+					break;
+				}
+			}
+		}
+		return $this->_missingView($defaultPath . $name . $this->ext, 'missingView');
+	}
+
+/**
+ * Returns layout filename for this template as a string.
+ *
+ * @return string Filename for layout file (.ctp).
+ * @access protected
+ */
+	function _getLayoutFileName($name = null) {
+		if ($name === null) {
+			$name = $this->layout;
+		}
+		$subDir = null;
+
+		if (!is_null($this->layoutPath)) {
+			$subDir = $this->layoutPath . DS;
+		}
+		$paths = $this->_paths($this->plugin);
+		$file = 'layouts' . DS . $subDir . $name;
+
+		$exts = array($this->ext, '.ctp', '.thtml');
+		foreach ($paths as $path) {
+			foreach ($exts as $ext) {
+				if (file_exists($path . $file . $ext)) {
+					return $path . $file . $ext;
+				}
+			}
+		}
+		return $this->_missingView($paths[0] . $file . $this->ext, 'missingLayout');
+	}
+/**
+ * Return a misssing view error message
+ *
+ * @param string $viewFileName the filename that should exist
+ * @return cakeError
+ */
+	function _missingView($file, $error = 'missingView') {
+
+		if ($error === 'missingView') {
+			$this->cakeError('missingView', array(
+				'className' => $this->name,
+				'action' => $this->action,
+				'file' => $file,
+				'base' => $this->base
+			));
+			return false;
+		} elseif ($error === 'missingLayout') {
+			$this->cakeError('missingLayout', array(
+				'layout' => $this->layout,
+				'file' => $file,
+				'base' => $this->base
+			));
+			return false;
+		}
+	}
+/**
+ * Return all possible paths to find view files in order
+ *
+ * @param string $plugin
+ * @return array paths
+ * @access protected
+ */
+	function _paths($plugin = null, $cached = true) {
+		if ($plugin === null && $cached === true && !empty($this->__paths)) {
+			return $this->__paths;
+		}
+		$paths = array();
+		$viewPaths = Configure::read('viewPaths');
+
+		if ($plugin !== null) {
+			$count = count($viewPaths);
+			for ($i = 0; $i < $count; $i++) {
+				$paths[] = $viewPaths[$i] . 'plugins' . DS . $plugin . DS;
+			}
+			$pluginPaths = Configure::read('pluginPaths');
+			$count = count($pluginPaths);
+
+			for ($i = 0; $i < $count; $i++) {
+				$paths[] = $pluginPaths[$i] . $plugin . DS . 'views' . DS;
+			}
+		}
+		$paths = array_merge($paths, $viewPaths);
+
+		if (empty($this->__paths)) {
+			$this->__paths = $paths;
+		}
+		return $paths;
+	}
+/**
+ * @deprecated
+ * @see View::element
+ */
+	function renderElement($name, $params = array(), $loadHelpers = false) {
+		return $this->element($name, $params, $loadHelpers);
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/js.php
===================================================================
--- cake/libs/view/helpers/js.php	(revision 0)
+++ cake/libs/view/helpers/js.php	(revision 0)
@@ -0,0 +1,451 @@
+<?php
+/* SVN FILE: $Id: js.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Javascript Generator class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2006-2008, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2006-2008, Cake Software Foundation, Inc.
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Javascript Generator helper class for easy use of JavaScript.
+ *
+ * JsHelper provides an abstract interface for authoring JavaScript with a
+ * given client-side library.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class JsHelper extends Overloadable2 {
+	var $base = null;
+	var $webroot = null;
+	var $here = null;
+	var $params = null;
+	var $action = null;
+	var $data = null;
+	var $themeWeb = null;
+	var $plugin = null;
+
+	var $helpers = array();
+
+	var $hook = null;
+
+	var $__objects = array();
+
+	var $effectMap = array(
+		'Appear', 'Fade', 'Puff', 'BlindDown', 'BlindUp', 'SwitchOff', 'SlideDown', 'SlideUp',
+		'DropOut', 'Shake', 'Pulsate', 'Squish', 'Fold', 'Grow', 'Shrink', 'Highlight', 'toggle'
+	);
+
+	var $output = false;
+
+	function __construct() {
+		$this->effectMap = array_combine(
+			array_map('strtolower', $this->effectMap),
+			$this->effectMap
+		);
+		parent::__construct();
+	}
+
+	function call__($method, $params) {
+		if (is_object($this->hook) && method_exists($this->hook, $method)) {
+			$this->hook->dispatchMethod($method . '_', $params);
+		}
+		if (method_exists($this, $method . '_')) {
+			return $this->dispatchMethod($method . '_', $params);
+		}
+	}
+
+	function alert_($message) {
+		return 'alert("' . $this->escape($message) . '");';
+	}
+
+	function if_($if, $then, $else = null, $elseIf = array()) {
+		$len = strlen($if) - 1;
+		if ($if{$len} == ';') {
+			$if{$len} = null;
+		}
+
+		$out = 'if (' . $if . ') { ' . $then . ' }';
+
+		foreach ($elseIf as $cond => $exec) {
+			//$out .=
+		}
+
+		if (!empty($else)) {
+			$out .= ' else { ' . $else . ' }';
+		}
+
+		return $out;
+	}
+
+	function confirm_($message) {
+		return 'confirm("' . $this->escape($message) . '");';
+	}
+
+	function prompt_($message, $default = '') {
+		return 'prompt("' . $this->escape($message) . '", "' . $this->escape($default) . '");';
+	}
+/*
+ * Tries a series of expressions, and executes after first successful completion.
+ * (See Prototype's Try.these).
+ *
+ * @return string
+ */
+	function tryThese_($expr1, $expr2, $expr3) {
+	}
+/**
+ * Loads a remote URL
+ *
+ * @param  string $url
+ * @param  array  $options
+ * @return string
+ */
+	function load_($url = null, $options = array()) {
+
+		if (isset($options['update'])) {
+			if (!is_array($options['update'])) {
+				$func = "new Ajax.Updater('{$options['update']}',";
+			} else {
+				$func = "new Ajax.Updater(document.createElement('div'),";
+			}
+			if (!isset($options['requestHeaders'])) {
+				$options['requestHeaders'] = array();
+			}
+			if (is_array($options['update'])) {
+				$options['update'] = join(' ', $options['update']);
+			}
+			$options['requestHeaders']['X-Update'] = $options['update'];
+		} else {
+			$func = "new Ajax.Request(";
+		}
+
+		$func .= "'" . Router::url($url) . "'";
+		$ajax =& new AjaxHelper();
+		$func .= ", " . $ajax->__optionsForAjax($options) . ")";
+
+		if (isset($options['before'])) {
+			$func = "{$options['before']}; $func";
+		}
+		if (isset($options['after'])) {
+			$func = "$func; {$options['after']};";
+		}
+		if (isset($options['condition'])) {
+			$func = "if ({$options['condition']}) { $func; }";
+		}
+		if (isset($options['confirm'])) {
+			$func = "if (confirm('" . $this->Javascript->escapeString($options['confirm'])
+				. "')) { $func; } else { return false; }";
+		}
+		return $func;
+	}
+/**
+ * Redirects to a URL
+ *
+ * @param  mixed $url
+ * @param  array  $options
+ * @return string
+ */
+	function redirect_($url = null) {
+		return 'window.location = "' . Router::url($url) . '";';
+	}
+/**
+ * Escape a string to be JavaScript friendly.
+ *
+ * List of escaped ellements:
+ *	+ "\r\n" => '\n'
+ *	+ "\r" => '\n'
+ *	+ "\n" => '\n'
+ *	+ '"' => '\"'
+ *	+ "'" => "\\'"
+ *
+ * @param  string $script String that needs to get escaped.
+ * @return string Escaped string.
+ */
+	function escape($string) {
+		$escape = array("\r\n" => '\n', "\r" => '\n', "\n" => '\n', '"' => '\"', "'" => "\\'");
+		return str_replace(array_keys($escape), array_values($escape), $string);
+	}
+
+	function get__($name) {
+		return $this->__object($name, 'id');
+	}
+
+	function select($pattern) {
+		return $this->__object($pattern, 'pattern');
+	}
+
+	function real($var) {
+		return $this->__object($var, 'real');
+	}
+
+	function __object($name, $var) {
+		if (!isset($this->__objects[$name])) {
+			$this->__objects[$name] = new JsHelperObject($this);
+			$this->__objects[$name]->{$var} = $name;
+		}
+		return $this->__objects[$name];
+	}
+/**
+ * Generates a JavaScript object in JavaScript Object Notation (JSON)
+ * from an array
+ *
+ * @param array $data Data to be converted
+ * @param boolean $block Wraps return value in a <script/> block if true
+ * @param string $prefix Prepends the string to the returned data
+ * @param string $postfix Appends the string to the returned data
+ * @param array $stringKeys A list of array keys to be treated as a string
+ * @param boolean $quoteKeys If false, treats $stringKey as a list of keys *not* to be quoted
+ * @param string $q The type of quote to use
+ * @return string A JSON code block
+ */
+	function object($data = array(), $block = false, $prefix = '', $postfix = '', $stringKeys = array(), $quoteKeys = true, $q = "\"") {
+		if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+
+		$out = array();
+		$key = array();
+
+		if (is_array($data)) {
+			$keys = array_keys($data);
+		}
+
+		$numeric = true;
+
+		if (!empty($keys)) {
+			foreach ($keys as $key) {
+				if (!is_numeric($key)) {
+					$numeric = false;
+					break;
+				}
+			}
+		}
+
+		foreach ($data as $key => $val) {
+			if (is_array($val) || is_object($val)) {
+				$val = $this->object($val, false, '', '', $stringKeys, $quoteKeys, $q);
+			} else {
+				if ((!count($stringKeys) && !is_numeric($val) && !is_bool($val)) || ($quoteKeys && in_array($key, $stringKeys)) || (!$quoteKeys && !in_array($key, $stringKeys)) && $val !== null) {
+					$val = $q . $this->escapeString($val) . $q;
+				}
+				if ($val == null) {
+					$val = 'null';
+				}
+			}
+
+			if (!$numeric) {
+				$val = $q . $key . $q . ':' . $val;
+			}
+
+			$out[] = $val;
+		}
+
+		if (!$numeric) {
+			$rt = '{' . join(', ', $out) . '}';
+		} else {
+			$rt = '[' . join(', ', $out) . ']';
+		}
+		$rt = $prefix . $rt . $postfix;
+
+		if ($block) {
+			$rt = $this->codeBlock($rt);
+		}
+
+		return $rt;
+	}
+}
+
+class JsHelperObject {
+	var $__parent = null;
+
+	var $id = null;
+
+	var $pattern = null;
+
+	var $real = null;
+
+	function __construct(&$parent) {
+		if (is_object($parent)) {
+			$this->setParent($parent);
+		}
+	}
+
+	function toString() {
+		return $this->__toString();
+	}
+
+	function __toString() {
+		return $this->literal;
+	}
+
+	function ref($ref = null) {
+		if ($ref == null) {
+			foreach (array('id', 'pattern', 'real') as $ref) {
+				if ($this->{$ref} !== null) {
+					return $this->{$ref};
+				}
+			}
+		} else {
+			return ($this->{$ref} !== null);
+		}
+		return null;
+	}
+
+	function literal($append = null) {
+		if (!empty($this->id)) {
+			$data = '$("' . $this->id . '")';
+		}
+		if (!empty($this->pattern)) {
+			$data = '$$("' . $this->pattern . '")';
+		}
+		if (!empty($this->real)) {
+			$data = $this->real;
+		}
+		if (!empty($append)) {
+			$data .= '.' . $append;
+		}
+		return $data;
+	}
+
+	function __call($name, $args) {
+		$data = '';
+
+		if (isset($this->__parent->effectMap[strtolower($name)])) {
+			array_unshift($args, $this->__parent->effectMap[strtolower($name)]);
+			$name = 'effect';
+		}
+
+		switch ($name) {
+			case 'effect':
+			case 'visualEffect':
+
+				if (strpos($args[0], '_') || $args[0]{0} != strtoupper($args[0]{0})) {
+					$args[0] = Inflector::camelize($args[0]);
+				}
+
+				if (strtolower($args[0]) == 'highlight') {
+					$data .= 'new ';
+				}
+				if ($this->pattern == null) {
+					$data .= 'Effect.' . $args[0] . '(' . $this->literal();
+				} else {
+					$data .= 'Effect.' . $args[0] . '(item';
+				}
+
+				if (isset($args[1]) && is_array($args[1])) {
+					$data .= ', {' . $this->__options($args[1]) . '}';
+				}
+				$data .= ');';
+
+				if ($this->pattern !== null) {
+					$data = $this->each($data);
+				}
+			break;
+			case 'remove':
+			case 'toggle':
+			case 'show':
+			case 'hide':
+				if (empty($args)) {
+					$obj = 'Element';
+					$params = '';
+				} else {
+					$obj = 'Effect';
+					$params = ', "' . $args[0] . '"';
+				}
+
+				if ($this->pattern != null) {
+					$data = $this->each($obj . ".{$name}(item);");
+				} else {
+					$data = $obj . ".{$name}(" . $this->literal() . ');';
+				}
+			break;
+			case 'visible':
+				$data = $this->literal() . '.visible();';
+			break;
+			case 'update':
+				$data = $this->literal() . ".update({$args[0]});";
+			break;
+			case 'load':
+				$data = 'new Ajax.Updater("' . $this->id . '", "' . $args[0] . '"';
+				if (isset($args[1]) && is_array($args[1])) {
+					$data .= ', {' . $this->__options($args[1]) . '}';
+				}
+				$data .= ');';
+			break;
+			case 'each':
+			case 'all':
+			case 'any':
+			case 'detect':
+			case 'findAll':
+				if ($this->pattern != null) {
+					$data = $this->__iterate($name, $args[0]);
+				}
+			break;
+			case 'addClass':
+			case 'removeClass':
+			case 'hasClass':
+			case 'toggleClass':
+				$data = $this->literal() . ".{$name}Name(\"{$args[0]}\");";
+			break;
+			case 'clone':
+			case 'inspect':
+			case 'keys':
+			case 'values':
+				$data = "Object.{$name}(" . $this->literal() . ");";
+			break;
+			case 'extend':
+				$data = "Object.extend(" . $this->literal() . ", {$args[0]});";
+			break;
+			case '...':
+				// Handle other methods here
+				// including interfaces to load other files on-the-fly
+				// that add support for additional methods/replacing existing methods
+			break;
+			default:
+				$data = $this->literal() . '.' . $name . '();';
+			break;
+		}
+
+		if ($this->__parent->output) {
+			echo $data;
+		} else {
+			return $data;
+		}
+	}
+
+	function __iterate($method, $data) {
+		return '$$("' . $this->pattern . '").' . $method . '(function(item) {' . $data . '});';
+	}
+
+	function setParent(&$parent) {
+		$this->__parent =& $parent;
+	}
+
+	function __options($opts) {
+		$options = array();
+		foreach ($opts as $key => $val) {
+			if (!is_int($val)) {
+				$val = '"' . $val . '"';
+			}
+			$options[] = $key . ':' . $val;
+		}
+		return join(', ', $options);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/time.php
===================================================================
--- cake/libs/view/helpers/time.php	(revision 0)
+++ cake/libs/view/helpers/time.php	(revision 0)
@@ -0,0 +1,537 @@
+<?php
+/* SVN FILE: $Id: time.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Time Helper class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Time Helper class for easy use of time data.
+ *
+ * Manipulation of time data.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class TimeHelper extends AppHelper {
+/**
+ * Converts given time (in server's time zone) to user's local time, given his/her offset from GMT.
+ *
+ * @param string $serverTime UNIX timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string UNIX timestamp
+ */
+	function convert($serverTime, $userOffset) {
+		$serverOffset = $this->serverOffset();
+		$gmtTime = $serverTime - $serverOffset;
+		$userTime = $gmtTime + $userOffset * (60*60);
+		return $userTime;
+	}
+/**
+ * Returns server's offset from GMT in seconds.
+ *
+ * @return int Offset
+ */
+	function serverOffset() {
+		return date('Z', time());
+	}
+/**
+ * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
+ *
+ * @param string $dateString Datetime string
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Parsed timestamp
+ */
+	function fromString($dateString, $userOffset = null) {
+		if (empty($dateString)) {
+			return false;
+		}
+		if (is_integer($dateString) || is_numeric($dateString)) {
+			$date = intval($dateString);
+		} else {
+			$date = strtotime($dateString);
+		}
+		if ($userOffset !== null) {
+			return $this->convert($date, $userOffset);
+		}
+		return $date;
+	}
+/**
+ * Returns a nicely formatted date string for given Datetime string.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Formatted date string
+ */
+	function nice($dateString = null, $userOffset = null) {
+		if ($dateString != null) {
+			$date = $this->fromString($dateString, $userOffset);
+		} else {
+			$date = time();
+		}
+
+		$ret = date("D, M jS Y, H:i", $date);
+		return $this->output($ret);
+	}
+/**
+ * Returns a formatted descriptive date string for given datetime string.
+ *
+ * If the given date is today, the returned string could be "Today, 16:54".
+ * If the given date was yesterday, the returned string could be "Yesterday, 16:54".
+ * If $dateString's year is the current year, the returned string does not
+ * include mention of the year.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Described, relative date string
+ */
+	function niceShort($dateString = null, $userOffset = null) {
+		$date = $dateString ? $this->fromString($dateString, $userOffset) : time();
+
+		$y = $this->isThisYear($date) ? '' : ' Y';
+
+		if ($this->isToday($date)) {
+			$ret = sprintf(__('Today, %s',true), date("H:i", $date));
+		} elseif ($this->wasYesterday($date)) {
+			$ret = sprintf(__('Yesterday, %s',true), date("H:i", $date));
+		} else {
+			$ret = date("M jS{$y}, H:i", $date);
+		}
+
+		return $this->output($ret);
+	}
+/**
+ * Returns a partial SQL string to search for all records between two dates.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param string $end Datetime string or Unix timestamp
+ * @param string $fieldName Name of database field to compare with
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Partial SQL string.
+ */
+	function daysAsSql($begin, $end, $fieldName, $userOffset = null) {
+		$begin = $this->fromString($begin, $userOffset);
+		$end = $this->fromString($end, $userOffset);
+		$begin = date('Y-m-d', $begin) . ' 00:00:00';
+		$end = date('Y-m-d', $end) . ' 23:59:59';
+
+		$ret  ="($fieldName >= '$begin') AND ($fieldName <= '$end')";
+		return $this->output($ret);
+	}
+/**
+ * Returns a partial SQL string to search for all records between two times
+ * occurring on the same day.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param string $fieldName Name of database field to compare with
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Partial SQL string.
+ */
+	function dayAsSql($dateString, $fieldName, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		$ret = $this->daysAsSql($dateString, $dateString, $fieldName);
+		return $this->output($ret);
+	}
+/**
+ * Returns true if given datetime string is today.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return boolean True if datetime string is today
+ */
+	function isToday($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('Y-m-d', $date) == date('Y-m-d', time());
+	}
+/**
+ * Returns true if given datetime string is within this week
+ * @param string $dateString
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return boolean True if datetime string is within current week
+ */
+	function isThisWeek($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('W Y', $date) == date('W Y', time());
+	}
+/**
+ * Returns true if given datetime string is within this month
+ * @param string $dateString
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return boolean True if datetime string is within current month
+ */
+	function isThisMonth($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString);
+		return date('m Y',$date) == date('m Y', time());
+	}
+/**
+ * Returns true if given datetime string is within current year.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @return boolean True if datetime string is within current year
+ */
+	function isThisYear($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return  date('Y', $date) == date('Y', time());
+	}
+/**
+ * Returns true if given datetime string was yesterday.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return boolean True if datetime string was yesterday
+ */
+	function wasYesterday($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('Y-m-d', $date) == date('Y-m-d', strtotime('yesterday'));
+	}
+/**
+ * Returns true if given datetime string is tomorrow.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return boolean True if datetime string was yesterday
+ */
+	function isTomorrow($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
+	}
+/**
+ * Returns the quart
+ * @param string $dateString
+ * @param boolean $range if true returns a range in Y-m-d format
+ * @return boolean True if datetime string is within current week
+ */
+	function toQuarter($dateString, $range = false) {
+		$time = $this->fromString($dateString);
+		$date = ceil(date('m', $time) / 3);
+
+		if ($range === true) {
+			$range = 'Y-m-d';
+		}
+
+		if ($range !== false) {
+			$year = date('Y', $time);
+
+			switch ($date) {
+				case 1:
+					$date = array($year.'-01-01', $year.'-03-31');
+					break;
+				case 2:
+					$date = array($year.'-04-01', $year.'-06-30');
+					break;
+				case 3:
+					$date = array($year.'-07-01', $year.'-09-30');
+					break;
+				case 4:
+					$date = array($year.'-10-01', $year.'-12-31');
+					break;
+			}
+		}
+		return $this->output($date);
+	}
+/**
+ * Returns a UNIX timestamp from a textual datetime description. Wrapper for PHP function strtotime().
+ *
+ * @param string $dateString Datetime string to be represented as a Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return integer Unix timestamp
+ */
+	function toUnix($dateString, $userOffset = null) {
+		$ret = $this->fromString($dateString, $userOffset);
+		return $this->output($ret);
+	}
+/**
+ * Returns a date formatted for Atom RSS feeds.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Formatted date string
+ */
+	function toAtom($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		$ret = date('Y-m-d\TH:i:s\Z', $date);
+		return $this->output($ret);
+	}
+/**
+ * Formats date for RSS feeds
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Formatted date string
+ */
+	function toRSS($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		$ret = date("r", $date);
+		return $this->output($ret);
+	}
+/**
+ * Returns either a relative date or a formatted date depending
+ * on the difference between the current time and given datetime.
+ * $datetime should be in a <i>strtotime</i>-parsable format, like MySQL's datetime datatype.
+ *
+ * Options:
+ *  'format' => a fall back format if the relative time is longer than the duration specified by end
+ *  'end' =>  The end of relative time telling
+ *  'userOffset' => Users offset from GMT (in hours)
+ *
+ * Relative dates look something like this:
+ *	3 weeks, 4 days ago
+ *	15 seconds ago
+ * Formatted dates look like this:
+ *	on 02/18/2004
+ *
+ * The returned string includes 'ago' or 'on' and assumes you'll properly add a word
+ * like 'Posted ' before the function output.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param array $options Default format if timestamp is used in $dateString
+ * @return string Relative time string.
+ */
+	function timeAgoInWords($dateTime, $options = array()) {
+		$userOffset = null;
+		if (is_array($options) && isset($options['userOffset'])) {
+			$userOffset = $options['userOffset'];
+		}
+		$now = time();
+		if (!is_null($userOffset)) {
+			$now = 	$this->convert(time(), $userOffset);
+		}
+		$inSeconds = $this->fromString($dateTime, $userOffset);
+		$backwards = ($inSeconds > $now);
+
+		$format = 'j/n/y';
+		$end = '+1 month';
+
+		if (is_array($options)) {
+			if (isset($options['format'])) {
+				$format = $options['format'];
+				unset($options['format']);
+			}
+			if (isset($options['end'])) {
+				$end = $options['end'];
+				unset($options['end']);
+			}
+		} else {
+			$format = $options;
+		}
+
+		if ($backwards) {
+			$futureTime = $inSeconds;
+			$pastTime = $now;
+		} else {
+			$futureTime = $now;
+			$pastTime = $inSeconds;
+		}
+		$diff = $futureTime - $pastTime;
+
+		// If more than a week, then take into account the length of months
+		if ($diff >= 604800) {
+			$current = array();
+			$date = array();
+
+			list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
+
+			list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
+			$years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
+
+			if ($future['Y'] == $past['Y'] && $future['m'] == $past['m']) {
+				$months = 0;
+				$years = 0;
+			} else {
+				if ($future['Y'] == $past['Y']) {
+					$months = $future['m'] - $past['m'];
+				} else {
+					$years = $future['Y'] - $past['Y'];
+					$months = $future['m'] + ((12 * $years) - $past['m']);
+
+					if ($months >= 12) {
+						$years = floor($months / 12);
+						$months = $months - ($years * 12);
+					}
+
+					if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
+						$years --;
+					}
+				}
+			}
+
+			if ($future['d'] >= $past['d']) {
+				$days = $future['d'] - $past['d'];
+			} else {
+				$daysInPastMonth = date('t', $pastTime);
+				$daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
+
+				if (!$backwards) {
+					$days = ($daysInPastMonth - $past['d']) + $future['d'];
+				} else {
+					$days = ($daysInFutureMonth - $past['d']) + $future['d'];
+				}
+
+				if ($future['m'] != $past['m']) {
+					$months --;
+				}
+			}
+
+			if ($months == 0 && $years >= 1 && $diff < ($years * 31536000)) {
+				$months = 11;
+				$years --;
+			}
+
+			if ($months >= 12) {
+				$years = $years + 1;
+				$months = $months - 12;
+			}
+
+			if ($days >= 7) {
+				$weeks = floor($days / 7);
+				$days = $days - ($weeks * 7);
+			}
+		} else {
+			$years = $months = $weeks = 0;
+			$days = floor($diff / 86400);
+
+			$diff = $diff - ($days * 86400);
+
+			$hours = floor($diff / 3600);
+			$diff = $diff - ($hours * 3600);
+
+			$minutes = floor($diff / 60);
+			$diff = $diff - ($minutes * 60);
+			$seconds = $diff;
+		}
+		$relativeDate = '';
+		$diff = $futureTime - $pastTime;
+
+		if ($diff > abs($now - $this->fromString($end))) {
+			$relativeDate = sprintf(__('on %s',true), date($format, $inSeconds));
+		} else {
+			if ($years > 0) {
+				// years and months and days
+				$relativeDate .= ($relativeDate ? ', ' : '') . $years . ' ' . __n('year', 'years', $years, true);
+				$relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months, true) : '';
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
+			} elseif (abs($months) > 0) {
+				// months, weeks and days
+				$relativeDate .= ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months, true);
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
+			} elseif (abs($weeks) > 0) {
+				// weeks and days
+				$relativeDate .= ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true);
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
+			} elseif (abs($days) > 0) {
+				// days and hours
+				$relativeDate .= ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true);
+				$relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours, true) : '';
+			} elseif (abs($hours) > 0) {
+				// hours and minutes
+				$relativeDate .= ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours, true);
+				$relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes, true) : '';
+			} elseif (abs($minutes) > 0) {
+				// minutes only
+				$relativeDate .= ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes, true);
+			} else {
+				// seconds only
+				$relativeDate .= ($relativeDate ? ', ' : '') . $seconds . ' ' . __n('second', 'seconds', $seconds, true);
+			}
+
+			if (!$backwards) {
+				$relativeDate = sprintf(__('%s ago', true), $relativeDate);
+			}
+		}
+		return $this->output($relativeDate);
+	}
+/**
+ * Alias for timeAgoInWords
+ *
+ * @param mixed $dateTime Datetime string (strtotime-compatible) or Unix timestamp
+ * @param mixed $options Default format string, if timestamp is used in $dateTime, or an array of options to be passed
+ *						 on to timeAgoInWords().
+ * @return string Relative time string.
+ * @see		TimeHelper::timeAgoInWords
+ */
+	function relativeTime($dateTime, $options = array()) {
+		return $this->timeAgoInWords($dateTime, $options);
+	}
+/**
+ * Returns true if specified datetime was within the interval specified, else false.
+ *
+ * @param mixed $timeInterval the numeric value with space then time type. Example of valid types: 6 hours, 2 days, 1 minute.
+ * @param mixed $dateString the datestring or unix timestamp to compare
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return bool
+ */
+	function wasWithinLast($timeInterval, $dateString, $userOffset = null) {
+		$tmp = r(' ', '', $timeInterval);
+		if (is_numeric($tmp)) {
+			$timeInterval = $tmp . ' ' . __('days', true);
+		}
+
+		$date = $this->fromString($dateString, $userOffset);
+		$interval = $this->fromString('-'.$timeInterval);
+
+		if ($date >= $interval && $date <= time()) {
+			return true;
+		}
+
+		return false;
+	}
+/**
+ * Returns gmt, given either a UNIX timestamp or a valid strtotime() date string.
+ *
+ * @param string $dateString Datetime string
+ * @return string Formatted date string
+ */
+	function gmt($string = null) {
+		if ($string != null) {
+			$string = $this->fromString($string);
+		} else {
+			$string = time();
+		}
+		$string = $this->fromString($string);
+		$hour = intval(date("G", $string));
+		$minute = intval(date("i", $string));
+		$second = intval(date("s", $string));
+		$month = intval(date("n", $string));
+		$day = intval(date("j", $string));
+		$year = intval(date("Y", $string));
+
+		$return = gmmktime($hour, $minute, $second, $month, $day, $year);
+		return $return;
+	}
+/**
+ * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
+ *
+ * @param string $format date format string. defaults to 'd-m-Y'
+ * @param string $dateString Datetime string
+ * @param boolean $invalid flag to ignore results of fromString == false
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Formatted date string
+ */
+	function format($format = 'd-m-Y', $date, $invalid = false, $userOffset = null) {
+		$date = $this->fromString($date, $userOffset);
+		if ($date === false && $invalid !== false) {
+			return $invalid;
+		}
+		return date($format, $date);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/app_helper.php
===================================================================
--- cake/libs/view/helpers/app_helper.php	(revision 0)
+++ cake/libs/view/helpers/app_helper.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+/* SVN FILE: $Id: app_helper.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * This file is application-wide helper file. You can put all
+ * application-wide helper-related methods here.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'Helper');
+/**
+ * This is a placeholder class.
+ * Create the same file in app/app_helper.php
+ *
+ * Add your application-wide methods in the class below, your helpers
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.cake
+ */
+class AppHelper extends Helper {
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/xml.php
===================================================================
--- cake/libs/view/helpers/xml.php	(revision 0)
+++ cake/libs/view/helpers/xml.php	(revision 0)
@@ -0,0 +1,159 @@
+<?php
+/* SVN FILE: $Id: xml.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * XML Helper class file.
+ *
+ * Simplifies the output of XML documents.
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', array('Xml', 'Set'));
+
+/**
+ * XML Helper class for easy output of XML structures.
+ *
+ * XmlHelper encloses all methods needed while working with XML documents.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class XmlHelper extends AppHelper {
+/**
+ * Default document encoding
+ *
+ * @access public
+ * @var string
+ */
+	var $encoding = 'UTF-8';
+/**
+ * Constructor
+ * @return void
+ */
+	function __construct() {
+		parent::__construct();
+		$this->Xml =& new Xml();
+		$this->Xml->options(array('verifyNs' => false));
+	}
+/**
+ * Returns an XML document header
+ *
+ * @param  array $attrib Header tag attributes
+ * @return string XML header
+ */
+	function header($attrib = array()) {
+		if (Configure::read('App.encoding') !== null) {
+			$this->encoding = Configure::read('App.encoding');
+		}
+
+		if (is_array($attrib)) {
+			$attrib = array_merge(array('encoding' => $this->encoding), $attrib);
+		}
+		if (is_string($attrib) && strpos($attrib, 'xml') !== 0) {
+			$attrib = 'xml ' . $attrib;
+		}
+
+		return $this->output($this->Xml->header($attrib));
+	}
+/**
+ * Adds a namespace to any documents generated
+ *
+ * @param  string  $name The namespace name
+ * @param  string  $url  The namespace URI; can be empty if in the default namespace map
+ * @return boolean False if no URL is specified, and the namespace does not exist
+ *                 default namespace map, otherwise true
+ * @deprecated
+ * @see Xml::addNs()
+ */
+	function addNs($name, $url = null) {
+		return $this->Xml->addNamespace($name, $url);
+	}
+/**
+ * Removes a namespace added in addNs()
+ *
+ * @param  string  $name The namespace name or URI
+ * @deprecated
+ * @see Xml::removeNs()
+ */
+	function removeNs($name) {
+		return $this->Xml->removeGlobalNamespace($name);
+	}
+/**
+ * Generates an XML element
+ *
+ * @param  string   $name The name of the XML element
+ * @param  array    $attrib The attributes of the XML element
+ * @param  mixed    $content XML element content
+ * @param  boolean  $endTag Whether the end tag of the element should be printed
+ * @return string XML
+ */
+	function elem($name, $attrib = array(), $content = null, $endTag = true) {
+		$namespace = null;
+		if (isset($attrib['namespace'])) {
+			$namespace = $attrib['namespace'];
+			unset($attrib['namespace']);
+		}
+		$cdata = false;
+		if (is_array($content) && isset($content['cdata'])) {
+			$cdata = true;
+			unset($content['cdata']);
+		}
+		if (is_array($content) && isset($content['value'])) {
+			$content = $content['value'];
+		}
+		$children = array();
+		if (is_array($content)) {
+			$children = $content;
+			$content = null;
+		}
+
+		$elem =& $this->Xml->createElement($name, $content, $attrib, $namespace);
+		foreach ($children as $child) {
+			$elem->createElement($child);
+		}
+		$out = $elem->toString(array('cdata' => $cdata, 'leaveOpen' => !$endTag));
+
+		if (!$endTag) {
+			$this->Xml =& $elem;
+		}
+		return $this->output($out);
+	}
+/**
+ * Create closing tag for current element
+ *
+ * @return string
+ */
+	function closeElem() {
+		$name = $this->Xml->name();
+		if ($parent =& $this->Xml->parent()) {
+			$this->Xml =& $parent;
+		}
+		return $this->output('</' . $name . '>');
+	}
+/**
+ * Serializes a model resultset into XML
+ *
+ * @param  mixed  $data The content to be converted to XML
+ * @param  array  $options The data formatting options
+ * @return string A copy of $data in XML format
+ */
+	function serialize($data, $options = array()) {
+		$data =& new Xml($data, array_merge(array('attributes' => false, 'format' => 'attributes'), $options));
+		return $data->toString(array_merge(array('header' => false), $options));
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/ajax.php
===================================================================
--- cake/libs/view/helpers/ajax.php	(revision 0)
+++ cake/libs/view/helpers/ajax.php	(revision 0)
@@ -0,0 +1,985 @@
+<?php
+/* SVN FILE: $Id: ajax.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Helper for AJAX operations.
+ *
+ * Helps doing AJAX using the Prototype library.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * AjaxHelper helper library.
+ *
+ * Helps doing AJAX using the Prototype library.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class AjaxHelper extends AppHelper {
+/**
+ * Included helpers.
+ *
+ * @var array
+ */
+	var $helpers = array('Html', 'Javascript', 'Form');
+/**
+ * HtmlHelper instance
+ *
+ * @var object
+ * @access public
+ */
+	var $Html = null;
+/**
+ * JavaScriptHelper instance
+ *
+ * @var object
+ * @access public
+ */
+	var $Javascript = null;
+/**
+ * Names of Javascript callback functions.
+ *
+ * @var array
+ */
+	var $callbacks = array(
+		'complete', 'create', 'exception', 'failure', 'interactive', 'loading',
+		'loaded', 'success', 'uninitialized'
+	);
+/**
+ * Names of AJAX options.
+ *
+ * @var array
+ */
+	var $ajaxOptions = array(
+		'after', 'asynchronous', 'before', 'confirm', 'condition', 'contentType', 'encoding',
+		'evalScripts', 'failure', 'fallback', 'form', 'indicator', 'insertion', 'interactive',
+		'loaded', 'loading', 'method', 'onCreate', 'onComplete', 'onException', 'onFailure',
+		'onInteractive', 'onLoaded', 'onLoading', 'onSuccess', 'onUninitialized', 'parameters',
+		'position', 'postBody', 'requestHeaders', 'success', 'type', 'update', 'with'
+	);
+/**
+ * Options for draggable.
+ *
+ * @var array
+ */
+	var $dragOptions = array(
+		'handle', 'revert', 'snap', 'zindex', 'constraint', 'change', 'ghosting',
+		'starteffect', 'reverteffect', 'endeffect'
+	);
+/**
+ * Options for droppable.
+ *
+ * @var array
+ */
+	var $dropOptions = array(
+		'accept', 'containment', 'greedy', 'hoverclass', 'onHover', 'onDrop', 'overlap'
+	);
+/**
+ * Options for sortable.
+ *
+ * @var array
+ */
+	var $sortOptions = array(
+		'constraint', 'containment', 'dropOnEmpty', 'ghosting', 'handle', 'hoverclass', 'onUpdate',
+		'onChange', 'only', 'overlap', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tag', 'tree',
+		'treeTag', 'update'
+	);
+/**
+ * Options for slider.
+ *
+ * @var array
+ */
+	var $sliderOptions = array(
+		'alignX', 'alignY', 'axis', 'disabled', 'handleDisabled', 'handleImage', 'increment',
+		'maximum', 'minimum', 'onChange', 'onSlide', 'range', 'sliderValue', 'values'
+	);
+/**
+ * Options for in-place editor.
+ *
+ * @var array
+ */
+	var $editorOptions = array(
+		'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'rows', 'cols', 'size',
+		'highlightcolor', 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL',
+		'loadingText', 'callback', 'ajaxOptions', 'clickToEditText', 'collection', 'okControl',
+		'cancelControl', 'submitOnBlur'
+	);
+/**
+ * Options for auto-complete editor.
+ *
+ * @var array
+ */
+	var $autoCompleteOptions = array(
+		'afterUpdateElement', 'callback', 'frequency', 'indicator', 'minChars', 'onShow', 'onHide',
+		'parameters', 'paramName', 'tokens', 'updateElement'
+	);
+/**
+ * Output buffer for Ajax update content
+ *
+ * @var array
+ */
+	var $__ajaxBuffer = array();
+/**
+ * Returns link to remote action
+ *
+ * Returns a link to a remote action defined by <i>options[url]</i>
+ * (using the url() format) that's called in the background using
+ * XMLHttpRequest. The result of that request can then be inserted into a
+ * DOM object whose id can be specified with <i>options[update]</i>.
+ *
+ * Examples:
+ * <code>
+ *  link("Delete this post",
+ * array("update" => "posts", "url" => "delete/{$postid->id}"));
+ *  link(imageTag("refresh"),
+ *		array("update" => "emails", "url" => "list_emails" ));
+ * </code>
+ *
+ * By default, these remote requests are processed asynchronous during
+ * which various callbacks can be triggered (for progress indicators and
+ * the likes).
+ *
+ * Example:
+ * <code>
+ *	link (word,
+ *		array("url" => "undo", "n" => word_counter),
+ *		array("complete" => "undoRequestCompleted(request)"));
+ * </code>
+ *
+ * The callbacks that may be specified are:
+ *
+ * - <i>loading</i>::		Called when the remote document is being
+ *							loaded with data by the browser.
+ * - <i>loaded</i>::		Called when the browser has finished loading
+ *							the remote document.
+ * - <i>interactive</i>::	Called when the user can interact with the
+ *							remote document, even though it has not
+ *							finished loading.
+ * - <i>complete</i>:: Called when the XMLHttpRequest is complete.
+ *
+ * If you for some reason or another need synchronous processing (that'll
+ * block the browser while the request is happening), you can specify
+ * <i>options[type] = synchronous</i>.
+ *
+ * You can customize further browser side call logic by passing
+ * in Javascript code snippets via some optional parameters. In
+ * their order of use these are:
+ *
+ * - <i>confirm</i>:: Adds confirmation dialog.
+ * -<i>condition</i>::	Perform remote request conditionally
+ *                      by this expression. Use this to
+ *                      describe browser-side conditions when
+ *                      request should not be initiated.
+ * - <i>before</i>::		Called before request is initiated.
+ * - <i>after</i>::		Called immediately after request was
+ *						initiated and before <i>loading</i>.
+ *
+ * @param string $title Title of link
+ * @param string $href Href string "/products/view/12"
+ * @param array $options		Options for JavaScript function
+ * @param string $confirm		Confirmation message. Calls up a JavaScript confirm() message.
+ * @param boolean $escapeTitle  Escaping the title string to HTML entities
+ *
+ * @return string				HTML code for link to remote action
+ */
+	function link($title, $href = null, $options = array(), $confirm = null, $escapeTitle = true) {
+		if (!isset($href)) {
+			$href = $title;
+		}
+		if (!isset($options['url'])) {
+			$options['url'] = $href;
+		}
+
+		if (isset($confirm)) {
+			$options['confirm'] = $confirm;
+			unset($confirm);
+		}
+		$htmlOptions = $this->__getHtmlOptions($options, array('url'));
+
+		if (empty($options['fallback']) || !isset($options['fallback'])) {
+			$options['fallback'] = $href;
+		}
+		$htmlDefaults = array('id' => 'link' . intval(mt_rand()), 'onclick' => '');
+		$htmlOptions = array_merge($htmlDefaults, $htmlOptions);
+
+		$htmlOptions['onclick'] .= ' event.returnValue = false; return false;';
+		$return = $this->Html->link($title, $href, $htmlOptions, null, $escapeTitle);
+		$callback = $this->remoteFunction($options);
+		$script = $this->Javascript->event("'{$htmlOptions['id']}'", "click", $callback);
+
+		if (is_string($script)) {
+			$return .= $script;
+		}
+		return $return;
+	}
+/**
+ * Creates JavaScript function for remote AJAX call
+ *
+ * This function creates the javascript needed to make a remote call
+ * it is primarily used as a helper for AjaxHelper::link.
+ *
+ * @param array $options options for javascript
+ * @return string html code for link to remote action
+ * @see AjaxHelper::link() for docs on options parameter.
+ */
+	function remoteFunction($options) {
+		if (isset($options['update'])) {
+			if (!is_array($options['update'])) {
+				$func = "new Ajax.Updater('{$options['update']}',";
+			} else {
+				$func = "new Ajax.Updater(document.createElement('div'),";
+			}
+			if (!isset($options['requestHeaders'])) {
+				$options['requestHeaders'] = array();
+			}
+			if (is_array($options['update'])) {
+				$options['update'] = join(' ', $options['update']);
+			}
+			$options['requestHeaders']['X-Update'] = $options['update'];
+		} else {
+			$func = "new Ajax.Request(";
+		}
+
+		$func .= "'" . $this->url(isset($options['url']) ? $options['url'] : "") . "'";
+		$func .= ", " . $this->__optionsForAjax($options) . ")";
+
+		if (isset($options['before'])) {
+			$func = "{$options['before']}; $func";
+		}
+		if (isset($options['after'])) {
+			$func = "$func; {$options['after']};";
+		}
+		if (isset($options['condition'])) {
+			$func = "if ({$options['condition']}) { $func; }";
+		}
+
+		if (isset($options['confirm'])) {
+			$func = "if (confirm('" . $this->Javascript->escapeString($options['confirm'])
+				. "')) { $func; } else { event.returnValue = false; return false; }";
+		}
+		return $func;
+	}
+/**
+ * Periodically call remote url via AJAX.
+ *
+ * Periodically calls the specified url (<i>options[url]</i>) every <i>options[frequency]</i>
+ * seconds (default is 10).  Usually used to update a specified div (<i>options[update]</i>) with
+ * the results of the remote call.  The options for specifying the target with url and defining
+ * callbacks is the same as AjaxHelper::link().
+ *
+ * @param array $options Callback options
+ * @return string Javascript code
+ * @see AjaxHelper::link()
+ */
+	function remoteTimer($options = null) {
+		$frequency = (isset($options['frequency'])) ? $options['frequency'] : 10;
+		$callback = $this->remoteFunction($options);
+		$code = "new PeriodicalExecuter(function() {{$callback}}, $frequency)";
+		return $this->Javascript->codeBlock($code);
+	}
+/**
+ * Returns form tag that will submit using Ajax.
+ *
+ * Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular
+ * reloading POST arrangement. Even though it's using Javascript to serialize the form elements,
+ * the form submission will work just like a regular submission as viewed by the receiving side
+ * (all elements available in params).  The options for defining callbacks is the same
+ * as AjaxHelper::link().
+ *
+ * @param mixed $params Either a string identifying the form target, or an array of method
+ *                      parameters, including:
+ *                          - 'params' => Acts as the form target
+ *                          - 'type' => 'post' or 'get'
+ *                          - 'options' => An array containing all HTML and script options used to
+ *                             generate the form tag and Ajax request.
+ * @param array $type How form data is posted: 'get' or 'post'
+ * @param array $options Callback/HTML options
+ * @return string JavaScript/HTML code
+ * @see AjaxHelper::link()
+ */
+	function form($params = null, $type = 'post', $options = array()) {
+		$model = false;
+		if (is_array($params)) {
+			extract($params, EXTR_OVERWRITE);
+		}
+
+		if (empty($options['url'])) {
+			$options['url'] = array('action' => $params);
+		}
+
+		$htmlDefaults = array(
+			'id' => 'form' . intval(mt_rand()),
+			'onsubmit'	=> "event.returnValue = false; return false;",
+			'type' => $type
+		);
+		$htmlOptions = $this->__getHtmlOptions($options, array('model', 'with'));
+		$htmlOptions = array_merge($htmlDefaults, $htmlOptions);
+
+		$defaults = array('model' => $model, 'with' => "Form.serialize('{$htmlOptions['id']}')");
+		$options = array_merge($defaults, $options);
+		$callback = $this->remoteFunction($options);
+
+		$form = $this->Form->create($options['model'], $htmlOptions);
+		$script = $this->Javascript->event("'" . $htmlOptions['id']. "'", 'submit', $callback);
+		return $form . $script;
+	}
+/**
+ * Returns a button input tag that will submit using Ajax
+ *
+ * Returns a button input tag that will submit form using XMLHttpRequest in the background instead
+ * of regular reloading POST arrangement. <i>options</i> argument is the same as
+ * in AjaxHelper::form().
+ *
+ * @param string $title Input button title
+ * @param array $options Callback options
+ * @return string Ajaxed input button
+ * @see AjaxHelper::form()
+ */
+	function submit($title = 'Submit', $options = array()) {
+		$htmlOptions = $this->__getHtmlOptions($options);
+		$htmlOptions['value'] = $title;
+
+		if (!isset($options['with'])) {
+			$options['with'] = 'Form.serialize(Event.element(event).form)';
+		}
+		if (!isset($htmlOptions['id'])) {
+			$htmlOptions['id'] = 'submit' . intval(mt_rand());
+		}
+
+		$htmlOptions['onclick'] = "event.returnValue = false; return false;";
+		$callback = $this->remoteFunction($options);
+
+		$form = $this->Form->submit($title, $htmlOptions);
+		$script = $this->Javascript->event('"' . $htmlOptions['id'] . '"', 'click', $callback);
+		return $form . $script;
+	}
+/**
+ * Observe field and call ajax on change.
+ *
+ * Observes the field with the DOM ID specified by <i>field</i> and makes
+ * an Ajax when its contents have changed.
+ *
+ * Required +options+ are:
+ * - <i>frequency</i>:: The frequency (in seconds) at which changes to
+ *						this field will be detected.
+ * - <i>url</i>::		@see url() -style options for the action to call
+ *						when the field has changed.
+ *
+ * Additional options are:
+ * - <i>update</i>::	Specifies the DOM ID of the element whose
+ *						innerHTML should be updated with the
+ *						XMLHttpRequest response text.
+ * - <i>with</i>:: A Javascript expression specifying the
+ *						parameters for the XMLHttpRequest. This defaults
+ *						to Form.Element.serialize('$field'), which can be
+ *						accessed from params['form']['field_id'].
+ *
+ * Additionally, you may specify any of the options documented in
+ * @see linkToRemote().
+ *
+ * @param string $field DOM ID of field to observe
+ * @param array $options ajax options
+ * @return string ajax script
+ */
+	function observeField($field, $options = array()) {
+		if (!isset($options['with'])) {
+			$options['with'] = 'Form.Element.serialize(\'' . $field . '\')';
+		}
+		$observer = 'Observer';
+		if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
+			$observer = 'EventObserver';
+		}
+		return $this->Javascript->codeBlock(
+			$this->_buildObserver('Form.Element.' . $observer, $field, $options)
+		);
+	}
+/**
+ * Observe entire form and call ajax on change.
+ *
+ * Like @see observeField(), but operates on an entire form identified by the
+ * DOM ID <b>form</b>. <b>options</b> are the same as <b>observeField</b>, except
+ * the default value of the <i>with</i> option evaluates to the
+ * serialized (request string) value of the form.
+ *
+ * @param string $form DOM ID of form to observe
+ * @param array $options ajax options
+ * @return string ajax script
+ */
+	function observeForm($form, $options = array()) {
+		if (!isset($options['with'])) {
+			$options['with'] = 'Form.serialize(\'' . $form . '\')';
+		}
+		$observer = 'Observer';
+		if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
+			$observer = 'EventObserver';
+		}
+		return $this->Javascript->codeBlock(
+			$this->_buildObserver('Form.' . $observer, $form, $options)
+		);
+	}
+/**
+ * Create a text field with Autocomplete.
+ *
+ * Creates an autocomplete field with the given ID and options.
+ *
+ * options['with'] defaults to "Form.Element.serialize('$field')",
+ * but can be any valid javascript expression defining the additional fields.
+ *
+ * @param string $field DOM ID of field to observe
+ * @param string $url URL for the autocomplete action
+ * @param array $options Ajax options
+ * @return string Ajax script
+ */
+	function autoComplete($field, $url = "", $options = array()) {
+		$var = '';
+		if (isset($options['var'])) {
+			$var = 'var ' . $options['var'] . ' = ';
+			unset($options['var']);
+		}
+
+		if (!isset($options['id'])) {
+			$options['id'] = Inflector::camelize(str_replace(".", "_", $field));
+		}
+
+		$divOptions = array(
+			'id' => $options['id'] . "_autoComplete",
+			'class' => isset($options['class']) ? $options['class'] : 'auto_complete'
+		);
+
+		if (isset($options['div_id'])) {
+			$divOptions['id'] = $options['div_id'];
+			unset($options['div_id']);
+		}
+
+		$htmlOptions = $this->__getHtmlOptions($options);
+		$htmlOptions['autocomplete'] = "off";
+
+		foreach ($this->autoCompleteOptions as $opt) {
+			unset($htmlOptions[$opt]);
+		}
+
+		if (isset($options['tokens'])) {
+			if (is_array($options['tokens'])) {
+				$options['tokens'] = $this->Javascript->object($options['tokens']);
+			} else {
+				$options['tokens'] = '"' . $options['tokens'] . '"';
+			}
+		}
+
+		$options = $this->_optionsToString($options, array('paramName', 'indicator'));
+		$options = $this->_buildOptions($options, $this->autoCompleteOptions);
+
+
+		$text = $this->Form->text($field, $htmlOptions);
+		$div = $this->Html->div(null, '', $divOptions);
+		$script = "{$var}new Ajax.Autocompleter('{$htmlOptions['id']}', '{$divOptions['id']}', '";
+		$script .= $this->Html->url($url) . "', {$options});";
+
+		return  "{$text}\n{$div}\n" . $this->Javascript->codeBlock($script);
+	}
+/**
+ * Creates an Ajax-updateable DIV element
+ *
+ * @param string $id options for javascript
+ * @return string HTML code
+ */
+	function div($id, $options = array()) {
+		if (env('HTTP_X_UPDATE') != null) {
+			$this->Javascript->enabled = false;
+			$divs = explode(' ', env('HTTP_X_UPDATE'));
+
+			if (in_array($id, $divs)) {
+				@ob_end_clean();
+				ob_start();
+				return '';
+			}
+		}
+		$attr = $this->_parseAttributes(array_merge($options, array('id' => $id)));
+		return $this->output(sprintf($this->Html->tags['blockstart'], $attr));
+	}
+/**
+ * Closes an Ajax-updateable DIV element
+ *
+ * @param string $id The DOM ID of the element
+ * @return string HTML code
+ */
+	function divEnd($id) {
+		if (env('HTTP_X_UPDATE') != null) {
+			$divs = explode(' ', env('HTTP_X_UPDATE'));
+			if (in_array($id, $divs)) {
+				$this->__ajaxBuffer[$id] = ob_get_contents();
+				ob_end_clean();
+				ob_start();
+				return '';
+			}
+		}
+		return $this->output($this->Html->tags['blockend']);
+	}
+/**
+ * Detects Ajax requests
+ *
+ * @return boolean True if the current request is a Prototype Ajax update call
+ */
+	function isAjax() {
+		return (isset($this->params['isAjax']) && $this->params['isAjax'] === true);
+	}
+/**
+ * Creates a draggable element.  For a reference on the options for this function,
+ * check out http://github.com/madrobby/scriptaculous/wikis/draggable
+ *
+ * @param unknown_type $id
+ * @param array $options
+ * @return unknown
+ */
+	function drag($id, $options = array()) {
+		$var = '';
+		if (isset($options['var'])) {
+			$var = 'var ' . $options['var'] . ' = ';
+			unset($options['var']);
+		}
+		$options = $this->_buildOptions(
+			$this->_optionsToString($options, array('handle', 'constraint')), $this->dragOptions
+		);
+		return $this->Javascript->codeBlock("{$var}new Draggable('$id', " .$options . ");");
+	}
+/**
+ * For a reference on the options for this function, check out
+ * http://github.com/madrobby/scriptaculous/wikis/droppables
+ *
+ * @param unknown_type $id
+ * @param array $options
+ * @return string
+ */
+	function drop($id, $options = array()) {
+		$optionsString = array('overlap', 'hoverclass');
+		if (!isset($options['accept']) || !is_array($options['accept'])) {
+			$optionsString[] = 'accept';
+		} else if (isset($options['accept'])) {
+			$options['accept'] = $this->Javascript->object($options['accept']);
+		}
+		$options = $this->_buildOptions(
+			$this->_optionsToString($options, $optionsString), $this->dropOptions
+		);
+		return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
+	}
+/**
+ * Make an element with the given $id droppable, and trigger an Ajax call when a draggable is
+ * dropped on it.
+ *
+ * For a reference on the options for this function, check out
+ * http://wiki.script.aculo.us/scriptaculous/show/Droppables.add
+ *
+ * @param string $id
+ * @param array $options
+ * @param array $ajaxOptions
+ * @return string JavaScript block to create a droppable element
+ */
+	function dropRemote($id, $options = array(), $ajaxOptions = array()) {
+		$callback = $this->remoteFunction($ajaxOptions);
+		$options['onDrop'] = "function(element, droppable, event) {{$callback}}";
+		$optionsString = array('overlap', 'hoverclass');
+
+		if (!isset($options['accept']) || !is_array($options['accept'])) {
+			$optionsString[] = 'accept';
+		} else if (isset($options['accept'])) {
+			$options['accept'] = $this->Javascript->object($options['accept']);
+		}
+
+		$options = $this->_buildOptions(
+			$this->_optionsToString($options, $optionsString),
+			$this->dropOptions
+		);
+		return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
+	}
+/**
+ * Makes a slider control.
+ *
+ * @param string $id DOM ID of slider handle
+ * @param string $trackId DOM ID of slider track
+ * @param array $options Array of options to control the slider
+ * @link          http://github.com/madrobby/scriptaculous/wikis/slider
+ */
+	function slider($id, $trackId, $options = array()) {
+		if (isset($options['var'])) {
+			$var = 'var ' . $options['var'] . ' = ';
+			unset($options['var']);
+		} else {
+			$var = 'var ' . $id . ' = ';
+		}
+
+		$options = $this->_optionsToString($options, array(
+			'axis', 'handleImage', 'handleDisabled'
+		));
+		$callbacks = array('change', 'slide');
+
+		foreach ($callbacks as $callback) {
+			if (isset($options[$callback])) {
+				$call = $options[$callback];
+				$options['on' . ucfirst($callback)] = "function(value) {{$call}}";
+				unset($options[$callback]);
+			}
+		}
+
+		if (isset($options['values']) && is_array($options['values'])) {
+			$options['values'] = $this->Javascript->object($options['values']);
+		}
+
+		$options = $this->_buildOptions($options, $this->sliderOptions);
+		$script = "{$var}new Control.Slider('$id', '$trackId', $options);";
+		return $this->Javascript->codeBlock($script);
+	}
+/**
+ * Makes an Ajax In Place editor control.
+ *
+ * @param string $id DOM ID of input element
+ * @param string $url Postback URL of saved data
+ * @param array $options Array of options to control the editor, including ajaxOptions (see link).
+ * @link          http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor
+ */
+	function editor($id, $url, $options = array()) {
+		$url = $this->url($url);
+		$options['ajaxOptions'] = $this->__optionsForAjax($options);
+
+		foreach ($this->ajaxOptions as $opt) {
+			if (isset($options[$opt])) {
+				unset($options[$opt]);
+			}
+		}
+
+		if (isset($options['callback'])) {
+			$options['callback'] = 'function(form, value) {' . $options['callback'] . '}';
+		}
+
+		$type = 'InPlaceEditor';
+		if (isset($options['collection']) && is_array($options['collection'])) {
+			$options['collection'] = $this->Javascript->object($options['collection']);
+			$type = 'InPlaceCollectionEditor';
+		}
+
+		$var = '';
+		if (isset($options['var'])) {
+			$var = 'var ' . $options['var'] . ' = ';
+			unset($options['var']);
+		}
+
+		$options = $this->_optionsToString($options, array(
+			'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'highlightcolor',
+			'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL', 'loadingText',
+			'clickToEditText', 'okControl', 'cancelControl'
+		));
+		$options = $this->_buildOptions($options, $this->editorOptions);
+		$script = "{$var}new Ajax.{$type}('{$id}', '{$url}', {$options});";
+		return $this->Javascript->codeBlock($script);
+	}
+/**
+ * Makes a list or group of floated objects sortable.
+ *
+ * @param string $id DOM ID of parent
+ * @param array $options Array of options to control sort.
+ * @link          http://github.com/madrobby/scriptaculous/wikis/sortable
+ */
+	function sortable($id, $options = array()) {
+		if (!empty($options['url'])) {
+			if (empty($options['with'])) {
+				$options['with'] = "Sortable.serialize('$id')";
+			}
+			$options['onUpdate'] = 'function(sortable) {' . $this->remoteFunction($options) . '}';
+		}
+		$block = true;
+
+		if (isset($options['block'])) {
+			$block = $options['block'];
+			unset($options['block']);
+		}
+		$strings = array(
+			'tag', 'constraint', 'only', 'handle', 'hoverclass', 'tree',
+			'treeTag', 'update', 'overlap'
+		);
+		$scrollIsObject = (
+			isset($options['scroll']) &&
+			$options['scroll'] != 'window' &&
+			strpos($options['scroll'], '$(') !== 0
+		);
+
+		if ($scrollIsObject) {
+			$strings[] = 'scroll';
+		}
+
+		$options = $this->_optionsToString($options, $strings);
+		$options = array_merge($options, $this->_buildCallbacks($options));
+		$options = $this->_buildOptions($options, $this->sortOptions);
+		$result = "Sortable.create('$id', $options);";
+
+		if (!$block) {
+			return $result;
+		}
+		return $this->Javascript->codeBlock($result);
+	}
+/**
+ * Private helper function for Javascript.
+ *
+ * @param array $options Set of options
+ * @access private
+ */
+	function __optionsForAjax($options) {
+		if (isset($options['indicator'])) {
+			if (isset($options['loading'])) {
+				$loading = $options['loading'];
+
+				if (!empty($loading) && substr(trim($loading), -1, 1) != ';') {
+					$options['loading'] .= '; ';
+				}
+				$options['loading'] .= "Element.show('{$options['indicator']}');";
+			} else {
+				$options['loading'] = "Element.show('{$options['indicator']}');";
+			}
+			if (isset($options['complete'])) {
+				$complete = $options['complete'];
+
+				if (!empty($complete) && substr(trim($complete), -1, 1) != ';') {
+					$options['complete'] .= '; ';
+				}
+				$options['complete'] .= "Element.hide('{$options['indicator']}');";
+			} else {
+				$options['complete'] = "Element.hide('{$options['indicator']}');";
+			}
+			unset($options['indicator']);
+		}
+
+		$jsOptions = array_merge(
+			array('asynchronous' => 'true', 'evalScripts'  => 'true'),
+			$this->_buildCallbacks($options)
+		);
+
+		$options = $this->_optionsToString($options, array(
+			'contentType', 'encoding', 'fallback', 'method', 'postBody', 'update', 'url'
+		));
+		$jsOptions = array_merge($jsOptions, array_intersect_key($options, array_flip(array(
+			'contentType', 'encoding', 'method', 'postBody'
+		))));
+
+		foreach ($options as $key => $value) {
+			switch ($key) {
+				case 'type':
+					$jsOptions['asynchronous'] = ($value == 'synchronous') ? 'false' : 'true';
+				break;
+				case 'evalScripts':
+					$jsOptions['evalScripts'] = ($value) ? 'true' : 'false';
+				break;
+				case 'position':
+					$pos = Inflector::camelize($options['position']);
+					$jsOptions['insertion'] = "Insertion.{$pos}";
+				break;
+				case 'with':
+					$jsOptions['parameters'] = $options['with'];
+				break;
+				case 'form':
+					$jsOptions['parameters'] = 'Form.serialize(this)';
+				break;
+				case 'requestHeaders':
+					$keys = array();
+					foreach ($value as $key => $val) {
+						$keys[] = "'" . $key . "'";
+						$keys[] = "'" . $val . "'";
+					}
+					$jsOptions['requestHeaders'] = '[' . join(', ', $keys) . ']';
+				break;
+			}
+		}
+		return $this->_buildOptions($jsOptions, $this->ajaxOptions);
+	}
+/**
+ * Private Method to return a string of html options
+ * option data as a JavaScript options hash.
+ *
+ * @param array $options	Options in the shape of keys and values
+ * @param array $extra	Array of legal keys in this options context
+ * @return array Array of html options
+ * @access private
+ */
+	function __getHtmlOptions($options, $extra = array()) {
+		foreach (array_merge($this->ajaxOptions, $this->callbacks, $extra) as $key) {
+			if (isset($options[$key])) {
+				unset($options[$key]);
+			}
+		}
+		return $options;
+	}
+/**
+ * Returns a string of JavaScript with the given option data as a JavaScript options hash.
+ *
+ * @param array $options	Options in the shape of keys and values
+ * @param array $acceptable	Array of legal keys in this options context
+ * @return string	String of Javascript array definition
+ */
+	function _buildOptions($options, $acceptable) {
+		if (is_array($options)) {
+			$out = array();
+
+			foreach ($options as $k => $v) {
+				if (in_array($k, $acceptable)) {
+					if ($v === true) {
+						$v = 'true';
+					} elseif ($v === false) {
+						$v = 'false';
+					}
+					$out[] = "$k:$v";
+				} elseif ($k === 'with' && in_array('parameters', $acceptable)) {
+					$out[] = "parameters:${v}";
+				}
+			}
+
+			$out = join(', ', $out);
+			$out = '{' . $out . '}';
+			return $out;
+		} else {
+			return false;
+		}
+	}
+/**
+ * Return JavaScript text for an observer...
+ *
+ * @param string $klass Name of JavaScript class
+ * @param string $name
+ * @param array $options	Ajax options
+ * @return string Formatted JavaScript
+ */
+	function _buildObserver($klass, $name, $options = null) {
+		if (!isset($options['with']) && isset($options['update'])) {
+			$options['with'] = 'value';
+		}
+
+		$callback = $this->remoteFunction($options);
+		$hasFrequency = !(!isset($options['frequency']) || intval($options['frequency']) == 0);
+    	$frequency = $hasFrequency ? $options['frequency'] . ', ' : '';
+
+		return "new $klass('$name', {$frequency}function(element, value) {{$callback}})";
+	}
+/**
+ * Return Javascript text for callbacks.
+ *
+ * @param array $options Option array where a callback is specified
+ * @return array Options with their callbacks properly set
+ * @access protected
+ */
+	function _buildCallbacks($options) {
+		$callbacks = array();
+
+		foreach ($this->callbacks as $callback) {
+			if (isset($options[$callback])) {
+				$name = 'on' . ucfirst($callback);
+				$code = $options[$callback];
+				switch ($name) {
+					case 'onComplete':
+						$callbacks[$name] = "function(request, json) {" . $code . "}";
+						break;
+					case 'onCreate':
+						$callbacks[$name] = "function(request, xhr) {" . $code . "}";
+						break;
+					case 'onException':
+						$callbacks[$name] = "function(request, exception) {" . $code . "}";
+						break;
+					default:
+						$callbacks[$name] = "function(request) {" . $code . "}";
+						break;
+				}
+				if (isset($options['bind'])) {
+					$bind = $options['bind'];
+					
+					$hasBinding = (
+						(is_array($bind) && in_array($callback, $bind)) ||
+						(is_string($bind) && strpos($bind, $callback) !== false)
+					);
+
+					if ($hasBinding) {
+						$callbacks[$name] .= ".bind(this)";
+					}
+				}
+			}
+		}
+		return $callbacks;
+	}
+/**
+ * Returns a string of JavaScript with a string representation of given options array.
+ *
+ * @param array $options	Ajax options array
+ * @param array $stringOpts	Options as strings in an array
+ * @access private
+ * @return array
+ */
+	function _optionsToString($options, $stringOpts = array()) {
+		foreach ($stringOpts as $option) {
+			$hasOption = (
+				isset($options[$option]) && !empty($options[$option]) &&
+				is_string($options[$option]) && $options[$option][0] != "'"
+			);
+
+			if ($hasOption) {
+				if ($options[$option] === true || $options[$option] === 'true') {
+					$options[$option] = 'true';
+				} elseif ($options[$option] === false || $options[$option] === 'false') {
+					$options[$option] = 'false';
+				} else {
+					$options[$option] = "'{$options[$option]}'";
+				}
+			}
+		}
+		return $options;
+	}
+/**
+ * Executed after a view has rendered, used to include bufferred code
+ * blocks.
+ *
+ * @access public
+ */
+	function afterRender() {
+		if (env('HTTP_X_UPDATE') != null && !empty($this->__ajaxBuffer)) {
+			@ob_end_clean();
+
+			$data = array();
+			$divs = explode(' ', env('HTTP_X_UPDATE'));
+			$keys = array_keys($this->__ajaxBuffer);
+
+			if (count($divs) == 1 && in_array($divs[0], $keys)) {
+				e($this->__ajaxBuffer[$divs[0]]);
+			} else {
+				foreach ($this->__ajaxBuffer as $key => $val) {
+					if (in_array($key, $divs)) {
+						$data[] = $key . ':"' . rawurlencode($val) . '"';
+					}
+				}
+				$out  = 'var __ajaxUpdater__ = {' . join(", \n", $data) . '};' . "\n";
+				$out .= 'for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string"';
+				$out .= ' && $(n)) Element.update($(n), unescape(decodeURIComponent(';
+				$out .= '__ajaxUpdater__[n]))); }';
+				e($this->Javascript->codeBlock($out, false));
+			}
+			$scripts = $this->Javascript->getCache();
+
+			if (!empty($scripts)) {
+				e($this->Javascript->codeBlock($scripts, false));
+			}
+
+			$this->_stop();
+		}
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/cache.php
===================================================================
--- cake/libs/view/helpers/cache.php	(revision 0)
+++ cake/libs/view/helpers/cache.php	(revision 0)
@@ -0,0 +1,293 @@
+<?php
+/* SVN FILE: $Id: cache.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.0.0.2277
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class CacheHelper extends AppHelper {
+/**
+ * Array of strings replaced in cached views.
+ * The strings are found between <cake:nocache><cake:nocache> in views
+ *
+ * @var array
+ * @access private
+ */
+	var $__replace = array();
+/**
+ * Array of string that are replace with there var replace above.
+ * The strings are any content inside <cake:nocache><cake:nocache> and includes the tags in views
+ *
+ * @var array
+ * @access private
+ */
+	var $__match = array();
+/**
+ * holds the View object passed in final call to CacheHelper::cache()
+ *
+ * @var object
+ * @access public
+ */
+	var $view;
+/**
+ * cache action time
+ *
+ * @var object
+ * @access public
+ */
+	var $cacheAction;
+/**
+ * Main method used to cache a view
+ *
+ * @param string $file File to cache
+ * @param string $out output to cache
+ * @param boolean $cache
+ * @return view ouput
+ */
+	function cache($file, $out, $cache = false) {
+		$cacheTime = 0;
+		$useCallbacks = false;
+		if (is_array($this->cacheAction)) {
+			$contoller = Inflector::underscore($this->controllerName);
+			$check = str_replace('/', '_', $this->here);
+			$replace = str_replace('/', '_', $this->base);
+			$match = str_replace($this->base, '', $this->here);
+			$match = str_replace('//', '/', $match);
+			$match = str_replace('/' . $contoller . '/', '', $match);
+			$match = str_replace('/' . $this->controllerName . '/', '', $match);
+			$check = str_replace($replace, '', $check);
+			$check = str_replace('_' . $contoller . '_', '', $check);
+			$check = str_replace('_' . $this->controllerName . '_', '', $check);
+			$check = Inflector::slug($check);
+			$check = preg_replace('/^_+/', '', $check);
+			$keys = str_replace('/', '_', array_keys($this->cacheAction));
+			$found = array_keys($this->cacheAction);
+			$index = null;
+			$count = 0;
+
+			foreach ($keys as $key => $value) {
+				if (strpos($check, $value) === 0) {
+					$index = $found[$count];
+					break;
+				}
+				$count++;
+			}
+
+			if (isset($index)) {
+				$pos1 = strrpos($match, '/');
+				$char = strlen($match) - 1;
+
+				if ($pos1 == $char) {
+					$match = substr($match, 0, $char);
+				}
+
+				$key = $match;
+			} elseif ($this->action == 'index') {
+				$index = 'index';
+			}
+
+			$options = $this->cacheAction;
+			if (isset($this->cacheAction[$index])) {
+				if (is_array($this->cacheAction[$index])) {
+					$options = array_merge(array('duration'=> 0, 'callbacks' => false), $this->cacheAction[$index]);
+				} else {
+					$cacheTime = $this->cacheAction[$index];
+				}
+			}
+
+			if (array_key_exists('duration', $options)) {
+				$cacheTime = $options['duration'];
+			}
+			if (array_key_exists('callbacks', $options)) {
+				$useCallbacks = $options['callbacks'];
+			}
+
+		} else {
+			$cacheTime = $this->cacheAction;
+		}
+
+		if ($cacheTime != '' && $cacheTime > 0) {
+			$this->__parseFile($file, $out);
+			if ($cache === true) {
+				$cached = $this->__parseOutput($out);
+				$this->__writeFile($cached, $cacheTime, $useCallbacks);
+			}
+			return $out;
+		} else {
+			return $out;
+		}
+	}
+/**
+ * Parse file searching for no cache tags
+ *
+ * @param string $file
+ * @param boolean $cache
+ * @access private
+ */
+	function __parseFile($file, $cache) {
+		if (is_file($file)) {
+			$file = file_get_contents($file);
+		} elseif ($file = fileExistsInPath($file)) {
+			$file = file_get_contents($file);
+		}
+
+		preg_match_all('/(<cake:nocache>(?<=<cake:nocache>)[\\s\\S]*?(?=<\/cake:nocache>)<\/cake:nocache>)/i', $cache, $oresult, PREG_PATTERN_ORDER);
+		preg_match_all('/(?<=<cake:nocache>)([\\s\\S]*?)(?=<\/cake:nocache>)/i', $file, $result, PREG_PATTERN_ORDER);
+
+		if (!empty($this->__replace)) {
+			foreach ($oresult['0'] as $k => $element) {
+				$index = array_search($element, $this->__match);
+				if ($index !== false) {
+					array_splice($oresult[0], $k, 1);
+				}
+			}
+		}
+
+		if (!empty($result['0'])) {
+			$count = 0;
+			foreach ($result['0'] as $block) {
+				if (isset($oresult['0'][$count])) {
+					$this->__replace[] = $block;
+					$this->__match[] = $oresult['0'][$count];
+				}
+				$count++;
+			}
+		}
+	}
+/**
+ * Parse the output and replace cache tags
+ *
+ * @param sting $cache
+ * @return string with all replacements made to <cake:nocache><cake:nocache>
+ * @access private
+ */
+	function __parseOutput($cache) {
+		$count = 0;
+		if (!empty($this->__match)) {
+
+			foreach ($this->__match as $found) {
+				$original = $cache;
+				$length = strlen($found);
+				$position = 0;
+
+					for ($i = 1; $i <= 1; $i++) {
+						$position = strpos($cache, $found, $position);
+
+						if ($position !== false) {
+							$cache = substr($original, 0, $position);
+							$cache .= $this->__replace[$count];
+							$cache .= substr($original, $position + $length);
+						} else {
+							break;
+						}
+					}
+					$count++;
+			}
+			return $cache;
+		}
+		return $cache;
+	}
+/**
+ * Write a cached version of the file
+ *
+ * @param string $file
+ * @param sting $timestamp
+ * @return cached view
+ * @access private
+ */
+	function __writeFile($content, $timestamp, $useCallbacks = false) {
+		$now = time();
+
+		if (is_numeric($timestamp)) {
+			$cacheTime = $now + $timestamp;
+		} else {
+			$cacheTime = strtotime($timestamp, $now);
+		}
+		$path = $this->here;
+		if ($this->here == '/') {
+			$path = 'home';
+		}
+		$cache = strtolower(Inflector::slug($path));
+
+		if (empty($cache)) {
+			return;
+		}
+		$cache = $cache . '.php';
+		$file = '<!--cachetime:' . $cacheTime . '--><?php';
+
+		if (empty($this->plugin)) {
+			$file .= '
+			App::import(\'Controller\', \'' . $this->controllerName. '\');
+			';
+		} else {
+			$file .= '
+			App::import(\'Controller\', \'' . $this->plugin . '.' . $this->controllerName. '\');
+			';
+		}
+
+		$file .= '$controller =& new ' . $this->controllerName . 'Controller();
+				$controller->plugin = $this->plugin = \''.$this->plugin.'\';
+				$controller->helpers = $this->helpers = unserialize(\'' . serialize($this->helpers) . '\');
+				$controller->base = $this->base = \'' . $this->base . '\';
+				$controller->layout = $this->layout = \'' . $this->layout. '\';
+				$controller->webroot = $this->webroot = \'' . $this->webroot . '\';
+				$controller->here = $this->here = \'' . $this->here . '\';
+				$controller->namedArgs  = $this->namedArgs  = \'' . $this->namedArgs . '\';
+				$controller->argSeparator = $this->argSeparator = \'' . $this->argSeparator . '\';
+				$controller->params = $this->params = unserialize(stripslashes(\'' . addslashes(serialize($this->params)) . '\'));
+				$controller->action = $this->action = unserialize(\'' . serialize($this->action) . '\');
+				$controller->data = $this->data = unserialize(stripslashes(\'' . addslashes(serialize($this->data)) . '\'));
+				$controller->themeWeb = $this->themeWeb = \'' . $this->themeWeb . '\';';
+
+		if ($useCallbacks == true) {
+			$file .= '
+				$controller->constructClasses();
+				$controller->Component->initialize($controller);
+				$controller->beforeFilter();
+				$controller->Component->startup($controller);';
+		}
+
+		$file .= '
+				Router::setRequestInfo(array($this->params, array(\'base\' => $this->base, \'webroot\' => $this->webroot)));
+				$loadedHelpers = array();
+				$loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
+				foreach (array_keys($loadedHelpers) as $helper) {
+					$camelBackedHelper = Inflector::variable($helper);
+					${$camelBackedHelper} =& $loadedHelpers[$helper];
+					$this->loaded[$camelBackedHelper] =& ${$camelBackedHelper};
+				}
+		?>';
+		$content = preg_replace("/(<\\?xml)/", "<?php echo '$1';?>",$content);
+		$file .= $content;
+		return cache('views' . DS . $cache, $file, $timestamp);
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/form.php
===================================================================
--- cake/libs/view/helpers/form.php	(revision 0)
+++ cake/libs/view/helpers/form.php	(revision 0)
@@ -0,0 +1,1854 @@
+<?php
+/* SVN FILE: $Id: form.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Automatic generation of HTML FORMs from given data.
+ *
+ * Used for scaffolding.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Form helper library.
+ *
+ * Automatic generation of HTML FORMs from given data.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class FormHelper extends AppHelper {
+/**
+ * Other helpers used by FormHelper
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html');
+/**
+ * Holds the fields array('field_name' => array('type'=> 'string', 'length'=> 100),
+ * primaryKey and validates array('field_name')
+ *
+ * @access public
+ */
+	var $fieldset = array('fields' => array(), 'key' => 'id', 'validates' => array());
+/**
+ * Options used by DateTime fields
+ *
+ * @var array
+ */
+	var $__options = array(
+		'day' => array(), 'minute' => array(), 'hour' => array(),
+		'month' => array(), 'year' => array(), 'meridian' => array()
+	);
+/**
+ * List of fields created, used with secure forms.
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array();
+/**
+ * Defines the type of form being created.  Set by FormHelper::create().
+ *
+ * @var string
+ * @access public
+ */
+	var $requestType = null;
+/**
+ * Returns an HTML FORM element.
+ *
+ * @access public
+ * @param string $model The model object which the form is being defined for
+ * @param array	 $options
+ *			'type' Form method defaults to POST
+ * 			'action'  The Action the form submits to. Can be a string or array,
+ *			'url'  The url the form submits to. Can be a string or a url array,
+ *			'default'  Allows for the creation of Ajax forms.
+ *			'onsubmit' Used in conjunction with 'default' to create ajax forms.
+ *
+ * @return string An formatted opening FORM tag.
+ */
+	function create($model = null, $options = array()) {
+		$defaultModel = null;
+		$view =& ClassRegistry::getObject('view');
+
+		if (is_array($model) && empty($options)) {
+			$options = $model;
+			$model = null;
+		}
+
+		if (empty($model) && $model !== false && !empty($this->params['models'])) {
+			$model = $this->params['models'][0];
+			$defaultModel = $this->params['models'][0];
+		} elseif (empty($model) && empty($this->params['models'])) {
+			$model = false;
+		} elseif (is_string($model) && strpos($model, '.') !== false) {
+			$path = explode('.', $model);
+			$model = $path[count($path) - 1];
+		}
+
+		if (ClassRegistry::isKeySet($model)) {
+			$object =& ClassRegistry::getObject($model);
+		}
+
+		$models = ClassRegistry::keys();
+		foreach ($models as $currentModel) {
+			if (ClassRegistry::isKeySet($currentModel)) {
+				$currentObject =& ClassRegistry::getObject($currentModel);
+				if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
+					$this->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors;
+				}
+			}
+		}
+
+		$this->setEntity($model . '.', true);
+		$append = '';
+		$created = $id = false;
+
+		if (isset($object)) {
+			$fields = $object->schema();
+			foreach ($fields as $key => $value) {
+				unset($fields[$key]);
+				$fields[$model . '.' . $key] = $value;
+			}
+
+			if (!empty($object->hasAndBelongsToMany)) {
+				foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
+					$fields[$alias] = array('type' => 'multiple');
+				}
+			}
+			$validates = array();
+			if (!empty($object->validate)) {
+				foreach ($object->validate as $validateField => $validateProperties) {
+					if (is_array($validateProperties)) {
+						$dims = Set::countDim($validateProperties);
+						if (($dims == 1 && !isset($validateProperties['required']) || (array_key_exists('required', $validateProperties) && $validateProperties['required'] !== false))) {
+							$validates[] = $validateField;
+						} elseif ($dims > 1) {
+							foreach ($validateProperties as $rule => $validateProp) {
+								if (is_array($validateProp) && (array_key_exists('required', $validateProp) && $validateProp['required'] !== false)) {
+									$validates[] = $validateField;
+								}
+							}
+						}
+					}
+				}
+			}
+			$key = $object->primaryKey;
+			$this->fieldset = compact('fields', 'key', 'validates');
+		}
+
+		$data = $this->fieldset;
+		$recordExists = (
+			isset($this->data[$model]) &&
+			isset($this->data[$model][$data['key']]) &&
+			!empty($this->data[$model][$data['key']])
+		);
+
+		if ($recordExists) {
+			$created = true;
+			$id = $this->data[$model][$data['key']];
+		}
+		$options = array_merge(array(
+			'type' => ($created && empty($options['action'])) ? 'put' : 'post',
+			'action' => null,
+			'url' => null,
+			'default' => true),
+		$options);
+
+		if (empty($options['url']) || is_array($options['url'])) {
+			if (empty($options['url']['controller'])) {
+				if (!empty($model) && $model != $defaultModel) {
+					$options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
+				} elseif (!empty($this->params['controller'])) {
+					$options['url']['controller'] = Inflector::underscore($this->params['controller']);
+				}
+			}
+			if (empty($options['action'])) {
+				$options['action'] = ($created) ? 'edit' : 'add';
+			}
+
+			$actionDefaults = array(
+				'plugin' => $this->plugin,
+				'controller' => $view->viewPath,
+				'action' => $options['action'],
+				'id' => $id
+			);
+			if (!empty($options['action']) && !isset($options['id'])) {
+				$options['id'] = $model . Inflector::camelize($options['action']) . 'Form';
+			}
+			$options['action'] = array_merge($actionDefaults, (array)$options['url']);
+		} elseif (is_string($options['url'])) {
+			$options['action'] = $options['url'];
+		}
+		unset($options['url']);
+
+		switch (strtolower($options['type'])) {
+			case 'get':
+				$htmlAttributes['method'] = 'get';
+			break;
+			case 'file':
+				$htmlAttributes['enctype'] = 'multipart/form-data';
+				$options['type'] = ($created) ? 'put' : 'post';
+			case 'post':
+			case 'put':
+			case 'delete':
+				$append .= $this->hidden('_method', array(
+					'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null
+				));
+			default:
+				$htmlAttributes['method'] = 'post';
+			break;
+		}
+		$this->requestType = strtolower($options['type']);
+
+		$htmlAttributes['action'] = $this->url($options['action']);
+		unset($options['type'], $options['action']);
+
+		if ($options['default'] == false) {
+			if (isset($htmlAttributes['onSubmit']) || isset($htmlAttributes['onsubmit'])) {
+				$htmlAttributes['onsubmit'] .= ' event.returnValue = false; return false;';
+			} else {
+				$htmlAttributes['onsubmit'] = 'event.returnValue = false; return false;';
+			}
+		}
+		unset($options['default']);
+		$htmlAttributes = array_merge($options, $htmlAttributes);
+
+		if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
+			$append .= $this->hidden('_Token.key', array(
+				'value' => $this->params['_Token']['key'], 'id' => 'Token' . mt_rand())
+			);
+		}
+
+		if (!empty($append)) {
+			$append = sprintf($this->Html->tags['fieldset'], ' style="display:none;"', $append);
+		}
+
+		$this->setEntity($model . '.', true);
+		$attributes = $this->_parseAttributes($htmlAttributes, null, '');
+		return $this->output(sprintf($this->Html->tags['form'], $attributes)) . $append;
+	}
+/**
+ * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
+ * input fields where appropriate.
+ *
+ * If $options is set a form submit button will be created.
+ *
+ * @param mixed $options as a string will use $options as the value of button,
+ * 	array usage:
+ * 		array('label' => 'save'); value="save"
+ * 		array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever"
+ * 		array('name' => 'Whatever'); value="Submit" name="Whatever"
+ * 		array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever"
+ * 		array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever"
+ *
+ * @return string a closing FORM tag optional submit button.
+ * @access public
+ */
+	function end($options = null) {
+		if (!empty($this->params['models'])) {
+			$models = $this->params['models'][0];
+		}
+		$out = null;
+		$submit = null;
+
+		if ($options !== null) {
+			$submitOptions = array();
+			if (is_string($options)) {
+				$submit = $options;
+			} else {
+				if (isset($options['label'])) {
+					$submit = $options['label'];
+					unset($options['label']);
+				}
+				$submitOptions = $options;
+
+				if (!$submit) {
+					$submit = __('Submit', true);
+				}
+			}
+			$out .= $this->submit($submit, $submitOptions);
+		}
+		if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
+			$out .= $this->secure($this->fields);
+			$this->fields = array();
+		}
+		$this->setEntity(null);
+		$out .= $this->Html->tags['formend'];
+
+		$view =& ClassRegistry::getObject('view');
+		$view->modelScope = false;
+		return $this->output($out);
+	}
+/**
+ * Generates a hidden field with a security hash based on the fields used in the form.
+ *
+ * @param array $fields The list of fields to use when generating the hash
+ * @return string A hidden input field with a security hash
+ * @access public
+ */
+	function secure($fields = array()) {
+		if (!isset($this->params['_Token']) || empty($this->params['_Token'])) {
+			return;
+		}
+		$out = '<fieldset style="display:none;">';
+		$locked = array();
+
+		foreach ($fields as $key => $value) {
+			if (!is_int($key)) {
+				$locked[$key] = $value;
+				unset($fields[$key]);
+			}
+		}
+		sort($fields, SORT_STRING);
+		ksort($locked, SORT_STRING);
+		$fields += $locked;
+
+		$fields = Security::hash(serialize($fields) . Configure::read('Security.salt'));
+		$locked = str_rot13(serialize(array_keys($locked)));
+
+		$out .= $this->hidden('_Token.fields', array(
+			'value' => urlencode($fields . ':' . $locked),
+			'id' => 'TokenFields' . mt_rand()
+		));
+		return $out .= '</fieldset>';
+	}
+/**
+ * Determine which fields of a form should be used for hash
+ *
+ * @param mixed $field Reference to field to be secured
+ * @param mixed $value Field value, if value should not be tampered with
+ * @access private
+ */
+	function __secure($field = null, $value = null) {
+		if (!$field) {
+			$view =& ClassRegistry::getObject('view');
+			$field = $view->entity();
+		} elseif (is_string($field)) {
+			$field = Set::filter(explode('.', $field), true);
+		}
+
+		if (!empty($this->params['_Token']['disabledFields'])) {
+			foreach ((array)$this->params['_Token']['disabledFields'] as $disabled) {
+				$disabled = explode('.', $disabled);
+				if (array_values(array_intersect($field, $disabled)) === $disabled) {
+					return;
+				}
+			}
+		}
+		$field = join('.', $field);
+		if (!in_array($field, $this->fields)) {
+			if ($value !== null) {
+				return $this->fields[$field] = $value;
+			}
+			$this->fields[] = $field;
+		}
+	}
+/**
+ * Returns true if there is an error for the given field, otherwise false
+ *
+ * @param string $field This should be "Modelname.fieldname"
+ * @return boolean If there are errors this method returns true, else false.
+ * @access public
+ */
+	function isFieldError($field) {
+		$this->setEntity($field);
+		return (bool)$this->tagIsInvalid();
+	}
+/**
+ * Returns a formatted error message for given FORM field, NULL if no errors.
+ *
+ * @param string $field A field name, like "Modelname.fieldname"
+ * @param mixed $text		Error message or array of $options
+ * @param array $options	Rendering options for <div /> wrapper tag
+ *			'escape'  bool  Whether or not to html escape the contents of the error.
+ *			'wrap'  mixed  Whether or not the error message should be wrapped in a div. If a
+ * 					string, will be used as the HTML tag to use.
+ *			'class'  string  The classname for the error message
+ * @return string If there are errors this method returns an error message, otherwise null.
+ * @access public
+ */
+	function error($field, $text = null, $options = array()) {
+		$defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
+		$options = array_merge($defaults, $options);
+		$this->setEntity($field);
+
+		if ($error = $this->tagIsInvalid()) {
+			if (is_array($error)) {
+				list(,,$field) = explode('.', $field);
+				if (isset($error[$field])) {
+					$error = $error[$field];
+				} else {
+					return null;
+				}
+			}
+
+			if (is_array($text) && is_numeric($error) && $error > 0) {
+				$error--;
+			}
+			if (is_array($text) && isset($text[$error])) {
+				$text = $text[$error];
+			} elseif (is_array($text)) {
+				$options = array_merge($options, $text);
+				$text = null;
+			}
+
+			if ($text != null) {
+				$error = $text;
+			} elseif (is_numeric($error)) {
+				$error = sprintf(__('Error in field %s', true), Inflector::humanize($this->field()));
+			}
+			if ($options['escape']) {
+				$error = h($error);
+				unset($options['escape']);
+			}
+			if ($options['wrap']) {
+				$tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
+				unset($options['wrap']);
+				return $this->Html->tag($tag, $error, $options);
+			} else {
+				return $error;
+			}
+		} else {
+			return null;
+		}
+	}
+/**
+ * Returns a formatted LABEL element for HTML FORMs.
+ *
+ * @param string $fieldName This should be "Modelname.fieldname"
+ * @param string $text Text that will appear in the label field.
+ * @param array $attributes Array of HTML attributes.
+ * @return string The formatted LABEL element
+ */
+	function label($fieldName = null, $text = null, $attributes = array()) {
+		if (empty($fieldName)) {
+			$view = ClassRegistry::getObject('view');
+			$fieldName = implode('.', $view->entity());
+		}
+
+		if ($text === null) {
+			if (strpos($fieldName, '.') !== false) {
+				$text = array_pop(explode('.', $fieldName));
+			} else {
+				$text = $fieldName;
+			}
+			if (substr($text, -3) == '_id') {
+				$text = substr($text, 0, strlen($text) - 3);
+			}
+			$text = __(Inflector::humanize(Inflector::underscore($text)), true);
+		}
+
+		if (isset($attributes['for'])) {
+			$labelFor = $attributes['for'];
+			unset($attributes['for']);
+		} else {
+			$labelFor = $this->domId($fieldName);
+		}
+
+		return $this->output(sprintf(
+			$this->Html->tags['label'],
+			$labelFor,
+			$this->_parseAttributes($attributes), $text
+		));
+	}
+/**
+ * Will display all the fields passed in an array expects fieldName as an array key
+ * replaces generateFields
+ *
+ * @access public
+ * @param array $fields works well with Controller::generateFields() or on its own;
+ * @param array $blacklist a simple array of fields to skip
+ * @return output
+ */
+	function inputs($fields = null, $blacklist = null) {
+		$fieldset = $legend = true;
+
+		if (is_array($fields)) {
+			if (array_key_exists('legend', $fields)) {
+				$legend = $fields['legend'];
+				unset($fields['legend']);
+			}
+
+			if (isset($fields['fieldset'])) {
+				$fieldset = $fields['fieldset'];
+				unset($fields['fieldset']);
+			}
+		} elseif ($fields !== null) {
+			$fieldset = $legend = $fields;
+			if (!is_bool($fieldset)) {
+				$fieldset = true;
+			}
+			$fields = array();
+		}
+
+		if (empty($fields)) {
+			$fields = array_keys($this->fieldset['fields']);
+		}
+
+		if ($legend === true) {
+			$actionName = __('New', true);
+			$isEdit = (
+				strpos($this->action, 'update') !== false ||
+				strpos($this->action, 'edit') !== false
+			);
+			if ($isEdit) {
+				$actionName = __('Edit', true);
+			}
+			$modelName = Inflector::humanize(Inflector::underscore($this->model()));
+			$legend = $actionName .' '. __($modelName, true);
+		}
+
+		$out = null;
+		foreach ($fields as $name => $options) {
+			if (is_numeric($name) && !is_array($options)) {
+				$name = $options;
+				$options = array();
+			}
+			$entity = explode('.', $name);
+			$blacklisted = (
+				is_array($blacklist) &&
+				(in_array($name, $blacklist) || in_array(end($entity), $blacklist))
+			);
+			if ($blacklisted) {
+				continue;
+			}
+			$out .= $this->input($name, $options);
+		}
+
+		if (is_string($fieldset)) {
+			$fieldsetClass = sprintf(' class="%s"', $fieldset);
+		} else {
+			$fieldsetClass = '';
+		}
+
+		if ($fieldset && $legend) {
+			return sprintf(
+				$this->Html->tags['fieldset'],
+				$fieldsetClass,
+				sprintf($this->Html->tags['legend'], $legend) . $out
+			);
+		} elseif ($fieldset) {
+			return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out);
+		} else {
+			return $out;
+		}
+	}
+/**
+ * Generates a form input element complete with label and wrapper div
+ *
+ * @param string $fieldName This should be "Modelname.fieldname"
+ * @param array $options - Each type of input takes different options.
+ * 		See each field type method for more information.
+ *		'type' - Force the type of widget you want. e.g. type => 'select'
+ *		'label' - control the label
+ *		'div' - control the wrapping div element
+ *		'options' - for widgets that take options e.g. radio, select
+ * 		'error' - control the error message that is produced
+ *
+ * @return string
+ */
+	function input($fieldName, $options = array()) {
+		$view =& ClassRegistry::getObject('view');
+		$this->setEntity($fieldName);
+		$entity = join('.', $view->entity());
+
+		$defaults = array('before' => null, 'between' => null, 'after' => null);
+		$options = array_merge($defaults, $options);
+
+		if (!isset($options['type'])) {
+			$options['type'] = 'text';
+
+			if (isset($options['options'])) {
+				$options['type'] = 'select';
+			} elseif (in_array($this->field(), array('psword', 'passwd', 'password'))) {
+				$options['type'] = 'password';
+			} elseif (isset($this->fieldset['fields'][$entity])) {
+				$fieldDef = $this->fieldset['fields'][$entity];
+				$type = $fieldDef['type'];
+				$primaryKey = $this->fieldset['key'];
+			} elseif (ClassRegistry::isKeySet($this->model())) {
+				$model =& ClassRegistry::getObject($this->model());
+				$type = $model->getColumnType($this->field());
+				$fieldDef = $model->schema();
+
+				if (isset($fieldDef[$this->field()])) {
+					$fieldDef = $fieldDef[$this->field()];
+				} else {
+					$fieldDef = array();
+				}
+				$primaryKey = $model->primaryKey;
+			}
+
+			if (isset($type)) {
+				$map = array(
+					'string'  => 'text',     'datetime'  => 'datetime',
+					'boolean' => 'checkbox', 'timestamp' => 'datetime',
+					'text'    => 'textarea', 'time'      => 'time',
+					'date'    => 'date',     'float'     => 'text'
+				);
+
+				if (isset($this->map[$type])) {
+					$options['type'] = $this->map[$type];
+				} elseif (isset($map[$type])) {
+					$options['type'] = $map[$type];
+				}
+				if ($this->field() == $primaryKey) {
+					$options['type'] = 'hidden';
+				}
+			}
+
+			if ($this->model() === $this->field()) {
+				$options['type'] = 'select';
+				if (!isset($options['multiple'])) {
+					$options['multiple'] = 'multiple';
+				}
+			}
+		}
+		$types = array('text', 'checkbox', 'radio', 'select');
+
+		if (!isset($options['options']) && in_array($options['type'], $types)) {
+			$view =& ClassRegistry::getObject('view');
+			$varName = Inflector::variable(
+				Inflector::pluralize(preg_replace('/_id$/', '', $this->field()))
+			);
+			$varOptions = $view->getVar($varName);
+			if (is_array($varOptions)) {
+				if ($options['type'] !== 'radio') {
+					$options['type'] = 'select';
+				}
+				$options['options'] = $varOptions;
+			}
+		}
+
+		$autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
+		if ($autoLength && $options['type'] == 'text') {
+			$options['maxlength'] = $fieldDef['length'];
+		}
+		if ($autoLength && $fieldDef['type'] == 'float') {
+			$options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1;
+		}
+
+		$out = '';
+		$div = true;
+		$divOptions = array();
+
+		if (array_key_exists('div', $options)) {
+			$div = $options['div'];
+			unset($options['div']);
+		}
+
+		if (!empty($div)) {
+			$divOptions['class'] = 'input';
+			$divOptions = $this->addClass($divOptions, $options['type']);
+			if (is_string($div)) {
+				$divOptions['class'] = $div;
+			} elseif (is_array($div)) {
+				$divOptions = array_merge($divOptions, $div);
+			}
+			if (in_array($this->field(), $this->fieldset['validates'])) {
+				$divOptions = $this->addClass($divOptions, 'required');
+			}
+			if (!isset($divOptions['tag'])) {
+				$divOptions['tag'] = 'div';
+			}
+		}
+
+		$label = null;
+		if (isset($options['label']) && $options['type'] !== 'radio') {
+			$label = $options['label'];
+			unset($options['label']);
+		}
+
+		if ($options['type'] === 'radio') {
+			$label = false;
+			if (isset($options['options'])) {
+				if (is_array($options['options'])) {
+					$radioOptions = $options['options'];
+				} else {
+					$radioOptions = array($options['options']);
+				}
+				unset($options['options']);
+			}
+		}
+
+		if ($label !== false) {
+			$labelAttributes = $this->domId(array(), 'for');
+			if (in_array($options['type'], array('date', 'datetime'))) {
+				$labelAttributes['for'] .= 'Month';
+			} else if ($options['type'] === 'time') {
+				$labelAttributes['for'] .= 'Hour';
+			}
+
+			if (is_array($label)) {
+				$labelText = null;
+				if (isset($label['text'])) {
+					$labelText = $label['text'];
+					unset($label['text']);
+				}
+				$labelAttributes = array_merge($labelAttributes, $label);
+			} else {
+				$labelText = $label;
+			}
+
+			if (isset($options['id'])) {
+				$labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
+			}
+			$out = $this->label($fieldName, $labelText, $labelAttributes);
+		}
+
+		$error = null;
+		if (isset($options['error'])) {
+			$error = $options['error'];
+			unset($options['error']);
+		}
+
+		$selected = null;
+		if (array_key_exists('selected', $options)) {
+			$selected = $options['selected'];
+			unset($options['selected']);
+		}
+		if (isset($options['rows']) || isset($options['cols'])) {
+			$options['type'] = 'textarea';
+		}
+
+		$empty = false;
+		if (isset($options['empty'])) {
+			$empty = $options['empty'];
+			unset($options['empty']);
+		}
+
+		$timeFormat = 12;
+		if (isset($options['timeFormat'])) {
+			$timeFormat = $options['timeFormat'];
+			unset($options['timeFormat']);
+		}
+
+		$dateFormat = 'MDY';
+		if (isset($options['dateFormat'])) {
+			$dateFormat = $options['dateFormat'];
+			unset($options['dateFormat']);
+		}
+
+		$type	 = $options['type'];
+		$before	 = $options['before'];
+		$between = $options['between'];
+		$after	 = $options['after'];
+		unset($options['type'], $options['before'], $options['between'], $options['after']);
+
+		switch ($type) {
+			case 'hidden':
+				$out = $this->hidden($fieldName, $options);
+				unset($divOptions);
+			break;
+			case 'checkbox':
+				$out = $before . $this->checkbox($fieldName, $options) . $between . $out;
+			break;
+			case 'radio':
+				$out = $before . $out . $this->radio($fieldName, $radioOptions, $options) . $between;
+			break;
+			case 'text':
+			case 'password':
+				$out = $before . $out . $between . $this->{$type}($fieldName, $options);
+			break;
+			case 'file':
+				$out = $before . $out . $between . $this->file($fieldName, $options);
+			break;
+			case 'select':
+				$options = array_merge(array('options' => array()), $options);
+				$list = $options['options'];
+				unset($options['options']);
+				$out = $before . $out . $between . $this->select(
+					$fieldName, $list, $selected, $options, $empty
+				);
+			break;
+			case 'time':
+				$out = $before . $out . $between . $this->dateTime(
+					$fieldName, null, $timeFormat, $selected, $options, $empty
+				);
+			break;
+			case 'date':
+				$out = $before . $out . $between . $this->dateTime(
+					$fieldName, $dateFormat, null, $selected, $options, $empty
+				);
+			break;
+			case 'datetime':
+				$out = $before . $out . $between . $this->dateTime(
+					$fieldName, $dateFormat, $timeFormat, $selected, $options, $empty
+				);
+			break;
+			case 'textarea':
+			default:
+				$out = $before . $out . $between . $this->textarea($fieldName, array_merge(
+					array('cols' => '30', 'rows' => '6'), $options
+				));
+			break;
+		}
+
+		if ($type != 'hidden') {
+			$out .= $after;
+			if ($error !== false) {
+				$errMsg = $this->error($fieldName, $error);
+				if ($errMsg) {
+					$out .= $errMsg;
+					$divOptions = $this->addClass($divOptions, 'error');
+				}
+			}
+		}
+		if (isset($divOptions) && isset($divOptions['tag'])) {
+			$tag = $divOptions['tag'];
+			unset($divOptions['tag']);
+			$out = $this->Html->tag($tag, $out, $divOptions);
+		}
+		return $out;
+	}
+/**
+ * Creates a checkbox input widget.
+ *
+ * @param string $fieldName Name of a field, like this "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ *		'value' - the value of the checkbox
+ *		'checked' - boolean indicate that this checkbox is checked.
+ * @todo Right now, automatically setting the 'checked' value is dependent on whether or not the
+ * 		 checkbox is bound to a model.  This should probably be re-evaluated in future versions.
+ * @return string An HTML text input element
+ */
+	function checkbox($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, $options);
+		$value = current($this->value());
+
+		if (!isset($options['value']) || empty($options['value'])) {
+			$options['value'] = 1;
+		} elseif (!empty($value) && $value === $options['value']) {
+			$options['checked'] = 'checked';
+		}
+
+		$output = $this->hidden($fieldName, array(
+			'id' => $options['id'] . '_', 'name' => $options['name'],
+			'value' => '0', 'secure' => false
+		));
+
+		return $this->output($output . sprintf(
+			$this->Html->tags['checkbox'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name'), null, ' ')
+		));
+	}
+/**
+ * Creates a set of radio widgets.
+ *
+ * @param  string  	$fieldName 		Name of a field, like this "Modelname.fieldname"
+ * @param  array	$options		Radio button options array.
+ * @param  array	$attributes		Array of HTML attributes.
+ *		'separator' - define the string in between the radio buttons
+ *		'legend' - control whether or not the widget set has a fieldset & legend
+ *		'value' - indicate a value that is should be checked
+ * 		'label' - boolean to indicate whether or not labels for widgets show be displayed
+ *
+ * @return string
+ */
+	function radio($fieldName, $options = array(), $attributes = array()) {
+		$attributes = $this->_initInputField($fieldName, $attributes);
+		$legend = false;
+
+		if (isset($attributes['legend'])) {
+			$legend = $attributes['legend'];
+			unset($attributes['legend']);
+		} elseif (count($options) > 1) {
+			$legend = __(Inflector::humanize($this->field()), true);
+		}
+		$label = true;
+
+		if (isset($attributes['label'])) {
+			$label = $attributes['label'];
+			unset($attributes['label']);
+		}
+		$inbetween = null;
+
+		if (isset($attributes['separator'])) {
+			$inbetween = $attributes['separator'];
+			unset($attributes['separator']);
+		}
+
+		if (isset($attributes['value'])) {
+			$value = $attributes['value'];
+		} else {
+			$value =  $this->value($fieldName);
+		}
+		$out = array();
+
+		foreach ($options as $optValue => $optTitle) {
+			$optionsHere = array('value' => $optValue);
+
+			if (isset($value) && $optValue == $value) {
+				$optionsHere['checked'] = 'checked';
+			}
+			$parsedOptions = $this->_parseAttributes(
+				array_merge($attributes, $optionsHere),
+				array('name', 'type', 'id'), '', ' '
+			);
+			$tagName = Inflector::camelize(
+				$attributes['id'] . '_' . Inflector::underscore($optValue)
+			);
+
+			if ($label) {
+				$optTitle =  sprintf($this->Html->tags['label'], $tagName, null, $optTitle);
+			}
+			$out[] =  sprintf(
+				$this->Html->tags['radio'], $attributes['name'],
+				$tagName, $parsedOptions, $optTitle
+			);
+		}
+		$hidden = null;
+
+		if (!isset($value) || $value === '') {
+			$hidden = $this->hidden($fieldName, array(
+				'id' => $attributes['id'] . '_', 'value' => ''
+			));
+		}
+		$out = $hidden . join($inbetween, $out);
+
+		if ($legend) {
+			$out = sprintf(
+				$this->Html->tags['fieldset'], '',
+				sprintf($this->Html->tags['legend'], $legend) . $out
+			);
+		}
+		return $this->output($out);
+	}
+/**
+ * Creates a text input widget.
+ *
+ * @param string $fieldNamem Name of a field, in the form "Modelname.fieldname"
+ * @param array  $options Array of HTML attributes.
+ * @return string An HTML text input element
+ */
+	function text($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, array_merge(
+			array('type' => 'text'), $options
+		));
+		return $this->output(sprintf(
+			$this->Html->tags['input'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name'), null, ' ')
+		));
+	}
+/**
+ * Creates a password input widget.
+ *
+ * @param  string  $fieldName Name of a field, like in the form "Modelname.fieldname"
+ * @param  array	$options Array of HTML attributes.
+ * @return string
+ */
+	function password($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, $options);
+		return $this->output(sprintf(
+			$this->Html->tags['password'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name'), null, ' ')
+		));
+	}
+/**
+ * Creates a textarea widget.
+ *
+ * @param string $fieldNamem Name of a field, in the form "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ * @return string An HTML text input element
+ */
+	function textarea($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, $options);
+		$value = null;
+
+		if (array_key_exists('value', $options)) {
+			$value = $options['value'];
+			if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
+				$value = h($value);
+			}
+			unset($options['value']);
+		}
+		return $this->output(sprintf(
+			$this->Html->tags['textarea'],
+			$options['name'],
+			$this->_parseAttributes($options, array('type', 'name'), null, ' '),
+			$value
+		));
+	}
+/**
+ * Creates a hidden input field.
+ *
+ * @param  string  $fieldName Name of a field, in the form"Modelname.fieldname"
+ * @param  array	$options Array of HTML attributes.
+ * @return string
+ * @access public
+ */
+	function hidden($fieldName, $options = array()) {
+		$secure = true;
+
+		if (isset($options['secure'])) {
+			$secure = $options['secure'];
+			unset($options['secure']);
+		}
+		$options = $this->_initInputField($fieldName, array_merge(
+			$options, array('secure' => false)
+		));
+		$model = $this->model();
+
+		if ($fieldName !== '_method' && $model !== '_Token' && $secure) {
+			$this->__secure(null, '' . $options['value']);
+		}
+
+		return $this->output(sprintf(
+			$this->Html->tags['hidden'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name', 'class'), '', ' ')
+		));
+	}
+/**
+ * Creates file input widget.
+ *
+ * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ * @return string
+ * @access public
+ */
+	function file($fieldName, $options = array()) {
+		$options = array_merge($options, array('secure' => false));
+		$options = $this->_initInputField($fieldName, $options);
+		$view =& ClassRegistry::getObject('view');
+		$field = $view->entity();
+
+		foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) {
+			$this->__secure(array_merge($field, array($suffix)));
+		}
+
+		$attributes = $this->_parseAttributes($options, array('name'), '', ' ');
+		return $this->output(sprintf($this->Html->tags['file'], $options['name'], $attributes));
+	}
+/**
+ * Creates a button tag.
+ *
+ * @param  string  $title  The button's caption
+ * @param  array  $options Array of options.
+ * @return string A HTML button tag.
+ * @access public
+ */
+	function button($title, $options = array()) {
+		$options = array_merge(array('type' => 'button', 'value' => $title), $options);
+
+		if (isset($options['name']) && strpos($options['name'], '.') !== false) {
+			if ($this->value($options['name'])) {
+				$options['checked'] = 'checked';
+			}
+			$name = $options['name'];
+			unset($options['name']);
+			$options = $this->_initInputField($name, $options);
+		}
+		return $this->output(sprintf(
+			$this->Html->tags['button'],
+			$options['type'],
+			$this->_parseAttributes($options, array('type'), '', ' ')
+		));
+	}
+/**
+ * Creates a submit button element.
+ *
+ * @param  string  $caption	 The label appearing on the button OR if string contains :// or the
+ * 						extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension
+ * 						exists, AND the first character is /, image is relative to webroot,
+ *						OR if the first character is not /, image is relative to webroot/img.
+ * @param  array   $options
+ * @return string A HTML submit button
+ */
+	function submit($caption = null, $options = array()) {
+		if (!$caption) {
+			$caption = __('Submit', true);
+		}
+		$out = null;
+		$div = true;
+
+		if (isset($options['div'])) {
+			$div = $options['div'];
+			unset($options['div']);
+		}
+		$divOptions = array('tag' => 'div');
+
+		if ($div === true) {
+			$divOptions['class'] = 'submit';
+		} elseif ($div === false) {
+			unset($divOptions);
+		} elseif (is_string($div)) {
+			$divOptions['class'] = $div;
+		} elseif (is_array($div)) {
+			$divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
+		}
+
+		if (strpos($caption, '://') !== false) {
+			$out .= $this->output(sprintf(
+				$this->Html->tags['submitimage'],
+				$caption,
+				$this->_parseAttributes($options, null, '', ' ')
+			));
+		} elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption)) {
+			if ($caption{0} !== '/') {
+				$url = $this->webroot(IMAGES_URL . $caption);
+			} else {
+				$caption = trim($caption, '/');
+				$url = $this->webroot($caption);
+			}
+			$out .= $this->output(sprintf(
+				$this->Html->tags['submitimage'],
+				$url,
+				$this->_parseAttributes($options, null, '', ' ')
+			));
+		} else {
+			$options['value'] = $caption;
+			$out .= $this->output(sprintf(
+				$this->Html->tags['submit'],
+				$this->_parseAttributes($options, null, '', ' ')
+			));
+		}
+
+		if (isset($divOptions)) {
+			$tag = $divOptions['tag'];
+			unset($divOptions['tag']);
+			$out = $this->Html->tag($tag, $out, $divOptions);
+		}
+		return $out;
+	}
+/**
+ * Returns a formatted SELECT element.
+ *
+ * @param string $fieldName Name attribute of the SELECT
+ * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the
+ * 						 SELECT element
+ * @param mixed $selected The option selected by default.  If null, the default value
+ *						  from POST data will be used when available.
+ * @param array $attributes	 The HTML attributes of the select element.
+ *		'showParents' - If included in the array and set to true, an additional option element
+ *						will be added for the parent of each option group.
+ *		'multiple' - show a multiple select box.  If set to 'checkbox' multiple checkboxes will be
+ * 					 created instead.
+ * @param mixed $showEmpty If true, the empty select option is shown.  If a string,
+ *						   that string is displayed as the empty element.
+ * @return string Formatted SELECT element
+ */
+	function select($fieldName, $options = array(), $selected = null, $attributes = array(), $showEmpty = '') {
+		$select = array();
+		$showParents = false;
+		$escapeOptions = true;
+		$style = null;
+		$tag = null;
+
+		if (isset($attributes['escape'])) {
+			$escapeOptions = $attributes['escape'];
+			unset($attributes['escape']);
+		}
+		$attributes = $this->_initInputField($fieldName, array_merge(
+			(array)$attributes, array('secure' => false)
+		));
+
+		if (is_string($options) && isset($this->__options[$options])) {
+			$options = $this->__generateOptions($options);
+		} elseif (!is_array($options)) {
+			$options = array();
+		}
+		if (isset($attributes['type'])) {
+			unset($attributes['type']);
+		}
+		if (in_array('showParents', $attributes)) {
+			$showParents = true;
+			unset($attributes['showParents']);
+		}
+
+		if (!isset($selected)) {
+			$selected = $attributes['value'];
+		}
+
+		if (isset($attributes) && array_key_exists('multiple', $attributes)) {
+			$style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
+			$template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
+			$tag = $this->Html->tags[$template];
+			$select[] = $this->hidden(null, array('value' => '', 'id' => null, 'secure' => false));
+		} else {
+			$tag = $this->Html->tags['selectstart'];
+		}
+
+		if (!empty($tag) || isset($template)) {
+			$this->__secure();
+			$select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes(
+				$attributes, array('name', 'value'))
+			);
+		}
+		$emptyMulti = (
+			$showEmpty !== null && $showEmpty !== false && !(
+				empty($showEmpty) && (isset($attributes) &&
+				array_key_exists('multiple', $attributes))
+			)
+		);
+
+		if ($emptyMulti) {
+			$showEmpty = ($showEmpty === true) ? '' : $showEmpty;
+			$options = array_reverse($options, true);
+			$options[''] = $showEmpty;
+			$options = array_reverse($options, true);
+		}
+
+		$select = array_merge($select, $this->__selectOptions(
+			array_reverse($options, true),
+			$selected,
+			array(),
+			$showParents,
+			array('escape' => $escapeOptions, 'style' => $style)
+		));
+
+		$template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend';
+		$select[] = $this->Html->tags[$template];
+		return $this->output(implode("\n", $select));
+	}
+/**
+ * Returns a SELECT element for days.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param array	 $attributes HTML attributes for the select element
+ * @param mixed $showEmpty Show/hide the empty select option
+ * @return string
+ */
+	function day($fieldName, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $day;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty) {
+						$selected = 'now';
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		}
+
+		if (strlen($selected) > 2) {
+			$selected = date('d', strtotime($selected));
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		return $this->select(
+			$fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a SELECT element for years
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param integer $minYear First year in sequence
+ * @param integer $maxYear Last year in sequence
+ * @param string $selected Option which is selected.
+ * @param array $attributes Attribute array for the select elements.
+ * @param boolean $showEmpty Show/hide the empty select option
+ * @return string
+ */
+	function year($fieldName, $minYear = null, $maxYear = null, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $year;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty && !$maxYear) {
+						$selected = 'now';
+
+					} elseif (!$showEmpty && $maxYear && !$selected) {
+						$selected = $maxYear;
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		}
+
+		if (strlen($selected) > 4 || $selected === 'now') {
+			$selected = date('Y', strtotime($selected));
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		$yearOptions = array('min' => $minYear, 'max' => $maxYear);
+		return $this->select(
+			$fieldName . ".year", $this->__generateOptions('year', $yearOptions),
+			$selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a SELECT element for months.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param array $attributes Attributes for the select element
+ *		'monthNames' is set and false 2 digit numbers will be used instead of text.
+ *
+ * @param boolean $showEmpty Show/hide the empty select option
+ * @return string
+ */
+	function month($fieldName, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $month;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty) {
+						$selected = 'now';
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		}
+
+		if (strlen($selected) > 2) {
+			$selected = date('m', strtotime($selected));
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		$defaults = array('monthNames' => true);
+		$attributes = array_merge($defaults, (array) $attributes);
+		$monthNames = $attributes['monthNames'];
+		unset($attributes['monthNames']);
+
+		return $this->select(
+			$fieldName . ".month",
+			$this->__generateOptions('month', array('monthNames' => $monthNames)),
+			$selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a SELECT element for hours.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param boolean $format24Hours True for 24 hours format
+ * @param string $selected Option which is selected.
+ * @param array $attributes List of HTML attributes
+ * @param mixed $showEmpty True to show an empty element, or a string to provide default empty element text
+ * @return string
+ */
+	function hour($fieldName, $format24Hours = false, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $hour;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty) {
+						$selected = 'now';
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		} else {
+			$value = $selected;
+		}
+
+		if (strlen($selected) > 2) {
+			if ($format24Hours) {
+				$selected = date('H', strtotime($value));
+			} else {
+				$selected = date('g', strtotime($value));
+			}
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		return $this->select(
+			$fieldName . ".hour",
+			$this->__generateOptions($format24Hours ? 'hour24' : 'hour'),
+			$selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a SELECT element for minutes.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param string $attributes Array of Attributes
+ * @param bool $showEmpty True to show an empty element, or a string to provide default empty element text
+ * @return string
+ */
+	function minute($fieldName, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $min;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty) {
+						$selected = 'now';
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		}
+
+		if (strlen($selected) > 2) {
+			$selected = date('i', strtotime($selected));
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		$minuteOptions = array();
+
+		if (isset($attributes['interval'])) {
+			$minuteOptions['interval'] = $attributes['interval'];
+			unset($attributes['interval']);
+		}
+		return $this->select(
+			$fieldName . ".min", $this->__generateOptions('minute', $minuteOptions),
+			$selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a SELECT element for AM or PM.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param string $attributes Array of Attributes
+ * @param bool $showEmpty Show/Hide an empty option
+ * @return string
+ */
+	function meridian($fieldName, $selected = null, $attributes = array(), $showEmpty = true) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $meridian;
+			} else {
+				if (empty($value)) {
+					if (!$showEmpty) {
+						$selected = date('a');
+					}
+				} else {
+					$selected = date('a', strtotime($value));
+				}
+			}
+		}
+
+		if ($selected === false) {
+			$selected = null;
+		}
+		return $this->select(
+			$fieldName . ".meridian", $this->__generateOptions('meridian'),
+			$selected, $attributes, $showEmpty
+		);
+	}
+/**
+ * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $dateFormat DMY, MDY, YMD or NONE.
+ * @param string $timeFormat 12, 24, NONE
+ * @param string $selected Option which is selected.
+ * @param string $attributes array of Attributes
+ *		'monthNames' If set and false numbers will be used for month select instead of text.
+ *		'minYear' The lowest year to use in the year select
+ *		'maxYear' The maximum year to use in the year select
+ *		'interval' The interval for the minutes select. Defaults to 1
+ *		'separator' The contents of the string between select elements. Defaults to '-'
+ * @param bool $showEmpty Whether or not to show an empty default value.
+ * @return string The HTML formatted OPTION element
+ */
+	function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $selected = null, $attributes = array(), $showEmpty = true) {
+		$year = $month = $day = $hour = $min = $meridian = null;
+
+		if (empty($selected)) {
+			$selected = $this->value($fieldName);
+		}
+
+		if ($selected === null && $showEmpty != true) {
+			$selected = time();
+		}
+
+		if (!empty($selected)) {
+			if (is_array($selected)) {
+				extract($selected);
+			} else {
+				if (is_numeric($selected)) {
+					$selected = strftime('%Y-%m-%d %H:%M:%S', $selected);
+				}
+				$meridian = 'am';
+				$pos = strpos($selected, '-');
+				if ($pos !== false) {
+					$date = explode('-', $selected);
+					$days = explode(' ', $date[2]);
+					$day = $days[0];
+					$month = $date[1];
+					$year = $date[0];
+				} else {
+					$days[1] = $selected;
+				}
+
+				if ($timeFormat != 'NONE' && !empty($timeFormat)) {
+					$time = explode(':', $days[1]);
+					$check = str_replace(':', '', $days[1]);
+
+					if (($check > 115959) && $timeFormat == '12') {
+						$time[0] = $time[0] - 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 = $time[0];
+					$min = $time[1];
+				}
+			}
+		}
+
+		$elements = array('Day','Month','Year','Hour','Minute','Meridian');
+		$defaults = array(
+			'minYear' => null, 'maxYear' => null, 'separator' => '-',
+			'interval' => 1, 'monthNames' => true
+		);
+		$attributes = array_merge($defaults, (array) $attributes);
+		if (isset($attributes['minuteInterval'])) {
+			$attributes['interval'] = $attributes['minuteInterval'];
+			unset($attributes['minuteInterval']);
+		}
+		$minYear = $attributes['minYear'];
+		$maxYear = $attributes['maxYear'];
+		$separator = $attributes['separator'];
+		$interval = $attributes['interval'];
+		$monthNames = $attributes['monthNames'];
+		$attributes = array_diff_key($attributes, $defaults);
+
+		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
+				foreach ($elements as $element) {
+					$selectAttrName = 'select' . $element . 'Attr';
+					${$selectAttrName} = $attributes;
+					${$selectAttrName}['id'] = $attributes['id'][strtolower($element)];
+				}
+			}
+		} else {
+			// build the selectAttrName with empty id's to pass
+			foreach ($elements as $element) {
+				$selectAttrName = 'select' . $element . 'Attr';
+				${$selectAttrName} = $attributes;
+			}
+		}
+
+		$opt = '';
+
+		if ($dateFormat != 'NONE') {
+			$selects = array();
+			foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
+				switch ($char) {
+					case 'Y':
+						$selects[] = $this->year(
+							$fieldName, $minYear, $maxYear, $year, $selectYearAttr, $showEmpty
+						);
+					break;
+					case 'M':
+						$selectMonthAttr['monthNames'] = $monthNames;
+						$selects[] = $this->month($fieldName, $month, $selectMonthAttr, $showEmpty);
+					break;
+					case 'D':
+						$selects[] = $this->day($fieldName, $day, $selectDayAttr, $showEmpty);
+					break;
+				}
+			}
+			$opt = implode($separator, $selects);
+		}
+
+		switch ($timeFormat) {
+			case '24':
+				$selectMinuteAttr['interval'] = $interval;
+				$opt .= $this->hour($fieldName, true, $hour, $selectHourAttr, $showEmpty) . ':' .
+				$this->minute($fieldName, $min, $selectMinuteAttr, $showEmpty);
+			break;
+			case '12':
+				$selectMinuteAttr['interval'] = $interval;
+				$opt .= $this->hour($fieldName, false, $hour, $selectHourAttr, $showEmpty) . ':' .
+				$this->minute($fieldName, $min, $selectMinuteAttr, $showEmpty) . ' ' .
+				$this->meridian($fieldName, $meridian, $selectMeridianAttr, $showEmpty);
+			break;
+			case 'NONE':
+			default:
+				$opt .= '';
+			break;
+		}
+		return $opt;
+	}
+/**
+ * Gets the input field name for the current tag
+ *
+ * @param array $options
+ * @param string $key
+ * @return array
+ */
+	function __name($options = array(), $field = null, $key = 'name') {
+		if ($this->requestType == 'get') {
+			if ($options === null) {
+				$options = array();
+			} elseif (is_string($options)) {
+				$field = $options;
+				$options = 0;
+			}
+
+			if (!empty($field)) {
+				$this->setEntity($field);
+			}
+
+			if (is_array($options) && isset($options[$key])) {
+				return $options;
+			}
+			$name = $this->field();
+
+			if (is_array($options)) {
+				$options[$key] = $name;
+				return $options;
+			} else {
+				return $name;
+			}
+		}
+		return parent::__name($options, $field, $key);
+	}
+/**
+ * Returns an array of formatted OPTION/OPTGROUP elements
+ * @access private
+ * @return array
+ */
+	function __selectOptions($elements = array(), $selected = null, $parents = array(), $showParents = null, $attributes = array()) {
+		$select = array();
+		$attributes = array_merge(array('escape' => true, 'style' => null), $attributes);
+		$selectedIsEmpty = ($selected === '' || $selected === null);
+		$selectedIsArray = is_array($selected);
+
+		foreach ($elements as $name => $title) {
+			$htmlOptions = array();
+			if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) {
+				if (!empty($name)) {
+					if ($attributes['style'] === 'checkbox') {
+						$select[] = $this->Html->tags['fieldsetend'];
+					} else {
+						$select[] = $this->Html->tags['optiongroupend'];
+					}
+					$parents[] = $name;
+				}
+				$select = array_merge($select, $this->__selectOptions(
+					$title, $selected, $parents, $showParents, $attributes
+				));
+
+				if (!empty($name)) {
+					if ($attributes['style'] === 'checkbox') {
+						$select[] = sprintf($this->Html->tags['fieldsetstart'], $name);
+					} else {
+						$select[] = sprintf($this->Html->tags['optiongroup'], $name, '');
+					}
+				}
+				$name = null;
+			} elseif (is_array($title)) {
+				$htmlOptions = $title;
+				$name = $title['value'];
+				$title = $title['name'];
+				unset($htmlOptions['name'], $htmlOptions['value']);
+			}
+
+			if ($name !== null) {
+				if ((!$selectedIsEmpty && $selected == $name) || ($selectedIsArray && in_array($name, $selected))) {
+					if ($attributes['style'] === 'checkbox') {
+						$htmlOptions['checked'] = true;
+					} else {
+						$htmlOptions['selected'] = 'selected';
+					}
+				}
+
+				if ($showParents || (!in_array($title, $parents))) {
+					$title = ($attributes['escape']) ? h($title) : $title;
+
+					if ($attributes['style'] === 'checkbox') {
+						$htmlOptions['value'] = $name;
+
+						$tagName = Inflector::camelize(
+							$this->model() . '_' . $this->field().'_'.Inflector::underscore($name)
+						);
+						$htmlOptions['id'] = $tagName;
+						$label = array('for' => $tagName);
+
+						if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) {
+							$label['class'] = 'selected';
+						}
+
+						list($name) = array_values($this->__name());
+
+						if (empty($attributes['class'])) {
+							$attributes['class'] = 'checkbox';
+						}
+						$label = $this->label(null, $title, $label);
+						$item = sprintf(
+							$this->Html->tags['checkboxmultiple'], $name,
+							$this->_parseAttributes($htmlOptions)
+						);
+						$select[] = $this->Html->div($attributes['class'], $item . $label);
+					} else {
+						$select[] = sprintf(
+							$this->Html->tags['selectoption'],
+							$name, $this->_parseAttributes($htmlOptions), $title
+						);
+					}
+				}
+			}
+		}
+
+		return array_reverse($select, true);
+	}
+/**
+ * Generates option lists for common <select /> menus
+ * @access private
+ */
+	function __generateOptions($name, $options = array()) {
+		if (!empty($this->options[$name])) {
+			return $this->options[$name];
+		}
+		$data = array();
+
+		switch ($name) {
+			case 'minute':
+				if (isset($options['interval'])) {
+					$interval = $options['interval'];
+				} else {
+					$interval = 1;
+				}
+				$i = 0;
+				while ($i < 60) {
+					$data[$i] = sprintf('%02d', $i);
+					$i += $interval;
+				}
+			break;
+			case 'hour':
+				for ($i = 1; $i <= 12; $i++) {
+					$data[sprintf('%02d', $i)] = $i;
+				}
+			break;
+			case 'hour24':
+				for ($i = 0; $i <= 23; $i++) {
+					$data[sprintf('%02d', $i)] = $i;
+				}
+			break;
+			case 'meridian':
+				$data = array('am' => 'am', 'pm' => 'pm');
+			break;
+			case 'day':
+				$min = 1;
+				$max = 31;
+
+				if (isset($options['min'])) {
+					$min = $options['min'];
+				}
+				if (isset($options['max'])) {
+					$max = $options['max'];
+				}
+
+				for ($i = $min; $i <= $max; $i++) {
+					$data[sprintf('%02d', $i)] = $i;
+				}
+			break;
+			case 'month':
+				if ($options['monthNames']) {
+					$data['01'] = __('January', true);
+					$data['02'] = __('February', true);
+					$data['03'] = __('March', true);
+					$data['04'] = __('April', true);
+					$data['05'] = __('May', true);
+					$data['06'] = __('June', true);
+					$data['07'] = __('July', true);
+					$data['08'] = __('August', true);
+					$data['09'] = __('September', true);
+					$data['10'] = __('October', true);
+					$data['11'] = __('November', true);
+					$data['12'] = __('December', true);
+				} else {
+					for ($m = 1; $m <= 12; $m++) {
+						$data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999));
+					}
+				}
+			break;
+			case 'year':
+				$current = intval(date('Y'));
+
+				if (!isset($options['min'])) {
+					$min = $current - 20;
+				} else {
+					$min = $options['min'];
+				}
+
+				if (!isset($options['max'])) {
+					$max = $current + 20;
+				} else {
+					$max = $options['max'];
+				}
+				if ($min > $max) {
+					list($min, $max) = array($max, $min);
+				}
+				for ($i = $min; $i <= $max; $i++) {
+					$data[$i] = $i;
+				}
+				$data = array_reverse($data, true);
+			break;
+		}
+		$this->__options[$name] = $data;
+		return $this->__options[$name];
+	}
+/**
+ * Sets field defaults and adds field to form security input hash
+ *
+ * @param string $field
+ * @param array $options
+ * @return array
+ * @access protected
+ */
+	function _initInputField($field, $options = array()) {
+		if (isset($options['secure'])) {
+			$secure = $options['secure'];
+			unset($options['secure']);
+		} else {
+			$secure = (isset($this->params['_Token']) && !empty($this->params['_Token']));
+		}
+		$result = parent::_initInputField($field, $options);
+
+		if ($secure) {
+			$this->__secure();
+		}
+		return $result;
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/session.php
===================================================================
--- cake/libs/view/helpers/session.php	(revision 0)
+++ cake/libs/view/helpers/session.php	(revision 0)
@@ -0,0 +1,202 @@
+<?php
+/* SVN FILE: $Id: session.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.1.7.3328
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('cakesession')) {
+	uses('session');
+}
+
+/**
+ * Session Helper.
+ *
+ * Session reading from the view.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ *
+ */
+class SessionHelper extends CakeSession {
+/**
+ * List of helpers used by this helper
+ *
+ * @var array
+ */
+	var $helpers = null;
+/**
+ * Used to determine if methods implementation is used, or bypassed
+ *
+ * @var boolean
+ */
+	var $__active = true;
+/**
+ * Class constructor
+ *
+ * @param string $base
+ */
+	function __construct($base = null) {
+		if (Configure::read('Session.start') === true) {
+			parent::__construct($base, false);
+		} else {
+			$this->__active = false;
+		}
+	}
+/**
+ * Turn sessions on if 'Session.start' is set to false in core.php
+ *
+ * @param string $base
+ */
+	function activate($base = null) {
+		$this->__active = true;
+	}
+/**
+ * Used to read a session values set in a controller for a key or return values for all keys.
+ *
+ * In your view: $session->read('Controller.sessKey');
+ * Calling the method without a param will return all session vars
+ *
+ * @param string $name the name of the session key you want to read
+ *
+ * @return values from the session vars
+ * @access public
+ */
+	function read($name = null) {
+		if ($this->__active === true && $this->__start()) {
+			return parent::read($name);
+		}
+		return false;
+	}
+/**
+ * Used to check is a session key has been set
+ *
+ * In your view: $session->check('Controller.sessKey');
+ *
+ * @param string $name
+ * @return boolean
+ * @access public
+ */
+	function check($name) {
+		if ($this->__active === true && $this->__start()) {
+			return parent::check($name);
+		}
+		return false;
+	}
+/**
+ * Returns last error encountered in a session
+ *
+ * In your view: $session->error();
+ *
+ * @return string last error
+ * @access public
+ */
+	function error() {
+		if ($this->__active === true && $this->__start()) {
+			return parent::error();
+		}
+		return false;
+	}
+/**
+ * Used to render the message set in Controller::Session::setFlash()
+ *
+ * In your view: $session->flash('somekey');
+ * 					Will default to flash if no param is passed
+ *
+ * @param string $key The [Message.]key you are rendering in the view.
+ * @return string Will echo the value if $key is set, or false if not set.
+ * @access public
+ */
+	function flash($key = 'flash') {
+		if ($this->__active === true && $this->__start()) {
+			if (parent::check('Message.' . $key)) {
+				$flash = parent::read('Message.' . $key);
+
+				if ($flash['layout'] == 'default') {
+					if (!empty($flash['params']['class'])) {
+						$class = $flash['params']['class'];
+					} else {
+						$class = 'message';
+					}
+					$out = '<div id="' . $key . 'Message" class="' . $class . '">' . $flash['message'] . '</div>';
+				} elseif ($flash['layout'] == '' || $flash['layout'] == null) {
+					$out = $flash['message'];
+				} else {
+					$view =& ClassRegistry::getObject('view');
+					list($tmpLayout, $tmpVars, $tmpTitle) = array($view->layout, $view->viewVars, $view->pageTitle);
+					list($view->layout, $view->viewVars, $view->pageTitle) = array($flash['layout'], $flash['params'], '');
+					$out = $view->renderLayout($flash['message']);
+					list($view->layout, $view->viewVars, $view->pageTitle) = array($tmpLayout, $tmpVars, $tmpTitle);
+				}
+				echo($out);
+				parent::del('Message.' . $key);
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Used to check is a session is valid in a view
+ *
+ * @return boolean
+ * @access public
+ */
+	function valid() {
+		if ($this->__active === true && $this->__start()) {
+			return parent::valid();
+		}
+	}
+/**
+ * Override CakeSession::write().
+ * This method should not be used in a view
+ *
+ * @return boolean
+ * @access public
+ */
+	function write() {
+		trigger_error(__('You can not write to a Session from the view', true), E_USER_WARNING);
+	}
+/**
+ * Session id
+ *
+ * @return string Session id
+ * @access public
+ */
+	function id() {
+		return parent::id();
+	}
+/**
+ * Determine if Session has been started
+ * and attempt to start it if not
+ *
+ * @return boolean true if Session is already started, false if
+ * Session could not be started
+ * @access public
+ */
+	function __start() {
+		if (!parent::started()) {
+			parent::start();
+		}
+		return true;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/html.php
===================================================================
--- cake/libs/view/helpers/html.php	(revision 0)
+++ cake/libs/view/helpers/html.php	(revision 0)
@@ -0,0 +1,640 @@
+<?php
+/* SVN FILE: $Id: html.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Html Helper class file.
+ *
+ * Simplifies the construction of HTML elements.
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.9.1
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Html Helper class for easy use of HTML widgets.
+ *
+ * HtmlHelper encloses all methods needed while working with HTML pages.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class HtmlHelper extends AppHelper {
+/*************************************************************************
+ * Public variables
+ *************************************************************************/
+/**#@+
+ * @access public
+ */
+/**
+ * html tags used by this helper.
+ *
+ * @var array
+ */
+	var $tags = array(
+		'meta' => '<meta%s/>',
+		'metalink' => '<link href="%s"%s/>',
+		'link' => '<a href="%s"%s>%s</a>',
+		'mailto' => '<a href="mailto:%s" %s>%s</a>',
+		'form' => '<form %s>',
+		'formend' => '</form>',
+		'input' => '<input name="%s" %s/>',
+		'textarea' => '<textarea name="%s" %s>%s</textarea>',
+		'hidden' => '<input type="hidden" name="%s" %s/>',
+		'checkbox' => '<input type="checkbox" name="%s" %s/>',
+		'checkboxmultiple' => '<input type="checkbox" name="%s[]"%s />',
+		'radio' => '<input type="radio" name="%s" id="%s" %s />%s',
+		'selectstart' => '<select name="%s"%s>',
+		'selectmultiplestart' => '<select name="%s[]"%s>',
+		'selectempty' => '<option value=""%s>&nbsp;</option>',
+		'selectoption' => '<option value="%s"%s>%s</option>',
+		'selectend' => '</select>',
+		'optiongroup' => '<optgroup label="%s"%s>',
+		'optiongroupend' => '</optgroup>',
+		'checkboxmultiplestart' => '',
+		'checkboxmultipleend' => '',
+		'password' => '<input type="password" name="%s" %s/>',
+		'file' => '<input type="file" name="%s" %s/>',
+		'file_no_model' => '<input type="file" name="%s" %s/>',
+		'submit' => '<input type="submit" %s/>',
+		'submitimage' => '<input type="image" src="%s" %s/>',
+		'button' => '<input type="%s" %s/>',
+		'image' => '<img src="%s" %s/>',
+		'tableheader' => '<th%s>%s</th>',
+		'tableheaderrow' => '<tr%s>%s</tr>',
+		'tablecell' => '<td%s>%s</td>',
+		'tablerow' => '<tr%s>%s</tr>',
+		'block' => '<div%s>%s</div>',
+		'blockstart' => '<div%s>',
+		'blockend' => '</div>',
+		'tag' => '<%s%s>%s</%s>',
+		'tagstart' => '<%s%s>',
+		'tagend' => '</%s>',
+		'para' => '<p%s>%s</p>',
+		'parastart' => '<p%s>',
+		'label' => '<label for="%s"%s>%s</label>',
+		'fieldset' => '<fieldset%s>%s</fieldset>',
+		'fieldsetstart' => '<fieldset><legend>%s</legend>',
+		'fieldsetend' => '</fieldset>',
+		'legend' => '<legend>%s</legend>',
+		'css' => '<link rel="%s" type="text/css" href="%s" %s/>',
+		'style' => '<style type="text/css"%s>%s</style>',
+		'charset' => '<meta http-equiv="Content-Type" content="text/html; charset=%s" />',
+		'ul' => '<ul%s>%s</ul>',
+		'ol' => '<ol%s>%s</ol>',
+		'li' => '<li%s>%s</li>',
+		'error' => '<div%s>%s</div>'
+	);
+/**
+ * Base URL
+ *
+ * @var string
+ */
+	var $base = null;
+/**
+ * URL to current action.
+ *
+ * @var string
+ */
+	var $here = null;
+/**
+ * Parameter array.
+ *
+ * @var array
+ */
+	var $params = array();
+/**
+ * Current action.
+ *
+ * @var string
+ */
+	var $action = null;
+/**
+ * Enter description here...
+ *
+ * @var array
+ */
+	var $data = null;
+/**#@-*/
+/*************************************************************************
+ * Private variables
+ *************************************************************************/
+/**#@+
+ * @access private
+ */
+/**
+ * Breadcrumbs.
+ *
+ * @var	array
+ * @access private
+ */
+	var $_crumbs = array();
+/**
+ * Document type definitions
+ *
+ * @var	array
+ * @access private
+ */
+	var $__docTypes = array(
+		'html4-strict'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
+		'html4-trans'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
+		'html4-frame'  => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
+		'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
+		'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
+		'xhtml-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
+		'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
+	);
+/**
+ * Adds a link to the breadcrumbs array.
+ *
+ * @param string $name Text for link
+ * @param string $link URL for link (if empty it won't be a link)
+ * @param mixed $options Link attributes e.g. array('id'=>'selected')
+ */
+	function addCrumb($name, $link = null, $options = null) {
+		$this->_crumbs[] = array($name, $link, $options);
+	}
+/**
+ * Returns a doctype string.
+ *
+ * Possible doctypes:
+ *   + html4-strict:  HTML4 Strict.
+ *   + html4-trans:  HTML4 Transitional.
+ *   + html4-frame:  HTML4 Frameset.
+ *   + xhtml-strict: XHTML1 Strict.
+ *   + xhtml-trans: XHTML1 Transitional.
+ *   + xhtml-frame: XHTML1 Frameset.
+ *   + xhtml11: XHTML1.1.
+ *
+ * @param  string $type Doctype to use.
+ * @return string Doctype.
+ */
+	function docType($type = 'xhtml-strict') {
+		if (isset($this->__docTypes[$type])) {
+			return $this->output($this->__docTypes[$type]);
+		}
+		return null;
+	}
+/**
+ * Creates a link to an external resource and handles basic meta tags
+ *
+ * @param  string  $title The title of the external resource
+ * @param  mixed   $url   The address of the external resource or string for content attribute
+ * @param  array   $attributes Other attributes for the generated tag. If the type attribute is html, rss, atom, or icon, the mime-type is returned.
+ * @param  boolean $inline If set to false, the generated tag appears in the head tag of the layout.
+ * @return string
+ */
+	function meta($type, $url = null, $attributes = array(), $inline = true) {
+		if (!is_array($type)) {
+			$types = array(
+				'rss'	=> array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url),
+				'atom'	=> array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url),
+				'icon'	=> array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url),
+				'keywords' => array('name' => 'keywords', 'content' => $url),
+				'description' => array('name' => 'description', 'content' => $url),
+			);
+
+			if ($type === 'icon' && $url === null) {
+				$types['icon']['link'] = $this->webroot('favicon.ico');
+			}
+
+			if (isset($types[$type])) {
+				$type = $types[$type];
+			} elseif (!isset($attributes['type']) && $url !== null) {
+				if (is_array($url) && isset($url['ext'])) {
+					$type = $types[$url['ext']];
+				} else {
+					$type = $types['rss'];
+				}
+			} elseif (isset($attributes['type']) && isset($types[$attributes['type']])) {
+				$type = $types[$attributes['type']];
+				unset($attributes['type']);
+			} else {
+				$type = array();
+			}
+		} elseif ($url !== null) {
+			$inline = $url;
+		}
+		$attributes = array_merge($type, $attributes);
+		$out = null;
+
+		if (isset($attributes['link'])) {
+			if (isset($attributes['rel']) && $attributes['rel'] === 'icon') {
+				$out = sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' '));
+				$attributes['rel'] = 'shortcut icon';
+			} else {
+				$attributes['link'] = $this->url($attributes['link'], true);
+			}
+			$out .= sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' '));
+		} else {
+			$out = sprintf($this->tags['meta'], $this->_parseAttributes($attributes, array('type')));
+		}
+
+		if ($inline) {
+			return $this->output($out);
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+/**
+ * Returns a charset META-tag.
+ *
+ * @param  string  $charset The character set to be used in the meta tag. Example: "utf-8".
+ * @return string A meta tag containing the specified character set.
+ */
+	function charset($charset = null) {
+		if (empty($charset)) {
+			$charset = strtolower(Configure::read('App.encoding'));
+		}
+		return $this->output(sprintf($this->tags['charset'], (!empty($charset) ? $charset : 'utf-8')));
+	}
+/**
+ * Creates an HTML link.
+ *
+ * If $url starts with "http://" this is treated as an external link. Else,
+ * it is treated as a path to controller/action and parsed with the
+ * HtmlHelper::url() method.
+ *
+ * If the $url is empty, $title is used instead.
+ *
+ * @param  string  $title The content to be wrapped by <a> tags.
+ * @param  mixed   $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
+ * @param  array   $htmlAttributes Array of HTML attributes.
+ * @param  string  $confirmMessage JavaScript confirmation message.
+ * @param  boolean $escapeTitle	Whether or not $title should be HTML escaped.
+ * @return string	An <a /> element.
+ */
+	function link($title, $url = null, $htmlAttributes = array(), $confirmMessage = false, $escapeTitle = true) {
+		if ($url !== null) {
+			$url = $this->url($url);
+		} else {
+			$url = $this->url($title);
+			$title = $url;
+			$escapeTitle = false;
+		}
+
+		if (isset($htmlAttributes['escape'])) {
+			$escapeTitle = $htmlAttributes['escape'];
+			unset($htmlAttributes['escape']);
+		}
+
+		if ($escapeTitle === true) {
+			$title = h($title);
+		} elseif (is_string($escapeTitle)) {
+			$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
+		}
+
+		if (!empty($htmlAttributes['confirm'])) {
+			$confirmMessage = $htmlAttributes['confirm'];
+			unset($htmlAttributes['confirm']);
+		}
+		if ($confirmMessage) {
+			$confirmMessage = str_replace("'", "\'", $confirmMessage);
+			$confirmMessage = str_replace('"', '\"', $confirmMessage);
+			$htmlAttributes['onclick'] = "return confirm('{$confirmMessage}');";
+		} elseif (isset($htmlAttributes['default']) && $htmlAttributes['default'] == false) {
+			if (isset($htmlAttributes['onclick'])) {
+				$htmlAttributes['onclick'] .= ' event.returnValue = false; return false;';
+			} else {
+				$htmlAttributes['onclick'] = 'event.returnValue = false; return false;';
+			}
+			unset($htmlAttributes['default']);
+		}
+		return $this->output(sprintf($this->tags['link'], $url, $this->_parseAttributes($htmlAttributes), $title));
+	}
+/**
+ * Creates a link element for CSS stylesheets.
+ *
+ * @param mixed $path The name of a CSS style sheet in /app/webroot/css, or an array containing names of CSS stylesheets in that directory.
+ * @param string $rel Rel attribute. Defaults to "stylesheet".
+ * @param array $htmlAttributes Array of HTML attributes.
+ * @param boolean $inline If set to false, the generated tag appears in the head tag of the layout.
+ * @return string CSS <link /> or <style /> tag, depending on the type of link.
+ */
+	function css($path, $rel = null, $htmlAttributes = array(), $inline = true) {
+		if (is_array($path)) {
+			$out = '';
+			foreach ($path as $i) {
+				$out .= "\n\t" . $this->css($i, $rel, $htmlAttributes, $inline);
+			}
+			if ($inline)  {
+				return $out . "\n";
+			}
+			return;
+		}
+
+		if (strpos($path, '://') !== false) {
+			$url = $path;
+		} else {
+			if ($path[0] !== '/') {
+				$path = CSS_URL . $path;
+			}
+
+			if (strpos($path, '?') === false) {
+				if (strpos($path, '.css') === false) {
+					$path .= '.css';
+				}
+			}
+
+			$path = $this->webroot($path);
+
+			$url = $path;
+			if (strpos($path, '?') === false && ((Configure::read('Asset.timestamp') === true && Configure::read() > 0) || Configure::read('Asset.timestamp') === 'force')) {
+				$url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $path));
+			}
+
+			if (Configure::read('Asset.filter.css')) {
+				$url = str_replace(CSS_URL, 'ccss/', $url);
+			}
+		}
+
+		if ($rel == 'import') {
+			$out = sprintf($this->tags['style'], $this->_parseAttributes($htmlAttributes, null, '', ' '), '@import url(' . $url . ');');
+		} else {
+			if ($rel == null) {
+				$rel = 'stylesheet';
+			}
+			$out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($htmlAttributes, null, '', ' '));
+		}
+		$out = $this->output($out);
+
+		if ($inline) {
+			return $out;
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+/**
+ * Builds CSS style data from an array of CSS properties
+ *
+ * @param array $data
+ * @return string CSS styling data
+ */
+	function style($data, $inline = true) {
+		if (!is_array($data)) {
+			return $data;
+		}
+		$out = array();
+		foreach ($data as $key=> $value) {
+			$out[] = $key.':'.$value.';';
+		}
+		if ($inline) {
+			return join(' ', $out);
+		}
+		return join("\n", $out);
+	}
+/**
+ * Returns the breadcrumb trail as a sequence of &raquo;-separated links.
+ *
+ * @param  string  $separator Text to separate crumbs.
+ * @param  string  $startText This will be the first crumb, if false it defaults to first crumb in array
+ * @return string
+ */
+	function getCrumbs($separator = '&raquo;', $startText = false) {
+		if (count($this->_crumbs)) {
+			$out = array();
+			if ($startText) {
+				$out[] = $this->link($startText, '/');
+			}
+
+			foreach ($this->_crumbs as $crumb) {
+				if (!empty($crumb[1])) {
+					$out[] = $this->link($crumb[0], $crumb[1], $crumb[2]);
+				} else {
+					$out[] = $crumb[0];
+				}
+			}
+			return $this->output(join($separator, $out));
+		} else {
+			return null;
+		}
+	}
+/**
+ * Creates a formatted IMG element.
+ *
+ * @param string $path Path to the image file, relative to the app/webroot/img/ directory.
+ * @param array	$htmlAttributes Array of HTML attributes.
+ * @return string
+ */
+	function image($path, $options = array()) {
+		if (is_array($path)) {
+			$path = $this->url($path);
+		} elseif ($path[0] === '/') {
+			$path = $this->webroot($path);
+		} elseif (strpos($path, '://') === false) {
+			if (Configure::read('Asset.timestamp') == true && Configure::read() > 0) {
+				$path .= '?' . @filemtime(str_replace('/', DS, WWW_ROOT . IMAGES_URL . $path));
+			}
+			$path = $this->webroot(IMAGES_URL . $path);
+		}
+
+		if (!isset($options['alt'])) {
+			$options['alt'] = '';
+		}
+
+		$url = false;
+		if (!empty($options['url'])) {
+			$url = $options['url'];
+			unset($options['url']);
+		}
+
+		$image = sprintf($this->tags['image'], $path, $this->_parseAttributes($options, null, '', ' '));
+
+		if ($url) {
+			return $this->output(sprintf($this->tags['link'], $this->url($url), null, $image));
+		}
+
+		return $this->output($image);
+	}
+/**
+ * Returns a row of formatted and named TABLE headers.
+ *
+ * @param array $names		Array of tablenames.
+ * @param array $trOptions	HTML options for TR elements.
+ * @param array $thOptions	HTML options for TH elements.
+ * @return string
+ */
+	function tableHeaders($names, $trOptions = null, $thOptions = null) {
+		$out = array();
+		foreach ($names as $arg) {
+			$out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg);
+		}
+		$data = sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out));
+		return $this->output($data);
+	}
+/**
+ * Returns a formatted string of table rows (TR's with TD's in them).
+ *
+ * @param array $data		Array of table data
+ * @param array $oddTrOptions HTML options for odd TR elements if true useCount is used
+ * @param array $evenTrOptions HTML options for even TR elements
+ * @param bool $useCount adds class "column-$i"
+ * @param bool $continueOddEven If false, will use a non-static $count variable, so that the odd/even count is reset to zero just for that call
+ * @return string	Formatted HTML
+ */
+	function tableCells($data, $oddTrOptions = null, $evenTrOptions = null, $useCount = false, $continueOddEven = true) {
+		if (empty($data[0]) || !is_array($data[0])) {
+			$data = array($data);
+		}
+
+		if ($oddTrOptions === true) {
+			$useCount = true;
+			$oddTrOptions = null;
+		}
+
+		if ($evenTrOptions === false) {
+			$continueOddEven = false;
+			$evenTrOptions = null;
+		}
+
+		if ($continueOddEven) {
+			static $count = 0;
+		} else {
+			$count = 0;
+		}
+
+		foreach ($data as $line) {
+			$count++;
+			$cellsOut = array();
+			$i = 0;
+			foreach ($line as $cell) {
+				$cellOptions = array();
+
+				if (is_array($cell)) {
+					$cellOptions = $cell[1];
+					$cell = $cell[0];
+				} elseif ($useCount) {
+					$cellOptions['class'] = 'column-' . ++$i;
+				}
+				$cellsOut[] = sprintf($this->tags['tablecell'], $this->_parseAttributes($cellOptions), $cell);
+			}
+			$options = $this->_parseAttributes($count % 2 ? $oddTrOptions : $evenTrOptions);
+			$out[] = sprintf($this->tags['tablerow'], $options, join(' ', $cellsOut));
+		}
+		return $this->output(join("\n", $out));
+	}
+/**
+ * Returns a formatted block tag, i.e DIV, SPAN, P.
+ *
+ * @param string $name Tag name.
+ * @param string $text String content that will appear inside the div element.
+ *			If null, only a start tag will be printed
+ * @param array $attributes Additional HTML attributes of the DIV tag
+ * @param boolean $escape If true, $text will be HTML-escaped
+ * @return string The formatted tag element
+ */
+	function tag($name, $text = null, $attributes = array(), $escape = false) {
+		if ($escape) {
+			$text = h($text);
+		}
+		if (!is_array($attributes)) {
+			$attributes = array('class' => $attributes);
+		}
+		if ($text === null) {
+			$tag = 'tagstart';
+		} else {
+			$tag = 'tag';
+		}
+		return $this->output(sprintf($this->tags[$tag], $name, $this->_parseAttributes($attributes, null, ' ', ''), $text, $name));
+	}
+/**
+ * Returns a formatted DIV tag for HTML FORMs.
+ *
+ * @param string $class CSS class name of the div element.
+ * @param string $text String content that will appear inside the div element.
+ *			If null, only a start tag will be printed
+ * @param array $attributes Additional HTML attributes of the DIV tag
+ * @param boolean $escape If true, $text will be HTML-escaped
+ * @return string The formatted DIV element
+ */
+	function div($class = null, $text = null, $attributes = array(), $escape = false) {
+		if ($class != null && !empty($class)) {
+			$attributes['class'] = $class;
+		}
+		return $this->tag('div', $text, $attributes, $escape);
+	}
+/**
+ * Returns a formatted P tag.
+ *
+ * @param string $class CSS class name of the p element.
+ * @param string $text String content that will appear inside the p element.
+ * @param array $attributes Additional HTML attributes of the P tag
+ * @param boolean $escape If true, $text will be HTML-escaped
+ * @return string The formatted P element
+ */
+	function para($class, $text, $attributes = array(), $escape = false) {
+		if ($escape) {
+			$text = h($text);
+		}
+		if ($class != null && !empty($class)) {
+			$attributes['class'] = $class;
+		}
+		if ($text === null) {
+			$tag = 'parastart';
+		} else {
+			$tag = 'para';
+		}
+		return $this->output(sprintf($this->tags[$tag], $this->_parseAttributes($attributes, null, ' ', ''), $text));
+	}
+/**
+ * Build a nested list (UL/OL) out of an associative array.
+ *
+ * @param array $list Set of elements to list
+ * @param array $attributes Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag
+ * @param array $itemAttributes Additional HTML attributes of the list item (LI) tag
+ * @param string $tag Type of list tag to use (ol/ul)
+ * @return string The nested list
+ * @access public
+ */
+	function nestedList($list, $attributes = array(), $itemAttributes = array(), $tag = 'ul') {
+		if (is_string($attributes)) {
+			$tag = $attributes;
+			$attributes = array();
+		}
+		$items = $this->__nestedListItem($list, $attributes, $itemAttributes, $tag);
+		return sprintf($this->tags[$tag], $this->_parseAttributes($attributes, null, ' ', ''), $items);
+	}
+/**
+ * Internal function to build a nested list (UL/OL) out of an associative array.
+ *
+ * @param array $list Set of elements to list
+ * @param array $attributes Additional HTML attributes of the list (ol/ul) tag
+ * @param array $itemAttributes Additional HTML attributes of the list item (LI) tag
+ * @param string $tag Type of list tag to use (ol/ul)
+ * @return string The nested list element
+ * @access private
+ * @see nestedList()
+ */
+	function __nestedListItem($items, $attributes, $itemAttributes, $tag) {
+		$out = '';
+
+		$index = 1;
+		foreach ($items as $key => $item) {
+			if (is_array($item)) {
+				$item = $key . $this->nestedList($item, $attributes, $itemAttributes, $tag);
+			}
+			if (isset($itemAttributes['even']) && $index % 2 == 0) {
+				$itemAttributes['class'] = $itemAttributes['even'];
+			} else if (isset($itemAttributes['odd']) && $index % 2 != 0) {
+				$itemAttributes['class'] = $itemAttributes['odd'];
+			}
+			$out .= sprintf($this->tags['li'], $this->_parseAttributes(array_diff_key($itemAttributes, array_flip(array('even', 'odd'))), null, ' ', ''), $item);
+			$index++;
+		}
+		return $out;
+	}
+}
+?>
Index: cake/libs/view/helpers/paginator.php
===================================================================
--- cake/libs/view/helpers/paginator.php	(revision 0)
+++ cake/libs/view/helpers/paginator.php	(revision 0)
@@ -0,0 +1,644 @@
+<?php
+/* SVN FILE: $Id: paginator.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Pagination Helper class file.
+ *
+ * Generates pagination links
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Pagination Helper class for easy generation of pagination links.
+ *
+ * PaginationHelper encloses all methods needed when working with pagination.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class PaginatorHelper extends AppHelper {
+/**
+ * Helper dependencies
+ *
+ * @var array
+ */
+	var $helpers = array('Html', 'Ajax');
+/**
+ * Holds the default model for paged recordsets
+ *
+ * @var string
+ */
+	var $__defaultModel = null;
+/**
+ * Holds the default options for pagination links
+ *
+ * The values that may be specified are:
+ * - <i>$options['format']</i> Format of the counter. Supported formats are 'range' and 'pages'
+ *                             and custom (default). In the default mode the supplied string is
+ *                             parsed and constants are replaced by their actual values.
+ *                             Constants: %page%, %pages%, %current%, %count%, %start%, %end% .
+ * - <i>$options['separator']</i> The separator of the actual page and number of pages (default: ' of ').
+ * - <i>$options['url']</i> Url of the action. See Router::url()
+ * - <i>$options['url']['sort']</i>  the key that the recordset is sorted.
+ * - <i>$options['url']['direction']</i> Direction of the sorting (default: 'asc').
+ * - <i>$options['url']['page']</i> Page # to display.
+ * - <i>$options['model']</i> The name of the model.
+ * - <i>$options['escape']</i> Defines if the title field for the link should be escaped (default: true).
+ * - <i>$options['update']</i> DOM id of the element updated with the results of the AJAX call.
+ *                             If this key isn't specified Paginator will use plain HTML links.
+ * - <i>$options['indicator']</i> DOM id of the element that will be shown when doing AJAX requests.
+ *
+ * @var array
+ */
+	var $options = array();
+/**
+ * Gets the current page of the in the recordset for the given model
+ *
+ * @param  string $model Optional model name.  Uses the default if none is specified.
+ * @return string The current page number of the paginated resultset.
+ */
+	function params($model = null) {
+		if (empty($model)) {
+			$model = $this->defaultModel();
+		}
+		if (!isset($this->params['paging']) || empty($this->params['paging'][$model])) {
+			return null;
+		}
+		return $this->params['paging'][$model];
+	}
+/**
+ * Sets default options for all pagination links
+ *
+ * @param  mixed $options Default options for pagination links. If a string is supplied - it
+ *                        is used as the DOM id element to update. See #options for list of keys.
+ */
+	function options($options = array()) {
+		if (is_string($options)) {
+			$options = array('update' => $options);
+		}
+
+		if (!empty($options['paging'])) {
+			if (!isset($this->params['paging'])) {
+				$this->params['paging'] = array();
+			}
+			$this->params['paging'] = array_merge($this->params['paging'], $options['paging']);
+			unset($options['paging']);
+		}
+		$model = $this->defaultModel();
+
+		if (!empty($options[$model])) {
+			if (!isset($this->params['paging'][$model])) {
+				$this->params['paging'][$model] = array();
+			}
+			$this->params['paging'][$model] = array_merge($this->params['paging'][$model], $options[$model]);
+			unset($options[$model]);
+		}
+		$this->options = array_filter(array_merge($this->options, $options));
+	}
+/**
+ * Gets the current page of the recordset for the given model
+ *
+ * @param  string $model Optional model name.  Uses the default if none is specified.
+ * @return string The current page number of the recordset.
+ */
+	function current($model = null) {
+		$params = $this->params($model);
+
+		if (isset($params['page'])) {
+			return $params['page'];
+		}
+		return 1;
+	}
+/**
+ * Gets the current key by which the recordset is sorted
+ *
+ * @param  string $model Optional model name.  Uses the default if none is specified.
+ * @param  mixed $options Options for pagination links. See #options for list of keys.
+ * @return string The name of the key by which the recordset is being sorted, or
+ *                null if the results are not currently sorted.
+ */
+	function sortKey($model = null, $options = array()) {
+		if (empty($options)) {
+			$params = $this->params($model);
+			$options = array_merge($params['defaults'], $params['options']);
+		}
+
+		if (isset($options['sort']) && !empty($options['sort'])) {
+			if (preg_match('/(?:\w+\.)?(\w+)/', $options['sort'], $result) && isset($result[1])) {
+				return $result[1];
+			}
+			return $options['sort'];
+		} elseif (isset($options['order']) && is_array($options['order'])) {
+			return preg_replace('/.*\./', '', key($options['order']));
+		} elseif (isset($options['order']) && is_string($options['order'])) {
+			if (preg_match('/(?:\w+\.)?(\w+)/', $options['order'], $result) && isset($result[1])) {
+				return $result[1];
+			}
+		}
+		return null;
+	}
+/**
+ * Gets the current direction the recordset is sorted
+ *
+ * @param  string $model Optional model name.  Uses the default if none is specified.
+ * @param  mixed $options Options for pagination links. See #options for list of keys.
+ * @return string The direction by which the recordset is being sorted, or
+ *                null if the results are not currently sorted.
+ */
+	function sortDir($model = null, $options = array()) {
+		$dir = null;
+
+		if (empty($options)) {
+			$params = $this->params($model);
+			$options = array_merge($params['defaults'], $params['options']);
+		}
+
+		if (isset($options['direction'])) {
+			$dir = strtolower($options['direction']);
+		} elseif (isset($options['order']) && is_array($options['order'])) {
+			$dir = strtolower(current($options['order']));
+		}
+
+		if ($dir == 'desc') {
+			return 'desc';
+		}
+		return 'asc';
+	}
+/**
+ * Generates a "previous" link for a set of paged records
+ *
+ * @param  string $title Title for the link. Defaults to '<< Previous'.
+ * @param  mixed $options Options for pagination link. See #options for list of keys.
+ * @param  string $disabledTitle Title when the link is disabled.
+ * @param  mixed $disabledOptions Options for the disabled pagination link. See #options for list of keys.
+ * @return string A "previous" link or $disabledTitle text if the link is disabled.
+ */
+	function prev($title = '<< Previous', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
+		return $this->__pagingLink('Prev', $title, $options, $disabledTitle, $disabledOptions);
+	}
+/**
+ * Generates a "next" link for a set of paged records
+ *
+ * @param  string $title Title for the link. Defaults to 'Next >>'.
+ * @param  mixed $options Options for pagination link. See #options for list of keys.
+ * @param  string $disabledTitle Title when the link is disabled.
+ * @param  mixed $disabledOptions Options for the disabled pagination link. See #options for list of keys.
+ * @return string A "next" link or or $disabledTitle text if the link is disabled.
+ */
+	function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
+		return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions);
+	}
+/**
+ * Generates a sorting link
+ *
+ * @param  string $title Title for the link.
+ * @param  string $key The name of the key that the recordset should be sorted.
+ * @param  array $options Options for sorting link. See #options for list of keys.
+ * @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
+ *                key the returned link will sort by 'desc'.
+ */
+	function sort($title, $key = null, $options = array()) {
+		$options = array_merge(array('url' => array(), 'model' => null), $options);
+		$url = $options['url'];
+		unset($options['url']);
+
+		if (empty($key)) {
+			$key = $title;
+			$title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
+		}
+		$dir = 'asc';
+		$model = null;
+
+		if (strpos($key, '.') !== false) {
+			list($model, $key) = explode('.', $key);
+			$model = $model . '.';
+		}
+		if ($this->sortKey($options['model']) == $key && $this->sortDir($options['model']) == 'asc') {
+			$dir = 'desc';
+		}
+		if (is_array($title) && array_key_exists($dir, $title)) {
+			$title = $title[$dir];
+		}
+		$url = array_merge(array('sort' => $model . $key, 'direction' => $dir), $url, array('order' => null));
+		return $this->link($title, $url, $options);
+	}
+/**
+ * Generates a plain or Ajax link with pagination parameters
+ *
+ * @param  string $title Title for the link.
+ * @param  mixed $url Url for the action. See Router::url()
+ * @param  array $options Options for the link. See #options for list of keys.
+ * @return string A link with pagination parameters.
+ */
+	function link($title, $url = array(), $options = array()) {
+		$options = array_merge(array('model' => null, 'escape' => true), $options);
+		$model = $options['model'];
+		unset($options['model']);
+
+		if (!empty($this->options)) {
+			$options = array_merge($this->options, $options);
+		}
+		if (isset($options['url'])) {
+			$url = array_merge((array)$options['url'], (array)$url);
+			unset($options['url']);
+		}
+		$url = $this->url($url, true, $model);
+
+		$obj = isset($options['update']) ? 'Ajax' : 'Html';
+		$url = array_merge(array('page' => $this->current($model)), $url);
+		return $this->{$obj}->link($title, Set::filter($url, true), $options);
+	}
+/**
+ * Merges passed URL options with current pagination state to generate a pagination URL.
+ *
+ * @param  array $options Pagination/URL options array
+ * @param  boolean $asArray
+ * @param  string $model Which model to paginate on
+ * @return mixed By default, returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript)
+ */
+	function url($options = array(), $asArray = false, $model = null) {
+		$paging = $this->params($model);
+		$url = array_merge(array_filter(Set::diff(array_merge($paging['defaults'], $paging['options']), $paging['defaults'])), $options);
+
+		if (isset($url['order'])) {
+			$sort = $direction = null;
+			if (is_array($url['order'])) {
+				list($sort, $direction) = array($this->sortKey($model, $url), current($url['order']));
+				$key = array_keys($url['order']);
+
+				if (strpos($key[0], '.') !== false) {
+					list($model) = explode('.', $key[0]);
+					$sort = $model . '.' . $sort;
+				}
+			}
+			unset($url['order']);
+			$url = array_merge($url, compact('sort', 'direction'));
+		}
+
+		if ($asArray) {
+			return $url;
+		}
+		return parent::url($url);
+	}
+/**
+ * Protected method for generating prev/next links
+ *
+ */
+	function __pagingLink($which, $title = null, $options = array(), $disabledTitle = null, $disabledOptions = array()) {
+		$check = 'has' . $which;
+		$_defaults = array('url' => array(), 'step' => 1, 'escape' => true, 'model' => null, 'tag' => 'div');
+		$options = array_merge($_defaults, (array)$options);
+		$paging = $this->params($options['model']);
+
+		if (!$this->{$check}($options['model']) && (!empty($disabledTitle) || !empty($disabledOptions))) {
+			if (!empty($disabledTitle) && $disabledTitle !== true) {
+				$title = $disabledTitle;
+			}
+			$options = array_merge($_defaults, (array)$disabledOptions);
+		} elseif (!$this->{$check}($options['model'])) {
+			return null;
+		}
+
+		foreach (array_keys($_defaults) as $key) {
+			${$key} = $options[$key];
+			unset($options[$key]);
+		}
+		$url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url);
+
+		if ($this->{$check}($model)) {
+			return $this->link($title, $url, array_merge($options, array('escape' => $escape)));
+		} else {
+			return $this->Html->tag($tag, $title, $options, $escape);
+		}
+	}
+/**
+ * Returns true if the given result set is not at the first page
+ *
+ * @param string $model Optional model name.  Uses the default if none is specified.
+ * @return boolean True if the result set is not at the first page.
+ */
+	function hasPrev($model = null) {
+		return $this->__hasPage($model, 'prev');
+	}
+/**
+ * Returns true if the given result set is not at the last page
+ *
+ * @param string $model Optional model name.  Uses the default if none is specified.
+ * @return boolean True if the result set is not at the last page.
+ */
+	function hasNext($model = null) {
+		return $this->__hasPage($model, 'next');
+	}
+/**
+ * Returns true if the given result set has the page number given by $page
+ *
+ * @param  string $model Optional model name.  Uses the default if none is specified.
+ * @param  int $page The page number - if not set defaults to 1.
+ * @return boolean True if the given result set has the specified page number.
+ */
+	function hasPage($model = null, $page = 1) {
+		if (is_numeric($model)) {
+			$page = $model;
+			$model = null;
+		}
+		$paging = $this->params($model);
+		return $page <= $paging['pageCount'];
+	}
+/**
+ * Protected method
+ *
+ */
+	function __hasPage($model, $page) {
+		$params = $this->params($model);
+		if (!empty($params)) {
+			if ($params["{$page}Page"] == true) {
+				return true;
+			}
+		}
+		return false;
+	}
+/**
+ * Gets the default model of the paged sets
+ *
+ * @return string Model name or null if the pagination isn't initialized.
+ */
+	function defaultModel() {
+		if ($this->__defaultModel != null) {
+			return $this->__defaultModel;
+		}
+		if (empty($this->params['paging'])) {
+			return null;
+		}
+		list($this->__defaultModel) = array_keys($this->params['paging']);
+		return $this->__defaultModel;
+	}
+/**
+ * Returns a counter string for the paged result set
+ *
+ * @param  mixed $options Options for the counter string. See #options for list of keys.
+ * @return string Counter string.
+ */
+	function counter($options = array()) {
+		if (is_string($options)) {
+			$options = array('format' => $options);
+		}
+
+		$options = array_merge(
+			array(
+				'model' => $this->defaultModel(),
+				'format' => 'pages',
+				'separator' => ' of '
+			),
+		$options);
+
+		$paging = $this->params($options['model']);
+		if ($paging['pageCount'] == 0) {
+			$paging['pageCount'] = 1;
+		}
+		$start = 0;
+		if ($paging['count'] >= 1) {
+			$start = (($paging['page'] - 1) * $paging['options']['limit']) + 1;
+		}
+		$end = $start + $paging['options']['limit'] - 1;
+		if ($paging['count'] < $end) {
+			$end = $paging['count'];
+		}
+
+		switch ($options['format']) {
+			case 'range':
+				if (!is_array($options['separator'])) {
+					$options['separator'] = array(' - ', $options['separator']);
+				}
+				$out = $start . $options['separator'][0] . $end . $options['separator'][1] . $paging['count'];
+			break;
+			case 'pages':
+				$out = $paging['page'] . $options['separator'] . $paging['pageCount'];
+			break;
+			default:
+				$replace = array(
+					'%page%' => $paging['page'],
+					'%pages%' => $paging['pageCount'],
+					'%current%' => $paging['current'],
+					'%count%' => $paging['count'],
+					'%start%' => $start,
+					'%end%' => $end
+				);
+				$out = str_replace(array_keys($replace), array_values($replace), $options['format']);
+			break;
+		}
+		return $this->output($out);
+	}
+/**
+ * Returns a set of numbers for the paged result set
+ * uses a modulus to decide how many numbers to show on each side of the current page (default: 8)
+ *
+ * @param  mixed $options Options for the numbers, (before, after, model, modulus, separator)
+ * @return string numbers string.
+ */
+	function numbers($options = array()) {
+		if ($options === true) {
+			$options = array(
+						'before' => ' | ', 'after' => ' | ',
+						'first' => 'first', 'last' => 'last',
+						);
+		}
+
+		$options = array_merge(
+			array(
+				'tag' => 'span',
+				'before'=> null, 'after'=> null,
+				'model' => $this->defaultModel(),
+				'modulus' => '8', 'separator' => ' | ',
+				'first' => null, 'last' => null,
+			),
+		(array)$options);
+
+		$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
+		unset($options['model']);
+
+		if ($params['pageCount'] <= 1) {
+			return false;
+		}
+
+		extract($options);
+		unset($options['tag'], $options['before'], $options['after'], $options['model'],
+			$options['modulus'], $options['separator'], $options['first'], $options['last']);
+
+		$out = '';
+
+		if ($modulus && $params['pageCount'] > $modulus) {
+			$half = intval($modulus / 2);
+			$end = $params['page'] + $half;
+
+			if ($end > $params['pageCount']) {
+				$end = $params['pageCount'];
+			}
+			$start = $params['page'] - ($modulus - ($end - $params['page']));
+			if ($start <= 1) {
+				$start = 1;
+				$end = $params['page'] + ($modulus  - $params['page']) + 1;
+			}
+
+			if ($first && $start > (int)$first) {
+				if ($start == $first + 1) {
+					$out .= $this->first($first, array('tag' => $tag, 'after' => $separator));
+				} else {
+					$out .= $this->first($first, array('tag' => $tag));
+				}
+			}
+
+			$out .= $before;
+
+			for ($i = $start; $i < $params['page']; $i++) {
+				$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)) . $separator;
+			}
+
+			$out .= $this->Html->tag($tag, $params['page'], array('class' => 'current'));
+			if ($i != $params['pageCount']) {
+				$out .= $separator;
+			}
+
+			$start = $params['page'] + 1;
+			for ($i = $start; $i < $end; $i++) {
+				$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options)). $separator;
+			}
+
+			if ($end != $params['page']) {
+				$out .= $this->Html->tag($tag, $this->link($i, array('page' => $end), $options));
+			}
+
+			$out .= $after;
+
+			if ($last && $end <= $params['pageCount'] - (int)$last) {
+				if ($end + 1 == $params['pageCount']) {
+					$out .= $this->last($last, array('tag' => $tag, 'before' => $separator));
+				} else {
+					$out .= $this->last($last, array('tag' => $tag));
+				}
+			}
+
+		} else {
+			$out .= $before;
+
+			for ($i = 1; $i <= $params['pageCount']; $i++) {
+				if ($i == $params['page']) {
+					$out .= $this->Html->tag($tag, $i, array('class' => 'current'));
+				} else {
+					$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
+				}
+				if ($i != $params['pageCount']) {
+					$out .= $separator;
+				}
+			}
+
+			$out .= $after;
+		}
+
+		return $this->output($out);
+	}
+/**
+ * Returns a first or set of numbers for the first pages
+ *
+ * @param  mixed $first if string use as label for the link, if numeric print page numbers
+ * @param  mixed $options
+ * @return string numbers string.
+ */
+	function first($first = '<< first', $options = array()) {
+		$options = array_merge(
+			array(
+				'tag' => 'span',
+				'after'=> null,
+				'model' => $this->defaultModel(),
+				'separator' => ' | ',
+			),
+		(array)$options);
+
+		$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
+		unset($options['model']);
+
+		if ($params['pageCount'] <= 1) {
+			return false;
+		}
+		extract($options);
+		unset($options['tag'], $options['after'], $options['model'], $options['separator']);
+
+		$out = '';
+
+		if (is_int($first) && $params['page'] > $first) {
+			if ($after === null) {
+				$after = '...';
+			}
+			for ($i = 1; $i <= $first; $i++) {
+				$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
+				if ($i != $first) {
+					$out .= $separator;
+				}
+			}
+			$out .= $after;
+		} elseif ($params['page'] > 1) {
+			$out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options)) . $after;
+		}
+		return $out;
+	}
+/**
+ * Returns a last or set of numbers for the last pages
+ *
+ * @param  mixed $last if string use as label for the link, if numeric print page numbers
+ * @param  mixed $options
+ * @return string numbers string.
+ */
+	function last($last = 'last >>', $options = array()) {
+		$options = array_merge(
+			array(
+				'tag' => 'span',
+				'before'=> null,
+				'model' => $this->defaultModel(),
+				'separator' => ' | ',
+			),
+		(array)$options);
+
+		$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
+		unset($options['model']);
+
+		if ($params['pageCount'] <= 1) {
+			return false;
+		}
+
+		extract($options);
+		unset($options['tag'], $options['before'], $options['model'], $options['separator']);
+
+		$out = '';
+		$lower = $params['pageCount'] - $last + 1;
+
+		if (is_int($last) && $params['page'] < $lower) {
+			if ($before === null) {
+				$before = '...';
+			}
+			for ($i = $lower; $i <= $params['pageCount']; $i++) {
+				$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
+				if ($i != $params['pageCount']) {
+					$out .= $separator;
+				}
+			}
+			$out = $before . $out;
+		} elseif ($params['page'] < $params['pageCount']) {
+			$out = $before . $this->Html->tag($tag, $this->link($last, array('page' => $params['pageCount']), $options));
+		}
+		return $out;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/text.php
===================================================================
--- cake/libs/view/helpers/text.php	(revision 0)
+++ cake/libs/view/helpers/text.php	(revision 0)
@@ -0,0 +1,341 @@
+<?php
+/* SVN FILE: $Id: text.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Text Helper
+ *
+ * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('HtmlHelper')) {
+	App::import('Helper', 'Html');
+}
+if (!class_exists('Multibyte')) {
+	App::import('Core', 'Multibyte');
+}
+/**
+ * Text helper library.
+ *
+ * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class TextHelper extends AppHelper {
+/**
+ * Highlights a given phrase in a text. You can specify any expression in highlighter that
+ * may include the \1 expression to include the $phrase found.
+ *
+ * @param string $text Text to search the phrase in
+ * @param string $phrase The phrase that will be searched
+ * @param string $highlighter The piece of html with that the phrase will be highlighted
+ * @param boolean $considerHtml If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
+ * @return string The highlighted text
+ * @access public
+ */
+	function highlight($text, $phrase, $highlighter = '<span class="highlight">\1</span>', $considerHtml = false) {
+		if (empty($phrase)) {
+			return $text;
+		}
+
+		if (is_array($phrase)) {
+			$replace = array();
+			$with = array();
+
+			foreach ($phrase as $key => $value) {
+				$key = $value;
+				$value = $highlighter;
+				$key = '(' . $key . ')';
+				if ($considerHtml) {
+					$key = '(?![^<]+>)' . $key . '(?![^<]+>)';
+				}
+				$replace[] = '|' . $key . '|iu';
+				$with[] = empty($value) ? $highlighter : $value;
+			}
+
+			return preg_replace($replace, $with, $text);
+		} else {
+			$phrase = '(' . $phrase . ')';
+			if ($considerHtml) {
+				$phrase = '(?![^<]+>)' . $phrase . '(?![^<]+>)';
+			}
+
+			return preg_replace('|'.$phrase.'|iu', $highlighter, $text);
+		}
+	}
+/**
+ * Strips given text of all links (<a href=....)
+ *
+ * @param string $text Text
+ * @return string The text without links
+ * @access public
+ */
+	function stripLinks($text) {
+		return preg_replace('|<a\s+[^>]+>|im', '', preg_replace('|<\/a>|im', '', $text));
+	}
+/**
+ * Adds links (<a href=....) to a given text, by finding text that begins with
+ * strings like http:// and ftp://.
+ *
+ * @param string $text Text to add links to
+ * @param array $htmlOptions Array of HTML options.
+ * @return string The text with links
+ * @access public
+ */
+	function autoLinkUrls($text, $htmlOptions = array()) {
+		$options = 'array(';
+		foreach ($htmlOptions as $option => $value) {
+				$value = var_export($value, true);
+				$options .= "'$option' => $value, ";
+		}
+		$options .= ')';
+
+		$text = preg_replace_callback('#(?<!href="|">)((?:http|https|ftp|nntp)://[^ <]+)#i', create_function('$matches',
+			'$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], $matches[0],' . $options . ');'), $text);
+
+		return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
+			create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "http://" . strtolower($matches[0]),' . $options . ');'), $text);
+	}
+/**
+ * Adds email links (<a href="mailto:....) to a given text.
+ *
+ * @param string $text Text
+ * @param array $htmlOptions Array of HTML options.
+ * @return string The text with links
+ * @access public
+ */
+	function autoLinkEmails($text, $htmlOptions = array()) {
+		$options = 'array(';
+
+		foreach ($htmlOptions as $option => $value) {
+			$options .= "'$option' => '$value', ";
+		}
+		$options .= ')';
+
+		return preg_replace_callback('#([_A-Za-z0-9+-]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#',
+						create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "mailto:" . $matches[0],' . $options . ');'), $text);
+	}
+/**
+ * Convert all links and email adresses to HTML links.
+ *
+ * @param string $text Text
+ * @param array $htmlOptions Array of HTML options.
+ * @return string The text with links
+ * @access public
+ */
+	function autoLink($text, $htmlOptions = array()) {
+		return $this->autoLinkEmails($this->autoLinkUrls($text, $htmlOptions), $htmlOptions);
+	}
+/**
+ * Truncates text.
+ *
+ * Cuts a string to the length of $length and replaces the last characters
+ * with the ending if the text is longer than length.
+ *
+ * @param string  $text String to truncate.
+ * @param integer $length Length of returned string, including ellipsis.
+ * @param mixed $ending If string, will be used as Ending and appended to the trimmed string. Can also be an associative array that can contain the last three params of this method.
+ * @param boolean $exact If false, $text will not be cut mid-word
+ * @param boolean $considerHtml If true, HTML tags would be handled correctly
+ * @return string Trimmed string.
+ */
+	function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) {
+		if (is_array($ending)) {
+			extract($ending);
+		}
+		if ($considerHtml) {
+			if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
+				return $text;
+			}
+			$totalLength = mb_strlen($ending);
+			$openTags = array();
+			$truncate = '';
+			preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
+			foreach ($tags as $tag) {
+				if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) {
+					if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) {
+						array_unshift($openTags, $tag[2]);
+					} else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) {
+						$pos = array_search($closeTag[1], $openTags);
+						if ($pos !== false) {
+							array_splice($openTags, $pos, 1);
+						}
+					}
+				}
+				$truncate .= $tag[1];
+
+				$contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
+				if ($contentLength + $totalLength > $length) {
+					$left = $length - $totalLength;
+					$entitiesLength = 0;
+					if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) {
+						foreach ($entities[0] as $entity) {
+							if ($entity[1] + 1 - $entitiesLength <= $left) {
+								$left--;
+								$entitiesLength += mb_strlen($entity[0]);
+							} else {
+								break;
+							}
+						}
+					}
+
+					$truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength);
+					break;
+				} else {
+					$truncate .= $tag[3];
+					$totalLength += $contentLength;
+				}
+				if ($totalLength >= $length) {
+					break;
+				}
+			}
+
+		} else {
+			if (mb_strlen($text) <= $length) {
+				return $text;
+			} else {
+				$truncate = mb_substr($text, 0, $length - strlen($ending));
+			}
+		}
+		if (!$exact) {
+			$spacepos = mb_strrpos($truncate, ' ');
+			if (isset($spacepos)) {
+				if ($considerHtml) {
+					$bits = mb_substr($truncate, $spacepos);
+					preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
+					if (!empty($droppedTags)) {
+						foreach ($droppedTags as $closingTag) {
+							if (!in_array($closingTag[1], $openTags)) {
+								array_unshift($openTags, $closingTag[1]);
+							}
+						}
+					}
+				}
+				$truncate = mb_substr($truncate, 0, $spacepos);
+			}
+		}
+
+		$truncate .= $ending;
+
+		if ($considerHtml) {
+			foreach ($openTags as $tag) {
+				$truncate .= '</'.$tag.'>';
+			}
+		}
+
+		return $truncate;
+	}
+/**
+ * Alias for truncate().
+ *
+ * @see TextHelper::truncate()
+ * @access public
+ */
+	function trim() {
+		$args = func_get_args();
+		return call_user_func_array(array(&$this, 'truncate'), $args);
+	}
+/**
+ * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side determined by radius.
+ *
+ * @param string $text String to search the phrase in
+ * @param string $phrase Phrase that will be searched for
+ * @param integer $radius The amount of characters that will be returned on each side of the founded phrase
+ * @param string $ending Ending that will be appended
+ * @return string Modified string
+ * @access public
+ */
+	function excerpt($text, $phrase, $radius = 100, $ending = "...") {
+		if (empty($text) or empty($phrase)) {
+			return $this->truncate($text, $radius * 2, $ending);
+		}
+
+		$phraseLen = strlen($phrase);
+		if ($radius < $phraseLen) {
+			$radius = $phraseLen;
+		}
+
+		$pos = strpos(strtolower($text), strtolower($phrase));
+		$startPos = 0;
+		if ($pos > $radius) {
+			$startPos = $pos - $radius;
+		}
+		$textLen = strlen($text);
+		$endPos = $pos + $phraseLen + $radius;
+		if ($endPos >= $textLen) {
+			$endPos = $textLen;
+		}
+		$excerpt = substr($text, $startPos, $endPos - $startPos);
+
+		if ($startPos != 0) {
+			$excerpt = substr_replace($excerpt, $ending, 0, $phraseLen);
+		}
+
+		if ($endPos != $textLen) {
+			$excerpt = substr_replace($excerpt, $ending, -$phraseLen);
+		}
+
+		return $excerpt;
+	}
+/**
+ * Creates a comma separated list where the last two items are joined with 'and', forming natural English
+ *
+ * @param array $list The list to be joined
+ * @return string
+ * @access public
+ */
+	function toList($list, $and = 'and') {
+		$r = '';
+		$c = count($list) - 1;
+		foreach ($list as $i => $item) {
+			$r .= $item;
+			if ($c > 0 && $i < $c)
+			{
+				$r .= ($i < $c - 1 ? ', ' : " {$and} ");
+			}
+		}
+		return $r;
+	}
+/**
+ * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax.
+ *
+ * @param string $text String to "flay"
+ * @param boolean $allowHtml Set to true if if html is allowed
+ * @return string "Flayed" text
+ * @access public
+ * @todo Change this. We need a real Textile parser.
+ * @codeCoverageIgnoreStart
+ */
+	function flay($text, $allowHtml = false) {
+		trigger_error(__('(TextHelper::flay) Deprecated: the Flay library is no longer supported and will be removed in a future version.', true), E_USER_WARNING);
+		if (!class_exists('Flay')) {
+			uses('flay');
+		}
+		return Flay::toHtml($text, false, $allowHtml);
+	}
+/**
+ * @codeCoverageIgnoreEnd
+ */
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/javascript.php
===================================================================
--- cake/libs/view/helpers/javascript.php	(revision 0)
+++ cake/libs/view/helpers/javascript.php	(revision 0)
@@ -0,0 +1,610 @@
+<?php
+/* SVN FILE: $Id: javascript.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Javascript Helper class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Javascript Helper class for easy use of JavaScript.
+ *
+ * JavascriptHelper encloses all methods needed while working with JavaScript.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class JavascriptHelper extends AppHelper {
+/**
+ * Determines whether native JSON extension is used for encoding.  Set by object constructor.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $useNative = false;
+/**
+ * If true, automatically writes events to the end of a script or to an external JavaScript file
+ * at the end of page execution
+ *
+ * @var boolean
+ * @access public
+ */
+	var $enabled = true;
+/**
+ * Indicates whether <script /> blocks should be written 'safely,' i.e. wrapped in CDATA blocks
+ *
+ * @var boolean
+ * @access public
+ */
+	var $safe = false;
+/**
+ * HTML tags used by this helper.
+ *
+ * @var array
+ * @access public
+ */
+	var $tags = array(
+		'javascriptblock' => '<script type="text/javascript">%s</script>',
+		'javascriptstart' => '<script type="text/javascript">',
+		'javascriptlink' => '<script type="text/javascript" src="%s"></script>',
+		'javascriptend' => '</script>'
+	);
+/**
+ * Holds options passed to codeBlock(), saved for when block is dumped to output
+ *
+ * @var array
+ * @access protected
+ * @see JavascriptHelper::codeBlock()
+ */
+	var $_blockOptions = array();
+/**
+ * Caches events written by event() for output at the end of page execution
+ *
+ * @var array
+ * @access protected
+ * @see JavascriptHelper::event()
+ */
+	var $_cachedEvents = array();
+/**
+ * Indicates whether generated events should be cached for later output (can be written at the
+ * end of the page, in the <head />, or to an external file).
+ *
+ * @var boolean
+ * @access protected
+ * @see JavascriptHelper::event()
+ * @see JavascriptHelper::writeEvents()
+ */
+	var $_cacheEvents = false;
+/**
+ * Indicates whether cached events should be written to an external file
+ *
+ * @var boolean
+ * @access protected
+ * @see JavascriptHelper::event()
+ * @see JavascriptHelper::writeEvents()
+ */
+	var $_cacheToFile = false;
+/**
+ * Indicates whether *all* generated JavaScript should be cached for later output
+ *
+ * @var boolean
+ * @access protected
+ * @see JavascriptHelper::codeBlock()
+ * @see JavascriptHelper::blockEnd()
+ */
+	var $_cacheAll = false;
+/**
+ * Contains event rules attached with CSS selectors.  Used with the event:Selectors JavaScript
+ * library.
+ *
+ * @var array
+ * @access protected
+ * @see JavascriptHelper::event()
+ * @link          http://alternateidea.com/event-selectors/
+ */
+	var $_rules = array();
+/**
+ * @var string
+ * @access private
+ */
+	var $__scriptBuffer = null;
+/**
+ * Constructor. Checks for presence of native PHP JSON extension to use for object encoding
+ *
+ * @access public
+ */
+	function __construct($options = array()) {
+		if (!empty($options)) {
+			foreach ($options as $key => $val) {
+				if (is_numeric($key)) {
+					$key = $val;
+					$val = true;
+				}
+				switch ($key) {
+					case 'cache':
+
+					break;
+					case 'safe':
+						$this->safe = $val;
+					break;
+				}
+			}
+		}
+		$this->useNative = function_exists('json_encode');
+		return parent::__construct($options);
+	}
+/**
+ * Returns a JavaScript script tag.
+ *
+ * @param string $script The JavaScript to be wrapped in SCRIPT tags.
+ * @param array $options Set of options:
+ *             - allowCache: boolean, designates whether this block is cacheable using the
+ *               current cache settings.
+ *             - safe: boolean, whether this block should be wrapped in CDATA tags.  Defaults
+ *               to helper's object configuration.
+ *             - inline: whether the block should be printed inline, or written
+ *               to cached for later output (i.e. $scripts_for_layout).
+ * @return string The full SCRIPT element, with the JavaScript inside it, or null,
+ *                if 'inline' is set to false.
+ */
+	function codeBlock($script = null, $options = array()) {
+		if (!empty($options) && !is_array($options)) {
+			$options = array('allowCache' => $options);
+		} elseif (empty($options)) {
+			$options = array();
+		}
+		$defaultOptions = array('allowCache' => true, 'safe' => true, 'inline' => true);
+		$options = array_merge($defaultOptions, compact('safe'), $options);
+
+		if ($this->_cacheEvents && $this->_cacheAll && $options['allowCache'] && $script !== null) {
+			$this->_cachedEvents[] = $script;
+		} else {
+			$block = ($script !== null);
+			$safe = ($options['safe'] || $this->safe);
+			if ($safe && !($this->_cacheAll && $options['allowCache'])) {
+				$script  = "\n" . '//<![CDATA[' . "\n" . $script;
+				if ($block) {
+					$script .= "\n" . '//]]>' . "\n";
+				}
+			}
+
+			if ($script === null) {
+				$this->__scriptBuffer = @ob_get_contents();
+				$this->_blockOptions = $options;
+				$this->inBlock = true;
+				@ob_end_clean();
+				ob_start();
+				return null;
+			} else if (!$block) {
+				$this->_blockOptions = $options;
+			}
+
+			if ($options['inline']) {
+				if ($block) {
+					return sprintf($this->tags['javascriptblock'], $script);
+				} else {
+					$safe = ($safe ? "\n" . '//<![CDATA[' . "\n" : '');
+					return $this->tags['javascriptstart'] . $safe;
+				}
+			} elseif ($block) {
+				$view =& ClassRegistry::getObject('view');
+				$view->addScript(sprintf($this->tags['javascriptblock'], $script));
+			}
+		}
+	}
+/**
+ * Ends a block of cached JavaScript code
+ *
+ * @return mixed
+ */
+	function blockEnd() {
+		$script = @ob_get_contents();
+		@ob_end_clean();
+		ob_start();
+		echo $this->__scriptBuffer;
+		$this->__scriptBuffer = null;
+		$options = $this->_blockOptions;
+		$safe = ((isset($options['safe']) && $options['safe']) || $this->safe);
+		$this->_blockOptions = array();
+		$this->inBlock = false;
+
+		if (isset($options['inline']) && !$options['inline']) {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript(sprintf($this->tags['javascriptblock'], $script));
+		}
+
+		if (!empty($script) && $this->_cacheAll && $options['allowCache']) {
+			$this->_cachedEvents[] = $script;
+			return null;
+		}
+		return ife($safe, "\n" . '//]]>' . "\n", '').$this->tags['javascriptend'];
+	}
+/**
+ * Returns a JavaScript include tag (SCRIPT element).  If the filename is prefixed with "/",
+ * the path will be relative to the base path of your application.  Otherwise, the path will
+ * be relative to your JavaScript path, usually webroot/js.
+ *
+ * @param  mixed  $url String URL to JavaScript file, or an array of URLs.
+ * @param  boolean $inline If true, the <script /> tag will be printed inline,
+ *                         otherwise it will be printed in the <head />, using $scripts_for_layout
+ * @see JS_URL
+ * @return string
+ */
+	function link($url, $inline = true) {
+		if (is_array($url)) {
+			$out = '';
+			foreach ($url as $i) {
+				$out .= "\n\t" . $this->link($i, $inline);
+			}
+			if ($inline)  {
+				return $out . "\n";
+			}
+			return;
+		}
+
+		if (strpos($url, '://') === false) {
+			if ($url[0] !== '/') {
+				$url = JS_URL . $url;
+			}
+			if (strpos($url, '?') === false) {
+				if (strpos($url, '.js') === false) {
+					$url .= '.js';
+				}
+			}
+
+			$url = $this->webroot($url);
+			$timestampEnabled = (
+				(Configure::read('Asset.timestamp') === true && Configure::read() > 0) ||
+				Configure::read('Asset.timestamp') === 'force'
+			);
+
+			if (strpos($url, '?') === false && $timestampEnabled) {
+				$url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $url));
+			}
+
+			if (Configure::read('Asset.filter.js')) {
+				$url = str_replace(JS_URL, 'cjs/', $url);
+			}
+		}
+		$out = $this->output(sprintf($this->tags['javascriptlink'], $url));
+
+		if ($inline) {
+			return $out;
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+/**
+ * Escape carriage returns and single and double quotes for JavaScript segments.
+ *
+ * @param string $script string that might have javascript elements
+ * @return string escaped string
+ */
+	function escapeScript($script) {
+		$script = str_replace(array("\r\n", "\n", "\r"), '\n', $script);
+		$script = str_replace(array('"', "'"), array('\"', "\\'"), $script);
+		return $script;
+	}
+/**
+ * Escape a string to be JavaScript friendly.
+ *
+ * List of escaped ellements:
+ *	+ "\r\n" => '\n'
+ *	+ "\r" => '\n'
+ *	+ "\n" => '\n'
+ *	+ '"' => '\"'
+ *	+ "'" => "\\'"
+ *
+ * @param  string $script String that needs to get escaped.
+ * @return string Escaped string.
+ */
+	function escapeString($string) {
+		$escape = array("\r\n" => '\n', "\r" => '\n', "\n" => '\n', '"' => '\"', "'" => "\\'");
+		return str_replace(array_keys($escape), array_values($escape), $string);
+	}
+/**
+ * Attach an event to an element. Used with the Prototype library.
+ *
+ * @param string $object Object to be observed
+ * @param string $event event to observe
+ * @param string $observer function to call
+ * @param array $options Set options: useCapture, allowCache, safe
+ * @return boolean true on success
+ */
+	function event($object, $event, $observer = null, $options = array()) {
+		if (!empty($options) && !is_array($options)) {
+			$options = array('useCapture' => $options);
+		} else if (empty($options)) {
+			$options = array();
+		}
+
+		$defaultOptions = array('useCapture' => false);
+		$options = array_merge($defaultOptions, $options);
+
+		if ($options['useCapture'] == true) {
+			$options['useCapture'] = 'true';
+		} else {
+			$options['useCapture'] = 'false';
+		}
+		$isObject = (
+			strpos($object, 'window') !== false || strpos($object, 'document') !== false ||
+			strpos($object, '$(') !== false || strpos($object, '"') !== false ||
+			strpos($object, '\'') !== false
+		);
+
+		if ($isObject) {
+			$b = "Event.observe({$object}, '{$event}', function(event) { {$observer} }, ";
+			$b .= "{$options['useCapture']});";
+		} elseif ($object[0] == '\'') {
+			$b = "Event.observe(" . substr($object, 1) . ", '{$event}', function(event) { ";
+			$b .= "{$observer} }, {$options['useCapture']});";
+		} else {
+			$chars = array('#', ' ', ', ', '.', ':');
+			$found = false;
+			foreach ($chars as $char) {
+				if (strpos($object, $char) !== false) {
+					$found = true;
+					break;
+				}
+			}
+			if ($found) {
+				$this->_rules[$object] = $event;
+			} else {
+				$b = "Event.observe(\$('{$object}'), '{$event}', function(event) { ";
+				$b .= "{$observer} }, {$options['useCapture']});";
+			}
+		}
+
+		if (isset($b) && !empty($b)) {
+			if ($this->_cacheEvents === true) {
+				$this->_cachedEvents[] = $b;
+				return;
+			} else {
+				return $this->codeBlock($b, array_diff_key($options, $defaultOptions));
+			}
+		}
+	}
+/**
+ * Cache JavaScript events created with event()
+ *
+ * @param boolean $file If true, code will be written to a file
+ * @param boolean $all If true, all code written with JavascriptHelper will be sent to a file
+ * @return null
+ */
+	function cacheEvents($file = false, $all = false) {
+		$this->_cacheEvents = true;
+		$this->_cacheToFile = $file;
+		$this->_cacheAll = $all;
+	}
+/**
+ * Gets (and clears) the current JavaScript event cache
+ *
+ * @param boolean $clear
+ * @return string
+ */
+	function getCache($clear = true) {
+		$out = '';
+		$rules = array();
+
+		if (!empty($this->_rules)) {
+			foreach ($this->_rules as $sel => $event) {
+				$rules[] = "\t'{$sel}': function(element, event) {\n\t\t{$event}\n\t}";
+			}
+		}
+		$data = implode("\n", $this->_cachedEvents);
+
+		if (!empty($rules)) {
+			$data .= "\nvar Rules = {\n" . implode(",\n\n", $rules) . "\n}";
+			$data .= "\nEventSelectors.start(Rules);\n";
+		}
+		if ($clear) {
+			$this->_rules = array();
+			$this->_cacheEvents = false;
+			$this->_cachedEvents = array();
+		}
+		return $data;
+	}
+/**
+ * Write cached JavaScript events
+ *
+ * @param boolean $inline If true, returns JavaScript event code.  Otherwise it is added to the
+ *                        output of $scripts_for_layout in the layout.
+ * @param array $options Set options for codeBlock
+ * @return string
+ */
+	function writeEvents($inline = true, $options = array()) {
+		$out = '';
+		$rules = array();
+
+		if (!$this->_cacheEvents) {
+			return;
+		}
+		$data = $this->getCache();
+
+		if (empty($data)) {
+			return;
+		}
+
+		if ($this->_cacheToFile) {
+			$filename = md5($data);
+			if (!file_exists(JS . $filename . '.js')) {
+				cache(str_replace(WWW_ROOT, '', JS) . $filename . '.js', $data, '+999 days', 'public');
+			}
+			$out = $this->link($filename);
+		} else {
+			$out = $this->codeBlock("\n" . $data . "\n", $options);
+		}
+
+		if ($inline) {
+			return $out;
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+/**
+ * Includes the Prototype Javascript library (and anything else) inside a single script tag.
+ *
+ * Note: The recommended approach is to copy the contents of
+ * javascripts into your application's
+ * public/javascripts/ directory, and use @see javascriptIncludeTag() to
+ * create remote script links.
+ *
+ * @param string $script Script file to include
+ * @param array $options Set options for codeBlock
+ * @return string script with all javascript in/javascripts folder
+ */
+	function includeScript($script = "", $options = array()) {
+		if ($script == "") {
+			$files = scandir(JS);
+			$javascript = '';
+
+			foreach ($files as $file) {
+				if (substr($file, -3) == '.js') {
+					$javascript .= file_get_contents(JS . "{$file}") . "\n\n";
+				}
+			}
+		} else {
+			$javascript = file_get_contents(JS . "$script.js") . "\n\n";
+		}
+		return $this->codeBlock("\n\n" . $javascript, $options);
+	}
+/**
+ * Generates a JavaScript object in JavaScript Object Notation (JSON)
+ * from an array
+ *
+ * @param array $data Data to be converted
+ * @param array $options Set of options: block, prefix, postfix, stringKeys, quoteKeys, q
+ * @param string $prefix DEPRECATED, use $options['prefix'] instead. Prepends the string to the returned data
+ * @param string $postfix DEPRECATED, use $options['postfix'] instead. Appends the string to the returned data
+ * @param array $stringKeys DEPRECATED, use $options['stringKeys'] instead. A list of array keys to be treated as a string
+ * @param boolean $quoteKeys DEPRECATED, use $options['quoteKeys'] instead. If false, treats $stringKey as a list of keys *not* to be quoted
+ * @param string $q DEPRECATED, use $options['q'] instead. The type of quote to use
+ * @return string A JSON code block
+ */
+	function object($data = array(), $options = array(), $prefix = null, $postfix = null, $stringKeys = null, $quoteKeys = null, $q = null) {
+		if (!empty($options) && !is_array($options)) {
+			$options = array('block' => $options);
+		} else if (empty($options)) {
+			$options = array();
+		}
+
+		$defaultOptions = array(
+			'block' => false, 'prefix' => '', 'postfix' => '',
+			'stringKeys' => array(), 'quoteKeys' => true, 'q' => '"'
+		);
+		$options = array_merge($defaultOptions, $options, array_filter(compact(array_keys($defaultOptions))));
+
+		if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+
+		$out = $keys = array();
+		$numeric = true;
+
+		if ($this->useNative) {
+			$rt = json_encode($data);
+		} else {
+			if (is_array($data)) {
+				$keys = array_keys($data);
+			}
+
+			if (!empty($keys)) {
+				$numeric = (array_values($keys) === array_keys(array_values($keys)));
+			}
+
+			foreach ($data as $key => $val) {
+				if (is_array($val) || is_object($val)) {
+					$val = $this->object($val, array_merge($options, array('block' => false)));
+				} else {
+					$quoteStrings = (
+						!count($options['stringKeys']) ||
+						($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) ||
+						(!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true))
+					);
+					$val = $this->value($val, $quoteStrings);
+				}
+				if (!$numeric) {
+					$val = $options['q'] . $this->value($key, false) . $options['q'] . ':' . $val;
+				}
+				$out[] = $val;
+			}
+
+			if (!$numeric) {
+				$rt = '{' . join(',', $out) . '}';
+			} else {
+				$rt = '[' . join(',', $out) . ']';
+			}
+		}
+		$rt = $options['prefix'] . $rt . $options['postfix'];
+
+		if ($options['block']) {
+			$rt = $this->codeBlock($rt, array_diff_key($options, $defaultOptions));
+		}
+
+		return $rt;
+	}
+/**
+ * Converts a PHP-native variable of any type to a JSON-equivalent representation
+ *
+ * @param mixed $val A PHP variable to be converted to JSON
+ * @param boolean $quoteStrings If false, leaves string values unquoted
+ * @return string a JavaScript-safe/JSON representation of $val
+ */
+	function value($val, $quoteStrings = true) {
+		switch (true) {
+			case (is_array($val) || is_object($val)):
+				$val = $this->object($val);
+			break;
+			case ($val === null):
+				$val = 'null';
+			break;
+			case (is_bool($val)):
+				$val = ife($val, 'true', 'false');
+			break;
+			case (is_int($val)):
+				$val = $val;
+			break;
+			case (is_float($val)):
+				$val = sprintf("%.11f", $val);
+			break;
+			default:
+				$val = $this->escapeString($val);
+				if ($quoteStrings) {
+					$val = '"' . $val . '"';
+				}
+			break;
+		}
+		return $val;
+	}
+/**
+ * AfterRender callback.  Writes any cached events to the view, or to a temp file.
+ *
+ * @return null
+ */
+	function afterRender() {
+		if (!$this->enabled) {
+			return;
+		}
+		echo $this->writeEvents(true);
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/rss.php
===================================================================
--- cake/libs/view/helpers/rss.php	(revision 0)
+++ cake/libs/view/helpers/rss.php	(revision 0)
@@ -0,0 +1,277 @@
+<?php
+/* SVN FILE: $Id: rss.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * RSS Helper class file.
+ *
+ * Simplifies the output of RSS feeds.
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Helper', 'Xml');
+
+/**
+ * XML Helper class for easy output of XML structures.
+ *
+ * XmlHelper encloses all methods needed while working with XML documents.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class RssHelper extends XmlHelper {
+/**
+ * Helpers used by RSS Helper
+ *
+ * @var array
+ * @access public
+ **/
+	var $helpers = array('Time');
+/**
+ * Base URL
+ *
+ * @access public
+ * @var string
+ */
+	var $base = null;
+/**
+ * URL to current action.
+ *
+ * @access public
+ * @var string
+ */
+	var $here = null;
+/**
+ * Parameter array.
+ *
+ * @access public
+ * @var array
+ */
+	var $params = array();
+/**
+ * Current action.
+ *
+ * @access public
+ * @var string
+ */
+	var $action = null;
+/**
+ * POSTed model data
+ *
+ * @access public
+ * @var array
+ */
+	var $data = null;
+/**
+ * Name of the current model
+ *
+ * @access public
+ * @var string
+ */
+	var $model = null;
+/**
+ * Name of the current field
+ *
+ * @access public
+ * @var string
+ */
+	var $field = null;
+/**
+ * Default spec version of generated RSS
+ *
+ * @access public
+ * @var string
+ */
+	var $version = '2.0';
+/**
+ * Returns an RSS document wrapped in <rss /> tags
+ *
+ * @param  array  $attrib <rss /> tag attributes
+ * @return string An RSS document
+ */
+	function document($attrib = array(), $content = null) {
+		if ($content === null) {
+			$content = $attrib;
+			$attrib = array();
+		}
+		if (!isset($attrib['version']) || empty($attrib['version'])) {
+			$attrib['version'] = $this->version;
+		}
+
+		return $this->elem('rss', $attrib, $content);
+	}
+/**
+ * Returns an RSS <channel /> element
+ *
+ * @param  array  $attrib   <channel /> tag attributes
+ * @param  mixed  $elements Named array elements which are converted to tags
+ * @param  mixed  $content  Content (<item />'s belonging to this channel
+ * @return string An RSS <channel />
+ */
+	function channel($attrib = array(), $elements = array(), $content = null) {
+		$view =& ClassRegistry::getObject('view');
+
+		if (!isset($elements['title']) && !empty($view->pageTitle)) {
+			$elements['title'] = $view->pageTitle;
+		}
+		if (!isset($elements['link'])) {
+			$elements['link'] = '/';
+		}
+		if (!isset($elements['description'])) {
+			$elements['description'] = '';
+		}
+		$elements['link'] = $this->url($elements['link'], true);
+
+		$elems = '';
+		foreach ($elements as $elem => $data) {
+			$attributes = array();
+			if (is_array($data)) {
+				if (strtolower($elem) == 'cloud') {
+					$attributes = $data;
+					$data = array();
+				} elseif (isset($data['attrib']) && is_array($data['attrib'])) {
+					$attributes = $data['attrib'];
+					unset($data['attrib']);
+				} else {
+					$innerElements = '';
+					foreach ($data as $subElement => $value) {
+						$innerElements .= $this->elem($subElement, array(), $value);
+					}
+					$data = $innerElements;
+				}
+			}
+			$elems .= $this->elem($elem, $attributes, $data);
+		}
+		return $this->elem('channel', $attrib, $elems . $content, !($content === null));
+	}
+/**
+ * Transforms an array of data using an optional callback, and maps it to a set
+ * of <item /> tags
+ *
+ * @param  array  $items    The list of items to be mapped
+ * @param  mixed  $callback A string function name, or array containing an object
+ *                          and a string method name
+ * @return string A set of RSS <item /> elements
+ */
+	function items($items, $callback = null) {
+		if ($callback != null) {
+			$items = array_map($callback, $items);
+		}
+
+		$out = '';
+		$c = count($items);
+
+		for ($i = 0; $i < $c; $i++) {
+			$out .= $this->item(array(), $items[$i]);
+		}
+		return $out;
+	}
+/**
+ * Converts an array into an <item /> element and its contents
+ *
+ * @param  array  $attrib      The attributes of the <item /> element
+ * @param  array  $elements    The list of elements contained in this <item />
+ * @return string An RSS <item /> element
+ */
+	function item($att = array(), $elements = array()) {
+		$content = null;
+
+		if (isset($elements['link']) && !isset($elements['guid'])) {
+			$elements['guid'] = $elements['link'];
+		}
+
+		foreach ($elements as $key => $val) {
+			$attrib = array();
+			switch ($key) {
+				case 'pubDate' :
+					$val = $this->time($val);
+				break;
+				case 'category' :
+					if (is_array($val) && !empty($val[0])) {
+						foreach ($val as $category) {
+							$attrib = array();
+							if (isset($category['domain'])) {
+								$attrib['domain'] = $category['domain'];
+								unset($category['domain']);
+							}
+							$categories[] = $this->elem($key, $attrib, $category);
+						}
+						$elements[$key] = join('', $categories);
+						continue 2;
+					} else if (is_array($val) && isset($val['domain'])) {
+						$attrib['domain'] = $val['domain'];
+					}
+				break;
+				case 'link':
+				case 'guid':
+				case 'comments':
+					if (is_array($val) && isset($val['url'])) {
+						$attrib = $val;
+						unset($attrib['url']);
+						$val = $val['url'];
+					}
+					$val = $this->url($val, true);
+				break;
+				case 'source':
+					if (is_array($val) && isset($val['url'])) {
+						$attrib['url'] = $this->url($val['url'], true);
+						$val = $val['title'];
+					} elseif (is_array($val)) {
+						$attrib['url'] = $this->url($val[0], true);
+						$val = $val[1];
+					}
+				break;
+				case 'enclosure':
+					if (is_string($val['url']) && is_file(WWW_ROOT . $val['url']) && file_exists(WWW_ROOT . $val['url'])) {
+						if (!isset($val['length']) && strpos($val['url'], '://') === false) {
+							$val['length'] = sprintf("%u", filesize(WWW_ROOT . $val['url']));
+						}
+						if (!isset($val['type']) && function_exists('mime_content_type')) {
+							$val['type'] = mime_content_type(WWW_ROOT . $val['url']);
+						}
+					}
+					$val['url'] = $this->url($val['url'], true);
+					$attrib = $val;
+					$val = null;
+				break;
+			}
+			$escape = true;
+			if (is_array($val) && isset($val['convertEntities'])) {
+				$escape = $val['convertEntities'];
+				unset($val['convertEntities']);
+			}
+			if (!is_null($val) && $escape) {
+				$val = h($val);
+			}
+			$elements[$key] = $this->elem($key, $attrib, $val);
+		}
+		if (!empty($elements)) {
+			$content = join('', $elements);
+		}
+		return $this->output($this->elem('item', $att, $content, !($content === null)));
+	}
+/**
+ * Converts a time in any format to an RSS time
+ *
+ * @param  mixed  $time
+ * @return string An RSS-formatted timestamp
+ * @see TimeHelper::toRSS
+ */
+	function time($time) {
+		return $this->Time->toRSS($time);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/helpers/number.php
===================================================================
--- cake/libs/view/helpers/number.php	(revision 0)
+++ cake/libs/view/helpers/number.php	(revision 0)
@@ -0,0 +1,192 @@
+<?php
+/* SVN FILE: $Id: number.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Number Helper.
+ *
+ * Methods to make numbers more readable.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Number helper library.
+ *
+ * Methods to make numbers more readable.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ */
+class NumberHelper extends AppHelper {
+/**
+ * Formats a number with a level of precision.
+ *
+ * @param  float	$number	A floating point number.
+ * @param  integer $precision The precision of the returned number.
+ * @return float Enter description here...
+ * @static
+ */
+	function precision($number, $precision = 3) {
+		return sprintf("%01.{$precision}f", $number);
+	}
+/**
+ * Returns a formatted-for-humans file size.
+ *
+ * @param integer $length Size in bytes
+ * @return string Human readable size
+ * @static
+ */
+	function toReadableSize($size) {
+		switch ($size) {
+			case 0:
+				return '0 Bytes';
+			case 1:
+				return '1 Byte';
+			case $size < 1024:
+				return $size . ' Bytes';
+			case round($size / 1024) < 1024:
+				return $this->precision($size / 1024, 0) . ' KB';
+			case round($size / 1024 / 1024, 2) < 1024:
+				return $this->precision($size / 1024 / 1024, 2) . ' MB';
+			case round($size / 1024 / 1024 / 1024, 2) < 1024:
+				return $this->precision($size / 1024 / 1024 / 1024, 2) . ' GB';
+			default:
+				return $this->precision($size / 1024 / 1024 / 1024 / 1024, 2) . ' TB';
+		}
+	}
+/**
+ * Formats a number into a percentage string.
+ *
+ * @param float $number A floating point number
+ * @param integer $precision The precision of the returned number
+ * @return string Percentage string
+ * @static
+ */
+	function toPercentage($number, $precision = 2) {
+		return $this->precision($number, $precision) . '%';
+	}
+/**
+ * Formats a number into a currency format.
+ *
+ * @param float $number A floating point number
+ * @param integer $options if int then places, if string then before, if (,.-) then use it
+ * 							or array with places and before keys
+ * @return string formatted number
+ * @static
+ */
+	function format($number, $options = false) {
+		$places = 0;
+		if (is_int($options)) {
+			$places = $options;
+		}
+
+		$separators = array(',', '.', '-', ':');
+
+		$before = $after = null;
+		if (is_string($options) && !in_array($options, $separators)) {
+			$before = $options;
+		}
+		$thousands = ',';
+		if (!is_array($options) && in_array($options, $separators)) {
+			$thousands = $options;
+		}
+		$decimals = '.';
+		if (!is_array($options) && in_array($options, $separators)) {
+			$decimals = $options;
+		}
+
+		$escape = true;
+		if (is_array($options)) {
+			$options = array_merge(array('before'=>'$', 'places' => 2, 'thousands' => ',', 'decimals' => '.'), $options);
+			extract($options);
+		}
+
+		$out = $before . number_format($number, $places, $decimals, $thousands) . $after;
+
+		if ($escape) {
+			return h($out);
+		}
+		return $out;
+	}
+/**
+ * Formats a number into a currency format.
+ *
+ * @param float $number
+ * @param string $currency Shortcut to default options. Valid values are 'USD', 'EUR', 'GBP', otherwise
+ *               set at least 'before' and 'after' options.
+ * @param array $options
+ * @return string Number formatted as a currency.
+ */
+	function currency($number, $currency = 'USD', $options = array()) {
+		$default = array(
+			'before'=>'', 'after' => '', 'zero' => '0', 'places' => 2, 'thousands' => ',',
+			'decimals' => '.','negative' => '()', 'escape' => true
+		);
+		$currencies = array(
+			'USD' => array(
+				'before' => '$', 'after' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => ',',
+				'decimals' => '.', 'negative' => '()', 'escape' => true
+			),
+			'GBP' => array(
+				'before'=>'&#163;', 'after' => 'p', 'zero' => 0, 'places' => 2, 'thousands' => ',',
+				'decimals' => '.', 'negative' => '()','escape' => false
+			),
+			'EUR' => array(
+				'before'=>'&#8364;', 'after' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => '.',
+				'decimals' => ',', 'negative' => '()', 'escape' => false
+			)
+		);
+
+		if (isset($currencies[$currency])) {
+			$default = $currencies[$currency];
+		} elseif (is_string($currency)) {
+			$options['before'] = $currency;
+		}
+
+		$options = array_merge($default, $options);
+
+		$result = null;
+
+		if ($number == 0 ) {
+			if ($options['zero'] !== 0 ) {
+				return $options['zero'];
+			}
+			$options['after'] = null;
+		} elseif ($number < 1 && $number > -1 ) {
+			$multiply = intval('1' . str_pad('', $options['places'], '0'));
+			$number = $number * $multiply;
+			$options['before'] = null;
+			$options['places'] = null;
+		} else {
+			$options['after'] = null;
+		}
+
+		$abs = abs($number);
+		$result = $this->format($abs, $options);
+
+		if ($number < 0 ) {
+			if ($options['negative'] == '()') {
+				$result = '(' . $result .')';
+			} else {
+				$result = $options['negative'] . $result;
+			}
+		}
+		return $result;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/scaffolds/view.ctp
===================================================================
--- cake/libs/view/scaffolds/view.ctp	(revision 0)
+++ cake/libs/view/scaffolds/view.ctp	(revision 0)
@@ -0,0 +1,159 @@
+<?php
+/* SVN FILE: $Id: view.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.scaffolds
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> view">
+<h2><?php echo sprintf(__("View %s", true), $singularHumanName);?></h2>
+	<dl>
+<?php
+$i = 0;
+foreach ($scaffoldFields as $_field) {
+	$class = null;
+	if ($i++ % 2 == 0) {
+		$class = ' class="altrow"';
+	}
+	$isKey = false;
+	if (!empty($associations['belongsTo'])) {
+		foreach ($associations['belongsTo'] as $_alias => $_details) {
+			if ($_field === $_details['foreignKey']) {
+				$isKey = true;
+				echo "\t\t<dt{$class}>".Inflector::humanize($_alias)."</dt>\n";
+				echo "\t\t<dd{$class}>\n\t\t\t" . $html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller'=> $_details['controller'], 'action'=>'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t&nbsp;</dd>\n";
+				break;
+			}
+		}
+	}
+	if ($isKey !== true) {
+		echo "\t\t<dt{$class}>".Inflector::humanize($_field)."</dt>\n";
+		echo "\t\t<dd{$class}>\n\t\t\t{${$singularVar}[$modelClass][$_field]}\n&nbsp;\t\t</dd>\n";
+	}
+}
+?>
+	</dl>
+</div>
+<div class="actions">
+	<ul>
+<?php
+	echo "\t\t<li>" .$html->link(sprintf(__('Edit %s', true), $singularHumanName),   array('action'=>'edit', ${$singularVar}[$modelClass][$primaryKey])). " </li>\n";
+	echo "\t\t<li>" .$html->link(sprintf(__('Delete %s', true), $singularHumanName), array('action'=>'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __('Are you sure you want to delete', true).' #' . ${$singularVar}[$modelClass][$primaryKey] . '?'). " </li>\n";
+	echo "\t\t<li>" .$html->link(sprintf(__('List %s', true), $pluralHumanName), array('action'=>'index')). " </li>\n";
+	echo "\t\t<li>" .$html->link(sprintf(__('New %s', true), $singularHumanName), array('action'=>'add')). " </li>\n";
+
+	$done = array();
+	foreach ($associations as $_type => $_data) {
+		foreach ($_data as $_alias => $_details) {
+			if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) {
+				echo "\t\t<li>".$html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller'=> $_details['controller'], 'action'=>'index'))."</li>\n";
+				echo "\t\t<li>".$html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller'=> $_details['controller'], 'action'=>'add'))."</li>\n";
+				$done[] = $_details['controller'];
+			}
+		}
+	}
+?>
+	</ul>
+</div>
+<?php
+if (!empty($associations['hasOne'])) :
+foreach ($associations['hasOne'] as $_alias => $_details): ?>
+<div class="related">
+	<h3><?php echo sprintf(__("Related %s", true), Inflector::humanize($_details['controller']));?></h3>
+<?php if (!empty(${$singularVar}[$_alias])):?>
+	<dl>
+<?php
+		$i = 0;
+		$otherFields = array_keys(${$singularVar}[$_alias]);
+		foreach ($otherFields as $_field) {
+			$class = null;
+			if ($i++ % 2 == 0) {
+				$class = ' class="altrow"';
+			}
+			echo "\t\t<dt{$class}>".Inflector::humanize($_field)."</dt>\n";
+			echo "\t\t<dd{$class}>\n\t" .${$singularVar}[$_alias][$_field] ."\n&nbsp;</dd>\n";
+		}
+?>
+	</dl>
+<?php endif; ?>
+	<div class="actions">
+		<ul>
+			<li><?php echo $html->link(sprintf(__('Edit %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller'=> $_details['controller'], 'action'=>'edit', ${$singularVar}[$_alias][$_details['primaryKey']]))."</li>\n";?>
+		</ul>
+	</div>
+</div>
+<?php
+endforeach;
+endif;
+
+if (empty($associations['hasMany'])) {
+	$associations['hasMany'] = array();
+}
+if (empty($associations['hasAndBelongsToMany'])) {
+	$associations['hasAndBelongsToMany'] = array();
+}
+$relations = array_merge($associations['hasMany'], $associations['hasAndBelongsToMany']);
+$i = 0;
+foreach ($relations as $_alias => $_details):
+$otherSingularVar = Inflector::variable($_alias);
+?>
+<div class="related">
+	<h3><?php echo sprintf(__("Related %s", true), Inflector::humanize($_details['controller']));?></h3>
+<?php if (!empty(${$singularVar}[$_alias])):?>
+	<table cellpadding="0" cellspacing="0">
+	<tr>
+<?php
+		$otherFields = array_keys(${$singularVar}[$_alias][0]);
+		foreach ($otherFields as $_field) {
+			echo "\t\t<th>".Inflector::humanize($_field)."</th>\n";
+		}
+?>
+		<th class="actions">Actions</th>
+	</tr>
+<?php
+		$i = 0;
+		foreach (${$singularVar}[$_alias] as ${$otherSingularVar}):
+			$class = null;
+			if ($i++ % 2 == 0) {
+				$class = ' class="altrow"';
+			}
+		echo "\t\t<tr{$class}>\n";
+
+			foreach ($otherFields as $_field) {
+				echo "\t\t\t<td>".${$otherSingularVar}[$_field]."</td>\n";
+			}
+
+			echo "\t\t\t<td class=\"actions\">\n";
+			echo "\t\t\t\t" . $html->link(__('View', true), array('controller'=> $_details['controller'], 'action'=>'view', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
+			echo "\t\t\t\t" . $html->link(__('Edit', true), array('controller'=> $_details['controller'], 'action'=>'edit', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
+			echo "\t\t\t\t" . $html->link(__('Delete', true), array('controller'=> $_details['controller'], 'action'=>'delete', ${$otherSingularVar}[$_details['primaryKey']]), null, __('Are you sure you want to delete', true).' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?'). "\n";
+			echo "\t\t\t</td>\n";
+		echo "\t\t</tr>\n";
+		endforeach;
+?>
+	</table>
+<?php endif; ?>
+	<div class="actions">
+		<ul>
+			<li><?php echo $html->link(sprintf(__("New %s", true), Inflector::humanize(Inflector::underscore($_alias))), array('controller'=> $_details['controller'], 'action'=>'add'));?> </li>
+		</ul>
+	</div>
+</div>
+<?php endforeach;?>
\ No newline at end of file
Index: cake/libs/view/scaffolds/edit.ctp
===================================================================
--- cake/libs/view/scaffolds/edit.ctp	(revision 0)
+++ cake/libs/view/scaffolds/edit.ctp	(revision 0)
@@ -0,0 +1,51 @@
+<?php
+/* SVN FILE: $Id: edit.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.scaffolds
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> form">
+<?php
+	echo $form->create();
+	echo $form->inputs(null, array('created', 'modified', 'updated'));
+	echo $form->end(__('Submit', true));
+?>
+</div>
+<div class="actions">
+	<ul>
+<?php if ($this->action != 'add'):?>
+		<li><?php echo $html->link(__('Delete', true), array('action'=>'delete', $form->value($modelClass.'.'.$primaryKey)), null, __('Are you sure you want to delete', true).' #' . $form->value($modelClass.'.'.$primaryKey)); ?></li>
+<?php endif;?>
+		<li><?php echo $html->link(__('List', true).' '.$pluralHumanName, array('action'=>'index'));?></li>
+<?php
+		$done = array();
+		foreach ($associations as $_type => $_data) {
+			foreach ($_data as $_alias => $_details) {
+				if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) {
+					echo "\t\t<li>".$html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller'=> $_details['controller'], 'action'=>'index'))."</li>\n";
+					echo "\t\t<li>".$html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller'=> $_details['controller'], 'action'=>'add'))."</li>\n";
+					$done[] = $_details['controller'];
+				}
+			}
+		}
+?>
+	</ul>
+</div>
\ No newline at end of file
Index: cake/libs/view/scaffolds/index.ctp
===================================================================
--- cake/libs/view/scaffolds/index.ctp	(revision 0)
+++ cake/libs/view/scaffolds/index.ctp	(revision 0)
@@ -0,0 +1,97 @@
+<?php
+/* SVN FILE: $Id: index.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div class="<?php echo $pluralVar;?> index">
+<h2><?php echo $pluralHumanName;?></h2>
+<p><?php
+echo $paginator->counter(array(
+	'format' => 'Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%'
+));
+?></p>
+<table cellpadding="0" cellspacing="0">
+<tr>
+<?php foreach ($scaffoldFields as $_field):?>
+	<th><?php echo $paginator->sort($_field);?></th>
+<?php endforeach;?>
+	<th><?php __('Actions');?></th>
+</tr>
+<?php
+$i = 0;
+foreach (${$pluralVar} as ${$singularVar}):
+	$class = null;
+	if ($i++ % 2 == 0) {
+		$class = ' class="altrow"';
+	}
+echo "\n";
+	echo "\t<tr{$class}>\n";
+		foreach ($scaffoldFields as $_field) {
+			$isKey = false;
+			if (!empty($associations['belongsTo'])) {
+				foreach ($associations['belongsTo'] as $_alias => $_details) {
+					if ($_field === $_details['foreignKey']) {
+						$isKey = true;
+						echo "\t\t<td>\n\t\t\t" . $html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller'=> $_details['controller'], 'action'=>'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t</td>\n";
+						break;
+					}
+				}
+			}
+			if ($isKey !== true) {
+				echo "\t\t<td>\n\t\t\t" . ${$singularVar}[$modelClass][$_field] . " \n\t\t</td>\n";
+			}
+		}
+
+		echo "\t\t<td class=\"actions\">\n";
+		echo "\t\t\t" . $html->link(__('View', true), array('action'=>'view', ${$singularVar}[$modelClass][$primaryKey])) . "\n";
+	 	echo "\t\t\t" . $html->link(__('Edit', true), array('action'=>'edit', ${$singularVar}[$modelClass][$primaryKey])) . "\n";
+	 	echo "\t\t\t" . $html->link(__('Delete', true), array('action'=>'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __('Are you sure you want to delete', true).' #' . ${$singularVar}[$modelClass][$primaryKey]) . "\n";
+		echo "\t\t</td>\n";
+	echo "\t</tr>\n";
+
+endforeach;
+echo "\n";
+?>
+</table>
+</div>
+<div class="paging">
+<?php echo "\t" . $paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled')) . "\n";?>
+ | <?php echo $paginator->numbers() . "\n"?>
+<?php echo "\t ". $paginator->next(__('next', true) .' >>', array(), null, array('class'=>'disabled')) . "\n";?>
+</div>
+<div class="actions">
+	<ul>
+		<li><?php echo $html->link('New '.$singularHumanName, array('action'=>'add')); ?></li>
+<?php
+		$done = array();
+		foreach ($associations as $_type => $_data) {
+			foreach ($_data as $_alias => $_details) {
+				if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) {
+					echo "\t\t<li>".$html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller'=> $_details['controller'], 'action'=>'index'))."</li>\n";
+					echo "\t\t<li>".$html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller'=> $_details['controller'], 'action'=>'add'))."</li>\n";
+					$done[] = $_details['controller'];
+				}
+			}
+		}
+?>
+	</ul>
+</div>
\ No newline at end of file
Index: cake/libs/view/helper.php
===================================================================
--- cake/libs/view/helper.php	(revision 0)
+++ cake/libs/view/helper.php	(revision 0)
@@ -0,0 +1,742 @@
+<?php
+/* SVN FILE: $Id: helper.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Backend for helpers.
+ *
+ * Internal methods for the Helpers.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libs
+ */
+App::import('Core', 'Overloadable');
+
+/**
+ * Backend for helpers.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ */
+class Helper extends Overloadable {
+/**
+ * List of helpers used by this helper
+ *
+ * @var array
+ */
+	var $helpers = null;
+/**
+ * Base URL
+ *
+ * @var string
+ */
+	var $base = null;
+/**
+ * Webroot path
+ *
+ * @var string
+ */
+	var $webroot = null;
+/**
+ * Theme name
+ *
+ * @var string
+ */
+	var $themeWeb = null;
+/**
+ * URL to current action.
+ *
+ * @var string
+ */
+	var $here = null;
+/**
+ * Parameter array.
+ *
+ * @var array
+ */
+	var $params = array();
+/**
+ * Current action.
+ *
+ * @var string
+ */
+	var $action = null;
+/**
+ * Plugin path
+ *
+ * @var string
+ */
+	var $plugin = null;
+/**
+ * POST data for models
+ *
+ * @var array
+ */
+	var $data = null;
+/**
+ * List of named arguments
+ *
+ * @var array
+ */
+	var $namedArgs = null;
+/**
+ * URL argument separator character
+ *
+ * @var string
+ */
+	var $argSeparator = null;
+/**
+ * Contains model validation errors of form post-backs
+ *
+ * @access public
+ * @var array
+ */
+	var $validationErrors = null;
+/**
+ * Holds tag templates.
+ *
+ * @access public
+ * @var array
+ */
+	var $tags = array();
+/**
+ * Holds the content to be cleaned.
+ *
+ * @access private
+ * @var mixed
+ */
+	var $__tainted = null;
+/**
+ * Holds the cleaned content.
+ *
+ * @access private
+ * @var mixed
+ */
+	var $__cleaned = null;
+/**
+ * Default overload methods
+ *
+ * @access protected
+ */
+	function get__($name) {}
+	function set__($name, $value) {}
+	function call__($method, $params) {
+		trigger_error(sprintf(__('Method %1$s::%2$s does not exist', true), get_class($this), $method), E_USER_WARNING);
+	}
+
+/**
+ * Parses tag templates into $this->tags.
+ *
+ * @param $name file name
+ * @return array merged tags from config/$name.php
+ */
+	function loadConfig($name = 'tags') {
+		if (file_exists(CONFIGS . $name .'.php')) {
+			require(CONFIGS . $name .'.php');
+			if (isset($tags)) {
+				$this->tags = array_merge($this->tags, $tags);
+			}
+		}
+		return $this->tags;
+	}
+/**
+ * Finds URL for specified action.
+ *
+ * Returns an URL pointing to a combination of controller and action. Param
+ * $url can be:
+ *	+ Empty - the method will find adress to actuall controller/action.
+ *	+ '/' - the method will find base URL of application.
+ *	+ A combination of controller/action - the method will find url for it.
+ *
+ * @param  mixed  $url    Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4"
+ *                        or an array specifying any of the following: 'controller', 'action',
+ *                        and/or 'plugin', in addition to named arguments (keyed array elements),
+ *                        and standard URL arguments (indexed array elements)
+ * @param boolean $full   If true, the full base URL will be prepended to the result
+ * @return string  Full translated URL with base path.
+ */
+	function url($url = null, $full = false) {
+		return Router::url($url, array('full' => $full, 'escape' => true));
+	}
+/**
+ * Checks if a file exists when theme is used, if no file is found default location is returned
+ *
+ * @param  string  $file
+ * @return string  $webPath web path to file.
+ */
+	function webroot($file) {
+		$webPath = "{$this->webroot}" . $file;
+		if (!empty($this->themeWeb)) {
+			$os = env('OS');
+			if (!empty($os) && strpos($os, 'Windows') !== false) {
+				if (strpos(WWW_ROOT . $this->themeWeb  . $file, '\\') !== false) {
+					$path = str_replace('/', '\\', WWW_ROOT . $this->themeWeb  . $file);
+				}
+			} else {
+				$path = WWW_ROOT . $this->themeWeb  . $file;
+			}
+			if (file_exists($path)) {
+				$webPath = "{$this->webroot}" . $this->themeWeb . $file;
+			}
+		}
+		if (strpos($webPath, '//') !== false) {
+			return str_replace('//', '/', $webPath);
+		}
+		return $webPath;
+	}
+
+/**
+ * Used to remove harmful tags from content
+ *
+ * @param mixed $output
+ * @return cleaned content for output
+ * @access public
+ */
+	function clean($output) {
+		$this->__reset();
+		if (empty($output)) {
+			return null;
+		}
+		if (is_array($output)) {
+			foreach ($output as $key => $value) {
+				$return[$key] = $this->clean($value);
+			}
+			return $return;
+		}
+		$this->__tainted = $output;
+		$this->__clean();
+		return $this->__cleaned;
+	}
+/**
+ * Returns a space-delimited string with items of the $options array. If a
+ * key of $options array happens to be one of:
+ *	+ 'compact'
+ *	+ 'checked'
+ *	+ 'declare'
+ *	+ 'readonly'
+ *	+ 'disabled'
+ *	+ 'selected'
+ *	+ 'defer'
+ *	+ 'ismap'
+ *	+ 'nohref'
+ *	+ 'noshade'
+ *	+ 'nowrap'
+ *	+ 'multiple'
+ *	+ 'noresize'
+ *
+ * And its value is one of:
+ *	+ 1
+ *	+ true
+ *	+ 'true'
+ *
+ * Then the value will be reset to be identical with key's name.
+ * If the value is not one of these 3, the parameter is not output.
+ *
+ * @param  array  $options Array of options.
+ * @param  array  $exclude Array of options to be excluded.
+ * @param  string $insertBefore String to be inserted before options.
+ * @param  string $insertAfter  String to be inserted ater options.
+ * @return string
+ */
+	function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
+		if (is_array($options)) {
+			$options = array_merge(array('escape' => true), $options);
+
+			if (!is_array($exclude)) {
+				$exclude = array();
+			}
+			$keys = array_diff(array_keys($options), array_merge((array)$exclude, array('escape')));
+			$values = array_intersect_key(array_values($options), $keys);
+			$escape = $options['escape'];
+			$attributes = array();
+
+			foreach ($keys as $index => $key) {
+				$attributes[] = $this->__formatAttribute($key, $values[$index], $escape);
+			}
+			$out = implode(' ', $attributes);
+		} else {
+			$out = $options;
+		}
+		return $out ? $insertBefore . $out . $insertAfter : '';
+	}
+/**
+ * @param  string $key
+ * @param  string $value
+ * @return string
+ * @access private
+ */
+	function __formatAttribute($key, $value, $escape = true) {
+		$attribute = '';
+		$attributeFormat = '%s="%s"';
+		$minimizedAttributes = array('compact', 'checked', 'declare', 'readonly', 'disabled', 'selected', 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize');
+		if (is_array($value)) {
+			$value = '';
+		}
+
+		if (in_array($key, $minimizedAttributes)) {
+			if ($value === 1 || $value === true || $value === 'true' || $value == $key) {
+				$attribute = sprintf($attributeFormat, $key, $key);
+			}
+		} else {
+			$attribute = sprintf($attributeFormat, $key, ($escape ? h($value) : $value));
+		}
+		return $attribute;
+	}
+/**
+ * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
+ *
+ * @param mixed $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
+ * @param boolean $setScope Sets the view scope to the model specified in $tagValue
+ * @return void
+ */
+	function setEntity($entity, $setScope = false) {
+		$view =& ClassRegistry::getObject('view');
+
+		if ($setScope) {
+			$view->modelScope = false;
+		} elseif (join('.', $view->entity()) == $entity) {
+			return;
+		}
+
+		if ($entity === null) {
+			$view->model = null;
+			$view->association = null;
+			$view->modelId = null;
+			$view->modelScope = false;
+			return;
+		}
+
+		$model = $view->model;
+		$sameScope = $hasField = false;
+		$parts = array_values(Set::filter(explode('.', $entity), true));
+
+		if (empty($parts)) {
+			return;
+		}
+
+		if (count($parts) === 1 || is_numeric($parts[0])) {
+			$sameScope = true;
+		} else {
+			if (ClassRegistry::isKeySet($parts[0])) {
+				$model = $parts[0];
+			}
+		}
+
+		if (ClassRegistry::isKeySet($model)) {
+			$ModelObj =& ClassRegistry::getObject($model);
+			for ($i = 0; $i < count($parts); $i++) {
+				if ($ModelObj->hasField($parts[$i]) || array_key_exists($parts[$i], $ModelObj->validate)) {
+					$hasField = $i;
+					if ($hasField === 0 || ($hasField === 1 && is_numeric($parts[0]))) {
+						$sameScope = true;
+					}
+					break;
+				}
+			}
+
+			if ($sameScope === true && in_array($parts[0], array_keys($ModelObj->hasAndBelongsToMany))) {
+				$sameScope = false;
+			}
+		}
+
+		if (!$view->association && $parts[0] == $view->field && $view->field != $view->model) {
+			array_unshift($parts, $model);
+			$hasField = true;
+		}
+		$view->field = $view->modelId = $view->fieldSuffix = $view->association = null;
+
+		switch (count($parts)) {
+			case 1:
+				if ($view->modelScope === false) {
+					$view->model = $parts[0];
+				} else {
+					$view->field = $parts[0];
+					if ($sameScope === false) {
+						$view->association = $parts[0];
+					}
+				}
+			break;
+			case 2:
+				if ($view->modelScope === false) {
+					list($view->model, $view->field) = $parts;
+				} elseif ($sameScope === true && $hasField === 0) {
+					list($view->field, $view->fieldSuffix) = $parts;
+				} elseif ($sameScope === true && $hasField === 1) {
+					list($view->modelId, $view->field) = $parts;
+				} else {
+					list($view->association, $view->field) = $parts;
+				}
+			break;
+			case 3:
+				if ($sameScope === true && $hasField === 1) {
+					list($view->modelId, $view->field, $view->fieldSuffix) = $parts;
+				} elseif ($hasField === 2) {
+					list($view->association, $view->modelId, $view->field) = $parts;
+				} else {
+					list($view->association, $view->field, $view->fieldSuffix) = $parts;
+				}
+			break;
+			case 4:
+				if ($parts[0] === $view->model) {
+					list($view->model, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
+				} else {
+					list($view->association, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
+				}
+			break;
+		}
+
+		if (!isset($view->model) || empty($view->model)) {
+			$view->model = $view->association;
+			$view->association = null;
+		} elseif ($view->model === $view->association) {
+			$view->association = null;
+		}
+
+		if ($setScope) {
+			$view->modelScope = true;
+		}
+	}
+/**
+ * Gets the currently-used model of the rendering context.
+ *
+ * @return string
+ */
+	function model() {
+		$view =& ClassRegistry::getObject('view');
+		if (!empty($view->association)) {
+			return $view->association;
+		} else {
+			return $view->model;
+		}
+	}
+/**
+ * Gets the ID of the currently-used model of the rendering context.
+ *
+ * @return mixed
+ */
+	function modelID() {
+		$view =& ClassRegistry::getObject('view');
+		return $view->modelId;
+	}
+/**
+ * Gets the currently-used model field of the rendering context.
+ *
+ * @return string
+ */
+	function field() {
+		$view =& ClassRegistry::getObject('view');
+		return $view->field;
+	}
+/**
+ * Returns false if given FORM field has no errors. Otherwise it returns the constant set in the array Model->validationErrors.
+ *
+ * @param string $model		Model name as string
+ * @param string $field		Fieldname as string
+ * @param integer $modelID	Unique index identifying this record within the form
+ * @return boolean True on errors.
+ */
+	function tagIsInvalid($model = null, $field = null, $modelID = null) {
+		foreach (array('model', 'field', 'modelID') as $key) {
+			if (empty(${$key})) {
+				${$key} = $this->{$key}();
+			}
+		}
+		$view =& ClassRegistry::getObject('view');
+		$errors = $this->validationErrors;
+
+		if ($view->model !== $model && isset($errors[$view->model][$model])) {
+			$errors = $errors[$view->model];
+		}
+
+		if (!isset($modelID)) {
+			return empty($errors[$model][$field]) ? 0 : $errors[$model][$field];
+		} else {
+			return empty($errors[$model][$modelID][$field]) ? 0 : $errors[$model][$modelID][$field];
+		}
+	}
+/**
+ * Generates a DOM ID for the selected element, if one is not set.
+ *
+ * @param mixed $options
+ * @param string $id
+ * @return mixed
+ */
+	function domId($options = null, $id = 'id') {
+		$view =& ClassRegistry::getObject('view');
+
+		if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
+			unset($options[$id]);
+			return $options;
+		} elseif (!is_array($options) && $options !== null) {
+			$this->setEntity($options);
+			return $this->domId();
+		}
+
+		$dom = $this->model() . $this->modelID() . Inflector::camelize($view->field) . Inflector::camelize($view->fieldSuffix);
+
+		if (is_array($options) && !array_key_exists($id, $options)) {
+			$options[$id] = $dom;
+		} elseif ($options === null) {
+			return $dom;
+		}
+		return $options;
+	}
+/**
+ * Gets the input field name for the current tag
+ *
+ * @param array $options
+ * @param string $key
+ * @return array
+ */
+	function __name($options = array(), $field = null, $key = 'name') {
+		$view =& ClassRegistry::getObject('view');
+
+		if ($options === null) {
+			$options = array();
+		} elseif (is_string($options)) {
+			$field = $options;
+			$options = 0;
+		}
+
+		if (!empty($field)) {
+			$this->setEntity($field);
+		}
+
+		if (is_array($options) && array_key_exists($key, $options)) {
+			return $options;
+		}
+
+		switch ($field) {
+			case '_method':
+				$name = $field;
+			break;
+			default:
+				$name = 'data[' . join('][', $view->entity()) . ']';
+			break;
+		}
+
+		if (is_array($options)) {
+			$options[$key] = $name;
+			return $options;
+		} else {
+			return $name;
+		}
+	}
+/**
+ * Gets the data for the current tag
+ *
+ * @param array $options
+ * @param string $key
+ * @return array
+ * @access public
+ */
+	function value($options = array(), $field = null, $key = 'value') {
+		if ($options === null) {
+			$options = array();
+		} elseif (is_string($options)) {
+			$field = $options;
+			$options = 0;
+		}
+
+		if (!empty($field)) {
+			$this->setEntity($field);
+		}
+
+		if (is_array($options) && isset($options[$key])) {
+			return $options;
+		}
+
+		$result = null;
+
+		if (isset($this->data[$this->model()][$this->field()])) {
+			$result = $this->data[$this->model()][$this->field()];
+		} elseif (isset($this->data[$this->field()]) && is_array($this->data[$this->field()])) {
+			if (ClassRegistry::isKeySet($this->field())) {
+				$model =& ClassRegistry::getObject($this->field());
+				$result = $this->__selectedArray($this->data[$this->field()], $model->primaryKey);
+			}
+		} elseif (isset($this->data[$this->model()][$this->modelID()][$this->field()])) {
+			$result = $this->data[$this->model()][$this->modelID()][$this->field()];
+		}
+
+		if (is_array($result)) {
+			$view =& ClassRegistry::getObject('view');
+			if (isset($result[$view->fieldSuffix])) {
+				$result = $result[$view->fieldSuffix];
+			}
+		}
+
+		if (is_array($options)) {
+			if (empty($result) && isset($options['default'])) {
+				$result = $options['default'];
+			}
+			unset($options['default']);
+		}
+
+		if (is_array($options)) {
+			$options[$key] = $result;
+			return $options;
+		} else {
+			return $result;
+		}
+	}
+/**
+ * Sets the defaults for an input tag
+ *
+ * @param array $options
+ * @param string $key
+ * @return array
+ * @access protected
+ */
+	function _initInputField($field, $options = array()) {
+		if ($field !== null) {
+			$this->setEntity($field);
+		}
+		$options = (array)$options;
+		$options = $this->__name($options);
+		$options = $this->value($options);
+		$options = $this->domId($options);
+		if ($this->tagIsInvalid()) {
+			$options = $this->addClass($options, 'form-error');
+		}
+		return $options;
+	}
+/**
+ * Adds the given class to the element options
+ *
+ * @param array $options
+ * @param string $class
+ * @param string $key
+ * @return array
+ */
+	function addClass($options = array(), $class = null, $key = 'class') {
+		if (isset($options[$key]) && trim($options[$key]) != '') {
+			$options[$key] .= ' ' . $class;
+		} else {
+			$options[$key] = $class;
+		}
+		return $options;
+	}
+/**
+ * Returns a string generated by a helper method
+ *
+ * This method can be overridden in subclasses to do generalized output post-processing
+ *
+ * @param  string  $str	String to be output.
+ * @return string
+ */
+	function output($str) {
+		return $str;
+	}
+/**
+ * Before render callback.  Overridden in subclasses.
+ *
+ */
+	function beforeRender() {
+	}
+/**
+ * After render callback.  Overridden in subclasses.
+ *
+ */
+	function afterRender() {
+	}
+/**
+ * Before layout callback.  Overridden in subclasses.
+ *
+ */
+	function beforeLayout() {
+	}
+/**
+ * After layout callback.  Overridden in subclasses.
+ *
+ */
+	function afterLayout() {
+	}
+/**
+ * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
+ * options for a multiple select element
+ *
+ * @param mixed $data
+ * @param string $key
+ * @return array
+ * @access private
+ */
+	function __selectedArray($data, $key = 'id') {
+		if (!is_array($data)) {
+			$model = $data;
+			if (!empty($this->data[$model][$model])) {
+				return $this->data[$model][$model];
+			}
+			if (!empty($this->data[$model])) {
+				$data = $this->data[$model];
+			}
+		}
+		$array = array();
+		if (!empty($data)) {
+			foreach ($data as $var) {
+				$array[$var[$key]] = $var[$key];
+			}
+		}
+		return $array;
+	}
+/**
+ * Resets the vars used by Helper::clean() to null
+ *
+ * @access private
+ */
+	function __reset() {
+		$this->__tainted = null;
+		$this->__cleaned = null;
+	}
+/**
+ * Removes harmful content from output
+ *
+ * @access private
+ */
+	function __clean() {
+		if (get_magic_quotes_gpc()) {
+			$this->__cleaned = stripslashes($this->__tainted);
+		} else {
+			$this->__cleaned = $this->__tainted;
+		}
+
+		$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
+		$this->__cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->__cleaned);
+		$this->__cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->__cleaned);
+		$this->__cleaned = html_entity_decode($this->__cleaned, ENT_COMPAT, "UTF-8");
+		$this->__cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->__cleaned);
+		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $this->__cleaned);
+		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $this->__cleaned);
+		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu','$1=$2nomozbinding...', $this->__cleaned);
+		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->__cleaned);
+		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
+		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
+		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*>#iUu', "$1>", $this->__cleaned);
+		$this->__cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->__cleaned);
+		do {
+			$oldstring = $this->__cleaned;
+			$this->__cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->__cleaned);
+		} while ($oldstring != $this->__cleaned);
+		$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/media.php
===================================================================
--- cake/libs/view/media.php	(revision 0)
+++ cake/libs/view/media.php	(revision 0)
@@ -0,0 +1,192 @@
+<?php
+/* SVN FILE: $Id: media.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Methods to display or download any type of file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 1.2.0.5714
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+class MediaView extends View {
+/**
+ * Holds known mime type mappings
+ *
+ * @var array
+ * @access public
+ */
+	var $mimeType = array('ai' => 'application/postscript', 'bcpio' => 'application/x-bcpio', 'bin' => 'application/octet-stream',
+								'ccad' => 'application/clariscad', 'cdf' => 'application/x-netcdf', 'class' => 'application/octet-stream',
+								'cpio' => 'application/x-cpio', 'cpt' => 'application/mac-compactpro', 'csh' => 'application/x-csh',
+								'csv' => 'application/csv', 'dcr' => 'application/x-director', 'dir' => 'application/x-director',
+								'dms' => 'application/octet-stream', 'doc' => 'application/msword', 'drw' => 'application/drafting',
+								'dvi' => 'application/x-dvi', 'dwg' => 'application/acad', 'dxf' => 'application/dxf', 'dxr' => 'application/x-director',
+								'eps' => 'application/postscript', 'exe' => 'application/octet-stream', 'ez' => 'application/andrew-inset',
+								'flv' => 'video/x-flv', 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip',
+								'bz2' => 'application/x-bzip', '7z' => 'application/x-7z-compressed', 'hdf' => 'application/x-hdf',
+								'hqx' => 'application/mac-binhex40', 'ips' => 'application/x-ipscript', 'ipx' => 'application/x-ipix',
+								'js' => 'application/x-javascript', 'latex' => 'application/x-latex', 'lha' => 'application/octet-stream',
+								'lsp' => 'application/x-lisp', 'lzh' => 'application/octet-stream', 'man' => 'application/x-troff-man',
+								'me' => 'application/x-troff-me', 'mif' => 'application/vnd.mif', 'ms' => 'application/x-troff-ms',
+								'nc' => 'application/x-netcdf', 'oda' => 'application/oda', 'pdf' => 'application/pdf',
+								'pgn' => 'application/x-chess-pgn', 'pot' => 'application/mspowerpoint', 'pps' => 'application/mspowerpoint',
+								'ppt' => 'application/mspowerpoint', 'ppz' => 'application/mspowerpoint', 'pre' => 'application/x-freelance',
+								'prt' => 'application/pro_eng', 'ps' => 'application/postscript', 'roff' => 'application/x-troff',
+								'scm' => 'application/x-lotusscreencam', 'set' => 'application/set', 'sh' => 'application/x-sh',
+								'shar' => 'application/x-shar', 'sit' => 'application/x-stuffit', 'skd' => 'application/x-koan',
+								'skm' => 'application/x-koan', 'skp' => 'application/x-koan', 'skt' => 'application/x-koan',
+								'smi' => 'application/smil', 'smil' => 'application/smil', 'sol' => 'application/solids',
+								'spl' => 'application/x-futuresplash', 'src' => 'application/x-wais-source', 'step' => 'application/STEP',
+								'stl' => 'application/SLA', 'stp' => 'application/STEP', 'sv4cpio' => 'application/x-sv4cpio',
+								'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml',
+								'swf' => 'application/x-shockwave-flash', 't' => 'application/x-troff',
+								'tar' => 'application/x-tar', 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex',
+								'texi' => 'application/x-texinfo', 'texinfo' => 'application/x-texinfo', 'tr' => 'application/x-troff',
+								'tsp' => 'application/dsptype', 'unv' => 'application/i-deas', 'ustar' => 'application/x-ustar',
+								'vcd' => 'application/x-cdlink', 'vda' => 'application/vda', 'xlc' => 'application/vnd.ms-excel',
+								'xll' => 'application/vnd.ms-excel', 'xlm' => 'application/vnd.ms-excel', 'xls' => 'application/vnd.ms-excel',
+								'xlw' => 'application/vnd.ms-excel', 'zip' => 'application/zip', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff',
+								'aiff' => 'audio/x-aiff', 'au' => 'audio/basic', 'kar' => 'audio/midi', 'mid' => 'audio/midi',
+								'midi' => 'audio/midi', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mpga' => 'audio/mpeg',
+								'ra' => 'audio/x-realaudio', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio',
+								'rpm' => 'audio/x-pn-realaudio-plugin', 'snd' => 'audio/basic', 'tsi' => 'audio/TSP-audio', 'wav' => 'audio/x-wav',
+								'asc' => 'text/plain', 'c' => 'text/plain', 'cc' => 'text/plain', 'css' => 'text/css', 'etx' => 'text/x-setext',
+								'f' => 'text/plain', 'f90' => 'text/plain', 'h' => 'text/plain', 'hh' => 'text/plain', 'htm' => 'text/html',
+								'html' => 'text/html', 'm' => 'text/plain', 'rtf' => 'text/rtf', 'rtx' => 'text/richtext', 'sgm' => 'text/sgml',
+								'sgml' => 'text/sgml', 'tsv' => 'text/tab-separated-values', 'tpl' => 'text/template', 'txt' => 'text/plain',
+								'xml' => 'text/xml', 'avi' => 'video/x-msvideo', 'fli' => 'video/x-fli', 'mov' => 'video/quicktime',
+								'movie' => 'video/x-sgi-movie', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg',
+								'qt' => 'video/quicktime', 'viv' => 'video/vnd.vivo', 'vivo' => 'video/vnd.vivo', 'gif' => 'image/gif',
+								'ief' => 'image/ief', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg',
+								'pbm' => 'image/x-portable-bitmap', 'pgm' => 'image/x-portable-graymap', 'png' => 'image/png',
+								'pnm' => 'image/x-portable-anymap', 'ppm' => 'image/x-portable-pixmap', 'ras' => 'image/cmu-raster',
+								'rgb' => 'image/x-rgb', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'xbm' => 'image/x-xbitmap',
+								'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'ice' => 'x-conference/x-cooltalk',
+								'iges' => 'model/iges', 'igs' => 'model/iges', 'mesh' => 'model/mesh', 'msh' => 'model/mesh',
+								'silo' => 'model/mesh', 'vrml' => 'model/vrml', 'wrl' => 'model/vrml',
+								'mime' => 'www/mime', 'pdb' => 'chemical/x-pdb', 'xyz' => 'chemical/x-pdb');
+/**
+ * Constructor
+ *
+ * @param object $controller
+ */
+	function __construct(&$controller) {
+		parent::__construct($controller);
+	}
+/**
+ * Display or download the given file
+ *
+ * @return unknown
+ */
+	function render() {
+		$name = $download = $extension = $id = $modified = $path = $size = $cache = $mimeType = null;
+		extract($this->viewVars, EXTR_OVERWRITE);
+
+		if ($size) {
+			$id = $id . '_' . $size;
+		}
+
+		if (is_dir($path)) {
+			$path = $path . $id;
+		} else {
+			$path = APP . $path . $id;
+		}
+
+		if (is_null($name)) {
+			$name = $id;
+		}
+
+		if (is_array($mimeType)) {
+			$this->mimeType = array_merge($this->mimeType, $mimeType);
+		}
+
+		if (file_exists($path) && isset($extension) && isset($this->mimeType[$extension]) && connection_status() == 0) {
+			$chunkSize = 8192;
+			$buffer = '';
+			$fileSize = @filesize($path);
+			$handle = fopen($path, 'rb');
+
+			if ($handle === false) {
+				return false;
+			}
+			if (!empty($modified)) {
+				$modified = gmdate('D, d M Y H:i:s', strtotime($modified, time())) . ' GMT';
+			} else {
+				$modified = gmdate('D, d M Y H:i:s') . ' GMT';
+			}
+
+			if ($download) {
+				$contentType = 'application/octet-stream';
+				$agent = env('HTTP_USER_AGENT');
+
+				if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
+					$contentType = 'application/octetstream';
+				}
+				header('Content-Type: ' . $contentType);
+				header('Content-Disposition: attachment; filename="' . $name . '.' . $extension . '";');
+				header('Expires: 0');
+				header('Accept-Ranges: bytes');
+				header('Cache-Control: private', false);
+				header('Pragma: private');
+
+				$httpRange = env('HTTP_RANGE');
+				if (isset($httpRange)) {
+					list($toss, $range) = explode('=', $httpRange);
+
+					$size = $fileSize - 1;
+					$length = $fileSize - $range;
+
+					header('HTTP/1.1 206 Partial Content');
+					header('Content-Length: ' . $length);
+					header('Content-Range: bytes ' . $range . $size . '/' . $fileSize);
+					fseek($handle, $range);
+				} else {
+					header('Content-Length: ' . $fileSize);
+				}
+			} else {
+				header('Date: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
+				if ($cache) {
+					if (!is_numeric($cache)) {
+						$cache = strtotime($cache) - time();
+					}
+					header('Cache-Control: max-age=' . $cache);
+					header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT');
+					header('Pragma: cache');
+				} else {
+					header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+					header('Pragma: no-cache');
+				}
+				header('Last-Modified: ' . $modified);
+				header('Content-Type: ' . $this->mimeType[$extension]);
+				header('Content-Length: ' . $fileSize);
+			}
+			@ob_end_clean();
+
+			while (!feof($handle) && connection_status() == 0 && !connection_aborted()) {
+				set_time_limit(0);
+				$buffer = fread($handle, $chunkSize);
+				echo $buffer;
+				@flush();
+				@ob_flush();
+			}
+			fclose($handle);
+			exit(0);
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/theme.php
===================================================================
--- cake/libs/view/theme.php	(revision 0)
+++ cake/libs/view/theme.php	(revision 0)
@@ -0,0 +1,95 @@
+<?php
+/* SVN FILE: $Id: theme.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * A custom view class that is used for themeing
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Theme view class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ */
+class ThemeView extends View {
+/**
+ * System path to themed element: themed . DS . theme . DS . elements . DS
+ *
+ * @var string
+ */
+	var $themeElement = null;
+/**
+ * System path to themed layout: themed . DS . theme . DS . layouts . DS
+ *
+ * @var string
+ */
+	var $themeLayout = null;
+/**
+ * System path to themed: themed . DS . theme . DS
+ *
+ * @var string
+ */
+	var $themePath = null;
+/**
+ * Enter description here...
+ *
+ * @param unknown_type $controller
+ */
+	function __construct (&$controller) {
+		parent::__construct($controller);
+		$this->theme =& $controller->theme;
+
+		if (!empty($this->theme)) {
+			if (is_dir(WWW_ROOT . 'themed' . DS . $this->theme)) {
+				$this->themeWeb = 'themed/'. $this->theme .'/';
+			}
+			/* deprecated: as of 6128 the following properties are no longer needed */
+			$this->themeElement = 'themed'. DS . $this->theme . DS .'elements'. DS;
+			$this->themeLayout =  'themed'. DS . $this->theme . DS .'layouts'. DS;
+			$this->themePath = 'themed'. DS . $this->theme . DS;
+		}
+	}
+
+/**
+ * Return all possible paths to find view files in order
+ *
+ * @param string $plugin
+ * @return array paths
+ * @access private
+ */
+	function _paths($plugin = null, $cached = true) {
+		$paths = parent::_paths($plugin, $cached);
+
+		if (!empty($this->theme)) {
+			$count = count($paths);
+			for ($i = 0; $i < $count; $i++) {
+				$themePaths[] = $paths[$i] . 'themed'. DS . $this->theme . DS;
+			}
+			$paths = array_merge($themePaths, $paths);
+		}
+
+		if (empty($this->__paths)) {
+			$this->__paths = $paths;
+		}
+
+		return $paths;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/view/elements/email/html/default.ctp
===================================================================
--- cake/libs/view/elements/email/html/default.ctp	(revision 0)
+++ cake/libs/view/elements/email/html/default.ctp	(revision 0)
@@ -0,0 +1,31 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php
+$content = explode("\n", $content);
+
+foreach ($content as $line):
+	echo '<p> ' . $line . '</p>';
+endforeach;
+?>
\ No newline at end of file
Index: cake/libs/view/elements/email/text/default.ctp
===================================================================
--- cake/libs/view/elements/email/text/default.ctp	(revision 0)
+++ cake/libs/view/elements/email/text/default.ctp	(revision 0)
@@ -0,0 +1,25 @@
+<?php
+/* SVN FILE: $Id: default.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<?php echo $content; ?>
\ No newline at end of file
Index: cake/libs/view/elements/dump.ctp
===================================================================
--- cake/libs/view/elements/dump.ctp	(revision 0)
+++ cake/libs/view/elements/dump.ctp	(revision 0)
@@ -0,0 +1,30 @@
+<?php
+/* SVN FILE: $Id: dump.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements
+ * @since         CakePHP(tm) v 0.10.5.1782
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<div id="cakeControllerDump">
+	<h2><?php __('Controller dump:'); ?></h2>
+	<pre>
+		<?php echo h(print_r($controller, true)); ?>
+	</pre>
+</div>
\ No newline at end of file
Index: cake/libs/view/errors/missing_component_file.ctp
===================================================================
--- cake/libs/view/errors/missing_component_file.ctp	(revision 0)
+++ cake/libs/view/errors/missing_component_file.ctp	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: missing_component_file.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Component File'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php __("The component file was not found."); ?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class %s in file: %s', true), "<em>". $component ."Component</em>", APP_DIR.DS."controllers".DS."components".DS.$file);?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $component;?>Component extends Object {<br />
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_component_file.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/scaffold_error.ctp
===================================================================
--- cake/libs/view/errors/scaffold_error.ctp	(revision 0)
+++ cake/libs/view/errors/scaffold_error.ctp	(revision 0)
@@ -0,0 +1,40 @@
+<?php
+/* SVN FILE: $Id: scaffold_error.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Scaffold Error'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php __('Method _scaffoldError in was not found in the controller'); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."scaffold_error.ctp");?>
+</p>
+<pre>
+&lt;?php
+function _scaffoldError() {<br />
+
+}
+?&gt;
+</pre>
\ No newline at end of file
Index: cake/libs/view/errors/missing_helper_class.ctp
===================================================================
--- cake/libs/view/errors/missing_helper_class.ctp	(revision 0)
+++ cake/libs/view/errors/missing_helper_class.ctp	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: missing_helper_class.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Helper Class'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("The helper class <em>%s</em> can not be found or does not exist.", true), $helperClass);?>
+</p>
+<p  class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class below in file: %s', true), APP_DIR.DS."views".DS."helpers".DS.$file);?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $helperClass;?> extends AppHelper {
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_helper_class.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_view.ctp
===================================================================
--- cake/libs/view/errors/missing_view.ctp	(revision 0)
+++ cake/libs/view/errors/missing_view.ctp	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: missing_view.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing View'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('The view for %1$s%2$s was not found.', true), "<em>". $controller."Controller::</em>", "<em>". $action ."()</em>");?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Confirm you have created the file: %s', true), $file);?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_view.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_action.ctp
===================================================================
--- cake/libs/view/errors/missing_action.ctp	(revision 0)
+++ cake/libs/view/errors/missing_action.ctp	(revision 0)
@@ -0,0 +1,51 @@
+<?php
+/* SVN FILE: $Id: missing_action.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php echo sprintf(__('Missing Method in %s', true), $controller);?></h2>
+<p class="error">
+	<strong><?php __('Error') ?>: </strong>
+	<?php echo sprintf(__('The action %1$s is not defined in controller %2$s', true), "<em>".$action."</em>", "<em>".$controller."</em>");?>
+</p>
+<p class="error">
+	<strong><?php __('Error') ?>: </strong>
+	<?php echo sprintf(__('Create %1$s%2$s in file: %3$s.', true), "<em>".$controller."::</em>", "<em>".$action."()</em>", APP_DIR.DS."controllers".DS.Inflector::underscore($controller).".php");?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $controller;?> extends AppController {
+
+	var $name = '<?php echo $controllerName;?>';
+
+<strong>
+	function <?php echo $action;?>() {
+
+	}
+</strong>
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice') ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s.', true), APP_DIR.DS."views".DS."errors".DS."missing_action.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_layout.ctp
===================================================================
--- cake/libs/view/errors/missing_layout.ctp	(revision 0)
+++ cake/libs/view/errors/missing_layout.ctp	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: missing_layout.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Layout'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("The layout file %s can not be found or does not exist.", true), "<em>". $file ."</em>");?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Confirm you have created the file: %s', true), "<em>". $file ."</em>");?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_layout.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_helper_file.ctp
===================================================================
--- cake/libs/view/errors/missing_helper_file.ctp	(revision 0)
+++ cake/libs/view/errors/missing_helper_file.ctp	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: missing_helper_file.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Helper File'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("The helper file %s can not be found or does not exist.", true), APP_DIR.DS."views".DS."helpers".DS.$file);?>
+</p>
+<p  class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class below in file: %s', true), APP_DIR.DS."views".DS."helpers".DS.$file);?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $helperClass;?> extends AppHelper {
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_helper_file.ctp");?>
+</p>
Index: cake/libs/view/errors/private_action.ctp
===================================================================
--- cake/libs/view/errors/private_action.ctp	(revision 0)
+++ cake/libs/view/errors/private_action.ctp	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+/* SVN FILE: $Id: private_action.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php echo sprintf(__('Private Method in %s', true), $controller);?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("%s%s cannot be accessed directly.", true), "<em>". $controller ."::</em>",  "<em>". $action ."()</em>");?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."private_action.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_controller.ctp
===================================================================
--- cake/libs/view/errors/missing_controller.ctp	(revision 0)
+++ cake/libs/view/errors/missing_controller.ctp	(revision 0)
@@ -0,0 +1,45 @@
+<?php
+/* SVN FILE: $Id: missing_controller.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Controller'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('%s could not be found.', true), "<em>".$controller."</em>");?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class %s below in file: %s', true), "<em>".$controller."</em>", APP_DIR.DS."controllers".DS.Inflector::underscore($controller).".php");?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $controller;?> extends AppController {
+
+	var $name = '<?php echo $controllerName;?>';
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_controller.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_component_class.ctp
===================================================================
--- cake/libs/view/errors/missing_component_class.ctp	(revision 0)
+++ cake/libs/view/errors/missing_component_class.ctp	(revision 0)
@@ -0,0 +1,44 @@
+<?php
+/* SVN FILE: $Id: missing_component_class.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Component Class'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Component class %1$s in %2$s was not found.', true), "<em>". $component ."Component</em>", "<em>". $controller ."Controller</em>");?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class %s in file: %s', true), "<em>". $component ."Component</em>", APP_DIR.DS."controllers".DS."components".DS.$file);?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $component;?>Component extends Object {<br />
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_component_class.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_scaffolddb.ctp
===================================================================
--- cake/libs/view/errors/missing_scaffolddb.ctp	(revision 0)
+++ cake/libs/view/errors/missing_scaffolddb.ctp	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: missing_scaffolddb.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Database Connection'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php __('Scaffold requires a database connection'); ?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Confirm you have created the file: %s', true), APP_DIR.DS."config".DS."database.php");?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_scaffolddb.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_table.ctp
===================================================================
--- cake/libs/view/errors/missing_table.ctp	(revision 0)
+++ cake/libs/view/errors/missing_table.ctp	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+/* SVN FILE: $Id: missing_table.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Database Table'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Database table %1$s for model %2$s was not found.', true),"<em>". $table ."</em>",  "<em>". $model ."</em>");?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_table.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/error404.ctp
===================================================================
--- cake/libs/view/errors/error404.ctp	(revision 0)
+++ cake/libs/view/errors/error404.ctp	(revision 0)
@@ -0,0 +1,29 @@
+<?php
+/* SVN FILE: $Id: error404.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php echo $name; ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("The requested address %s was not found on this server.", true), "<strong>'{$message}'</strong>")?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_connection.ctp
===================================================================
--- cake/libs/view/errors/missing_connection.ctp	(revision 0)
+++ cake/libs/view/errors/missing_connection.ctp	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+/* SVN FILE: $Id: missing_connection.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Database Connection'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('%s requires a database connection', true), $model);?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Confirm you have created the file : %s.', true), APP_DIR.DS.'config'.DS.'database.php');?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s.', true), APP_DIR.DS.'views'.DS.'errors'.DS.basename(__FILE__));?>
+</p>
\ No newline at end of file
Index: cake/libs/view/errors/missing_model.ctp
===================================================================
--- cake/libs/view/errors/missing_model.ctp	(revision 0)
+++ cake/libs/view/errors/missing_model.ctp	(revision 0)
@@ -0,0 +1,46 @@
+<?php
+/* SVN FILE: $Id: missing_model.ctp 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<h2><?php __('Missing Model'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__("<em>%s</em> could not be found.", true), $model);?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php echo sprintf(__('Create the class %s in file: %s', true), "<em>". $model . "</em>", APP_DIR.DS."models".DS.Inflector::underscore($model).".php");?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $model;?> extends AppModel {
+
+	var $name = '<?php echo $model;?>';
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR.DS."views".DS."errors".DS."missing_model.ctp");?>
+</p>
\ No newline at end of file
Index: cake/libs/socket.php
===================================================================
--- cake/libs/socket.php	(revision 0)
+++ cake/libs/socket.php	(revision 0)
@@ -0,0 +1,281 @@
+<?php
+/* SVN FILE: $Id: socket.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Cake Socket connection class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'Validation');
+/**
+ * Cake network socket connection class.
+ *
+ * Core base class for network communication.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CakeSocket extends Object {
+/**
+ * Object description
+ *
+ * @var string
+ * @access public
+ */
+	var $description = 'Remote DataSource Network Socket Interface';
+/**
+ * Base configuration settings for the socket connection
+ *
+ * @var array
+ * @access protected
+ */
+	var $_baseConfig = array(
+		'persistent'	=> false,
+		'host'			=> 'localhost',
+		'protocol'		=> 'tcp',
+		'port'			=> 80,
+		'timeout'		=> 30
+	);
+/**
+ * Configuration settings for the socket connection
+ *
+ * @var array
+ * @access public
+ */
+	var $config = array();
+/**
+ * Reference to socket connection resource
+ *
+ * @var resource
+ * @access public
+ */
+	var $connection = null;
+/**
+ * This boolean contains the current state of the CakeSocket class
+ *
+ * @var boolean
+ * @access public
+ */
+	var $connected = false;
+/**
+ * This variable contains an array with the last error number (num) and string (str)
+ *
+ * @var array
+ * @access public
+ */
+	var $lastError = array();
+/**
+ * Constructor.
+ *
+ * @param array $config Socket configuration, which will be merged with the base configuration
+ */
+	function __construct($config = array()) {
+		parent::__construct();
+
+		$this->config = array_merge($this->_baseConfig, $config);
+		if (!is_numeric($this->config['protocol'])) {
+			$this->config['protocol'] = getprotobyname($this->config['protocol']);
+		}
+	}
+/**
+ * Connect the socket to the given host and port.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function connect() {
+		if ($this->connection != null) {
+			$this->disconnect();
+		}
+
+		$scheme = null;
+		if (isset($this->config['request']) && $this->config['request']['uri']['scheme'] == 'https') {
+			$scheme = 'ssl://';
+		}
+
+		if ($this->config['persistent'] == true) {
+			$tmp = null;
+			$this->connection = @pfsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
+		} else {
+			$this->connection = @fsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
+		}
+
+		if (!empty($errNum) || !empty($errStr)) {
+			$this->setLastError($errStr, $errNum);
+		}
+
+		return $this->connected = is_resource($this->connection);
+	}
+
+/**
+ * Get the host name of the current connection.
+ *
+ * @return string Host name
+ * @access public
+ */
+	function host() {
+		if (Validation::ip($this->config['host'])) {
+			return gethostbyaddr($this->config['host']);
+		} else {
+			return gethostbyaddr($this->address());
+		}
+	}
+/**
+ * Get the IP address of the current connection.
+ *
+ * @return string IP address
+ * @access public
+ */
+	function address() {
+		if (Validation::ip($this->config['host'])) {
+			return $this->config['host'];
+		} else {
+			return gethostbyname($this->config['host']);
+		}
+	}
+/**
+ * Get all IP addresses associated with the current connection.
+ *
+ * @return array IP addresses
+ * @access public
+ */
+	function addresses() {
+		if (Validation::ip($this->config['host'])) {
+			return array($this->config['host']);
+		} else {
+			return gethostbynamel($this->config['host']);
+		}
+	}
+/**
+ * Get the last error as a string.
+ *
+ * @return string Last error
+ * @access public
+ */
+	function lastError() {
+		if (!empty($this->lastError)) {
+			return $this->lastError['num'].': '.$this->lastError['str'];
+		} else {
+			return null;
+		}
+	}
+/**
+ * Set the last error.
+ *
+ * @param integer $errNum Error code
+ * @param string $errStr Error string
+ * @access public
+ */
+	function setLastError($errNum, $errStr) {
+		$this->lastError = array('num' => $errNum, 'str' => $errStr);
+	}
+/**
+ * Write data to the socket.
+ *
+ * @param string $data The data to write to the socket
+ * @return boolean Success
+ * @access public
+ */
+	function write($data) {
+		if (!$this->connected) {
+			if (!$this->connect()) {
+				return false;
+			}
+		}
+
+		return fwrite($this->connection, $data, strlen($data));
+	}
+
+/**
+ * Read data from the socket. Returns false if no data is available or no connection could be
+ * established.
+ *
+ * @param integer $length Optional buffer length to read; defaults to 1024
+ * @return mixed Socket data
+ * @access public
+ */
+	function read($length = 1024) {
+		if (!$this->connected) {
+			if (!$this->connect()) {
+				return false;
+			}
+		}
+
+		if (!feof($this->connection)) {
+			return fread($this->connection, $length);
+		} else {
+			return false;
+		}
+	}
+/**
+ * Abort socket operation.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function abort() {
+	}
+/**
+ * Disconnect the socket from the current connection.
+ *
+ * @return boolean Success
+ * @access public
+ */
+	function disconnect() {
+		if (!is_resource($this->connection)) {
+			$this->connected = false;
+			return true;
+		}
+		$this->connected = !fclose($this->connection);
+
+		if (!$this->connected) {
+			$this->connection = null;
+		}
+		return !$this->connected;
+	}
+/**
+ * Destructor, used to disconnect from current connection.
+ *
+ * @access private
+ */
+	function __destruct() {
+		$this->disconnect();
+	}
+/**
+ * Resets the state of this Socket instance to it's initial state (before Object::__construct got executed)
+ *
+ * @return boolean True on success
+ * @access public
+ */
+	function reset($state = null) {
+		if (empty($state)) {
+			static $initalState = array();
+			if (empty($initalState)) {
+				$initalState = get_class_vars(__CLASS__);
+			}
+			$state = $initalState;
+		}
+
+		foreach ($state as $property => $value) {
+			$this->{$property} = $value;
+		}
+		return true;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/set.php
===================================================================
--- cake/libs/set.php	(revision 0)
+++ cake/libs/set.php	(revision 0)
@@ -0,0 +1,1114 @@
+<?php
+/* SVN FILE: $Id: set.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Library of array functions for Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Class used for manipulation of arrays.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Set extends Object {
+/**
+ * Deprecated
+ *
+ */
+	var $value = array();
+/**
+ * This function can be thought of as a hybrid between PHP's array_merge and array_merge_recursive. The difference
+ * to the two is that if an array key contains another array then the function behaves recursive (unlike array_merge)
+ * but does not do if for keys containing strings (unlike array_merge_recursive). See the unit test for more information.
+ *
+ * Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays.
+ *
+ * @param array $arr1 Array to be merged
+ * @param array $arr2 Array to merge with
+ * @return array Merged array
+ * @access public
+ * @static
+ */
+	function merge($arr1, $arr2 = null) {
+		$args = func_get_args();
+
+		$r = (array)current($args);
+		while (($arg = next($args)) !== false) {
+			foreach ((array)$arg as $key => $val)	 {
+				if (is_array($val) && isset($r[$key]) && is_array($r[$key])) {
+					$r[$key] = Set::merge($r[$key], $val);
+				} elseif (is_int($key)) {
+					$r[] = $val;
+				} else {
+					$r[$key] = $val;
+				}
+			}
+		}
+		return $r;
+	}
+/**
+ * Filters empty elements out of a route array, excluding '0'.
+ *
+ * @param mixed $var Either an array to filter, or value when in callback
+ * @param boolean $isArray Force to tell $var is an array when $var is empty
+ * @return mixed Either filtered array, or true/false when in callback
+ * @access public
+ * @static
+ */
+	function filter($var, $isArray = false) {
+		if (is_array($var) && (!empty($var) || $isArray)) {
+			return array_filter($var, array('Set', 'filter'));
+		}
+
+		if ($var === 0 || $var === '0' || !empty($var)) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Pushes the differences in $array2 onto the end of $array
+ *
+ * @param mixed $array Original array
+ * @param mixed $array2 Differences to push
+ * @return array Combined array
+ * @access public
+ * @static
+ */
+	function pushDiff($array, $array2) {
+		if (empty($array) && !empty($array2)) {
+			return $array2;
+		}
+		if (!empty($array) && !empty($array2)) {
+			foreach ($array2 as $key => $value) {
+				if (!array_key_exists($key, $array)) {
+					$array[$key] = $value;
+				} else {
+					if (is_array($value)) {
+						$array[$key] = Set::pushDiff($array[$key], $array2[$key]);
+					}
+				}
+			}
+		}
+		return $array;
+	}
+/**
+ * Maps the contents of the Set object to an object hierarchy.
+ * Maintains numeric keys as arrays of objects
+ *
+ * @param string $class A class name of the type of object to map to
+ * @param string $tmp A temporary class name used as $class if $class is an array
+ * @return object Hierarchical object
+ * @access public
+ * @static
+ */
+	function map($class = 'stdClass', $tmp = 'stdClass') {
+		if (is_array($class)) {
+			$val = $class;
+			$class = $tmp;
+		}
+
+		if (empty($val)) {
+			return null;
+		}
+		return Set::__map($val, $class);
+	}
+
+/**
+ * Get the array value of $array. If $array is null, it will return
+ * the current array Set holds. If it is an object of type Set, it
+ * will return its value. If it is another object, its object variables.
+ * If it is anything else but an array, it will return an array whose first
+ * element is $array.
+ *
+ * @param mixed $array Data from where to get the array.
+ * @return array Array from $array.
+ * @access private
+ */
+	function __array($array) {
+		if (empty($array)) {
+			$array = array();
+		} elseif (is_object($array)) {
+			$array = get_object_vars($array);
+		} elseif (!is_array($array)) {
+			$array = array($array);
+		}
+		return $array;
+	}
+
+/**
+ * Maps the given value as an object. If $value is an object,
+ * it returns $value. Otherwise it maps $value as an object of
+ * type $class, and if primary assign _name_ $key on first array.
+ * If $value is not empty, it will be used to set properties of
+ * returned object (recursively). If $key is numeric will maintain array
+ * structure
+ *
+ * @param mixed $value Value to map
+ * @param string $class Class name
+ * @param boolean $primary whether to assign first array key as the _name_
+ * @return mixed Mapped object
+ * @access private
+ * @static
+ */
+	function __map(&$array, $class, $primary = false) {
+		if ($class === true) {
+			$out = new stdClass;
+		} else {
+			$out = new $class;
+		}
+		if (is_array($array)) {
+			$keys = array_keys($array);
+			foreach ($array as $key => $value) {
+				if ($keys[0] === $key && $class !== true) {
+					$primary = true;
+				}
+				if (is_numeric($key)) {
+					if (is_object($out)) {
+						$out = get_object_vars($out);
+					}
+					$out[$key] = Set::__map($value, $class);
+					if (is_object($out[$key])) {
+						if ($primary !== true && is_array($value) && Set::countDim($value, true) === 2) {
+							if (!isset($out[$key]->_name_)) {
+								$out[$key]->_name_ = $primary;
+							}
+						}
+					}
+				} elseif (is_array($value)) {
+					if ($primary === true) {
+						if (!isset($out->_name_)) {
+							$out->_name_ = $key;
+						}
+						$primary = false;
+						foreach ($value as $key2 => $value2) {
+							$out->{$key2} = Set::__map($value2, true);
+						}
+					} else {
+						if (!is_numeric($key)) {
+							$out->{$key} = Set::__map($value, true, $key);
+							if (is_object($out->{$key}) && !is_numeric($key)) {
+								if (!isset($out->{$key}->_name_)) {
+									$out->{$key}->_name_ = $key;
+								}
+							}
+						} else {
+							$out->{$key} = Set::__map($value, true);
+						}
+					}
+				} else {
+					$out->{$key} = $value;
+				}
+			}
+		} else {
+			$out = $array;
+		}
+		return $out;
+	}
+/**
+ * Checks to see if all the values in the array are numeric
+ *
+ * @param array $array The array to check.  If null, the value of the current Set object
+ * @return boolean true if values are numeric, false otherwise
+ * @access public
+ * @static
+ */
+	function numeric($array = null) {
+		if (empty($array)) {
+			return null;
+		}
+
+		if ($array === range(0, count($array) - 1)) {
+			return true;
+		}
+
+		$numeric = true;
+		$keys = array_keys($array);
+		$count = count($keys);
+
+		for ($i = 0; $i < $count; $i++) {
+			if (!is_numeric($array[$keys[$i]])) {
+				$numeric = false;
+				break;
+			}
+		}
+		return $numeric;
+	}
+/**
+ * Return a value from an array list if the key exists.
+ *
+ * If a comma separated $list is passed arrays are numeric with the key of the first being 0
+ * $list = 'no, yes' would translate to  $list = array(0 => 'no', 1 => 'yes');
+ *
+ * If an array is used, keys can be strings example: array('no' => 0, 'yes' => 1);
+ *
+ * $list defaults to 0 = no 1 = yes if param is not passed
+ *
+ * @param mixed $select Key in $list to return
+ * @param mixed $list can be an array or a comma-separated list.
+ * @return string the value of the array key or null if no match
+ * @access public
+ * @static
+ */
+	function enum($select, $list = null) {
+		if (empty($list)) {
+			$list = array('no', 'yes');
+		}
+
+		$return = null;
+		$list = Set::normalize($list, false);
+
+		if (array_key_exists($select, $list)) {
+			$return = $list[$select];
+		}
+		return $return;
+	}
+/**
+ * Returns a series of values extracted from an array, formatted in a format string.
+ *
+ * @param array		$data Source array from which to extract the data
+ * @param string	$format Format string into which values will be inserted, see sprintf()
+ * @param array		$keys An array containing one or more Set::extract()-style key paths
+ * @return array	An array of strings extracted from $keys and formatted with $format
+ * @access public
+ * @static
+ */
+	function format($data, $format, $keys) {
+
+		$extracted = array();
+		$count = count($keys);
+
+		if (!$count) {
+			return;
+		}
+
+		for ($i = 0; $i < $count; $i++) {
+			$extracted[] = Set::extract($data, $keys[$i]);
+		}
+		$out = array();
+		$data = $extracted;
+		$count = count($data[0]);
+
+		if (preg_match_all('/\{([0-9]+)\}/msi', $format, $keys2) && isset($keys2[1])) {
+			$keys = $keys2[1];
+			$format = preg_split('/\{([0-9]+)\}/msi', $format);
+			$count2 = count($format);
+
+			for ($j = 0; $j < $count; $j++) {
+				$formatted = '';
+				for ($i = 0; $i <= $count2; $i++) {
+					if (isset($format[$i])) {
+						$formatted .= $format[$i];
+					}
+					if (isset($keys[$i]) && isset($data[$keys[$i]][$j])) {
+						$formatted .= $data[$keys[$i]][$j];
+					}
+				}
+				$out[] = $formatted;
+			}
+		} else {
+			$count2 = count($data);
+			for ($j = 0; $j < $count; $j++) {
+				$args = array();
+				for ($i = 0; $i < $count2; $i++) {
+					if (isset($data[$i][$j])) {
+						$args[] = $data[$i][$j];
+					}
+				}
+				$out[] = vsprintf($format, $args);
+			}
+		}
+		return $out;
+	}
+/**
+ * Implements partial support for XPath 2.0. If $path is an array or $data is empty it the call is delegated to Set::classicExtract.
+ *
+ * Currently implemented selectors:
+ * - /User/id (similar to the classic {n}.User.id)
+ * - /User[2]/name (selects the name of the second User)
+ * - /User[id>2] (selects all Users with an id > 2)
+ * - /User[id>2][<5] (selects all Users with an id > 2 but < 5)
+ * - /Post/Comment[author_name=john]/../name (Selects the name of all Posts that have at least one Comment written by john)
+ * - /Posts[name] (Selects all Posts that have a 'name' key)
+ * - /Comment/.[1] (Selects the contents of the first comment)
+ * - /Comment/.[:last] (Selects the last comment)
+ * - /Comment/.[:first] (Selects the first comment)
+ * - /Comment[text=/cakephp/i] (Selects the all comments that have a text matching the regex /cakephp/i)
+ * - /Comment/@* (Selects the all key names of all comments)
+ *
+ * Other limitations:
+ * - Only absolute paths starting with a single '/' are supported right now
+ *
+ * Warning: Even so it has plenty of unit tests the XPath support has not gone through a lot of real-world testing. Please report
+ * Bugs as you find them. Suggestions for additional features to imlement are also very welcome!
+ *
+ * @param string $path An absolute XPath 2.0 path
+ * @param string $data An array of data to extract from
+ * @param string $options Currently only supports 'flatten' which can be disabled for higher XPath-ness
+ * @return array An array of matched items
+ * @access public
+ * @static
+ */
+	function extract($path, $data = null, $options = array()) {
+		if (empty($data) && is_string($path) && $path{0} === '/') {
+			return array();
+		}
+		if (is_string($data) && $data{0} === '/') {
+			$tmp = $path;
+			$path = $data;
+			$data = $tmp;
+		}
+		if (is_array($path) || empty($data) || is_object($path) || empty($path)) {
+			return Set::classicExtract($path, $data);
+		}
+		if ($path === '/') {
+			return $data;
+		}
+		$contexts = $data;
+		$options = array_merge(array('flatten' => true), $options);
+		if (!isset($contexts[0])) {
+			$contexts = array($data);
+		}
+		$tokens = array_slice(preg_split('/(?<!=)\/(?![a-z]*\])/', $path), 1);
+
+		do {
+			$token = array_shift($tokens);
+			$conditions = false;
+			if (preg_match_all('/\[([^\]]+)\]/', $token, $m)) {
+				$conditions = $m[1];
+				$token = substr($token, 0, strpos($token, '['));
+			}
+			$matches = array();
+			foreach ($contexts as $key => $context) {
+				if (!isset($context['trace'])) {
+					$context = array('trace' => array(null), 'item' => $context, 'key' => $key);
+				}
+				if ($token === '..') {
+					if (count($context['trace']) == 1) {
+						$context['trace'][] = $context['key'];
+					}
+					$parent = join('/', $context['trace']) . '/.';
+					$context['item'] = Set::extract($parent, $data);
+					$context['key'] = array_pop($context['trace']);
+					if (isset($context['trace'][1]) && $context['trace'][1] > 0) {
+						$context['item'] = $context['item'][0];
+					} else {
+						$context['item'] = $context['item'][$key];
+					}
+					$matches[] = $context;
+					continue;
+				}
+				$match = false;
+				if ($token === '@*' && is_array($context['item'])) {
+					$matches[] = array(
+						'trace' => array_merge($context['trace'], (array)$key),
+						'key' => $key,
+						'item' => array_keys($context['item']),
+					);
+				} elseif (is_array($context['item']) && array_key_exists($token, $context['item'])) {
+					$items = $context['item'][$token];
+					if (!is_array($items)) {
+						$items = array($items);
+					} elseif (!isset($items[0])) {
+						$current = current($items);
+						if ((is_array($current) && count($items) <= 1) || !is_array($current)) {
+							$items = array($items);
+						}
+					}
+
+					foreach ($items as $key => $item) {
+						$ctext = array($context['key']);
+						if (!is_numeric($key)) {
+							$ctext[] = $token;
+							$token = array_shift($tokens);
+							if (isset($items[$token])) {
+								$ctext[] = $token;
+								$item = $items[$token];
+								$matches[] = array(
+									'trace' => array_merge($context['trace'], $ctext),
+									'key' => $key,
+									'item' => $item,
+								);
+								break;
+							}
+						} else {
+							$key = $token;
+						}
+
+						$matches[] = array(
+							'trace' => array_merge($context['trace'], $ctext),
+							'key' => $key,
+							'item' => $item,
+						);
+					}
+				} elseif (($key === $token || (ctype_digit($token) && $key == $token) || $token === '.')) {
+					$context['trace'][] = $key;
+					$matches[] = array(
+						'trace' => $context['trace'],
+						'key' => $key,
+						'item' => $context['item'],
+					);
+				}
+			}
+			if ($conditions) {
+				foreach ($conditions as $condition) {
+					$filtered = array();
+					$length = count($matches);
+					foreach ($matches as $i => $match) {
+						if (Set::matches(array($condition), $match['item'], $i + 1, $length)) {
+							$filtered[] = $match;
+						}
+					}
+					$matches = $filtered;
+				}
+			}
+			$contexts = $matches;
+
+			if (empty($tokens)) {
+				break;
+			}
+		} while(1);
+
+		$r = array();
+
+		foreach ($matches as $match) {
+			if ((!$options['flatten'] || is_array($match['item'])) && !is_int($match['key'])) {
+				$r[] = array($match['key'] => $match['item']);
+			} else {
+				$r[] = $match['item'];
+			}
+		}
+		return $r;
+	}
+/**
+ * This function can be used to see if a single item or a given xpath match certain conditions.
+ *
+ * @param mixed $conditions An array of condition strings or an XPath expression
+ * @param array $data  An array of data to execute the match on
+ * @param integer $i Optional: The 'nth'-number of the item being matched.
+ * @return boolean
+ * @access public
+ * @static
+ */
+	function matches($conditions, $data = array(), $i = null, $length = null) {
+		if (empty($conditions)) {
+			return true;
+		}
+		if (is_string($conditions)) {
+			return !!Set::extract($conditions, $data);
+		}
+		foreach ($conditions as $condition) {
+			if ($condition === ':last') {
+				if ($i != $length) {
+					return false;
+				}
+				continue;
+			} elseif ($condition === ':first') {
+				if ($i != 1) {
+					return false;
+				}
+				continue;
+			}
+			if (!preg_match('/(.+?)([><!]?[=]|[><])(.*)/', $condition, $match)) {
+				if (ctype_digit($condition)) {
+					if ($i != $condition) {
+						return false;
+					}
+				} elseif (preg_match_all('/(?:^[0-9]+|(?<=,)[0-9]+)/', $condition, $matches)) {
+					return in_array($i, $matches[0]);
+				} elseif (!array_key_exists($condition, $data)) {
+					return false;
+				}
+				continue;
+			}
+			list(,$key,$op,$expected) = $match;
+			if (!isset($data[$key])) {
+				return false;
+			}
+
+			$val = $data[$key];
+
+			if ($op === '=' && $expected && $expected{0} === '/') {
+				return preg_match($expected, $val);
+			}
+			if ($op === '=' && $val != $expected) {
+				return false;
+			}
+			if ($op === '!=' && $val == $expected) {
+				return false;
+			}
+			if ($op === '>' && $val <= $expected) {
+				return false;
+			}
+			if ($op === '<' && $val >= $expected) {
+				return false;
+			}
+			if ($op === '<=' && $val > $expected) {
+				return false;
+			}
+			if ($op === '>=' && $val < $expected) {
+				return false;
+			}
+		}
+		return true;
+	}
+/**
+ * Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.:
+ * "{n}.Person.{[a-z]+}" - Where "{n}" represents a numeric key, "Person" represents a string literal,
+ * and "{[a-z]+}" (i.e. any string literal enclosed in brackets besides {n} and {s}) is interpreted as
+ * a regular expression.
+ *
+ * @param array $data Array from where to extract
+ * @param mixed $path As an array, or as a dot-separated string.
+ * @return array Extracted data
+ * @access public
+ * @static
+ */
+	function classicExtract($data, $path = null) {
+		if (empty($path)) {
+			return $data;
+		}
+		if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+		if (!is_array($data)) {
+			return $data;
+		}
+
+		if (!is_array($path)) {
+			if (!class_exists('String')) {
+				App::import('Core', 'String');
+			}
+			$path = String::tokenize($path, '.', '{', '}');
+		}
+		$tmp = array();
+
+		if (!is_array($path) || empty($path)) {
+			return null;
+		}
+
+		foreach ($path as $i => $key) {
+			if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+				if (isset($data[intval($key)])) {
+					$data = $data[intval($key)];
+				} else {
+					return null;
+				}
+			} elseif ($key === '{n}') {
+				foreach ($data as $j => $val) {
+					if (is_int($j)) {
+						$tmpPath = array_slice($path, $i + 1);
+						if (empty($tmpPath)) {
+							$tmp[] = $val;
+						} else {
+							$tmp[] = Set::classicExtract($val, $tmpPath);
+						}
+					}
+				}
+				return $tmp;
+			} elseif ($key === '{s}') {
+				foreach ($data as $j => $val) {
+					if (is_string($j)) {
+						$tmpPath = array_slice($path, $i + 1);
+						if (empty($tmpPath)) {
+							$tmp[] = $val;
+						} else {
+							$tmp[] = Set::classicExtract($val, $tmpPath);
+						}
+					}
+				}
+				return $tmp;
+			} elseif (false !== strpos($key,'{') && false !== strpos($key,'}')) {
+				$pattern = substr($key, 1, -1);
+
+				foreach ($data as $j => $val) {
+					if (preg_match('/^'.$pattern.'/s', $j) !== 0) {
+						$tmpPath = array_slice($path, $i + 1);
+						if (empty($tmpPath)) {
+							$tmp[$j] = $val;
+						} else {
+							$tmp[$j] = Set::classicExtract($val, $tmpPath);
+						}
+					}
+				}
+				return $tmp;
+			} else {
+				if (isset($data[$key])) {
+					$data = $data[$key];
+				} else {
+					return null;
+				}
+			}
+		}
+		return $data;
+	}
+/**
+ * Inserts $data into an array as defined by $path.
+ *
+ * @param mixed $list Where to insert into
+ * @param mixed $path A dot-separated string.
+ * @param array $data Data to insert
+ * @return array
+ * @access public
+ * @static
+ */
+	function insert($list, $path, $data = null) {
+		if (!is_array($path)) {
+			$path = explode('.', $path);
+		}
+		$_list =& $list;
+
+		foreach ($path as $i => $key) {
+			if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+				$key = intval($key);
+			}
+			if ($i === count($path) - 1) {
+				$_list[$key] = $data;
+			} else {
+				if (!isset($_list[$key])) {
+					$_list[$key] = array();
+				}
+				$_list =& $_list[$key];
+			}
+		}
+		return $list;
+	}
+/**
+ * Removes an element from a Set or array as defined by $path.
+ *
+ * @param mixed $list From where to remove
+ * @param mixed $path A dot-separated string.
+ * @return array Array with $path removed from its value
+ * @access public
+ * @static
+ */
+	function remove($list, $path = null) {
+		if (empty($path)) {
+			return $list;
+		}
+		if (!is_array($path)) {
+			$path = explode('.', $path);
+		}
+		$_list =& $list;
+
+		foreach ($path as $i => $key) {
+			if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+				$key = intval($key);
+			}
+			if ($i === count($path) - 1) {
+				unset($_list[$key]);
+			} else {
+				if (!isset($_list[$key])) {
+					return $list;
+				}
+				$_list =& $_list[$key];
+			}
+		}
+		return $list;
+	}
+/**
+ * Checks if a particular path is set in an array
+ *
+ * @param mixed $data Data to check on
+ * @param mixed $path A dot-separated string.
+ * @return boolean true if path is found, false otherwise
+ * @access public
+ * @static
+ */
+	function check($data, $path = null) {
+		if (empty($path)) {
+			return $data;
+		}
+		if (!is_array($path)) {
+			$path = explode('.', $path);
+		}
+
+		foreach ($path as $i => $key) {
+			if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+				$key = intval($key);
+			}
+			if ($i === count($path) - 1) {
+				return (is_array($data) && array_key_exists($key, $data));
+			}
+
+			if (!is_array($data) || !array_key_exists($key, $data)) {
+				return false;
+			}
+			$data =& $data[$key];
+		}
+		return true;
+	}
+/**
+ * Computes the difference between a Set and an array, two Sets, or two arrays
+ *
+ * @param mixed $val1 First value
+ * @param mixed $val2 Second value
+ * @return array Computed difference
+ * @access public
+ * @static
+ */
+	function diff($val1, $val2 = null) {
+		if (empty($val1)) {
+			return (array)$val2;
+		}
+		if (empty($val2)) {
+			return (array)$val1;
+		}
+		$out = array();
+
+		foreach ($val1 as $key => $val) {
+			$exists = array_key_exists($key, $val2);
+
+			if ($exists && $val2[$key] != $val) {
+				$out[$key] = $val;
+			} elseif (!$exists) {
+				$out[$key] = $val;
+			}
+			unset($val2[$key]);
+		}
+
+		foreach ($val2 as $key => $val) {
+			if (!array_key_exists($key, $out)) {
+				$out[$key] = $val;
+			}
+		}
+		return $out;
+	}
+/**
+ * Determines if two Sets or arrays are equal
+ *
+ * @param array $val1 First value
+ * @param array $val2 Second value
+ * @return boolean true if they are equal, false otherwise
+ * @access public
+ * @static
+ */
+	function isEqual($val1, $val2 = null) {
+		return ($val1 == $val2);
+	}
+/**
+ * Determines if one Set or array contains the exact keys and values of another.
+ *
+ * @param array $val1 First value
+ * @param array $val2 Second value
+ * @return boolean true if $val1 contains $val2, false otherwise
+ * @access public
+ * @static
+ */
+	function contains($val1, $val2 = null) {
+		if (empty($val1) || empty($val2)) {
+			return false;
+		}
+
+		foreach ($val2 as $key => $val) {
+			if (is_numeric($key)) {
+				Set::contains($val, $val1);
+			} else {
+				if (!isset($val1[$key]) || $val1[$key] != $val) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * 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
+ * @access 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[] = Set::countDim($value, true, $count + 1);
+				}
+			}
+			$return = max($depth);
+		} else {
+			if (is_array(reset($array))) {
+				$return = Set::countDim(reset($array)) + 1;
+			} else {
+				$return = 1;
+			}
+		}
+		return $return;
+	}
+/**
+ * Normalizes a string or array list.
+ *
+ * @param mixed $list List to normalize
+ * @param boolean $assoc If true, $list will be converted to an associative array
+ * @param string $sep If $list is a string, it will be split into an array with $sep
+ * @param boolean $trim If true, separated strings will be trimmed
+ * @return array
+ * @access public
+ * @static
+ */
+	function normalize($list, $assoc = true, $sep = ',', $trim = true) {
+		if (is_string($list)) {
+			$list = explode($sep, $list);
+			if ($trim) {
+				foreach ($list as $key => $value) {
+					$list[$key] = trim($value);
+				}
+			}
+			if ($assoc) {
+				return Set::normalize($list);
+			}
+		} elseif (is_array($list)) {
+			$keys = array_keys($list);
+			$count = count($keys);
+			$numeric = true;
+
+			if (!$assoc) {
+				for ($i = 0; $i < $count; $i++) {
+					if (!is_int($keys[$i])) {
+						$numeric = false;
+						break;
+					}
+				}
+			}
+			if (!$numeric || $assoc) {
+				$newList = array();
+				for ($i = 0; $i < $count; $i++) {
+					if (is_int($keys[$i])) {
+						$newList[$list[$keys[$i]]] = null;
+					} else {
+						$newList[$keys[$i]] = $list[$keys[$i]];
+					}
+				}
+				$list = $newList;
+			}
+		}
+		return $list;
+	}
+/**
+ * Creates an associative array using a $path1 as the path to build its keys, and optionally
+ * $path2 as path to get the values. If $path2 is not specified, all values will be initialized
+ * to null (useful for Set::merge). You can optionally group the values by what is obtained when
+ * following the path specified in $groupPath.
+ *
+ * @param array $data Array from where to extract keys and values
+ * @param mixed $path1 As an array, or as a dot-separated string.
+ * @param mixed $path2 As an array, or as a dot-separated string.
+ * @param string $groupPath As an array, or as a dot-separated string.
+ * @return array Combined array
+ * @access public
+ * @static
+ */
+	function combine($data, $path1 = null, $path2 = null, $groupPath = null) {
+		if (empty($data)) {
+			return array();
+		}
+
+		if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+
+		if (is_array($path1)) {
+			$format = array_shift($path1);
+			$keys = Set::format($data, $format, $path1);
+		} else {
+			$keys = Set::extract($data, $path1);
+		}
+
+		if (!empty($path2) && is_array($path2)) {
+			$format = array_shift($path2);
+			$vals = Set::format($data, $format, $path2);
+
+		} elseif (!empty($path2)) {
+			$vals = Set::extract($data, $path2);
+
+		} else {
+			$count = count($keys);
+			for ($i = 0; $i < $count; $i++) {
+				$vals[$i] = null;
+			}
+		}
+
+		if ($groupPath != null) {
+			$group = Set::extract($data, $groupPath);
+			if (!empty($group)) {
+				$c = count($keys);
+				for ($i = 0; $i < $c; $i++) {
+					if (!isset($group[$i])) {
+						$group[$i] = 0;
+					}
+					if (!isset($out[$group[$i]])) {
+						$out[$group[$i]] = array();
+					}
+					$out[$group[$i]][$keys[$i]] = $vals[$i];
+				}
+				return $out;
+			}
+		}
+
+		return array_combine($keys, $vals);
+	}
+/**
+ * Converts an object into an array. If $object is no object, reverse
+ * will return the same value.
+ *
+ * @param object $object Object to reverse
+ * @return array
+ * @static
+ */
+	function reverse($object) {
+		$out = array();
+		if (is_a($object, 'XmlNode')) {
+			$out = $object->toArray();
+			return $out;
+		} else if (is_object($object)) {
+			$keys = get_object_vars($object);
+			if (isset($keys['_name_'])) {
+				$identity = $keys['_name_'];
+				unset($keys['_name_']);
+			}
+			$new = array();
+			foreach ($keys as $key => $value) {
+				if (is_array($value)) {
+					$new[$key] = (array)Set::reverse($value);
+				} else {
+					if (isset($value->_name_)) {
+						$new = array_merge($new, Set::reverse($value));
+					} else {
+						$new[$key] = Set::reverse($value);
+					}
+				}
+			}
+			if (isset($identity)) {
+				$out[$identity] = $new;
+			} else {
+				$out = $new;
+			}
+		} elseif (is_array($object)) {
+			foreach ($object as $key => $value) {
+				$out[$key] = Set::reverse($value);
+			}
+		} else {
+			$out = $object;
+		}
+		return $out;
+	}
+/**
+ * Collapses a multi-dimensional array into a single dimension, using a delimited array path for
+ * each array element's key, i.e. array(array('Foo' => array('Bar' => 'Far'))) becomes
+ * array('0.Foo.Bar' => 'Far').
+ *
+ * @param array $data Array to flatten
+ * @param string $separator String used to separate array key elements in a path, defaults to '.'
+ * @return array
+ * @access public
+ * @static
+ */
+	function flatten($data, $separator = '.') {
+		$result = array();
+		$path = null;
+
+		if (is_array($separator)) {
+			extract($separator, EXTR_OVERWRITE);
+		}
+
+		if (!is_null($path)) {
+			$path .= $separator;
+		}
+
+		foreach ($data as $key => $val) {
+			if (is_array($val)) {
+				$result += (array)Set::flatten($val, array(
+					'separator' => $separator,
+					'path' => $path . $key
+				));
+			} else {
+				$result[$path . $key] = $val;
+			}
+		}
+		return $result;
+	}
+/**
+ * Flattens an array for sorting
+ *
+ * @param array $results
+ * @param string $key
+ * @return array
+ * @access private
+ */
+	function __flatten($results, $key = null) {
+		$stack = array();
+		foreach ($results as $k => $r) {
+			$id = $k;
+			if (!is_null($key)) {
+				$id = $key;
+			}
+			if (is_array($r)) {
+				$stack = array_merge($stack, Set::__flatten($r, $id));
+			} else {
+				$stack[] = array('id' => $id, 'value' => $r);
+			}
+		}
+		return $stack;
+	}
+/**
+ * Sorts an array by any value, determined by a Set-compatible path
+ *
+ * @param array $data
+ * @param string $path A Set-compatible path to the array value
+ * @param string $dir asc/desc
+ * @return array
+ * @static
+ */
+	function sort($data, $path, $dir) {
+		$result = Set::__flatten(Set::extract($data, $path));
+		list($keys, $values) = array(Set::extract($result, '{n}.id'), Set::extract($result, '{n}.value'));
+		
+		$dir = strtolower($dir);
+		if ($dir === 'asc') {
+			$dir = SORT_ASC;
+		} elseif ($dir === 'desc') {
+			$dir = SORT_DESC;
+		}
+		array_multisort($values, $dir, $keys, $dir);
+		$sorted = array();
+
+		$keys = array_unique($keys);
+
+		foreach ($keys as $k) {
+			$sorted[] = $data[$k];
+		}
+		return $sorted;
+	}
+/**
+ * Deprecated, Set class should be called statically
+ *
+ */
+	function &get() {
+		trigger_error('get() is deprecated. Set class should be called statically', E_USER_WARNING);
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/overloadable.php
===================================================================
--- cake/libs/overloadable.php	(revision 0)
+++ cake/libs/overloadable.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+/* SVN FILE: $Id: overloadable.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Overloadable class selector
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+
+/**
+ * Load the interface class based on the version of PHP.
+ *
+ */
+if (!PHP5) {
+	require(LIBS . 'overloadable_php4.php');
+} else {
+	require(LIBS . 'overloadable_php5.php');
+}
+?>
\ No newline at end of file
Index: cake/libs/xml.php
===================================================================
--- cake/libs/xml.php	(revision 0)
+++ cake/libs/xml.php	(revision 0)
@@ -0,0 +1,1369 @@
+<?php
+/* SVN FILE: $Id: xml.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * XML handling for Cake.
+ *
+ * The methods in these classes enable the datasources that use XML to work.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v .0.10.3.1400
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', 'Set');
+/**
+ * XML node.
+ *
+ * Single XML node in an XML tree.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v .0.10.3.1400
+ */
+class XmlNode extends Object {
+/**
+ * Name of node
+ *
+ * @var string
+ * @access public
+ */
+	var $name = null;
+/**
+ * Node namespace
+ *
+ * @var string
+ * @access public
+ */
+	var $namespace = null;
+/**
+ * Namespaces defined for this node and all child nodes
+ *
+ * @var array
+ * @access public
+ */
+	var $namespaces = array();
+/**
+ * Value of node
+ *
+ * @var string
+ * @access public
+ */
+	var $value;
+/**
+ * Attributes on this node
+ *
+ * @var array
+ * @access public
+ */
+	var $attributes = array();
+/**
+ * This node's children
+ *
+ * @var array
+ * @access public
+ */
+	var $children = array();
+/**
+ * Reference to parent node.
+ *
+ * @var XmlNode
+ * @access private
+ */
+	var $__parent = null;
+/**
+ * Constructor.
+ *
+ * @param string $name Node name
+ * @param array $attributes Node attributes
+ * @param mixed $value Node contents (text)
+ * @param array $children Node children
+ */
+	function __construct($name = null, $value = null, $namespace = null) {
+		if (strpos($name, ':') !== false) {
+			list($prefix, $name) = explode(':', $name);
+			if (!$namespace) {
+				$namespace = $prefix;
+			}
+		}
+		$this->name = $name;
+		if ($namespace) {
+			$this->namespace = $namespace;
+		}
+
+		if (is_array($value) || is_object($value)) {
+			$this->normalize($value);
+		} elseif (!empty($value) || $value === 0 || $value === '0') {
+			$this->createTextNode($value);
+		}
+	}
+
+/**
+ * Adds a namespace to the current node
+ *
+ * @param string $prefix The namespace prefix
+ * @param string $url The namespace DTD URL
+ * @return void
+ */
+	function addNamespace($prefix, $url) {
+		if ($ns = Xml::addGlobalNs($prefix, $url)) {
+			$this->namespaces = array_merge($this->namespaces, $ns);
+			return true;
+		}
+		return false;
+	}
+/**
+ * Adds a namespace to the current node
+ *
+ * @param string $prefix The namespace prefix
+ * @param string $url The namespace DTD URL
+ * @return void
+ */
+	function removeNamespace($prefix) {
+		if (Xml::removeGlobalNs($prefix)) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Creates an XmlNode object that can be appended to this document or a node in it
+ *
+ * @param string $name Node name
+ * @param string $value Node value
+ * @param string $namespace Node namespace
+ * @return object XmlNode
+ */
+	function &createNode($name = null, $value = null, $namespace = false) {
+		$node =& new XmlNode($name, $value, $namespace);
+		$node->setParent($this);
+		return $node;
+	}
+/**
+ * Creates an XmlElement object that can be appended to this document or a node in it
+ *
+ * @param string $name Element name
+ * @param string $value Element value
+ * @param array $attributes Element attributes
+ * @param string $namespace Node namespace
+ * @return object XmlElement
+ */
+	function &createElement($name = null, $value = null, $attributes = array(), $namespace = false) {
+		$element =& new XmlElement($name, $value, $attributes, $namespace);
+		$element->setParent($this);
+		return $element;
+	}
+/**
+ * Creates an XmlTextNode object that can be appended to this document or a node in it
+ *
+ * @param string $value Node value
+ * @return object XmlTextNode
+ */
+	function &createTextNode($value = null) {
+		$node = new XmlTextNode($value);
+		$node->setParent($this);
+		return $node;
+	}
+/**
+ * Gets the XML element properties from an object.
+ *
+ * @param object $object Object to get properties from
+ * @return array Properties from object
+ * @access public
+ */
+	function normalize($object, $keyName = null, $options = array()) {
+		if (is_a($object, 'XmlNode')) {
+			return $object;
+		}
+		$name = null;
+		$options = array_merge(array('format' => 'attributes'), $options);
+
+		if ($keyName !== null && !is_numeric($keyName)) {
+			$name = $keyName;
+		} elseif (!empty($object->_name_)) {
+			$name = $object->_name_;
+		} elseif (isset($object->name)) {
+			$name = $object->name;
+		} elseif ($options['format'] == 'attributes') {
+			$name = get_class($object);
+		}
+
+		$tagOpts = $this->__tagOptions($name);
+		if ($tagOpts === false) {
+			return;
+		}
+
+		if (isset($tagOpts['name'])) {
+			$name = $tagOpts['name'];
+		} elseif ($name != strtolower($name)) {
+			$name = Inflector::slug(Inflector::underscore($name));
+		}
+
+		if (!empty($name)) {
+			$node =& $this->createElement($name);
+		} else {
+			$node =& $this;
+		}
+
+		$namespace = array();
+		$attributes = array();
+		$children = array();
+		$chldObjs = array();
+		$document =& $this->document();
+
+		if (is_object($object)) {
+			$chldObjs = get_object_vars($object);
+		} elseif (is_array($object)) {
+			$chldObjs = $object;
+		} elseif (!empty($object) || $object === 0) {
+			$node->createTextNode($object);
+		}
+		$attr = array();
+
+		if (isset($tagOpts['attributes'])) {
+			$attr = $tagOpts['attributes'];
+		}
+		if (isset($tagOpts['value']) && isset($chldObjs[$tagOpts['value']])) {
+			$node->createTextNode($chldObjs[$tagOpts['value']]);
+			unset($chldObjs[$tagOpts['value']]);
+		}
+		unset($chldObjs['_name_']);
+		$c = 0;
+
+		foreach ($chldObjs as $key => $val) {
+			if (in_array($key, $attr) && !is_object($val) && !is_array($val)) {
+				$attributes[$key] = $val;
+			} else {
+				if (!isset($tagOpts['children']) || $tagOpts['children'] === array() || (is_array($tagOpts['children']) && in_array($key, $tagOpts['children']))) {
+					$n = $key;
+
+					if (is_numeric($n)) {
+						$n = $name;
+					}
+					if (is_array($val)) {
+						foreach ($val as $i => $obj2) {
+							$n2 = $i;
+							if (is_numeric($n2)) {
+								$n2 = $n;
+							}
+							$node->normalize($obj2, $n2, $options);
+						}
+					} else {
+						if (is_object($val)) {
+							$node->normalize($val, $n, $options);
+						} elseif ($options['format'] == 'tags' && $this->__tagOptions($key) !== false) {
+							$tmp =& $node->createElement($key);
+							if (!empty($val) || $val === 0) {
+								$tmp->createTextNode($val);
+							}
+						} elseif ($options['format'] == 'attributes') {
+							$node->addAttribute($key, $val);
+						}
+					}
+				}
+			}
+			$c++;
+		}
+		if (!empty($name)) {
+			return $node;
+		}
+		return $children;
+	}
+/**
+ * Gets the tag-specific options for the given node name
+ *
+ * @param string $name XML tag name
+ * @param string $option The specific option to query.  Omit for all options
+ * @return mixed A specific option value if $option is specified, otherwise an array of all options
+ * @access private
+ */
+	function __tagOptions($name, $option = null) {
+		if (isset($this->__tags[$name])) {
+			$tagOpts = $this->__tags[$name];
+		} elseif (isset($this->__tags[strtolower($name)])) {
+			$tagOpts = $this->__tags[strtolower($name)];
+		} else {
+			return null;
+		}
+		if ($tagOpts === false) {
+			return false;
+		}
+		if (empty($option)) {
+			return $tagOpts;
+		}
+		if (isset($tagOpts[$option])) {
+			return $tagOpts[$option];
+		}
+		return null;
+	}
+/**
+ * Returns the fully-qualified XML node name, with namespace
+ *
+ * @access public
+ */
+	function name() {
+		if (!empty($this->namespace)) {
+			$_this =& XmlManager::getInstance();
+			if (!isset($_this->options['verifyNs']) || !$_this->options['verifyNs'] || in_array($this->namespace, array_keys($_this->namespaces))) {
+				return $this->namespace . ':' . $this->name;
+			}
+		}
+		return $this->name;
+	}
+/**
+ * Sets the parent node of this XmlNode.
+ *
+ * @access public
+ */
+	function setParent(&$parent) {
+		if (strtolower(get_class($this)) == 'xml') {
+			return;
+		}
+		if (isset($this->__parent) && is_object($this->__parent)) {
+			if ($this->__parent->compare($parent)) {
+				return;
+			}
+			foreach ($this->__parent->children as $i => $child) {
+				if ($this->compare($child)) {
+					array_splice($this->__parent->children, $i, 1);
+					break;
+				}
+			}
+		}
+		if ($parent == null) {
+			unset($this->__parent);
+		} else {
+			$parent->children[] =& $this;
+			$this->__parent =& $parent;
+		}
+	}
+/**
+ * Returns a copy of self.
+ *
+ * @return object Cloned instance
+ * @access public
+ */
+	function cloneNode() {
+		return clone($this);
+	}
+/**
+ * Compares $node to this XmlNode object
+ *
+ * @param object An XmlNode or subclass instance
+ * @return boolean True if the nodes match, false otherwise
+ * @access public
+ */
+	function compare($node) {
+		$keys = array(get_object_vars($this), get_object_vars($node));
+		return ($keys[0] === $keys[1]);
+	}
+/**
+ * Append given node as a child.
+ *
+ * @param object $child XmlNode with appended child
+ * @param array $options XML generator options for objects and arrays
+ * @return object A reference to the appended child node
+ * @access public
+ */
+	function &append(&$child, $options = array()) {
+		if (empty($child)) {
+			$return = false;
+			return $return;
+		}
+
+		if (is_object($child)) {
+			if ($this->compare($child)) {
+				trigger_error('Cannot append a node to itself.');
+				$return = false;
+				return $return;
+			}
+		} else if (is_array($child)) {
+			$child = Set::map($child);
+			if (is_array($child)) {
+				if (!is_a(current($child), 'XmlNode')) {
+					foreach ($child as $i => $childNode) {
+						$child[$i] = $this->normalize($childNode, null, $options);
+					}
+				} else {
+					foreach ($child as $childNode) {
+						$this->append($childNode, $options);
+					}
+				}
+				return $child;
+			}
+		} else {
+			$attributes = array();
+			if (func_num_args() >= 2) {
+				$attributes = func_get_arg(1);
+			}
+			$child =& $this->createNode($child, null, $attributes);
+		}
+
+		$child = $this->normalize($child, null, $options);
+
+		if (empty($child->namespace) && !empty($this->namespace)) {
+			$child->namespace = $this->namespace;
+		}
+
+		if (is_a($child, 'XmlNode')) {
+			$child->setParent($this);
+		}
+
+		return $child;
+	}
+/**
+ * Returns first child node, or null if empty.
+ *
+ * @return object First XmlNode
+ * @access public
+ */
+	function &first() {
+		if (isset($this->children[0])) {
+			return $this->children[0];
+		} else {
+			$return = null;
+			return $return;
+		}
+	}
+/**
+ * Returns last child node, or null if empty.
+ *
+ * @return object Last XmlNode
+ * @access public
+ */
+	function &last() {
+		if (count($this->children) > 0) {
+			return $this->children[count($this->children) - 1];
+		} else {
+			$return = null;
+			return $return;
+		}
+	}
+/**
+ * Returns child node with given ID.
+ *
+ * @param string $id Name of child node
+ * @return object Child XmlNode
+ * @access public
+ */
+	function &child($id) {
+		$null = null;
+
+		if (is_int($id)) {
+			if (isset($this->children[$id])) {
+				return $this->children[$id];
+			} else {
+				return null;
+			}
+		} elseif (is_string($id)) {
+			for ($i = 0; $i < count($this->children); $i++) {
+				if ($this->children[$i]->name == $id) {
+					return $this->children[$i];
+				}
+			}
+		}
+		return $null;
+	}
+/**
+ * Gets a list of childnodes with the given tag name.
+ *
+ * @param string $name Tag name of child nodes
+ * @return array An array of XmlNodes with the given tag name
+ * @access public
+ */
+	function children($name) {
+		$nodes = array();
+		$count = count($this->children);
+		for ($i = 0; $i < $count; $i++) {
+			if ($this->children[$i]->name == $name) {
+				$nodes[] =& $this->children[$i];
+			}
+		}
+		return $nodes;
+	}
+/**
+ * Gets a reference to the next child node in the list of this node's parent.
+ *
+ * @return object A reference to the XmlNode object
+ * @access public
+ */
+	function &nextSibling() {
+		$null = null;
+		$count = count($this->__parent->children);
+		for ($i = 0; $i < $count; $i++) {
+			if ($this->__parent->children[$i] == $this) {
+				if ($i >= $count - 1 || !isset($this->__parent->children[$i + 1])) {
+					return $null;
+				}
+				return $this->__parent->children[$i + 1];
+			}
+		}
+		return $null;
+	}
+/**
+ * Gets a reference to the previous child node in the list of this node's parent.
+ *
+ * @return object A reference to the XmlNode object
+ * @access public
+ */
+	function &previousSibling() {
+		$null = null;
+		$count = count($this->__parent->children);
+		for ($i = 0; $i < $count; $i++) {
+			if ($this->__parent->children[$i] == $this) {
+				if ($i == 0 || !isset($this->__parent->children[$i - 1])) {
+					return $null;
+				}
+				return $this->__parent->children[$i - 1];
+			}
+		}
+		return $null;
+	}
+/**
+ * Returns parent node.
+ *
+ * @return object Parent XmlNode
+ * @access public
+ */
+	function &parent() {
+		return $this->__parent;
+	}
+/**
+ * Returns the XML document to which this node belongs
+ *
+ * @return object Parent XML object
+ * @access public
+ */
+	function &document() {
+		$document =& $this;
+		while (true) {
+			if (get_class($document) == 'Xml' || $document == null) {
+				break;
+			}
+			$document =& $document->parent();
+		}
+		return $document;
+	}
+/**
+ * Returns true if this structure has child nodes.
+ *
+ * @return bool
+ * @access public
+ */
+	function hasChildren() {
+		if (is_array($this->children) && count($this->children) > 0) {
+			return true;
+		}
+		return false;
+	}
+/**
+ * Returns this XML structure as a string.
+ *
+ * @return string String representation of the XML structure.
+ * @access public
+ */
+	function toString($options = array(), $depth = 0) {
+		if (is_int($options)) {
+			$depth = $options;
+			$options = array();
+		}
+		$defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false, 'showEmpty' => true, 'leaveOpen' => false);
+		$options = array_merge($defaults, Xml::options(), $options);
+		$tag = !(strpos($this->name, '#') === 0);
+		$d = '';
+
+		if ($tag) {
+			if ($options['whitespace']) {
+				$d .= str_repeat("\t", $depth);
+			}
+
+			$d .= '<' . $this->name();
+			if (count($this->namespaces) > 0) {
+				foreach ($this->namespaces as $key => $val) {
+					$val = str_replace('"', '\"', $val);
+					$d .= ' xmlns:' . $key . '="' . $val . '"';
+				}
+			}
+
+			$parent =& $this->parent();
+			if ($parent->name === '#document' && count($parent->namespaces) > 0) {
+				foreach ($parent->namespaces as $key => $val) {
+					$val = str_replace('"', '\"', $val);
+					$d .= ' xmlns:' . $key . '="' . $val . '"';
+				}
+			}
+
+			if (is_array($this->attributes) && count($this->attributes) > 0) {
+				foreach ($this->attributes as $key => $val) {
+					$d .= ' ' . $key . '="' . htmlspecialchars($val, ENT_QUOTES, Configure::read('App.encoding')) . '"';
+				}
+			}
+		}
+
+		if (!$this->hasChildren() && empty($this->value) && $this->value !== 0 && $tag) {
+			if (!$options['leaveOpen']) {
+				$d .= ' />';
+			}
+			if ($options['whitespace']) {
+				$d .= "\n";
+			}
+		} elseif ($tag || $this->hasChildren()) {
+			if ($tag) {
+				$d .= '>';
+			}
+			if ($this->hasChildren()) {
+				if ($options['whitespace']) {
+					$d .= "\n";
+				}
+
+				$count = count($this->children);
+				$cDepth = $depth + 1;
+				for ($i = 0; $i < $count; $i++) {
+					$d .= $this->children[$i]->toString($options, $cDepth);
+				}
+				if ($tag) {
+					if ($options['whitespace'] && $tag) {
+						$d .= str_repeat("\t", $depth);
+					}
+					if (!$options['leaveOpen']) {
+						$d .= '</' . $this->name() . '>';
+					}
+					if ($options['whitespace']) {
+						$d .= "\n";
+					}
+				}
+			}
+		}
+		return $d;
+	}
+/**
+ * Return array representation of current object.
+ *
+ * @param boolean $camelize true will camelize child nodes, false will not alter node names
+ * @return array Array representation
+ * @access public
+ */
+	function toArray($camelize = true) {
+		$out = $this->attributes;
+		$multi = null;
+
+		foreach ($this->children as $child) {
+			$key = $camelize ? Inflector::camelize($child->name) : $child->name;
+
+			if (is_a($child, 'XmlTextNode')) {
+				$out['value'] = $child->value;
+				continue;
+			} elseif (isset($child->children[0]) && is_a($child->children[0], 'XmlTextNode')) {
+				$value = $child->children[0]->value;
+
+				if ($child->attributes) {
+					$value = array_merge(array('value' => $value), $child->attributes);
+				}
+
+				if (isset($out[$child->name]) || isset($multi[$key])) {
+					if (!isset($multi[$key])) {
+						$multi[$key] = array($out[$child->name]);
+						unset($out[$child->name]);
+					}
+					$multi[$key][] = $value;
+				} else {
+					$out[$child->name] = $value;
+				}
+				continue;
+			} else {
+				$value = $child->toArray($camelize);
+			}
+
+			if (!isset($out[$key])) {
+				$out[$key] = $value;
+			} else {
+				if (!is_array($out[$key]) || !isset($out[$key][0])) {
+					$out[$key] = array($out[$key]);
+				}
+				$out[$key][] = $value;
+			}
+		}
+
+		if (isset($multi)) {
+			$out = array_merge($out, $multi);
+		}
+		return $out;
+	}
+/**
+ * Returns data from toString when this object is converted to a string.
+ *
+ * @return string String representation of this structure.
+ * @access private
+ */
+	function __toString() {
+		return $this->toString();
+	}
+/**
+ * Debug method. Deletes the parent. Also deletes this node's children,
+ * if given the $recursive parameter.
+ *
+ * @param boolean $recursive Recursively delete elements.
+ * @access private
+ */
+	function __killParent($recursive = true) {
+		unset($this->__parent, $this->_log);
+		if ($recursive && $this->hasChildren()) {
+			for ($i = 0; $i < count($this->children); $i++) {
+				$this->children[$i]->__killParent(true);
+			}
+		}
+	}
+}
+
+/**
+ * Main XML class.
+ *
+ * Parses and stores XML data, representing the root of an XML document
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v .0.10.3.1400
+ */
+class Xml extends XmlNode {
+
+/**
+ * Resource handle to XML parser.
+ *
+ * @var resource
+ * @access private
+ */
+	var $__parser;
+/**
+ * File handle to XML indata file.
+ *
+ * @var resource
+ * @access private
+ */
+	var $__file;
+/**
+ * Raw XML string data (for loading purposes)
+ *
+ * @var string
+ * @access private
+ */
+	var $__rawData = null;
+
+/**
+ * XML document header
+ *
+ * @var string
+ * @access private
+ */
+	var $__header = null;
+
+/**
+ * Default array keys/object properties to use as tag names when converting objects or array structures to XML.
+ * Set by passing $options['tags'] to this object's constructor.
+ *
+ * @var array
+ * @access private
+ */
+	var $__tags = array();
+
+/**
+ * XML document version
+ *
+ * @var string
+ * @access private
+ */
+	var $version = '1.0';
+
+/**
+ * XML document encoding
+ *
+ * @var string
+ * @access private
+ */
+	var $encoding = 'UTF-8';
+
+/**
+ * Constructor.  Sets up the XML parser with options, gives it this object as
+ * its XML object, and sets some variables.
+ *
+ * @param string $input What should be used to set up
+ * @param array $options Options to set up with
+ */
+	function __construct($input = null, $options = array()) {
+		$defaults = array(
+			'root' => '#document', 'tags' => array(), 'namespaces' => array(),
+			'version' => '1.0', 'encoding' => 'UTF-8', 'format' => 'attributes'
+		);
+		$options = array_merge($defaults, Xml::options(), $options);
+
+		foreach (array('version', 'encoding', 'namespaces') as $key) {
+			$this->{$key} = $options[$key];
+		}
+		$this->__tags = $options['tags'];
+		parent::__construct($options['root']);
+
+		if (!empty($input)) {
+			if (is_string($input)) {
+				$this->load($input);
+			} elseif (is_array($input) || is_object($input)) {
+				$this->append($input, $options);
+			}
+		}
+
+		// if (Configure::read('App.encoding') !== null) {
+		// 	$this->encoding = Configure::read('App.encoding');
+		// }
+	}
+/**
+ * Initialize XML object from a given XML string. Returns false on error.
+ *
+ * @param string $input XML string, a path to a file, or an HTTP resource to load
+ * @return boolean Success
+ * @access public
+ */
+	function load($input) {
+		if (!is_string($input)) {
+			return false;
+		}
+		$this->__rawData = null;
+		$this->__header = null;
+
+		if (strstr($input, "<")) {
+			$this->__rawData = $input;
+		} elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
+			App::import('Core', 'HttpSocket');
+			$socket = new HttpSocket();
+			$this->__rawData = $socket->get($input);
+		} elseif (file_exists($input)) {
+			$this->__rawData = file_get_contents($input);
+		} else {
+			trigger_error('XML cannot be read');
+			return false;
+		}
+		return $this->parse();
+	}
+/**
+ * Parses and creates XML nodes from the __rawData property.
+ *
+ * @return boolean Success
+ * @access public
+ * @see Xml::load()
+ * @todo figure out how to link attributes and namespaces
+ */
+	function parse() {
+		$this->__initParser();
+		$this->__header = trim(str_replace(a('<' . '?', '?' . '>'), a('', ''), substr(trim($this->__rawData), 0, strpos($this->__rawData, "\n"))));
+
+		xml_parse_into_struct($this->__parser, $this->__rawData, $vals);
+		$xml =& $this;
+		$count = count($vals);
+
+		for ($i = 0; $i < $count; $i++) {
+			$data = $vals[$i];
+			$data = array_merge(array('tag' => null, 'value' => null, 'attributes' => array()), $data);
+			switch ($data['type']) {
+				case "open" :
+					$xml =& $xml->createElement($data['tag'], $data['value'], $data['attributes']);
+				break;
+				case "close" :
+					$xml =& $xml->parent();
+				break;
+				case "complete" :
+					$xml->createElement($data['tag'], $data['value'], $data['attributes']);
+				break;
+				case 'cdata':
+					$xml->createTextNode($data['value']);
+				break;
+			}
+		}
+		return true;
+	}
+/**
+ * Initializes the XML parser resource
+ *
+ * @return void
+ * @access private
+ */
+	function __initParser() {
+		if (empty($this->__parser)) {
+			$this->__parser = xml_parser_create();
+			xml_set_object($this->__parser, $this);
+			xml_parser_set_option($this->__parser, XML_OPTION_CASE_FOLDING, 0);
+			xml_parser_set_option($this->__parser, XML_OPTION_SKIP_WHITE, 1);
+		}
+	}
+/**
+ * Returns a string representation of the XML object
+ *
+ * @param mixed $options If boolean: whether to include the XML header with the document (defaults to true); if array:
+ *						 overrides the default XML generation options
+ * @return string XML data
+ * @access public
+ * @deprecated
+ * @see Xml::toString()
+ */
+	function compose($options = array()) {
+		return $this->toString($options);
+	}
+/**
+ * If debug mode is on, this method echoes an error message.
+ *
+ * @param string $msg Error message
+ * @param integer $code Error code
+ * @param integer $line Line in file
+ * @access public
+ */
+	function error($msg, $code = 0, $line = 0) {
+		if (Configure::read('debug')) {
+			echo $msg . " " . $code . " " . $line;
+		}
+	}
+/**
+ * Returns a string with a textual description of the error code, or FALSE if no description was found.
+ *
+ * @param integer $code Error code
+ * @return string Error message
+ * @access public
+ */
+	function getError($code) {
+		$r = @xml_error_string($code);
+		return $r;
+	}
+
+// Overridden functions from superclass
+
+/**
+ * Get next element. NOT implemented.
+ *
+ * @return object
+ * @access public
+ */
+	function &next() {
+		$return = null;
+		return $return;
+	}
+/**
+ * Get previous element. NOT implemented.
+ *
+ * @return object
+ * @access public
+ */
+	function &previous() {
+		$return = null;
+		return $return;
+	}
+/**
+ * Get parent element. NOT implemented.
+ *
+ * @return object
+ * @access public
+ */
+	function &parent() {
+		$return = null;
+		return $return;
+	}
+/**
+ * Adds a namespace to the current document
+ *
+ * @param string $prefix The namespace prefix
+ * @param string $url The namespace DTD URL
+ * @return void
+ */
+	function addNamespace($prefix, $url) {
+		if ($count = count($this->children)) {
+			for ($i = 0; $i < $count; $i++) {
+				$this->children[$i]->addNamespace($prefix, $url);
+			}
+			return true;
+		}
+		return parent::addNamespace($prefix, $url);
+	}
+/**
+ * Removes a namespace to the current document
+ *
+ * @param string $prefix The namespace prefix
+ * @return void
+ */
+	function removeNamespace($prefix) {
+		if ($count = count($this->children)) {
+			for ($i = 0; $i < $count; $i++) {
+				$this->children[$i]->removeNamespace($prefix);
+			}
+			return true;
+		}
+		return parent::removeNamespace($prefix);
+	}
+/**
+ * Return string representation of current object.
+ *
+ * @return string String representation
+ * @access public
+ */
+	function toString($options = array()) {
+		if (is_bool($options)) {
+			$options = array('header' => $options);
+		}
+
+		$defaults = array('header' => false, 'encoding' => $this->encoding);
+		$options = array_merge($defaults, Xml::options(), $options);
+		$data = parent::toString($options, 0);
+
+		if ($options['header']) {
+			if (!empty($this->__header)) {
+				return $this->header($this->__header)  . "\n" . $data;
+			}
+			return $this->header()  . "\n" . $data;
+		}
+
+		return $data;
+	}
+/**
+ * Return a header used on the first line of the xml file
+ *
+ * @param  mixed  $attrib attributes of the header element
+ * @return string formated header
+ */
+	function header($attrib = array()) {
+		$header = 'xml';
+		if (is_string($attrib)) {
+			$header = $attrib;
+		} else {
+
+			$attrib = array_merge(array('version' => $this->version, 'encoding' => $this->encoding), $attrib);
+			foreach ($attrib as $key=>$val) {
+				$header .= ' ' . $key . '="' . $val . '"';
+			}
+		}
+		return '<' . '?' . $header . ' ?' . '>';
+	}
+
+/**
+ * Destructor, used to free resources.
+ *
+ * @access private
+ */
+	function __destruct() {
+		if (is_resource($this->__parser)) {
+			xml_parser_free($this->__parser);
+		}
+	}
+/**
+ * Adds a namespace to any XML documents generated or parsed
+ *
+ * @param  string  $name The namespace name
+ * @param  string  $url  The namespace URI; can be empty if in the default namespace map
+ * @return boolean False if no URL is specified, and the namespace does not exist
+ *                 default namespace map, otherwise true
+ * @access public
+ * @static
+ */
+	function addGlobalNs($name, $url = null) {
+		$_this =& XmlManager::getInstance();
+		if ($ns = Xml::resolveNamespace($name, $url)) {
+			$_this->namespaces = array_merge($_this->namespaces, $ns);
+			return $ns;
+		}
+		return false;
+	}
+/**
+ * Resolves current namespace
+ *
+ * @param  string  $name
+ * @param  string  $url
+ * @return array
+ */
+	function resolveNamespace($name, $url) {
+		$_this =& XmlManager::getInstance();
+		if ($url == null && isset($_this->defaultNamespaceMap[$name])) {
+			$url = $_this->defaultNamespaceMap[$name];
+		} elseif ($url == null) {
+			return false;
+		}
+
+		if (!strpos($url, '://') && isset($_this->defaultNamespaceMap[$name])) {
+			$_url = $_this->defaultNamespaceMap[$name];
+			$name = $url;
+			$url = $_url;
+		}
+		return array($name => $url);
+	}
+/**
+ * Alias to Xml::addNs
+ *
+ * @access public
+ * @static
+ */
+	function addGlobalNamespace($name, $url = null) {
+		return Xml::addGlobalNs($name, $url);
+	}
+/**
+ * Removes a namespace added in addNs()
+ *
+ * @param  string  $name The namespace name or URI
+ * @access public
+ * @static
+ */
+	function removeGlobalNs($name) {
+		$_this =& XmlManager::getInstance();
+		if (isset($_this->namespaces[$name])) {
+			unset($_this->namespaces[$name]);
+			unset($this->namespaces[$name]);
+			return true;
+		} elseif (in_array($name, $_this->namespaces)) {
+			$keys = array_keys($_this->namespaces);
+			$count = count($keys);
+			for ($i = 0; $i < $count; $i++) {
+				if ($_this->namespaces[$keys[$i]] == $name) {
+					unset($_this->namespaces[$keys[$i]]);
+					unset($this->namespaces[$keys[$i]]);
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+/**
+ * Alias to Xml::removeNs
+ *
+ * @access public
+ * @static
+ */
+	function removeGlobalNamespace($name) {
+		return Xml::removeGlobalNs($name);
+	}
+/**
+ * Sets/gets global XML options
+ *
+ * @param array $options
+ * @return array
+ * @access public
+ * @static
+ */
+	function options($options = array()) {
+		$_this =& XmlManager::getInstance();
+		$_this->options = array_merge($_this->options, $options);
+		return $_this->options;
+	}
+}
+/**
+ * The XML Element
+ *
+ */
+class XmlElement extends XmlNode {
+/**
+ * Construct an Xml element
+ *
+ * @param  string  $name name of the node
+ * @param  string  $value value of the node
+ * @param  array  $attributes
+ * @param  string  $namespace
+ * @return string A copy of $data in XML format
+ */
+	function __construct($name = null, $value = null, $attributes = array(), $namespace = false) {
+		parent::__construct($name, $value, $namespace);
+		$this->addAttribute($attributes);
+	}
+/**
+ * Get all the attributes for this element
+ *
+ * @return array
+ */
+	function attributes() {
+		return $this->attributes;
+	}
+/**
+ * Add attributes to this element
+ *
+ * @param  string  $name name of the node
+ * @param  string  $value value of the node
+ * @return boolean
+ */
+	function addAttribute($name, $val = null) {
+		if (is_object($name)) {
+			$name = get_object_vars($name);
+		}
+		if (is_array($name)) {
+			foreach ($name as $key => $val) {
+				$this->addAttribute($key, $val);
+			}
+			return true;
+		}
+		if (is_numeric($name)) {
+			$name = $val;
+			$val = null;
+		}
+		if (!empty($name)) {
+			if (strpos($name, 'xmlns') === 0) {
+				if ($name == 'xmlns') {
+					$this->namespace = $val;
+				} else {
+					list($pre, $prefix) = explode(':', $name);
+					$this->addNamespace($prefix, $val);
+					return true;
+				}
+			}
+			$this->attributes[$name] = $val;
+			return true;
+		}
+		return false;
+	}
+/**
+ * Remove attributes to this element
+ *
+ * @param  string  $name name of the node
+ * @return boolean
+ */
+	function removeAttribute($attr) {
+		if (array_key_exists($attr, $this->attributes)) {
+			unset($this->attributes[$attr]);
+			return true;
+		}
+		return false;
+	}
+}
+
+/**
+ * XML text or CDATA node
+ *
+ * Stores XML text data according to the encoding of the parent document
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v .1.2.6000
+ */
+class XmlTextNode extends XmlNode {
+/**
+ * Harcoded XML node name, represents this object as a text node
+ *
+ * @var string
+ */
+	var $name = '#text';
+/**
+ * The text/data value which this node contains
+ *
+ * @var string
+ */
+	var $value = null;
+/**
+ * Construct text node with the given parent object and data
+ *
+ * @param object $parent Parent XmlNode/XmlElement object
+ * @param mixed $value Node value
+ */
+	function __construct($value = null) {
+		$this->value = $value;
+	}
+/**
+ * Looks for child nodes in this element
+ *
+ * @return boolean False - not supported
+ */
+	function hasChildren() {
+		return false;
+	}
+/**
+ * Append an XML node: XmlTextNode does not support this operation
+ *
+ * @return boolean False - not supported
+ * @todo make convertEntities work without mb support, convert entities to number entities
+ */
+	function append() {
+		return false;
+	}
+/**
+ * Return string representation of current text node object.
+ *
+ * @return string String representation
+ * @access public
+ */
+	function toString($options = array(), $depth = 0) {
+		if (is_int($options)) {
+			$depth = $options;
+			$options = array();
+		}
+
+		$defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities'	=> false);
+		$options = array_merge($defaults, Xml::options(), $options);
+		$val = $this->value;
+
+		if ($options['convertEntities'] && function_exists('mb_convert_encoding')) {
+			$val = mb_convert_encoding($val,'UTF-8', 'HTML-ENTITIES');
+		}
+
+		if ($options['cdata'] === true && !is_numeric($val)) {
+			$val = '<![CDATA[' . $val . ']]>';
+		}
+
+		if ($options['whitespace']) {
+			return str_repeat("\t", $depth) . $val . "\n";
+		}
+		return $val;
+	}
+}
+/**
+ * Manages application-wide namespaces and XML parsing/generation settings.
+ * Private class, used exclusively within scope of XML class.
+ *
+ * @access private
+ */
+class XmlManager {
+
+/**
+ * Global XML namespaces.  Used in all XML documents processed by this application
+ *
+ * @var array
+ * @access public
+ */
+	var $namespaces = array();
+/**
+ * Global XML document parsing/generation settings.
+ *
+ * @var array
+ * @access public
+ */
+	var $options = array();
+/**
+ * Map of common namespace URIs
+ *
+ * @access private
+ * @var array
+ */
+	var $defaultNamespaceMap = array(
+		'dc'     => 'http://purl.org/dc/elements/1.1/',					// Dublin Core
+		'dct'    => 'http://purl.org/dc/terms/',						// Dublin Core Terms
+		'g'			=> 'http://base.google.com/ns/1.0',					// Google Base
+		'rc'		=> 'http://purl.org/rss/1.0/modules/content/',		// RSS 1.0 Content Module
+		'wf'		=> 'http://wellformedweb.org/CommentAPI/',			// Well-Formed Web Comment API
+		'fb'		=> 'http://rssnamespace.org/feedburner/ext/1.0',	// FeedBurner extensions
+		'lj'		=> 'http://www.livejournal.org/rss/lj/1.0/',		// Live Journal
+		'itunes'	=> 'http://www.itunes.com/dtds/podcast-1.0.dtd',	// iTunes
+		'xhtml'		=> 'http://www.w3.org/1999/xhtml',					// XHTML,
+		'atom'	 	=> 'http://www.w3.org/2005/Atom'					// Atom
+	);
+/**
+ * Returns a reference to the global XML object that manages app-wide XML settings
+ *
+ * @return object
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new XmlManager();
+		}
+		return $instance[0];
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/cake_log.php
===================================================================
--- cake/libs/cake_log.php	(revision 0)
+++ cake/libs/cake_log.php	(revision 0)
@@ -0,0 +1,101 @@
+<?php
+/* SVN FILE: $Id: cake_log.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Logging.
+ *
+ * Log messages to text files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+	if (!class_exists('File')) {
+		require LIBS . 'file.php';
+	}
+/**
+ * Set up error level constants to be used within the framework if they are not defined within the
+ * system.
+ *
+ */
+	if (!defined('LOG_WARNING')) {
+		define('LOG_WARNING', 3);
+	}
+	if (!defined('LOG_NOTICE')) {
+		define('LOG_NOTICE', 4);
+	}
+	if (!defined('LOG_DEBUG')) {
+		define('LOG_DEBUG', 5);
+	}
+	if (!defined('LOG_INFO')) {
+		define('LOG_INFO', 6);
+	}
+/**
+ * Logs messages to text files
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CakeLog {
+/**
+ * Writes given message to a log file in the logs directory.
+ *
+ * @param string $type Type of log, becomes part of the log's filename
+ * @param string $msg  Message to log
+ * @return boolean Success
+ * @access public
+ * @static
+ */
+	function write($type, $msg) {
+		if (!defined('LOG_ERROR')) {
+			define('LOG_ERROR', 2);
+		}
+		if (!defined('LOG_ERR')) {
+			define('LOG_ERR', LOG_ERROR);
+		}
+		$levels = array(
+			LOG_WARNING => 'warning',
+			LOG_NOTICE => 'notice',
+			LOG_INFO => 'info',
+			LOG_DEBUG => 'debug',
+			LOG_ERR => 'error',
+			LOG_ERROR => 'error'
+		);
+
+		if (is_int($type) && isset($levels[$type])) {
+			$type = $levels[$type];
+		}
+
+		if ($type == 'error' || $type == 'warning') {
+			$filename = LOGS . 'error.log';
+		} elseif (in_array($type, $levels)) {
+			$filename = LOGS . 'debug.log';
+		} else {
+			$filename = LOGS . $type . '.log';
+		}
+		$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $msg . "\n";
+		$log = new File($filename, true);
+		if ($log->writable()) {
+			return $log->append($output);
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/object.php
===================================================================
--- cake/libs/object.php	(revision 0)
+++ cake/libs/object.php	(revision 0)
@@ -0,0 +1,291 @@
+<?php
+/* SVN FILE: $Id: object.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Object class, allowing __construct and __destruct in PHP4.
+ *
+ * Also includes methods for logging and the special method RequestAction,
+ * to call other Controllers' Actions from anywhere.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Object class, allowing __construct and __destruct in PHP4.
+ *
+ * Also includes methods for logging and the special method RequestAction,
+ * to call other Controllers' Actions from anywhere.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Object {
+/**
+ * Log object
+ *
+ * @var object
+ * @access protected
+ */
+	var $_log = null;
+/**
+ * A hack to support __construct() on PHP 4
+ * Hint: descendant classes have no PHP4 class_name() constructors,
+ * so this constructor gets called first and calls the top-layer __construct()
+ * which (if present) should call parent::__construct()
+ *
+ * @return Object
+ */
+	function Object() {
+		$args = func_get_args();
+		if (method_exists($this, '__destruct')) {
+			register_shutdown_function (array(&$this, '__destruct'));
+		}
+		call_user_func_array(array(&$this, '__construct'), $args);
+	}
+/**
+ * Class constructor, overridden in descendant classes.
+ */
+	function __construct() {
+	}
+
+/**
+ * Object-to-string conversion.
+ * Each class can override this method as necessary.
+ *
+ * @return string The name of this class
+ * @access public
+ */
+	function toString() {
+		$class = get_class($this);
+		return $class;
+	}
+/**
+ * Calls a controller's method from any location.
+ *
+ * @param string $url URL in the form of Cake URL ("/controller/method/parameter")
+ * @param array $extra if array includes the key "return" it sets the AutoRender to true.
+ * @return mixed Success (true/false) or contents if 'return' is set in $extra
+ * @access public
+ */
+	function requestAction($url, $extra = array()) {
+		if (empty($url)) {
+			return false;
+		}
+		if (!class_exists('dispatcher')) {
+			require CAKE . 'dispatcher.php';
+		}
+		if (in_array('return', $extra, true)) {
+			$extra = array_merge($extra, array('return' => 0, 'autoRender' => 1));
+		}
+		if (is_array($url) && !isset($extra['url'])) {
+			$extra['url'] = array();
+		}
+		$params = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra);
+		$dispatcher = new Dispatcher;
+		return $dispatcher->dispatch($url, $params);
+	}
+/**
+ * Calls a method on this object with the given parameters. Provides an OO wrapper
+ * for call_user_func_array, and improves performance by using straight method calls
+ * in most cases.
+ *
+ * @param string $method  Name of the method to call
+ * @param array $params  Parameter list to use when calling $method
+ * @return mixed  Returns the result of the method call
+ * @access public
+ */
+	function dispatchMethod($method, $params = array()) {
+		switch (count($params)) {
+			case 0:
+				return $this->{$method}();
+			case 1:
+				return $this->{$method}($params[0]);
+			case 2:
+				return $this->{$method}($params[0], $params[1]);
+			case 3:
+				return $this->{$method}($params[0], $params[1], $params[2]);
+			case 4:
+				return $this->{$method}($params[0], $params[1], $params[2], $params[3]);
+			case 5:
+				return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]);
+			default:
+				return call_user_func_array(array(&$this, $method), $params);
+			break;
+		}
+	}
+/**
+ * Stop execution of the current script
+ *
+ * @param $status see http://php.net/exit for values
+ * @return void
+ * @access public
+ */
+	function _stop($status = 0) {
+		exit($status);
+	}
+/**
+ * API for logging events.
+ *
+ * @param string $msg Log message
+ * @param integer $type Error type constant. Defined in app/config/core.php.
+ * @return boolean Success of log write
+ * @access public
+ */
+	function log($msg, $type = LOG_ERROR) {
+		if (!class_exists('CakeLog')) {
+			uses('cake_log');
+		}
+		if (is_null($this->_log)) {
+			$this->_log = new CakeLog();
+		}
+		if (!is_string($msg)) {
+			$msg = print_r($msg, true);
+		}
+		return $this->_log->write($type, $msg);
+	}
+/**
+ * Allows setting of multiple properties of the object in a single line of code.
+ *
+ * @param array $properties An associative array containing properties and corresponding values.
+ * @return void
+ * @access protected
+ */
+	function _set($properties = array()) {
+		if (is_array($properties) && !empty($properties)) {
+			$vars = get_object_vars($this);
+			foreach ($properties as $key => $val) {
+				if (array_key_exists($key, $vars)) {
+					$this->{$key} = $val;
+				}
+			}
+		}
+	}
+/**
+ * Used to report user friendly errors.
+ * If there is a file app/error.php or app/app_error.php this file will be loaded
+ * error.php is the AppError class it should extend ErrorHandler class.
+ *
+ * @param string $method Method to be called in the error class (AppError or ErrorHandler classes)
+ * @param array $messages Message that is to be displayed by the error class
+ * @return error message
+ * @access public
+ */
+	function cakeError($method, $messages = array()) {
+		if (!class_exists('ErrorHandler')) {
+			App::import('Core', 'Error');
+
+			if (file_exists(APP . 'error.php')) {
+				include_once (APP . 'error.php');
+			} elseif (file_exists(APP . 'app_error.php')) {
+				include_once (APP . 'app_error.php');
+			}
+		}
+
+		if (class_exists('AppError')) {
+			$error = new AppError($method, $messages);
+		} else {
+			$error = new ErrorHandler($method, $messages);
+		}
+		return $error;
+	}
+/**
+ * Checks for a persistent class file, if found file is opened and true returned
+ * If file is not found a file is created and false returned
+ * If used in other locations of the model you should choose a unique name for the persistent file
+ * There are many uses for this method, see manual for examples
+ *
+ * @param string $name name of the class to persist
+ * @param string $object the object to persist
+ * @return boolean Success
+ * @access protected
+ * @todo add examples to manual
+ */
+	function _persist($name, $return = null, &$object, $type = null) {
+		$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
+		if ($return === null) {
+			if (!file_exists($file)) {
+				return false;
+			} else {
+				return true;
+			}
+		}
+
+		if (!file_exists($file)) {
+			$this->_savePersistent($name, $object);
+			return false;
+		} else {
+			$this->__openPersistent($name, $type);
+			return true;
+		}
+	}
+/**
+ * You should choose a unique name for the persistent file
+ *
+ * There are many uses for this method, see manual for examples
+ *
+ * @param string $name name used for object to cache
+ * @param object $object the object to persist
+ * @return boolean true on save, throws error if file can not be created
+ * @access protected
+ */
+	function _savePersistent($name, &$object) {
+		$file = 'persistent' . DS . strtolower($name) . '.php';
+		$objectArray = array(&$object);
+		$data = str_replace('\\', '\\\\', serialize($objectArray));
+		$data = '<?php $' . $name . ' = \'' . str_replace('\'', '\\\'', $data) . '\' ?>';
+		cache($file, $data, '+1 day');
+	}
+/**
+ * Open the persistent class file for reading
+ * Used by Object::_persist()
+ *
+ * @param string $name Name of persisted class
+ * @param string $type Type of persistance (e.g: registry)
+ * @return void
+ * @access private
+ */
+	function __openPersistent($name, $type = null) {
+		$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
+		include($file);
+
+		switch ($type) {
+			case 'registry':
+				$vars = unserialize(${$name});
+				foreach ($vars['0'] as $key => $value) {
+					App::import('Model', Inflector::classify($key));
+				}
+				unset($vars);
+				$vars = unserialize(${$name});
+				foreach ($vars['0'] as $key => $value) {
+					foreach ($vars['0'][$key]->Behaviors->_attached as $behavior) {
+						App::import('Behavior', $behavior);
+					}
+					ClassRegistry::addObject($key, $value);
+					unset ($value);
+				}
+				unset($vars);
+			break;
+			default:
+				$vars = unserialize(${$name});
+				$this->{$name} = $vars['0'];
+				unset($vars);
+			break;
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/string.php
===================================================================
--- cake/libs/string.php	(revision 0)
+++ cake/libs/string.php	(revision 0)
@@ -0,0 +1,325 @@
+<?php
+/* SVN FILE: $Id: string.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * String handling methods.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.5551
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * String handling methods.
+ *
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class String extends Object {
+/**
+ * Gets a reference to the String object instance
+ *
+ * @return object String instance
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new String();
+		}
+		return $instance[0];
+	}
+/**
+ * Generate a random UUID
+ *
+ * @see http://www.ietf.org/rfc/rfc4122.txt
+ * @return RFC 4122 UUID
+ * @static
+ */
+	function uuid() {
+		$node = env('SERVER_ADDR');
+		$pid = null;
+
+		if (strpos($node, ':') !== false) {
+			if (substr_count($node, '::')) {
+				$node = str_replace('::', str_repeat(':0000', 8 - substr_count($node, ':')) . ':', $node);
+			}
+			$node = explode(':', $node) ;
+			$ipv6 = '' ;
+
+			foreach ($node as $id) {
+				$ipv6 .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT);
+			}
+			$node =  base_convert($ipv6, 2, 10);
+
+			if (strlen($node) < 38) {
+				$node = null;
+			} else {
+				$node = crc32($node);
+			}
+		} elseif (empty($node)) {
+			$host = env('HOSTNAME');
+
+			if (empty($host)) {
+				$host = env('HOST');
+			}
+
+			if (!empty($host)) {
+				$ip = gethostbyname($host);
+
+				if ($ip === $host) {
+					$node = crc32($host);
+				} else {
+					$node = ip2long($ip);
+				}
+			}
+		} elseif ($node !== '127.0.0.1') {
+			$node = ip2long($node);
+		} else {
+			$node = null;
+		}
+
+		if (empty($node)) {
+			$node = crc32(Configure::read('Security.salt'));
+		}
+
+		if (function_exists('zend_thread_id')) {
+			$pid = zend_thread_id();
+		} else {
+			$pid = getmypid();
+		}
+
+		if (!$pid || $pid > 65535) {
+			$pid = mt_rand(0, 0xfff) | 0x4000;
+		}
+
+		list($timeMid, $timeLow) = explode(' ', microtime());
+		$uuid = sprintf("%08x-%04x-%04x-%02x%02x-%04x%08x", (int)$timeLow, (int)substr($timeMid, 2) & 0xffff,
+					mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node);
+
+		return $uuid;
+	}
+/**
+ * Tokenizes a string using $separator, ignoring any instance of $separator that appears between $leftBound
+ * and $rightBound
+ *
+ * @param string $data The data to tokenize
+ * @param string $separator The token to split the data on
+ * @return array
+ * @access public
+ * @static
+ */
+	function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') {
+		if (empty($data) || is_array($data)) {
+			return $data;
+		}
+
+		$depth = 0;
+		$offset = 0;
+		$buffer = '';
+		$results = array();
+		$length = strlen($data);
+		$open = false;
+
+		while ($offset <= $length) {
+			$tmpOffset = -1;
+			$offsets = array(strpos($data, $separator, $offset), strpos($data, $leftBound, $offset), strpos($data, $rightBound, $offset));
+			for ($i = 0; $i < 3; $i++) {
+				if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
+					$tmpOffset = $offsets[$i];
+				}
+			}
+			if ($tmpOffset !== -1) {
+				$buffer .= substr($data, $offset, ($tmpOffset - $offset));
+				if ($data{$tmpOffset} == $separator && $depth == 0) {
+					$results[] = $buffer;
+					$buffer = '';
+				} else {
+					$buffer .= $data{$tmpOffset};
+				}
+				if ($leftBound != $rightBound) {
+					if ($data{$tmpOffset} == $leftBound) {
+						$depth++;
+					}
+					if ($data{$tmpOffset} == $rightBound) {
+						$depth--;
+					}
+				} else {
+					if ($data{$tmpOffset} == $leftBound) {
+						if (!$open) {
+							$depth++;
+							$open = true;
+						} else {
+							$depth--;
+							$open = false;
+						}
+					}
+				}
+				$offset = ++$tmpOffset;
+			} else {
+				$results[] = $buffer . substr($data, $offset);
+				$offset = $length + 1;
+			}
+		}
+		if (empty($results) && !empty($buffer)) {
+			$results[] = $buffer;
+		}
+
+		if (!empty($results)) {
+			$data = array_map('trim', $results);
+		} else {
+			$data = array();
+		}
+		return $data;
+	}
+/**
+ * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array corresponds to a variable
+ * placeholder name in $str. Example:
+ *
+ * Sample: String::insert('My name is :name and I am :age years old.', array('name' => 'Bob', '65'));
+ * Returns: My name is Bob and I am 65 years old.
+ *
+ * Available $options are:
+ * 	before: The character or string in front of the name of the variable placeholder (Defaults to ':')
+ * 	after: The character or string after the name of the variable placeholder (Defaults to null)
+ * 	escape: The character or string used to escape the before character / string (Defaults to '\')
+ * 	format: A regex to use for matching variable placeholders. Default is: '/(?<!\\)\:%s/' (Overwrites before, after, breaks escape / clean)
+ * 	clean: A boolean or array with instructions for String::cleanInsert
+ *
+ * @param string $str A string containing variable placeholders
+ * @param string $data A key => val array where each key stands for a placeholder variable name to be replaced with val
+ * @param string $options An array of options, see description above
+ * @return string
+ * @access public
+ * @static
+ */
+	function insert($str, $data, $options = array()) {
+		$options = array_merge(
+			array('before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false),
+			$options
+		);
+		$format = $options['format'];
+
+		if (!isset($format)) {
+			$format = sprintf(
+				'/(?<!%s)%s%%s%s/',
+				preg_quote($options['escape'], '/'),
+				str_replace('%', '%%', preg_quote($options['before'], '/')),
+				str_replace('%', '%%', preg_quote($options['after'], '/'))
+			);
+		}
+		if (!is_array($data)) {
+			$data = array($data);
+		}
+
+		if (array_keys($data) === array_keys(array_values($data))) {
+			$offset = 0;
+			while ($pos = strpos($str, '?', $offset)) {
+				$val = array_shift($data);
+				$offset = $pos + strlen($val);
+				$str = substr_replace($str, $val, $pos, 1);
+			}
+		} else {
+			$hashKeys = array_map('md5', array_keys($data));
+			$tempData = array_combine(array_keys($data), array_values($hashKeys));
+			foreach ($tempData as $key => $hashVal) {
+				$key = sprintf($format, preg_quote($key, '/'));
+				$str = preg_replace($key, $hashVal, $str);
+			}
+			$dataReplacements = array_combine($hashKeys, array_values($data));
+			foreach ($dataReplacements as $tmpHash => $data) {
+				$str = str_replace($tmpHash, $data, $str);
+			}
+		}
+
+		if (!isset($options['format']) && isset($options['before'])) {
+			$str = str_replace($options['escape'].$options['before'], $options['before'], $str);
+		}
+		if (!$options['clean']) {
+			return $str;
+		}
+		return String::cleanInsert($str, $options);
+	}
+/**
+ * Cleans up a Set::insert formated string with given $options depending on the 'clean' key in $options. The default method used is
+ * text but html is also available. The goal of this function is to replace all whitespace and uneeded markup around placeholders
+ * that did not get replaced by Set::insert.
+ *
+ * @param string $str
+ * @param string $options
+ * @return string
+ * @access public
+ * @static
+ */
+	function cleanInsert($str, $options) {
+		$clean = $options['clean'];
+		if (!$clean) {
+			return $str;
+		}
+		if ($clean === true) {
+			$clean = array('method' => 'text');
+		}
+		if (!is_array($clean)) {
+			$clean = array('method' => $options['clean']);
+		}
+		switch ($clean['method']) {
+			case 'html':
+				$clean = array_merge(array(
+					'word' => '[\w,.]+',
+					'andText' => true,
+					'replacement' => '',
+				), $clean);
+				$kleenex = sprintf(
+					'/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
+					preg_quote($options['before'], '/'),
+					$clean['word'],
+					preg_quote($options['after'], '/')
+				);
+				$str = preg_replace($kleenex, $clean['replacement'], $str);
+				if ($clean['andText']) {
+					$options['clean'] = array('method' => 'text');
+					$str = String::cleanInsert($str, $options);
+				}
+				break;
+			case 'text':
+				$clean = array_merge(array(
+					'word' => '[\w,.]+',
+					'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
+					'replacement' => '',
+				), $clean);
+
+				$kleenex = sprintf(
+					'/(%s%s%s%s|%s%s%s%s)/',
+					preg_quote($options['before'], '/'),
+					$clean['word'],
+					preg_quote($options['after'], '/'),
+					$clean['gap'],
+					$clean['gap'],
+					preg_quote($options['before'], '/'),
+					$clean['word'],
+					preg_quote($options['after'], '/')
+				);
+				$str = preg_replace($kleenex, $clean['replacement'], $str);
+				break;
+		}
+		return $str;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/security.php
===================================================================
--- cake/libs/security.php	(revision 0)
+++ cake/libs/security.php	(revision 0)
@@ -0,0 +1,196 @@
+<?php
+/* SVN FILE: $Id: security.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v .0.10.0.1233
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Security extends Object {
+/**
+ * Default hash method
+ *
+ * @var string
+ * @access public
+ */
+	var $hashType = null;
+/**
+  * Singleton implementation to get object instance.
+  *
+  * @return object
+  * @access public
+  * @static
+  */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new Security;
+		}
+		return $instance[0];
+	}
+/**
+  * Get allowed minutes of inactivity based on security level.
+  *
+  * @return integer Allowed inactivity in minutes
+  * @access public
+  * @static
+  */
+	function inactiveMins() {
+		$_this =& Security::getInstance();
+		switch (Configure::read('Security.level')) {
+			case 'high':
+				return 10;
+			break;
+			case 'medium':
+				return 100;
+			break;
+			case 'low':
+			default:
+				return 300;
+				break;
+		}
+	}
+/**
+  * Generate authorization hash.
+  *
+  * @return string Hash
+  * @access public
+  * @static
+  */
+	function generateAuthKey() {
+		if (!class_exists('String')) {
+			App::import('Core', 'String');
+		}
+		return Security::hash(String::uuid());
+	}
+/**
+ * Validate authorization hash.
+ *
+ * @param string $authKey Authorization hash
+ * @return boolean Success
+ * @access public
+ * @static
+ * @todo Complete implementation
+ */
+	function validateAuthKey($authKey) {
+		return true;
+	}
+/**
+ * Create a hash from string using given method.
+ * Fallback on next available method.
+ *
+ * @param string $string String to hash
+ * @param string $type Method to use (sha1/sha256/md5)
+ * @param boolean $salt If true, automatically appends the application's salt
+ * 				  value to $string (Security.salt)
+ * @return string Hash
+ * @access public
+ * @static
+ */
+	function hash($string, $type = null, $salt = false) {
+		$_this =& Security::getInstance();
+
+		if ($salt) {
+			if (is_string($salt)) {
+				$string = $salt . $string;
+			} else {
+				$string = Configure::read('Security.salt') . $string;
+			}
+		}
+
+		if (empty($type)) {
+			$type = $_this->hashType;
+		}
+		$type = strtolower($type);
+
+		if ($type == 'sha1' || $type == null) {
+			if (function_exists('sha1')) {
+				$return = sha1($string);
+				return $return;
+			}
+			$type = 'sha256';
+		}
+
+		if ($type == 'sha256' && function_exists('mhash')) {
+			return bin2hex(mhash(MHASH_SHA256, $string));
+		}
+
+		if (function_exists('hash')) {
+			return hash($type, $string);
+		}
+		return md5($string);
+	}
+/**
+ * Sets the default hash method for the Security object.  This affects all objects using
+ * Security::hash().
+ *
+ * @param string $hash Method to use (sha1/sha256/md5)
+ * @access public
+ * @return void
+ * @static
+ * @see Security::hash()
+ */
+	function setHash($hash) {
+		$_this =& Security::getInstance();
+		$_this->hashType = $hash;
+	}
+/**
+ * Encrypts/Decrypts a text using the given key.
+ *
+ * @param string $text Encrypted string to decrypt, normal string to encrypt
+ * @param string $key Key to use
+ * @return string Encrypted/Decrypted string
+ * @access public
+ * @static
+ */
+	function cipher($text, $key) {
+		if (empty($key)) {
+			trigger_error(__('You cannot use an empty key for Security::cipher()', true), E_USER_WARNING);
+			return '';
+		}
+
+		$_this =& Security::getInstance();
+		if (!defined('CIPHER_SEED')) {
+			//This is temporary will change later
+			define('CIPHER_SEED', '76859309657453542496749683645');
+		}
+		srand(CIPHER_SEED);
+		$out = '';
+
+		for ($i = 0; $i < strlen($text); $i++) {
+			for ($j = 0; $j < ord(substr($key, $i % strlen($key), 1)); $j++) {
+				$toss = rand(0, 255);
+			}
+			$mask = rand(0, 255);
+			$out .= chr(ord(substr($text, $i, 1)) ^ $mask);
+		}
+		return $out;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/l10n.php
===================================================================
--- cake/libs/l10n.php	(revision 0)
+++ cake/libs/l10n.php	(revision 0)
@@ -0,0 +1,467 @@
+<?php
+/* SVN FILE: $Id: l10n.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4116
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class L10n extends Object {
+/**
+ * The language for current locale
+ *
+ * @var string
+ * @access public
+ */
+	var $language = 'English (United States)';
+/**
+ * Locale search paths
+ *
+ * @var array
+ * @access public
+ */
+	var $languagePath = array('eng');
+/**
+ * ISO 639-3 for current locale
+ *
+ * @var string
+ * @access public
+ */
+	var $lang = 'eng';
+/**
+ * Locale
+ *
+ * @var string
+ * @access public
+ */
+	var $locale = 'en_us';
+/**
+ * Default ISO 639-3 language.
+ *
+ * DEFAULT_LANGUAGE is defined in an application this will be set as a fall back
+ *
+ * @var string
+ * @access public
+ */
+	var $default = null;
+/**
+ * Encoding used for current locale
+ *
+ * @var string
+ * @access public
+ */
+	var $charset = 'utf-8';
+/**
+ * Set to true if a locale is found
+ *
+ * @var string
+ * @access public
+ */
+	var $found = false;
+/**
+ * Maps ISO 639-3 to I10n::__l10nCatalog
+ *
+ * @var array
+ * @access private
+ */
+	var $__l10nMap = array(/* Afrikaans */ 'afr' => 'af',
+								/* Albanian */ 'alb' => 'sq',
+								/* Arabic */ 'ara' => 'ar',
+								/* Armenian - Armenia */ 'hye' => 'hy',
+								/* Basque */ 'baq' => 'eu',
+								/* Bosnian */ 'bos' => 'bs',
+								/* Bulgarian */ 'bul' => 'bg',
+								/* Byelorussian */ 'bel' => 'be',
+								/* Catalan */ 'cat' => 'ca',
+								/* Chinese */ 'chi' => 'zh',
+								/* Chinese */ 'zho' => 'zh',
+								/* Croatian */ 'hrv' => 'hr',
+								/* Czech */ 'cze' => 'cs',
+								/* Czech */ 'ces' => 'cs',
+								/* Danish */ 'dan' => 'da',
+								/* Dutch (Standard) */ 'dut' => 'nl',
+								/* Dutch (Standard) */ 'nld' => 'nl',
+								/* English */ 'eng' => 'en',
+								/* Estonian */ 'est' => 'et',
+								/* Faeroese */ 'fao' => 'fo',
+								/* Farsi */ 'fas' => 'fa',
+								/* Farsi */ 'per' => 'fa',
+								/* Finnish */ 'fin' => 'fi',
+								/* French (Standard) */ 'fre' => 'fr',
+								/* French (Standard) */ 'fra' => 'fr',
+								/* Gaelic (Scots) */ 'gla' => 'gd',
+								/* Galician */ 'glg' => 'gl',
+								/* German (Standard) */ 'deu' => 'de',
+								/* German (Standard) */ 'ger' => 'de',
+								/* Greek */ 'gre' => 'el',
+								/* Greek */ 'ell' => 'el',
+								/* Hebrew */ 'heb' => 'he',
+								/* Hindi */ 'hin' => 'hi',
+								/* Hungarian */ 'hun' => 'hu',
+								/* Icelandic */ 'ice' => 'is',
+								/* Icelandic */ 'isl' => 'is',
+								/* Indonesian */ 'ind' => 'id',
+								/* Irish */ 'gle' => 'ga',
+								/* Italian */ 'ita' => 'it',
+								/* Japanese */ 'jpn' => 'ja',
+								/* Korean */ 'kor' => 'ko',
+								/* Latvian */ 'lav' => 'lv',
+								/* Lithuanian */ 'lit' => 'lt',
+								/* Macedonian */ 'mac' => 'mk',
+								/* Macedonian */ 'mkd' => 'mk',
+								/* Malaysian */ 'may' => 'ms',
+								/* Malaysian */ 'msa' => 'ms',
+								/* Maltese */ 'mlt' => 'mt',
+								/* Norwegian */ 'nor' => 'no',
+								/* Norwegian Bokmal */ 'nob' => 'nb',
+								/* Norwegian Nynorsk */ 'nno' => 'nn',
+								/* Polish */ 'pol' => 'pl',
+								/* Portuguese (Portugal) */ 'por' => 'pt',
+								/* Rhaeto-Romanic */ 'roh' => 'rm',
+								/* Romanian */ 'rum' => 'ro',
+								/* Romanian */ 'ron' => 'ro',
+								/* Russian */ 'rus' => 'ru',
+								/* Sami (Lappish) */ 'smi' => 'sz',
+								/* Serbian */ 'scc' => 'sr',
+								/* Serbian */ 'srp' => 'sr',
+								/* Slovack */ 'slo' => 'sk',
+								/* Slovack */ 'slk' => 'sk',
+								/* Slovenian */ 'slv' => 'sl',
+								/* Sorbian */ 'wen' => 'sb',
+								/* Spanish (Spain - Traditional) */ 'spa' => 'es',
+								/* Swedish */ 'swe' => 'sv',
+								/* Thai */ 'tha' => 'th',
+								/* Tsonga */ 'tso' => 'ts',
+								/* Tswana */ 'tsn' => 'tn',
+								/* Turkish */ 'tur' => 'tr',
+								/* Ukrainian */ 'ukr' => 'uk',
+								/* Urdu */ 'urd' => 'ur',
+								/* Venda */ 'ven' => 've',
+								/* Vietnamese */ 'vie' => 'vi',
+								/* Xhosa */ 'xho' => 'xh',
+								/* Yiddish */ 'yid' => 'yi',
+								/* Zulu */ 'zul' => 'zu');
+/**
+ * HTTP_ACCEPT_LANGUAGE catalog
+ *
+ * holds all information related to a language
+ *
+ * @var array
+ * @access private
+ */
+	var $__l10nCatalog = array('af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8'),
+										'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
+										'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8'),
+										'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8'),
+										'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8'),
+										'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8'),
+										'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8'),
+										'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8'),
+										'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
+										'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
+										'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
+										'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
+										'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
+										'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8'),
+										'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8'),
+										'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8'),
+										'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8'),
+										'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8'),
+										'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
+										'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8'),
+										'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8'),
+										'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8'),
+										'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8'),
+										'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8'),
+										'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8'),
+										'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8'),
+										'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8'),
+										'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8'),
+										'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'),
+										'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'),
+										'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8'),
+										'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8'),
+										'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8'),
+										'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8'),
+										'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr'),
+										'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr'),
+										'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr'),
+										'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r'),
+										'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8'),
+										'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8'),
+										'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'),
+										'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'),
+										'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8'),
+										'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8'),
+										'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
+										'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
+										'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
+										'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
+										'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
+										'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
+										'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'),
+										'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'),
+										'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8'),
+										'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8'),
+										'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8'),
+										'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8'),
+										'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8'),
+										'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8'),
+										'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8'),
+										'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8'),
+										'sk' => array('language' => 'Slovack', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8'),
+										'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8'),
+										'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8'),
+										'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8'),
+										'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8'),
+										'sv-fi' => array('language' => 'Swedish (Findland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8'),
+										'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8'),
+										'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8'),
+										'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8'),
+										'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8'),
+										'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8'),
+										'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8'),
+										'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8'),
+										'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8'),
+										've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8'),
+										'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8'),
+										'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8'),
+										'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8'),
+										'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
+										'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312'),
+										'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
+										'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
+										'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
+										'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8'));
+/**
+ * Class constructor
+ */
+	function __construct() {
+		if (defined('DEFAULT_LANGUAGE')) {
+			$this->default = DEFAULT_LANGUAGE;
+		}
+		parent::__construct();
+	}
+/**
+ * Gets the settings for $language.
+ * If $language is null it attempt to get settings from I10n::__autoLanguage(); if this fails
+ * the method will get the settings from I10n::__setLanguage();
+ *
+ * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
+ * @access public
+ */
+	function get($language = null) {
+		if ($language !== null) {
+			return $this->__setLanguage($language);
+		} elseif ($this->__autoLanguage() === false) {
+			return $this->__setLanguage();
+		}
+	}
+/**
+ * Sets the class vars to correct values for $language.
+ * If $language is null it will use the DEFAULT_LANGUAGE if defined
+ *
+ * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
+ * @access private
+ */
+	function __setLanguage($language = null) {
+		$langKey = null;
+		if ($language !== null && isset($this->__l10nMap[$language]) && isset($this->__l10nCatalog[$this->__l10nMap[$language]])) {
+			$langKey = $this->__l10nMap[$language];
+		} else if ($language !== null && isset($this->__l10nCatalog[$language])) {
+			$langKey = $language;
+		} else if (defined('DEFAULT_LANGUAGE')) {
+			$langKey = DEFAULT_LANGUAGE;
+		}
+
+		if ($langKey !== null && isset($this->__l10nCatalog[$langKey])) {
+			$this->language = $this->__l10nCatalog[$langKey]['language'];
+			$this->languagePath = array(
+				$this->__l10nCatalog[$langKey]['locale'],
+				$this->__l10nCatalog[$langKey]['localeFallback']
+			);
+			$this->lang = $language;
+			$this->locale = $this->__l10nCatalog[$langKey]['locale'];
+			$this->charset = $this->__l10nCatalog[$langKey]['charset'];
+		} else {
+			$this->lang = $language;
+			$this->languagePath = array($language);
+		}
+
+		if ($this->default) {
+			if (isset($this->__l10nMap[$this->default]) && isset($this->__l10nCatalog[$this->__l10nMap[$this->default]])) {
+				$this->languagePath[] = $this->__l10nCatalog[$this->__l10nMap[$this->default]]['localeFallback'];
+			} else if (isset($this->__l10nCatalog[$this->default])) {
+				$this->languagePath[] = $this->__l10nCatalog[$this->default]['localeFallback'];
+			}
+		}
+		$this->found = true;
+
+		if (Configure::read('Config.language') === null) {
+			Configure::write('Config.language', $this->lang);
+		}
+		Configure::write('charset', $this->charset);
+
+		if ($language) {
+			return $language;
+		}
+	}
+/**
+ * Attempts to find the locale settings based on the HTTP_ACCEPT_LANGUAGE variable
+ *
+ * @return boolean Success
+ * @access private
+ */
+	function __autoLanguage() {
+		$_detectableLanguages = split('[,;]', env('HTTP_ACCEPT_LANGUAGE'));
+		foreach ($_detectableLanguages as $key => $langKey) {
+			$langKey = strtolower($langKey);
+			if (strpos($langKey, '_') !== false) {
+				$langKey = str_replace('_', '-', $langKey);
+			}
+
+			if (isset($this->__l10nCatalog[$langKey])) {
+				$this->__setLanguage($langKey);
+				return true;
+			} else if (strpos($langKey, '-') !== false) {
+				$langKey = substr($langKey, 0, 2);
+				if (isset($this->__l10nCatalog[$langKey])) {
+					$this->__setLanguage($langKey);
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+/**
+ * Attempts to find locale for language, or language for locale
+ *
+ * @param mixed $mixed 2/3 char string (language/locale), array of those strings, or null
+ * @return mixed string language/locale, array of those values, whole map as an array, or false when language/locale doesn't exist
+ * @access public
+ */
+	function map($mixed = null) {
+		if (is_array($mixed)) {
+			$result = array();
+			foreach ($mixed as $_mixed) {
+				if ($_result = $this->map($_mixed)) {
+					$result[$_mixed] = $_result;
+				}
+			}
+			return $result;
+		} else if (is_string($mixed)) {
+			if (strlen($mixed) === 2 && in_array($mixed, $this->__l10nMap)) {
+				return array_search($mixed, $this->__l10nMap);
+			} else if (isset($this->__l10nMap[$mixed])) {
+				return $this->__l10nMap[$mixed];
+			}
+			return false;
+		}
+		return $this->__l10nMap;
+	}
+/**
+ * Attempts to find catalog record for requested language
+ *
+ * @param mixed $language string requested language, array of requested languages, or null for whole catalog
+ * @return mixed array catalog record for requested language, array of catalog records, whole catalog, or false when language doesn't exist
+ */
+	function catalog($language = null) {
+		if (is_array($language)) {
+			$result = array();
+			foreach ($language as $_language) {
+				if ($_result = $this->catalog($_language)) {
+					$result[$_language] = $_result;
+				}
+			}
+			return $result;
+		} else if (is_string($language)) {
+			if (isset($this->__l10nCatalog[$language])) {
+				return $this->__l10nCatalog[$language];
+			}
+			return false;
+		}
+		return $this->__l10nCatalog;
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/multibyte.php
===================================================================
--- cake/libs/multibyte.php	(revision 0)
+++ cake/libs/multibyte.php	(revision 0)
@@ -0,0 +1,1126 @@
+<?php
+/* SVN FILE: $Id: multibyte.php 8004 2009-01-16 20:15:21Z gwoo $ */
+/**
+ * Multibyte handling methods.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.6833
+ * @version       $Revision: 8004 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (function_exists('mb_internal_encoding')) {
+	$encoding = Configure::read('App.encoding');
+	if (!empty($encoding)) {
+		mb_internal_encoding($encoding);
+	}
+}
+/**
+ * Find position of first occurrence of a case-insensitive string.
+ *
+ * @param string $haystack The string from which to get the position of the first occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param integer $offset The position in $haystack to start searching.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string, or false if $needle is not found.
+ */
+if (!function_exists('mb_stripos')) {
+	function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) {
+		return Multibyte::stripos($haystack, $needle, $offset);
+	}
+}
+/**
+ * Finds first occurrence of a string within another, case insensitive.
+ *
+ * @param string $haystack The string from which to get the first occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
+ *                If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is false.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string|boolean The portion of $haystack, or false if $needle is not found.
+ */
+if (!function_exists('mb_stristr')) {
+	function mb_stristr($haystack, $needle, $part = false, $encoding = null) {
+		return Multibyte::stristr($haystack, $needle, $part);
+	}
+}
+/**
+ * Get string length.
+ *
+ * @param string $string The string being checked for length.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer The number of characters in string $string having character encoding encoding.
+ *                 A multi-byte character is counted as 1.
+ */
+if (!function_exists('mb_strlen')) {
+	function mb_strlen($string, $encoding = null) {
+		return Multibyte::strlen($string);
+	}
+}
+/**
+ * Find position of first occurrence of a string.
+ *
+ * @param string $haystack The string being checked.
+ * @param string $needle The position counted from the beginning of haystack.
+ * @param integer $offset The search offset. If it is not specified, 0 is used.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string.
+ *                         If $needle is not found, it returns false.
+ */
+if (!function_exists('mb_strpos')) {
+	function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) {
+		return Multibyte::strpos($haystack, $needle, $offset);
+	}
+}
+/**
+ * Finds the last occurrence of a character in a string within another.
+ *
+ * @param string $haystack The string from which to get the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string|boolean The portion of $haystack. or false if $needle is not found.
+ */
+if (!function_exists('mb_strrchr')) {
+	function mb_strrchr($haystack, $needle, $part = false, $encoding = null) {
+		return Multibyte::strrchr($haystack, $needle, $part);
+	}
+}
+/**
+ * Finds the last occurrence of a character in a string within another, case insensitive.
+ *
+ * @param string $haystack The string from which to get the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string|boolean The portion of $haystack. or false if $needle is not found.
+ */
+if (!function_exists('mb_strrichr')) {
+	function mb_strrichr($haystack, $needle, $part = false, $encoding = null) {
+		return Multibyte::strrichr($haystack, $needle, $part);
+	}
+}
+/**
+ * Finds position of last occurrence of a string within another, case insensitive
+ *
+ * @param string $haystack The string from which to get the position of the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param integer $offset The position in $haystack to start searching.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string, or false if $needle is not found.
+ */
+if (!function_exists('mb_strripos')) {
+	function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) {
+		return Multibyte::strripos($haystack, $needle, $offset);
+	}
+}
+/**
+ * Find position of last occurrence of a string in a string.
+ *
+ * @param string $haystack The string being checked, for the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string.
+ *                        Negative values will stop searching at an arbitrary point prior to the end of the string.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string. If $needle is not found, it returns false.
+ */
+if (!function_exists('mb_strrpos')) {
+	function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) {
+		return Multibyte::strrpos($haystack, $needle, $offset);
+	}
+}
+/**
+ * Finds first occurrence of a string within another
+ *
+ * @param string $haystack The string from which to get the first occurrence of $needle.
+ * @param string $needle The string to find in $haystack
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is FALSE.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string|boolean The portion of $haystack, or true if $needle is not found.
+ */
+if (!function_exists('mb_strstr')) {
+	function mb_strstr($haystack, $needle, $part = false, $encoding = null) {
+		return Multibyte::strstr($haystack, $needle, $part);
+	}
+}
+/**
+ * Make a string lowercase
+ *
+ * @param string $string The string being lowercased.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string with all alphabetic characters converted to lowercase.
+ */
+if (!function_exists('mb_strtolower')) {
+	function mb_strtolower($string, $encoding = null) {
+		return Multibyte::strtolower($string);
+	}
+}
+/**
+ * Make a string uppercase
+ *
+ * @param string $string The string being uppercased.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string with all alphabetic characters converted to uppercase.
+ */
+if (!function_exists('mb_strtoupper')) {
+	function mb_strtoupper($string, $encoding = null) {
+		return Multibyte::strtoupper($string);
+	}
+}
+/**
+ * Count the number of substring occurrences
+ *
+ * @param string $haystack The string being checked.
+ * @param string $needle The string being found.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return integer The number of times the $needle substring occurs in the $haystack string.
+ */
+if (!function_exists('mb_substr_count')) {
+	function mb_substr_count($haystack, $needle, $encoding = null) {
+		return Multibyte::substrCount($haystack, $needle);
+	}
+}
+/**
+ * Get part of string
+ *
+ * @param string $string The string being checked.
+ * @param integer $start The first position used in $string.
+ * @param integer $length The maximum length of the returned string.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string The portion of $string specified by the $string and $length parameters.
+ */
+if (!function_exists('mb_substr')) {
+	function mb_substr($string, $start, $length = null, $encoding = null) {
+		return Multibyte::substr($string, $start, $length);
+	}
+}
+/**
+ * Encode string for MIME header
+ *
+ * @param string $str The string being encoded
+ * @param string $charset specifies the name of the character set in which str is represented in.
+ * 						The default value is determined by the current NLS setting (mbstring.language).
+ * @param string $transfer_encoding specifies the scheme of MIME encoding. It should be either "B" (Base64) or "Q" (Quoted-Printable).
+ * 						Falls back to "B" if not given.
+ * @param string $linefeed specifies the EOL (end-of-line) marker with which mb_encode_mimeheader() performs line-folding
+ * 						(a » RFC term, the act of breaking a line longer than a certain length into multiple lines.
+ * 						The length is currently hard-coded to 74 characters). Falls back to "\r\n" (CRLF) if not given.
+ * @param integer $indent [definition unknown and appears to have no affect]
+ * @return string A converted version of the string represented in ASCII.
+ */
+if (!function_exists('mb_encode_mimeheader')) {
+	function mb_encode_mimeheader($str, $charset = 'UTF-8', $transfer_encoding = 'B', $linefeed = "\r\n", $indent = 1) {
+		return Multibyte::mimeEncode($str, $charset, $linefeed);
+	}
+}
+/**
+ * Multibyte handling methods.
+ *
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Multibyte extends Object {
+/**
+ *  Holds the case folding values
+ *
+ * @var array
+ * @access private
+ */
+	var $__caseFold = array();
+/**
+ * Holds an array of Unicode code point ranges
+ *
+ * @var array
+ * @access private
+ */
+	var $__codeRange = array();
+/**
+ * Holds the current code point range
+ *
+ * @var string
+ * @access private
+ */
+	var $__table = null;
+/**
+ * Gets a reference to the Multibyte object instance
+ *
+ * @return object Multibyte instance
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Multibyte();
+		}
+		return $instance[0];
+	}
+/**
+ * Converts a multibyte character string
+ * to the decimal value of the character
+ *
+ * @param multibyte string $string
+ * @return array
+ * @access public
+ * @static
+ */
+	function utf8($string) {
+		$map = array();
+
+		$values = array();
+		$find = 1;
+		$length = strlen($string);
+
+		for ($i = 0; $i < $length; $i++) {
+			$value = ord($string[$i]);
+
+			if ($value < 128) {
+				$map[] = $value;
+			} else {
+				if (count($values) == 0) {
+					$find = ($value < 224) ? 2 : 3;
+				}
+				$values[] = $value;
+
+				if (count($values) === $find) {
+					if ($find == 3) {
+						$map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
+					} else {
+						$map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
+					}
+					$values = array();
+					$find = 1;
+				}
+			}
+		}
+		return $map;
+	}
+/**
+ * Converts the decimal value of a multibyte character string
+ * to a string
+ *
+ * @param array $array
+ * @return string
+ * @access public
+ * @static
+ */
+	function ascii($array) {
+		$ascii = '';
+
+		foreach ($array as $utf8) {
+			if ($utf8 < 128) {
+				$ascii .= chr($utf8);
+			} elseif ($utf8 < 2048) {
+				$ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64));
+				$ascii .= chr(128 + ($utf8 % 64));
+			} else {
+				$ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096));
+				$ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64));
+				$ascii .= chr(128 + ($utf8 % 64));
+			}
+		}
+		return $ascii;
+	}
+/**
+ * Find position of first occurrence of a case-insensitive string.
+ *
+ * @param multi-byte string $haystack The string from which to get the position of the first occurrence of $needle.
+ * @param multi-byte string $needle The string to find in $haystack.
+ * @param integer $offset The position in $haystack to start searching.
+ * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string, or false if $needle is not found.
+ * @access public
+ * @static
+ */
+	function stripos($haystack, $needle, $offset = 0) {
+		if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
+			$haystack = Multibyte::strtoupper($haystack);
+			$needle = Multibyte::strtoupper($needle);
+			return Multibyte::strpos($haystack, $needle, $offset);
+		}
+		return stripos($haystack, $needle, $offset);
+	}
+/**
+ * Finds first occurrence of a string within another, case insensitive.
+ *
+ * @param string $haystack The string from which to get the first occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
+ *                If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is false.
+ * @return int|boolean The portion of $haystack, or false if $needle is not found.
+ * @access public
+ * @static
+ */
+	function stristr($haystack, $needle, $part = false) {
+		$php = (PHP_VERSION < 5.3);
+
+		if (($php && $part) || Multibyte::checkMultibyte($haystack)) {
+			$check = Multibyte::strtoupper($haystack);
+			$check = Multibyte::utf8($check);
+			$found = false;
+
+			$haystack = Multibyte::utf8($haystack);
+			$haystackCount = count($haystack);
+
+			$needle = Multibyte::strtoupper($needle);
+			$needle = Multibyte::utf8($needle);
+			$needleCount = count($needle);
+
+			$parts = array();
+			$position = 0;
+
+			while (($found === false) && ($position < $haystackCount)) {
+				if (isset($needle[0]) && $needle[0] === $check[$position]) {
+					for ($i = 1; $i < $needleCount; $i++) {
+						if ($needle[$i] !== $check[$position + $i]) {
+							break;
+						}
+					}
+					if ($i === $needleCount) {
+						$found = true;
+					}
+				}
+				if (!$found) {
+					$parts[] = $haystack[$position];
+					unset($haystack[$position]);
+				}
+				$position++;
+			}
+
+			if ($found && $part && !empty($parts)) {
+				return Multibyte::ascii($parts);
+			} elseif ($found && !empty($haystack)) {
+				return Multibyte::ascii($haystack);
+			}
+			return false;
+		}
+
+		if (!$php) {
+			return stristr($haystack, $needle, $part);
+		}
+		return stristr($haystack, $needle);
+	}
+/**
+ * Get string length.
+ *
+ * @param string $string The string being checked for length.
+ * @return integer The number of characters in string $string
+ * @access public
+ * @static
+ */
+	function strlen($string) {
+		if (Multibyte::checkMultibyte($string)) {
+			$string = Multibyte::utf8($string);
+			return count($string);
+		}
+		return strlen($string);
+	}
+/**
+ * Find position of first occurrence of a string.
+ *
+ * @param string $haystack The string being checked.
+ * @param string $needle The position counted from the beginning of haystack.
+ * @param integer $offset The search offset. If it is not specified, 0 is used.
+ * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string.
+ *                         If $needle is not found, it returns false.
+ * @access public
+ * @static
+ */
+	function strpos($haystack, $needle, $offset = 0) {
+		if (Multibyte::checkMultibyte($haystack)) {
+			$found = false;
+
+			$haystack = Multibyte::utf8($haystack);
+			$haystackCount = count($haystack);
+
+			$needle = Multibyte::utf8($needle);
+			$needleCount = count($needle);
+
+			$position = $offset;
+
+			while (($found === false) && ($position < $haystackCount)) {
+				if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
+					for ($i = 1; $i < $needleCount; $i++) {
+						if ($needle[$i] !== $haystack[$position + $i]) {
+							break;
+						}
+					}
+					if ($i === $needleCount) {
+						$found = true;
+						$position--;
+					}
+				}
+				$position++;
+			}
+			if ($found) {
+				return $position;
+			}
+			return false;
+		}
+		return strpos($haystack, $needle, $offset);
+	}
+/**
+ * Finds the last occurrence of a character in a string within another.
+ *
+ * @param string $haystack The string from which to get the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false.
+ * @return string|boolean The portion of $haystack. or false if $needle is not found.
+ * @access public
+ * @static
+ */
+	function strrchr($haystack, $needle, $part = false) {
+		$check = Multibyte::utf8($haystack);
+		$found = false;
+
+		$haystack = Multibyte::utf8($haystack);
+		$haystackCount = count($haystack);
+
+		$matches = array_count_values($check);
+
+		$needle = Multibyte::utf8($needle);
+		$needleCount = count($needle);
+
+		$parts = array();
+		$position = 0;
+
+		while (($found === false) && ($position < $haystackCount)) {
+			if (isset($needle[0]) && $needle[0] === $check[$position]) {
+				for ($i = 1; $i < $needleCount; $i++) {
+					if ($needle[$i] !== $check[$position + $i]) {
+						if ($needle[$i] === $check[($position + $i) -1]) {
+							$found = true;
+						}
+						unset($parts[$position - 1]);
+						$haystack = array_merge(array($haystack[$position]), $haystack);
+						break;
+					}
+				}
+				if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
+					$matches[$needle[0]] = $matches[$needle[0]] - 1;
+				} elseif ($i === $needleCount) {
+					$found = true;
+				}
+			}
+
+			if (!$found && isset($haystack[$position])) {
+				$parts[] = $haystack[$position];
+				unset($haystack[$position]);
+			}
+			$position++;
+		}
+
+		if ($found && $part && !empty($parts)) {
+			return Multibyte::ascii($parts);
+		} elseif ($found && !empty($haystack)) {
+			return Multibyte::ascii($haystack);
+		}
+		return false;
+	}
+/**
+ * Finds the last occurrence of a character in a string within another, case insensitive.
+ *
+ * @param string $haystack The string from which to get the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false.
+ * @return string|boolean The portion of $haystack. or false if $needle is not found.
+ * @access public
+ * @static
+ */
+	function strrichr($haystack, $needle, $part = false) {
+		$check = Multibyte::strtoupper($haystack);
+		$check = Multibyte::utf8($check);
+		$found = false;
+
+		$haystack = Multibyte::utf8($haystack);
+		$haystackCount = count($haystack);
+
+		$matches = array_count_values($check);
+
+		$needle = Multibyte::strtoupper($needle);
+		$needle = Multibyte::utf8($needle);
+		$needleCount = count($needle);
+
+		$parts = array();
+		$position = 0;
+
+		while (($found === false) && ($position < $haystackCount)) {
+			if (isset($needle[0]) && $needle[0] === $check[$position]) {
+				for ($i = 1; $i < $needleCount; $i++) {
+					if ($needle[$i] !== $check[$position + $i]) {
+						if ($needle[$i] === $check[($position + $i) -1]) {
+							$found = true;
+						}
+						unset($parts[$position - 1]);
+						$haystack = array_merge(array($haystack[$position]), $haystack);
+						break;
+					}
+				}
+				if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
+					$matches[$needle[0]] = $matches[$needle[0]] - 1;
+				} elseif ($i === $needleCount) {
+					$found = true;
+				}
+			}
+
+			if (!$found && isset($haystack[$position])) {
+				$parts[] = $haystack[$position];
+				unset($haystack[$position]);
+			}
+			$position++;
+		}
+
+		if ($found && $part && !empty($parts)) {
+			return Multibyte::ascii($parts);
+		} elseif ($found && !empty($haystack)) {
+			return Multibyte::ascii($haystack);
+		}
+		return false;
+	}
+/**
+ * Finds position of last occurrence of a string within another, case insensitive
+ *
+ * @param string $haystack The string from which to get the position of the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param integer $offset The position in $haystack to start searching.
+ * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string, or false if $needle is not found.
+ * @access public
+ * @static
+ */
+	function strripos($haystack, $needle, $offset = 0) {
+		if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
+			$found = false;
+			$haystack = Multibyte::strtoupper($haystack);
+			$haystack = Multibyte::utf8($haystack);
+			$haystackCount = count($haystack);
+
+			$matches = array_count_values($haystack);
+
+			$needle = Multibyte::strtoupper($needle);
+			$needle = Multibyte::utf8($needle);
+			$needleCount = count($needle);
+
+			$position = $offset;
+
+			while (($found === false) && ($position < $haystackCount)) {
+				if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
+					for ($i = 1; $i < $needleCount; $i++) {
+						if ($needle[$i] !== $haystack[$position + $i]) {
+							if ($needle[$i] === $haystack[($position + $i) -1]) {
+								$position--;
+								$found = true;
+								continue;
+							}
+						}
+					}
+
+					if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
+						$matches[$needle[0]] = $matches[$needle[0]] - 1;
+					} elseif ($i === $needleCount) {
+						$found = true;
+						$position--;
+					}
+				}
+				$position++;
+			}
+			return ($found) ? $position : false;
+		}
+		return strripos($haystack, $needle, $offset);
+	}
+
+/**
+ * Find position of last occurrence of a string in a string.
+ *
+ * @param string $haystack The string being checked, for the last occurrence of $needle.
+ * @param string $needle The string to find in $haystack.
+ * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string.
+ *                        Negative values will stop searching at an arbitrary point prior to the end of the string.
+ * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string. If $needle is not found, it returns false.
+ * @access public
+ * @static
+ */
+	function strrpos($haystack, $needle, $offset = 0) {
+		if (!PHP5 || Multibyte::checkMultibyte($haystack)) {
+			$found = false;
+
+			$haystack = Multibyte::utf8($haystack);
+			$haystackCount = count($haystack);
+
+			$matches = array_count_values($haystack);
+
+			$needle = Multibyte::utf8($needle);
+			$needleCount = count($needle);
+
+			$position = $offset;
+
+			while (($found === false) && ($position < $haystackCount)) {
+				if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
+					for ($i = 1; $i < $needleCount; $i++) {
+						if ($needle[$i] !== $haystack[$position + $i]) {
+							if ($needle[$i] === $haystack[($position + $i) -1]) {
+								$position--;
+								$found = true;
+								continue;
+							}
+						}
+					}
+
+					if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
+						$matches[$needle[0]] = $matches[$needle[0]] - 1;
+					} elseif ($i === $needleCount) {
+						$found = true;
+						$position--;
+					}
+				}
+				$position++;
+			}
+			return ($found) ? $position : false;
+		}
+		return strrpos($haystack, $needle, $offset);
+	}
+/**
+ * Finds first occurrence of a string within another
+ *
+ * @param string $haystack The string from which to get the first occurrence of $needle.
+ * @param string $needle The string to find in $haystack
+ * @param boolean $part Determines which portion of $haystack this function returns.
+ *                      If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
+ *                      If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is FALSE.
+ * @return string|boolean The portion of $haystack, or true if $needle is not found.
+ * @access public
+ * @static
+ */
+	function strstr($haystack, $needle, $part = false) {
+		$php = (PHP_VERSION < 5.3);
+
+		if (($php && $part) || Multibyte::checkMultibyte($haystack)) {
+			$check = Multibyte::utf8($haystack);
+			$found = false;
+
+			$haystack = Multibyte::utf8($haystack);
+			$haystackCount = count($haystack);
+
+			$needle = Multibyte::utf8($needle);
+			$needleCount = count($needle);
+
+			$parts = array();
+			$position = 0;
+
+			while (($found === false) && ($position < $haystackCount)) {
+				if (isset($needle[0]) && $needle[0] === $check[$position]) {
+					for ($i = 1; $i < $needleCount; $i++) {
+						if ($needle[$i] !== $check[$position + $i]) {
+							break;
+						}
+					}
+					if ($i === $needleCount) {
+						$found = true;
+					}
+				}
+				if (!$found) {
+					$parts[] = $haystack[$position];
+					unset($haystack[$position]);
+				}
+				$position++;
+			}
+
+			if ($found && $part && !empty($parts)) {
+				return Multibyte::ascii($parts);
+			} elseif ($found && !empty($haystack)) {
+				return Multibyte::ascii($haystack);
+			}
+			return false;
+		}
+
+		if (!$php) {
+			return strstr($haystack, $needle, $part);
+		}
+		return strstr($haystack, $needle);
+	}
+/**
+ * Make a string lowercase
+ *
+ * @param string $string The string being lowercased.
+ * @return string with all alphabetic characters converted to lowercase.
+ * @access public
+ * @static
+ */
+	function strtolower($string) {
+		$_this =& Multibyte::getInstance();
+		$utf8Map = Multibyte::utf8($string);
+
+		$length = count($utf8Map);
+		$lowerCase = array();
+		$matched = false;
+
+		for ($i = 0 ; $i < $length; $i++) {
+			$char = $utf8Map[$i];
+
+			if ($char < 128) {
+				$str = strtolower(chr($char));
+				$strlen = strlen($str);
+				for ($ii = 0 ; $ii < $strlen; $ii++) {
+					$lower = ord(substr($str, $ii, 1));
+				}
+				$lowerCase[] = $lower;
+				$matched = true;
+			} else {
+				$matched = false;
+				$keys = $_this->__find($char, 'upper');
+
+				if (!empty($keys)) {
+					foreach ($keys as $key => $value) {
+						if ($keys[$key]['upper'] == $char && count($keys[$key]['lower'][0]) === 1) {
+							$lowerCase[] = $keys[$key]['lower'][0];
+							$matched = true;
+							break 1;
+						}
+					}
+				}
+			}
+			if ($matched === false) {
+				$lowerCase[] = $char;
+			}
+		}
+		return Multibyte::ascii($lowerCase);
+	}
+/**
+ * Make a string uppercase
+ *
+ * @param string $string The string being uppercased.
+ * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used.
+ * @return string with all alphabetic characters converted to uppercase.
+ * @access public
+ * @static
+ */
+	function strtoupper($string) {
+		$_this =& Multibyte::getInstance();
+		$utf8Map = Multibyte::utf8($string);
+
+		$length = count($utf8Map);
+		$matched = false;
+		$replaced = array();
+		$upperCase = array();
+
+		for ($i = 0 ; $i < $length; $i++) {
+			$char = $utf8Map[$i];
+
+			if ($char < 128) {
+				$str = strtoupper(chr($char));
+				$strlen = strlen($str);
+				for ($ii = 0 ; $ii < $strlen; $ii++) {
+					$upper = ord(substr($str, $ii, 1));
+				}
+				$upperCase[] = $upper;
+				$matched = true;
+
+			} else {
+				$matched = false;
+				$keys = $_this->__find($char);
+				$keyCount = count($keys);
+
+				if (!empty($keys)) {
+					foreach ($keys as $key => $value) {
+						$matched = false;
+						$replace = 0;
+						if ($length > 1 && count($keys[$key]['lower']) > 1) {
+							$j = 0;
+
+							for ($ii = 0; $ii < count($keys[$key]['lower']); $ii++) {
+								$nextChar = $utf8Map[$i + $ii];
+
+								if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) {
+									$replace++;
+								}
+							}
+							if ($replace == count($keys[$key]['lower'])) {
+								$upperCase[] = $keys[$key]['upper'];
+								$replaced = array_merge($replaced, array_values($keys[$key]['lower']));
+								$matched = true;
+								break 1;
+							}
+						} elseif ($length > 1 && $keyCount > 1) {
+							$j = 0;
+							for ($ii = 1; $ii < $keyCount; $ii++) {
+								$nextChar = $utf8Map[$i + $ii - 1];
+
+								if (in_array($nextChar, $keys[$ii]['lower'])) {
+
+									for ($jj = 0; $jj < count($keys[$ii]['lower']); $jj++) {
+										$nextChar = $utf8Map[$i + $jj];
+
+										if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) {
+											$replace++;
+										}
+									}
+									if ($replace == count($keys[$ii]['lower'])) {
+										$upperCase[] = $keys[$ii]['upper'];
+										$replaced = array_merge($replaced, array_values($keys[$ii]['lower']));
+										$matched = true;
+										break 2;
+									}
+								}
+							}
+						}
+						if ($keys[$key]['lower'][0] == $char) {
+							$upperCase[] = $keys[$key]['upper'];
+							$matched = true;
+							break 1;
+						}
+					}
+				}
+			}
+			if ($matched === false && !in_array($char, $replaced, true)) {
+				$upperCase[] = $char;
+			}
+		}
+		return Multibyte::ascii($upperCase);
+	}
+/**
+ * Count the number of substring occurrences
+ *
+ * @param string $haystack The string being checked.
+ * @param string $needle The string being found.
+ * @return integer The number of times the $needle substring occurs in the $haystack string.
+ * @access public
+ * @static
+ */
+	function substrCount($haystack, $needle) {
+		$count = 0;
+		$haystack = Multibyte::utf8($haystack);
+		$haystackCount = count($haystack);
+		$matches = array_count_values($haystack);
+		$needle = Multibyte::utf8($needle);
+		$needleCount = count($needle);
+
+		if ($needleCount === 1 && isset($matches[$needle[0]])) {
+			return $matches[$needle[0]];
+		}
+
+		for ($i = 0; $i < $haystackCount; $i++) {
+			if (isset($needle[0]) && $needle[0] === $haystack[$i]) {
+				for ($ii = 1; $ii < $needleCount; $ii++) {
+					if ($needle[$ii] === $haystack[$i + 1]) {
+						if ((isset($needle[$ii + 1]) && $haystack[$i + 2]) && $needle[$ii + 1] !== $haystack[$i + 2]) {
+							$count--;
+						} else {
+							$count++;
+						}
+					}
+				}
+			}
+		}
+		return $count;
+	}
+/**
+ * Get part of string
+ *
+ * @param string $string The string being checked.
+ * @param integer $start The first position used in $string.
+ * @param integer $length The maximum length of the returned string.
+ * @return string The portion of $string specified by the $string and $length parameters.
+ * @access public
+ * @static
+ */
+	function substr($string, $start, $length = null) {
+		if ($start === 0 && $length === null) {
+			return $string;
+		}
+
+		$string = Multibyte::utf8($string);
+		$stringCount = count($string);
+
+		for ($i = 1; $i <= $start; $i++) {
+			unset($string[$i - 1]);
+		}
+
+		if ($length === null || count($string) < $length) {
+			return Multibyte::ascii($string);
+		}
+		$string = array_values($string);
+
+		$value = array();
+		for ($i = 0; $i < $length; $i++) {
+			$value[] = $string[$i];
+		}
+		return Multibyte::ascii($value);
+	}
+/**
+ * Prepare a string for mail transport, using the provided encoding
+ *
+ * @param string $string value to encode
+ * @param string $charset charset to use for encoding. defaults to UTF-8
+ * @param string $newline
+ * @return string
+ * @access public
+ * @static
+ * @TODO: add support for 'Q'('Quoted Printable') encoding
+ */
+	function mimeEncode($string, $charset = null, $newline = "\r\n") {
+		if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) {
+			return $string;
+		}
+
+		if (empty($charset)) {
+			$charset = Configure::read('App.encoding');
+		}
+		$charset = strtoupper($charset);
+
+		$start = '=?' . $charset . '?B?';
+		$end = '?=';
+		$spacer = $end . $newline . ' ' . $start;
+
+		$length = 75 - strlen($start) - strlen($end);
+		$length = $length - ($length % 4);
+		if ($charset == 'UTF-8') {
+			$parts = array();
+			$maxchars = floor(($length * 3) / 4);
+			while (strlen($string) > $maxchars) {
+				$i = $maxchars;
+				$test = ord($string[$i]);
+				while ($test >= 128 && $test <= 191) {
+					$i--;
+					$test = ord($string[$i]);
+				}
+				$parts[] = base64_encode(substr($string, 0, $i));
+				$string = substr($string, $i);
+			}
+			$parts[] = base64_encode($string);
+			$string = implode($spacer, $parts);
+		} else {
+			$string = chunk_split(base64_encode($string), $length, $spacer);
+			$string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string);
+		}
+		return $start . $string . $end;
+	}
+/**
+ * Return the Code points range for Unicode characters
+ *
+ * @param interger $decimal
+ * @return string
+ * @access private
+ */
+	function __codepoint ($decimal) {
+		if ($decimal > 128 && $decimal < 256)  {
+			$return = '0080_00ff'; // Latin-1 Supplement
+		} elseif ($decimal < 384) {
+			$return = '0100_017f'; // Latin Extended-A
+		} elseif ($decimal < 592) {
+			$return = '0180_024F'; // Latin Extended-B
+		} elseif ($decimal < 688) {
+			$return = '0250_02af'; // IPA Extensions
+		} elseif ($decimal >= 880 && $decimal < 1024) {
+			$return = '0370_03ff'; // Greek and Coptic
+		} elseif ($decimal < 1280) {
+			$return = '0400_04ff'; // Cyrillic
+		} elseif ($decimal < 1328) {
+			$return = '0500_052f'; // Cyrillic Supplement
+		} elseif ($decimal < 1424) {
+			$return = '0530_058f'; // Armenian
+		} elseif ($decimal >= 7680 && $decimal < 7936) {
+			$return = '1e00_1eff'; // Latin Extended Additional
+		} elseif ($decimal < 8192) {
+			$return = '1f00_1fff'; // Greek Extended
+		} elseif ($decimal >= 8448 && $decimal < 8528) {
+			$return = '2100_214f'; // Letterlike Symbols
+		} elseif ($decimal < 8592) {
+			$return = '2150_218f'; // Number Forms
+		} elseif ($decimal >= 9312 && $decimal < 9472) {
+			$return = '2460_24ff'; // Enclosed Alphanumerics
+		} elseif ($decimal >= 11264 && $decimal < 11360) {
+			$return = '2c00_2c5f'; // Glagolitic
+		} elseif ($decimal < 11392) {
+			$return = '2c60_2c7f'; // Latin Extended-C
+		} elseif ($decimal < 11520) {
+			$return = '2c80_2cff'; // Coptic
+		} elseif ($decimal >= 65280 && $decimal < 65520) {
+			$return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms
+		} else {
+			$return = false;
+		}
+		$this->__codeRange[$decimal] = $return;
+		return $return;
+	}
+/**
+ * Find the related code folding values for $char
+ *
+ * @param integer $char decimal value of character
+ * @param string $type
+ * @return array
+ * @access private
+ */
+	function __find($char, $type = 'lower') {
+		$value = false;
+		$found = array();
+		if (!isset($this->__codeRange[$char])) {
+			$range = $this->__codepoint($char);
+			if ($range === false) {
+				return null;
+			}
+			Configure::load('unicode' . DS . 'casefolding' . DS . $range);
+			$this->__caseFold[$range] = Configure::read($range);
+			Configure::delete($range);
+		}
+
+		if (!$this->__codeRange[$char]) {
+			return null;
+		}
+		$this->__table = $this->__codeRange[$char];
+		$count = count($this->__caseFold[$this->__table]);
+
+		for ($i = 0; $i < $count; $i++) {
+			if ($type === 'lower' && $this->__caseFold[$this->__table][$i][$type][0] === $char) {
+				$found[] = $this->__caseFold[$this->__table][$i];
+			} elseif ($type === 'upper' && $this->__caseFold[$this->__table][$i][$type] === $char) {
+				$found[] = $this->__caseFold[$this->__table][$i];
+			}
+		}
+		return $found;
+	}
+/**
+ * Check the $string for multibyte characters
+ * @param string $string value to test
+ * @return boolean
+ * @access public
+ * @static
+ */
+	function checkMultibyte($string) {
+		$length = strlen($string);
+
+		for ($i = 0; $i < $length; $i++ ) {
+			$value = ord(($string[$i]));
+			if ($value > 128) {
+				return true;
+			}
+		}
+		return false;
+	}
+}
+?>
Index: cake/libs/configure.php
===================================================================
--- cake/libs/configure.php	(revision 0)
+++ cake/libs/configure.php	(revision 0)
@@ -0,0 +1,1191 @@
+<?php
+/* SVN FILE: $Id: configure.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Short description for file.
+ *
+ * Long description for filec
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.0.0.2363
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Configuration class (singleton). Used for managing runtime configuration information.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @link          http://book.cakephp.org/view/42/The-Configuration-Class
+ */
+class Configure extends Object {
+/**
+ * List of additional path(s) where model files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $modelPaths = array();
+/**
+ * List of additional path(s) where behavior files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $behaviorPaths = array();
+/**
+ * List of additional path(s) where controller files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $controllerPaths = array();
+/**
+ * List of additional path(s) where component files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $componentPaths = array();
+/**
+ * List of additional path(s) where view files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $viewPaths = array();
+/**
+ * List of additional path(s) where helper files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $helperPaths = array();
+/**
+ * List of additional path(s) where plugins reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $pluginPaths = array();
+/**
+ * List of additional path(s) where vendor packages reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $vendorPaths = array();
+/**
+ * List of additional path(s) where locale files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $localePaths = array();
+/**
+ * List of additional path(s) where console shell files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $shellPaths = array();
+/**
+ * Current debug level.
+ *
+ * @link          http://book.cakephp.org/view/44/CakePHP-Core-Configuration-Variables
+ * @var integer
+ * @access public
+ */
+	var $debug = null;
+/**
+ * Determines if $__objects cache should be written.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__cache = false;
+/**
+ * Holds and key => value array of objects' types.
+ *
+ * @var array
+ * @access private
+ */
+	var $__objects = array();
+/**
+ * Returns a singleton instance of the Configure class.
+ *
+ * @return Configure instance
+ * @access public
+ */
+	function &getInstance($boot = true) {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new Configure();
+			$instance[0]->__loadBootstrap($boot);
+		}
+		return $instance[0];
+	}
+/**
+ * Returns an index of objects of the given type, with the physical path to each object.
+ *
+ * @param string	$type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin'
+ * @param mixed		$path Optional
+ * @return Configure instance
+ * @access public
+ */
+	function listObjects($type, $path = null, $cache = true) {
+		$objects = array();
+		$extension = false;
+		$name = $type;
+
+		if ($type === 'file' && !$path) {
+			return false;
+		} elseif ($type === 'file') {
+			$extension = true;
+			$name = $type . str_replace(DS, '', $path);
+		}
+		$_this =& Configure::getInstance();
+
+		if (empty($_this->__objects) && $cache === true) {
+			$_this->__objects = Cache::read('object_map', '_cake_core_');
+		}
+
+		if (empty($_this->__objects) || !isset($_this->__objects[$type]) || $cache !== true) {
+			$types = array(
+				'model' => array('suffix' => '.php', 'base' => 'AppModel', 'core' => false),
+				'behavior' => array('suffix' => '.php', 'base' => 'ModelBehavior'),
+				'controller' => array('suffix' => '_controller.php', 'base' => 'AppController'),
+				'component' => array('suffix' => '.php', 'base' => null),
+				'view' => array('suffix' => '.php', 'base' => null),
+				'helper' => array('suffix' => '.php', 'base' => 'AppHelper'),
+				'plugin' => array('suffix' => '', 'base' => null),
+				'vendor' => array('suffix' => '', 'base' => null),
+				'class' => array('suffix' => '.php', 'base' => null),
+				'file' => array('suffix' => '.php', 'base' => null)
+			);
+
+			if (!isset($types[$type])) {
+				return false;
+			}
+			$objects = array();
+
+			if (empty($path)) {
+				$path = $_this->{$type . 'Paths'};
+				if (isset($types[$type]['core']) && $types[$type]['core'] === false) {
+					array_pop($path);
+				}
+			}
+			$items = array();
+
+			foreach ((array)$path as $dir) {
+				if ($type === 'file' || $type === 'class' || strpos($dir, $type) !== false) {
+					$items = $_this->__list($dir, $types[$type]['suffix'], $extension);
+					$objects = array_merge($items, array_diff($objects, $items));
+				}
+			}
+
+			if ($type !== 'file') {
+				foreach ($objects as $key => $value) {
+					$objects[$key] = Inflector::camelize($value);
+				}
+			}
+			if ($cache === true && !empty($objects)) {
+				$_this->__objects[$name] = $objects;
+				$_this->__cache = true;
+			} else {
+				return $objects;
+			}
+		}
+		return $_this->__objects[$name];
+	}
+/**
+ * Returns an array of filenames of PHP files in the given directory.
+ *
+ * @param  string $path Path to scan for files
+ * @param  string $suffix if false, return only directories. if string, match and return files
+ * @return array  List of directories or files in directory
+ */
+	function __list($path, $suffix = false, $extension = false) {
+		if (!class_exists('Folder')) {
+			require LIBS . 'folder.php';
+		}
+		$items = array();
+		$Folder =& new Folder($path);
+		$contents = $Folder->read(false, true);
+
+		if (is_array($contents)) {
+			if (!$suffix) {
+				return $contents[0];
+			} else {
+				foreach ($contents[1] as $item) {
+					if (substr($item, - strlen($suffix)) === $suffix) {
+						if ($extension) {
+							$items[] = $item;
+						} else {
+							$items[] = substr($item, 0, strlen($item) - strlen($suffix));
+						}
+					}
+				}
+			}
+		}
+		return $items;
+	}
+/**
+ * Used to store a dynamic variable in the Configure instance.
+ *
+ * Usage
+ * Configure::write('One.key1', 'value of the Configure::One[key1]');
+ * Configure::write(array('One.key1' => 'value of the Configure::One[key1]'));
+ * Configure::write('One', array(
+ *     'key1' => 'value of the Configure::One[key1]',
+ *     'key2' => 'value of the Configure::One[key2]'
+ * );
+ * Configure::write(array(
+ *     'One.key1' => 'value of the Configure::One[key1]',
+ *     'One.key2' => 'value of the Configure::One[key2]'
+ * ));
+ *
+ * @link          http://book.cakephp.org/view/412/write
+ * @param array $config Name of var to write
+ * @param mixed $value Value to set for var
+ * @return void
+ * @access public
+ */
+	function write($config, $value = null) {
+		$_this =& Configure::getInstance();
+
+		if (!is_array($config)) {
+			$config = array($config => $value);
+		}
+
+		foreach ($config as $names => $value) {
+			$name = $_this->__configVarNames($names);
+
+			switch (count($name)) {
+				case 3:
+					$_this->{$name[0]}[$name[1]][$name[2]] = $value;
+				break;
+				case 2:
+					$_this->{$name[0]}[$name[1]] = $value;
+				break;
+				case 1:
+					$_this->{$name[0]} = $value;
+				break;
+			}
+		}
+
+		if (isset($config['debug'])) {
+			if ($_this->debug) {
+				error_reporting(E_ALL);
+
+				if (function_exists('ini_set')) {
+					ini_set('display_errors', 1);
+				}
+
+				if (!class_exists('Debugger')) {
+					require LIBS . 'debugger.php';
+				}
+				if (!class_exists('CakeLog')) {
+					require LIBS . 'cake_log.php';
+				}
+				Configure::write('log', LOG_NOTICE);
+			} else {
+				error_reporting(0);
+				Configure::write('log', LOG_NOTICE);
+			}
+		}
+	}
+/**
+ * Used to read information stored in the Configure instance.
+ *
+ * Usage
+ * Configure::read('Name'); will return all values for Name
+ * Configure::read('Name.key'); will return only the value of Configure::Name[key]
+ *
+ * @link          http://book.cakephp.org/view/413/read
+ * @param string $var Variable to obtain
+ * @return string value of Configure::$var
+ * @access public
+ */
+	function read($var = 'debug') {
+		$_this =& Configure::getInstance();
+
+		if ($var === 'debug') {
+			if (!isset($_this->debug)) {
+				if (defined('DEBUG')) {
+					$_this->debug = DEBUG;
+				} else {
+					$_this->debug = 0;
+				}
+			}
+			return $_this->debug;
+		}
+		$name = $_this->__configVarNames($var);
+
+		switch (count($name)) {
+			case 3:
+				if (isset($_this->{$name[0]}[$name[1]][$name[2]])) {
+					return $_this->{$name[0]}[$name[1]][$name[2]];
+				}
+			break;
+			case 2:
+				if (isset($_this->{$name[0]}[$name[1]])) {
+					return $_this->{$name[0]}[$name[1]];
+				}
+			break;
+			case 1:
+				if (isset($_this->{$name[0]})) {
+					return $_this->{$name[0]};
+				}
+			break;
+		}
+		return null;
+	}
+/**
+ * Used to delete a variable from the Configure instance.
+ *
+ * Usage:
+ * Configure::delete('Name'); will delete the entire Configure::Name
+ * Configure::delete('Name.key'); will delete only the Configure::Name[key]
+ *
+ * @link          http://book.cakephp.org/view/414/delete
+ * @param string $var the var to be deleted
+ * @return void
+ * @access public
+ */
+	function delete($var = null) {
+		$_this =& Configure::getInstance();
+		$name = $_this->__configVarNames($var);
+
+		if (count($name) > 1) {
+			unset($_this->{$name[0]}[$name[1]]);
+		} else {
+			unset($_this->{$name[0]});
+		}
+	}
+/**
+ * Loads a file from app/config/configure_file.php.
+ * Config file variables should be formated like:
+ *  $config['name'] = 'value';
+ * These will be used to create dynamic Configure vars.
+ *
+ * Usage Configure::load('configure_file');
+ *
+ * @link          http://book.cakephp.org/view/415/load
+ * @param string $fileName name of file to load, extension must be .php and only the name
+ *                         should be used, not the extenstion
+ * @return mixed false if file not found, void if load successful
+ * @access public
+ */
+	function load($fileName) {
+		$found = false;
+
+		if (file_exists(CONFIGS . $fileName . '.php')) {
+			include(CONFIGS . $fileName . '.php');
+			$found = true;
+		} elseif (file_exists(CACHE . 'persistent' . DS . $fileName . '.php')) {
+			include(CACHE . 'persistent' . DS . $fileName . '.php');
+			$found = true;
+		} else {
+			foreach (Configure::corePaths('cake') as $key => $path) {
+				if (file_exists($path . DS . 'config' . DS . $fileName . '.php')) {
+					include($path . DS . 'config' . DS . $fileName . '.php');
+					$found = true;
+					break;
+				}
+			}
+		}
+
+		if (!$found) {
+			return false;
+		}
+
+		if (!isset($config)) {
+			$error = __("Configure::load() - no variable \$config found in %s.php", true);
+			trigger_error(sprintf($error, $fileName), E_USER_WARNING);
+			return false;
+		}
+		return Configure::write($config);
+	}
+/**
+ * Used to determine the current version of CakePHP.
+ *
+ * Usage Configure::version();
+ *
+ * @link          http://book.cakephp.org/view/416/version
+ * @return string Current version of CakePHP
+ * @access public
+ */
+	function version() {
+		$_this =& Configure::getInstance();
+
+		if (!isset($_this->Cake['version'])) {
+			require(CORE_PATH . 'cake' . DS . 'config' . DS . 'config.php');
+			$_this->write($config);
+		}
+		return $_this->Cake['version'];
+	}
+/**
+ * Used to write a config file to disk.
+ *
+ * Configure::store('Model', 'class.paths', array('Users' => array(
+ *      'path' => 'users', 'plugin' => true
+ * )));
+ *
+ * @param string $type Type of config file to write, ex: Models, Controllers, Helpers, Components
+ * @param string $name file name.
+ * @param array $data array of values to store.
+ * @return void
+ * @access public
+ */
+	function store($type, $name, $data = array()) {
+		$write = true;
+		$content = '';
+
+		foreach ($data as $key => $value) {
+			$content .= "\$config['$type']['$key']";
+
+			if (is_array($value)) {
+				$content .= " = array(";
+
+				foreach ($value as $key1 => $value2) {
+					$value2 = addslashes($value2);
+					$content .= "'$key1' => '$value2', ";
+				}
+				$content .= ");\n";
+			} else {
+				$value = addslashes($value);
+				$content .= " = '$value';\n";
+			}
+		}
+		if (is_null($type)) {
+			$write = false;
+		}
+		Configure::__writeConfig($content, $name, $write);
+	}
+/**
+ * Returns a key/value list of all paths where core libs are found.
+ * Passing $type only returns the values for a given value of $key.
+ *
+ * @param string $type valid values are: 'model', 'behavior', 'controller', 'component',
+ *                      'view', 'helper', 'datasource', 'libs', and 'cake'
+ * @return array numeric keyed array of core lib paths
+ * @access public
+ */
+	function corePaths($type = null) {
+		$paths = Cache::read('core_paths', '_cake_core_');
+		if (!$paths) {
+			$paths = array();
+			$openBasedir = ini_get('open_basedir');
+			if ($openBasedir) {
+				$all = explode(PATH_SEPARATOR, $openBasedir);
+				$all = array_flip(array_flip((array_merge(array(CAKE_CORE_INCLUDE_PATH), $all))));
+			} else {
+				$all = explode(PATH_SEPARATOR, ini_get('include_path'));
+				$all = array_flip(array_flip((array_merge(array(CAKE_CORE_INCLUDE_PATH), $all))));
+			}
+			foreach ($all as $path) {
+				if ($path !== DS) {
+					$path = rtrim($path, DS);
+				}
+				if (empty($path) || $path === '.') {
+					continue;
+				}
+				$cake = $path .  DS . 'cake' . DS;
+				$libs = $cake . 'libs' . DS;
+				if (is_dir($libs)) {
+					$paths['libs'][] = $libs;
+					$paths['model'][] = $libs . 'model' . DS;
+					$paths['behavior'][] = $libs . 'model' . DS . 'behaviors' . DS;
+					$paths['controller'][] = $libs . 'controller' . DS;
+					$paths['component'][] = $libs . 'controller' . DS . 'components' . DS;
+					$paths['view'][] = $libs . 'view' . DS;
+					$paths['helper'][] = $libs . 'view' . DS . 'helpers' . DS;
+					$paths['cake'][] = $cake;
+					$paths['vendor'][] = $path . DS . 'vendors' . DS;
+					$paths['shell'][] = $cake . 'console' . DS . 'libs' . DS;
+					break;
+				}
+			}
+			Cache::write('core_paths', array_filter($paths), '_cake_core_');
+		}
+		if ($type && isset($paths[$type])) {
+			return $paths[$type];
+		}
+		return $paths;
+	}
+/**
+ * Creates a cached version of a configuration file.
+ * Appends values passed from Configure::store() to the cached file
+ *
+ * @param string $content Content to write on file
+ * @param string $name Name to use for cache file
+ * @param boolean $write true if content should be written, false otherwise
+ * @return void
+ * @access private
+ */
+	function __writeConfig($content, $name, $write = true) {
+		$file = CACHE . 'persistent' . DS . $name . '.php';
+
+		if (Configure::read() > 0) {
+			$expires = "+10 seconds";
+		} else {
+			$expires = "+999 days";
+		}
+		$cache = cache('persistent' . DS . $name . '.php', null, $expires);
+
+		if ($cache === null) {
+			cache('persistent' . DS . $name . '.php', "<?php\n\$config = array();\n", $expires);
+		}
+
+		if ($write === true) {
+			if (!class_exists('File')) {
+				require LIBS . 'file.php';
+			}
+			$fileClass = new File($file);
+
+			if ($fileClass->writable()) {
+				$fileClass->append($content);
+			}
+		}
+	}
+/**
+ * Checks $name for dot notation to create dynamic Configure::$var as an array when needed.
+ *
+ * @param mixed $name Name to split
+ * @return array Name separated in items through dot notation
+ * @access private
+ */
+	function __configVarNames($name) {
+		if (is_string($name)) {
+			if (strpos($name, ".")) {
+				return explode(".", $name);
+			}
+			return array($name);
+		}
+		return $name;
+	}
+/**
+ * Build path references. Merges the supplied $paths
+ * with the base paths and the default core paths.
+ *
+ * @param array $paths paths defines in config/bootstrap.php
+ * @return void
+ * @access public
+ */
+	function buildPaths($paths) {
+		$_this =& Configure::getInstance();
+		$core = $_this->corePaths();
+		$basePaths = array(
+			'model' => array(MODELS),
+			'behavior' => array(BEHAVIORS),
+			'controller' => array(CONTROLLERS),
+			'component' => array(COMPONENTS),
+			'view' => array(VIEWS),
+			'helper' => array(HELPERS),
+			'plugin' => array(APP . 'plugins' . DS),
+			'vendor' => array(APP . 'vendors' . DS, VENDORS),
+			'locale' => array(APP . 'locale' . DS),
+			'shell' => array(),
+			'datasource' => array(MODELS . 'datasources')
+		);
+
+		foreach ($basePaths as $type => $default) {
+			$pathsVar = $type . 'Paths';
+			$merge = array();
+
+			if (isset($core[$type])) {
+				$merge = $core[$type];
+			}
+			if ($type === 'model' || $type === 'controller' || $type === 'helper') {
+				$merge = array_merge(array(APP), $merge);
+			}
+
+			if (!is_array($default)) {
+				$default = array($default);
+			}
+			$_this->{$pathsVar} = $default;
+
+			if (isset($paths[$pathsVar]) && !empty($paths[$pathsVar])) {
+				$path = array_flip(array_flip((array_merge(
+					$_this->{$pathsVar}, (array)$paths[$pathsVar], $merge
+				))));
+				$_this->{$pathsVar} = array_values($path);
+			} else {
+				$path = array_flip(array_flip((array_merge($_this->{$pathsVar}, $merge))));
+				$_this->{$pathsVar} = array_values($path);
+			}
+		}
+	}
+/**
+ * Loads app/config/bootstrap.php.
+ * If the alternative paths are set in this file
+ * they will be added to the paths vars.
+ *
+ * @param boolean $boot Load application bootstrap (if true)
+ * @return void
+ * @access private
+ */
+	function __loadBootstrap($boot) {
+		$modelPaths = $behaviorPaths = $controllerPaths = $componentPaths = $viewPaths = $helperPaths = $pluginPaths = $vendorPaths = $localePaths = $shellPaths = null;
+
+		if ($boot) {
+			Configure::write('App', array('base' => false, 'baseUrl' => false, 'dir' => APP_DIR, 'webroot' => WEBROOT_DIR));
+
+			if (!include(CONFIGS . 'core.php')) {
+				trigger_error(sprintf(__("Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR);
+			}
+
+			if (!include(CONFIGS . 'bootstrap.php')) {
+				trigger_error(sprintf(__("Can't find application bootstrap file. Please create %sbootstrap.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR);
+			}
+
+			if (Configure::read('Cache.disable') !== true) {
+				$cache = Cache::config('default');
+
+				if (empty($cache['settings'])) {
+					trigger_error('Cache not configured properly. Please check Cache::config(); in APP/config/core.php', E_USER_WARNING);
+					$cache = Cache::config('default', array('engine' => 'File'));
+				}
+				$path = $prefix = $duration = null;
+
+				if (!empty($cache['settings']['path'])) {
+					$path = realpath($cache['settings']['path']);
+				} else {
+					$prefix = $cache['settings']['prefix'];
+				}
+
+				if (Configure::read() >= 1) {
+					$duration = '+10 seconds';
+				} else {
+					$duration = '+999 days';
+				}
+
+				if (Cache::config('_cake_core_') === false) {
+					Cache::config('_cake_core_', array_merge($cache['settings'], array(
+						'prefix' => $prefix . 'cake_core_', 'path' => $path . DS . 'persistent' . DS,
+						'serialize' => true, 'duration' => $duration
+					)));
+				}
+
+				if (Cache::config('_cake_model_') === false) {
+					Cache::config('_cake_model_', array_merge($cache['settings'], array(
+						'prefix' => $prefix . 'cake_model_', 'path' => $path . DS . 'models' . DS,
+						'serialize' => true, 'duration' => $duration
+					)));
+				}
+				Cache::config('default');
+			}
+			Configure::buildPaths(compact(
+				'modelPaths', 'viewPaths', 'controllerPaths', 'helperPaths', 'componentPaths',
+				'behaviorPaths', 'pluginPaths', 'vendorPaths', 'localePaths', 'shellPaths'
+			));
+		}
+	}
+/**
+ * Caches the object map when the instance of the Configure class is destroyed
+ *
+ * @access public
+ */
+	function __destruct() {
+		if ($this->__cache) {
+			Cache::write('object_map', array_filter($this->__objects), '_cake_core_');
+		}
+	}
+}
+/**
+ * Class and file loader.
+ *
+ * @link          http://book.cakephp.org/view/499/The-App-Class
+ * @since         CakePHP(tm) v 1.2.0.6001
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class App extends Object {
+/**
+ * Paths to search for files.
+ *
+ * @var array
+ * @access public
+ */
+	var $search = array();
+/**
+ * Whether or not to return the file that is loaded.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $return = false;
+/**
+ * Determines if $__maps and $__paths cache should be written.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__cache = false;
+/**
+ * Holds key/value pairs of $type => file path.
+ *
+ * @var array
+ * @access private
+ */
+	var $__map = array();
+/**
+ * Holds paths for deep searching of files.
+ *
+ * @var array
+ * @access private
+ */
+	var $__paths = array();
+/**
+ * Holds loaded files.
+ *
+ * @var array
+ * @access private
+ */
+	var $__loaded = array();
+/**
+ * Finds classes based on $name or specific file(s) to search.
+ *
+ * @link          http://book.cakephp.org/view/529/Using-App-import
+ * @param mixed $type The type of Class if passed as a string, or all params can be passed as
+ *                    an single array to $type,
+ * @param string $name Name of the Class or a unique name for the file
+ * @param mixed $parent boolean true if Class Parent should be searched, accepts key => value
+ *              array('parent' => $parent ,'file' => $file, 'search' => $search, 'ext' => '$ext');
+ *              $ext allows setting the extension of the file name
+ *              based on Inflector::underscore($name) . ".$ext";
+ * @param array $search paths to search for files, array('path 1', 'path 2', 'path 3');
+ * @param string $file full name of the file to search for including extension
+ * @param boolean $return, return the loaded file, the file must have a return
+ *                         statement in it to work: return $variable;
+ * @return boolean true if Class is already in memory or if file is found and loaded, false if not
+ * @access public
+ */
+	function import($type = null, $name = null, $parent = true, $search = array(), $file = null, $return = false) {
+		$plugin = $directory = null;
+
+		if (is_array($type)) {
+			extract($type, EXTR_OVERWRITE);
+		}
+
+		if (is_array($parent)) {
+			extract($parent, EXTR_OVERWRITE);
+		}
+
+		if ($name === null && $file === null) {
+			$name = $type;
+			$type = 'Core';
+		} elseif ($name === null) {
+			$type = 'File';
+		}
+
+		if (is_array($name)) {
+			foreach ($name as $class) {
+				$tempType = $type;
+				$plugin = null;
+
+				if (strpos($class, '.') !== false) {
+					$value = explode('.', $class);
+					$count = count($value);
+
+					if ($count > 2) {
+						$tempType = $value[0];
+						$plugin = $value[1] . '.';
+						$class = $value[2];
+					} elseif ($count === 2 && ($type === 'Core' || $type === 'File')) {
+						$tempType = $value[0];
+						$class = $value[1];
+					} else {
+						$plugin = $value[0] . '.';
+						$class = $value[1];
+					}
+				}
+
+				if (!App::import($tempType, $plugin . $class)) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		if ($name != null && strpos($name, '.') !== false) {
+			list($plugin, $name) = explode('.', $name);
+		}
+		$_this =& App::getInstance();
+		$_this->return = $return;
+
+		if (isset($ext)) {
+			$file = Inflector::underscore($name) . ".$ext";
+		}
+		$ext = $_this->__settings($type, $plugin, $parent);
+
+		if ($name != null && !class_exists($name . $ext['class'])) {
+			if ($load = $_this->__mapped($name . $ext['class'], $type, $plugin)) {
+				if ($_this->__load($load)) {
+					$_this->__overload($type, $name . $ext['class']);
+
+					if ($_this->return) {
+						$value = include $load;
+						return $value;
+					}
+					return true;
+				} else {
+					$_this->__remove($name . $ext['class'], $type, $plugin);
+					$_this->__cache = true;
+				}
+			}
+			if (!empty($search)) {
+				$_this->search = $search;
+			} elseif ($plugin) {
+				$_this->search = $_this->__paths('plugin');
+			} else {
+				$_this->search = $_this->__paths($type);
+			}
+			$find = $file;
+
+			if ($find === null) {
+				$find = Inflector::underscore($name . $ext['suffix']).'.php';
+
+				if ($plugin) {
+					$paths = $_this->search;
+					foreach ($paths as $key => $value) {
+						$_this->search[$key] = $value . $ext['path'];
+					}
+					$plugin = Inflector::camelize($plugin);
+				}
+			}
+
+			if (strtolower($type) !== 'vendor' && empty($search) && $_this->__load($file)) {
+				$directory = false;
+			} else {
+				$file = $find;
+				$directory = $_this->__find($find, true);
+			}
+
+			if ($directory !== null) {
+				$_this->__cache = true;
+				$_this->__map($directory . $file, $name . $ext['class'], $type, $plugin);
+				$_this->__overload($type, $name . $ext['class']);
+
+				if ($_this->return) {
+					$value = include $directory . $file;
+					return $value;
+				}
+				return true;
+			}
+			return false;
+		}
+		return true;
+	}
+/**
+ * Returns a single instance of App.
+ *
+ * @return object
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new App();
+			$instance[0]->__map = Cache::read('file_map', '_cake_core_');
+		}
+		return $instance[0];
+	}
+/**
+ * Locates the $file in $__paths, searches recursively.
+ *
+ * @param string $file full file name
+ * @param boolean $recursive search $__paths recursively
+ * @return mixed boolean on fail, $file directory path on success
+ * @access private
+ */
+	function __find($file, $recursive = true) {
+		if (empty($this->search)) {
+			return null;
+		} elseif (is_string($this->search)) {
+			$this->search = array($this->search);
+		}
+
+		if (empty($this->__paths)) {
+			$this->__paths = Cache::read('dir_map', '_cake_core_');
+		}
+
+		foreach ($this->search as $path) {
+			$path = rtrim($path, DS);
+
+			if ($path === rtrim(APP, DS)) {
+				$recursive = false;
+			}
+			if ($recursive === false) {
+				if ($this->__load($path . DS . $file)) {
+					return $path . DS;
+				}
+				continue;
+			}
+			if (!isset($this->__paths[$path])) {
+				if (!class_exists('Folder')) {
+					require LIBS . 'folder.php';
+				}
+				$Folder =& new Folder();
+				$directories = $Folder->tree($path, false, 'dir');
+				$this->__paths[$path] = $directories;
+			}
+
+			foreach ($this->__paths[$path] as $directory) {
+				if ($this->__load($directory . DS . $file)) {
+					return $directory . DS;
+				}
+			}
+		}
+		return null;
+	}
+/**
+ * Attempts to load $file.
+ *
+ * @param string $file full path to file including file name
+ * @return boolean
+ * @access private
+ */
+	function __load($file) {
+		if (empty($file)) {
+			return false;
+		}
+		if (!$this->return && isset($this->__loaded[$file])) {
+			return true;
+		}
+		if (file_exists($file)) {
+			if (!$this->return) {
+				require($file);
+				$this->__loaded[$file] = true;
+			}
+			return true;
+		}
+		return false;
+	}
+/**
+ * Maps the $name to the $file.
+ *
+ * @param string $file full path to file
+ * @param string $name unique name for this map
+ * @param string $type type object being mapped
+ * @param string $plugin if object is from a plugin, the name of the plugin
+ * @access private
+ */
+	function __map($file, $name, $type, $plugin) {
+		if ($plugin) {
+			$plugin = Inflector::camelize($plugin);
+			$this->__map['Plugin'][$plugin][$type][$name] = $file;
+		} else {
+			$this->__map[$type][$name] = $file;
+		}
+	}
+/**
+ * Returns a file's complete path.
+ *
+ * @param string $name unique name
+ * @param string $type type object
+ * @param string $plugin if object is from a plugin, the name of the plugin
+ * @return mixed, file path if found, false otherwise
+ * @access private
+ */
+	function __mapped($name, $type, $plugin) {
+		if ($plugin) {
+			$plugin = Inflector::camelize($plugin);
+
+			if (isset($this->__map['Plugin'][$plugin][$type]) && isset($this->__map['Plugin'][$plugin][$type][$name])) {
+				return $this->__map['Plugin'][$plugin][$type][$name];
+			}
+			return false;
+		}
+
+		if (isset($this->__map[$type]) && isset($this->__map[$type][$name])) {
+			return $this->__map[$type][$name];
+		}
+		return false;
+	}
+/**
+ * Used to overload objects as needed.
+ *
+ * @param string $type Model or Helper
+ * @param string $name Class name to overload
+ * @access private
+ */
+	function __overload($type, $name) {
+		if (($type === 'Model' || $type === 'Helper') && strtolower($name) != 'schema') {
+			Overloadable::overload($name);
+		}
+	}
+/**
+ * Loads parent classes based on $type.
+ * Returns a prefix or suffix needed for loading files.
+ *
+ * @param string $type type of object
+ * @param string $plugin name of plugin
+ * @param boolean $parent false will not attempt to load parent
+ * @return array
+ * @access private
+ */
+	function __settings($type, $plugin, $parent) {
+		if (!$parent) {
+			return null;
+		}
+
+		if ($plugin) {
+			$plugin = Inflector::underscore($plugin);
+			$name = Inflector::camelize($plugin);
+		}
+		$path = null;
+		$load = strtolower($type);
+
+		switch ($load) {
+			case 'model':
+				if (!class_exists('Model')) {
+					App::import('Core', 'Model', false, Configure::corePaths('model'));
+				}
+				if (!class_exists('AppModel')) {
+					App::import($type, 'AppModel', false, Configure::read('modelPaths'));
+				}
+				if ($plugin) {
+					if (!class_exists($name . 'AppModel')) {
+						App::import($type, $plugin . '.' . $name . 'AppModel', false, array(), $plugin . DS . $plugin . '_app_model.php');
+					}
+					$path = $plugin . DS . 'models' . DS;
+				}
+				return array('class' => null, 'suffix' => null, 'path' => $path);
+			break;
+			case 'behavior':
+				if ($plugin) {
+					$path = $plugin . DS . 'models' . DS . 'behaviors' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'controller':
+				App::import($type, 'AppController', false);
+				if ($plugin) {
+					App::import($type, $plugin . '.' . $name . 'AppController', false, array(), $plugin . DS . $plugin . '_app_controller.php');
+					$path = $plugin . DS . 'controllers' . DS;
+				}
+				return array('class' => $type, 'suffix' => $type, 'path' => $path);
+			break;
+			case 'component':
+				if ($plugin) {
+					$path = $plugin . DS . 'controllers' . DS . 'components' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'view':
+				if ($plugin) {
+					$path = $plugin . DS . 'views' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'helper':
+				if (!class_exists('AppHelper')) {
+					App::import($type, 'AppHelper', false);
+				}
+				if ($plugin) {
+					$path = $plugin . DS . 'views' . DS . 'helpers' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'vendor':
+				if ($plugin) {
+					$path = $plugin . DS . 'vendors' . DS;
+				}
+				return array('class' => null, 'suffix' => null, 'path' => $path);
+			break;
+			default:
+				$type = $suffix = $path = null;
+			break;
+		}
+		return array('class' => null, 'suffix' => null, 'path' => null);
+	}
+/**
+ * Returns default search paths.
+ *
+ * @param string $type type of object to be searched
+ * @return array list of paths
+ * @access private
+ */
+	function __paths($type) {
+		$type = strtolower($type);
+
+		if ($type === 'core') {
+			$path = Configure::corePaths();
+			$paths = array();
+
+			foreach ($path as $key => $value) {
+				$count = count($key);
+				for ($i = 0; $i < $count; $i++) {
+					$paths[] = $path[$key][$i];
+				}
+			}
+			return $paths;
+		}
+
+		if ($paths = Configure::read($type . 'Paths')) {
+			return $paths;
+		}
+
+		switch ($type) {
+			case 'plugin':
+				return array(APP . 'plugins' . DS);
+			case 'vendor':
+				return array(APP . 'vendors' . DS, VENDORS, APP . 'plugins' . DS);
+			case 'controller':
+				return array(APP . 'controllers' . DS, APP);
+			case 'model':
+				return array(APP . 'models' . DS, APP);
+			case 'view':
+				return array(APP . 'views' . DS);
+		}
+	}
+/**
+ * Removes file location from map if the file has been deleted.
+ *
+ * @param string $name name of object
+ * @param string $type type of object
+ * @param string $plugin name of plugin
+ * @return void
+ * @access private
+ */
+	function __remove($name, $type, $plugin) {
+		if ($plugin) {
+			$plugin = Inflector::camelize($plugin);
+			unset($this->__map['Plugin'][$plugin][$type][$name]);
+		} else {
+			unset($this->__map[$type][$name]);
+		}
+	}
+/**
+ * Object destructor.
+ *
+ * Writes cache file if changes have been made to the $__map or $__paths
+ *
+ * @return void
+ * @access private
+ */
+	function __destruct() {
+		if ($this->__cache) {
+			$core = Configure::corePaths('cake');
+			unset($this->__paths[rtrim($core[0], DS)]);
+			Cache::write('dir_map', array_filter($this->__paths), '_cake_core_');
+			Cache::write('file_map', array_filter($this->__map), '_cake_core_');
+		}
+	}
+}
+?>
\ No newline at end of file
Index: cake/libs/debugger.php
===================================================================
--- cake/libs/debugger.php	(revision 0)
+++ cake/libs/debugger.php	(revision 0)
@@ -0,0 +1,576 @@
+<?php
+/* SVN FILE: $Id: debugger.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Framework debugging and PHP error-handling class
+ *
+ * Provides enhanced logging, stack traces, and rendering debug views
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.4560
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+	if (!class_exists('Object')) {
+		uses('object');
+	}
+	if (!class_exists('CakeLog')) {
+		uses('cake_log');
+	}
+/**
+ * Provide custom logging and error handling.
+ *
+ * Debugger overrides PHP's default error handling to provide stack traces and enhanced logging
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @link          http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+class Debugger extends Object {
+/**
+ * A list of errors generated by the application.
+ *
+ * @var array
+ * @access public
+ */
+	var $errors = array();
+/**
+ * Contains the base URL for error code documentation.
+ *
+ * @var string
+ * @access public
+ */
+	var $helpPath = null;
+/**
+ * The current output format.
+ *
+ * @var string
+ * @access protected
+ */
+	var $_outputFormat = 'js';
+/**
+ * Holds current output data when outputFormat is false.
+ *
+ * @var string
+ * @access private
+ */
+	var $__data = array();
+/**
+ * Constructor.
+ *
+ */
+	function __construct() {
+		$docRef = ini_get('docref_root');
+		if (empty($docRef)) {
+			ini_set('docref_root', 'http://php.net/');
+		}
+		if (!defined('E_RECOVERABLE_ERROR')) {
+			define('E_RECOVERABLE_ERROR', 4096);
+		}
+	}
+/**
+ * Returns a reference to the Debugger singleton object instance.
+ *
+ * @return object
+ * @access public
+ * @static
+ */
+	function &getInstance($class = null) {
+		static $instance = array();
+		if (!empty($class)) {
+			if (!$instance || strtolower($class) != strtolower(get_class($instance[0]))) {
+				$instance[0] = & new $class();
+				if (Configure::read() > 0) {
+					Configure::version(); // Make sure the core config is loaded
+					$instance[0]->helpPath = Configure::read('Cake.Debugger.HelpPath');
+				}
+			}
+		}
+
+		if (!$instance) {
+			$instance[0] =& new Debugger();
+			if (Configure::read() > 0) {
+				Configure::version(); // Make sure the core config is loaded
+				$instance[0]->helpPath = Configure::read('Cake.Debugger.HelpPath');
+			}
+		}
+		return $instance[0];
+	}
+/**
+ * Formats and outputs the contents of the supplied variable.
+ *
+ * @param $var mixed the variable to dump
+ * @return void
+ * @see exportVar
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+*/
+	function dump($var) {
+		$_this = Debugger::getInstance();
+		pr($_this->exportVar($var));
+	}
+/**
+ * Creates a detailed stack trace log at the time of invocation, much like dump()
+ * but to debug.log.
+ *
+ * @param $var mixed Variable or content to log
+ * @param $level int type of log to use. Defaults to LOG_DEBUG
+ * @return void
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+	function log($var, $level = LOG_DEBUG) {
+		$_this = Debugger::getInstance();
+		$trace = $_this->trace(array('start' => 1, 'depth' => 2, 'format' => 'array'));
+		$source = null;
+
+		if (is_object($trace[0]['object']) && isset($trace[0]['object']->_reporter->_test_stack)) {
+			$stack = $trace[0]['object']->_reporter->_test_stack;
+			$source = "[". $stack[0].", ". $stack[2] ."::" . $stack[3] ."()]\n";
+		}
+
+		CakeLog::write($level, $source . $_this->exportVar($var));
+	}
+
+/**
+ * Overrides PHP's default error handling.
+ *
+ * @param integer $code Code of error
+ * @param string $description Error description
+ * @param string $file File on which error occurred
+ * @param integer $line Line that triggered the error
+ * @param array $context Context
+ * @return boolean true if error was handled
+ * @access public
+ */
+	function handleError($code, $description, $file = null, $line = null, $context = null) {
+		if (error_reporting() == 0 || $code === 2048) {
+			return;
+		}
+
+		$_this = Debugger::getInstance();
+
+		if (empty($file)) {
+			$file = '[internal]';
+		}
+		if (empty($line)) {
+			$line = '??';
+		}
+		$file = $_this->trimPath($file);
+
+		$info = compact('code', 'description', 'file', 'line');
+		if (!in_array($info, $_this->errors)) {
+			$_this->errors[] = $info;
+		} else {
+			return;
+		}
+
+		$level = LOG_DEBUG;
+		switch ($code) {
+			case E_PARSE:
+			case E_ERROR:
+			case E_CORE_ERROR:
+			case E_COMPILE_ERROR:
+			case E_USER_ERROR:
+				$error = 'Fatal Error';
+				$level = LOG_ERROR;
+			break;
+			case E_WARNING:
+			case E_USER_WARNING:
+			case E_COMPILE_WARNING:
+			case E_RECOVERABLE_ERROR:
+				$error = 'Warning';
+				$level = LOG_WARNING;
+			break;
+			case E_NOTICE:
+			case E_USER_NOTICE:
+				$error = 'Notice';
+				$level = LOG_NOTICE;
+			break;
+			default:
+				return false;
+			break;
+		}
+
+		$helpCode = null;
+		if (!empty($_this->helpPath) && preg_match('/.*\[([0-9]+)\]$/', $description, $codes)) {
+			if (isset($codes[1])) {
+				$helpCode = $codes[1];
+				$description = trim(preg_replace('/\[[0-9]+\]$/', '', $description));
+			}
+		}
+
+		echo $_this->_output($level, $error, $code, $helpCode, $description, $file, $line, $context);
+
+		if (Configure::read('log')) {
+			CakeLog::write($level, "{$error} ({$code}): {$description} in [{$file}, line {$line}]");
+		}
+
+		if ($error == 'Fatal Error') {
+			die();
+		}
+		return true;
+	}
+/**
+ * Outputs a stack trace based on the supplied options.
+ *
+ * @param array $options Format for outputting stack trace
+ * @return string Formatted stack trace
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+	function trace($options = array()) {
+		$options = array_merge(array(
+				'depth'		=> 999,
+				'format'	=> '',
+				'args'		=> false,
+				'start'		=> 0,
+				'scope'		=> null,
+				'exclude'	=> null
+			),
+			$options
+		);
+
+		$backtrace = debug_backtrace();
+		$back = array();
+		$count = count($backtrace);
+
+		for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
+			$trace = array_merge(
+				array(
+					'file' => '[internal]',
+					'line' => '??'
+				),
+				$backtrace[$i]
+			);
+
+			if (isset($backtrace[$i + 1])) {
+				$next = array_merge(
+					array(
+						'line'		=> '??',
+						'file'		=> '[internal]',
+						'class'		=> null,
+						'function'	=> '[main]'
+					),
+					$backtrace[$i + 1]
+				);
+				$function = $next['function'];
+
+				if (!empty($next['class'])) {
+					$function = $next['class'] . '::' . $function . '(';
+					if ($options['args'] && isset($next['args'])) {
+						$args = array();
+						foreach ($next['args'] as $arg) {
+							$args[] = Debugger::exportVar($arg);
+						}
+						$function .= join(', ', $args);
+					}
+					$function .= ')';
+				}
+			} else {
+				$function = '[main]';
+			}
+			if (in_array($function, array('call_user_func_array', 'trigger_error'))) {
+				continue;
+			}
+			if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
+				$back[] = array('file' => $trace['file'], 'line' => $trace['line']);
+			} elseif (empty($options['format'])) {
+				$back[] = $function . ' - ' . Debugger::trimPath($trace['file']) . ', line ' . $trace['line'];
+			} else {
+				$back[] = $trace;
+			}
+		}
+
+		if ($options['format'] == 'array' || $options['format'] == 'points') {
+			return $back;
+		}
+		return join("\n", $back);
+	}
+/**
+ * Shortens file paths by replacing the application base path with 'APP', and the CakePHP core
+ * path with 'CORE'.
+ *
+ * @param string $path Path to shorten
+ * @return string Normalized path
+ * @access public
+ * @static
+ */
+	function trimPath($path) {
+		if (!defined('CAKE_CORE_INCLUDE_PATH') || !defined('APP')) {
+			return $path;
+		}
+
+		if (strpos($path, APP) === 0) {
+			return str_replace(APP, 'APP' . DS, $path);
+		} elseif (strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
+			return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
+		} elseif (strpos($path, ROOT) === 0) {
+			return str_replace(ROOT, 'ROOT', $path);
+		}
+		$corePaths = Configure::corePaths('cake');
+		foreach ($corePaths as $corePath) {
+			if (strpos($path, $corePath) === 0) {
+				return str_replace($corePath, 'CORE' .DS . 'cake' .DS, $path);
+			}
+		}
+		return $path;
+	}
+/**
+ * Grabs an excerpt from a file and highlights a given line of code
+ *
+ * @param string $file Absolute path to a PHP file
+ * @param integer $line Line number to highlight
+ * @param integer $context Number of lines of context to extract above and below $line
+ * @return array Set of lines highlighted
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+	function excerpt($file, $line, $context = 2) {
+		$data = $lines = array();
+		if (!file_exists($file)) {
+			return array();
+		}
+		$data = @explode("\n", file_get_contents($file));
+
+		if (empty($data) || !isset($data[$line])) {
+			return;
+		}
+		for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
+			if (!isset($data[$i])) {
+				continue;
+			}
+			$string = str_replace(array("\r\n", "\n"), "", highlight_string($data[$i], true));
+			if ($i == $line) {
+				$lines[] = '<span class="code-highlight">' . $string . '</span>';
+			} else {
+				$lines[] = $string;
+			}
+		}
+		return $lines;
+	}
+/**
+ * Converts a variable to a string for debug output.
+ *
+ * @param string $var Variable to convert
+ * @return string Variable as a formatted string
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+	function exportVar($var, $recursion = 0) {
+		$_this =  Debugger::getInstance();
+		switch (strtolower(gettype($var))) {
+			case 'boolean':
+				return ($var) ? 'true' : 'false';
+			break;
+			case 'integer':
+			case 'double':
+				return $var;
+			break;
+			case 'string':
+				if (trim($var) == "") {
+					return '""';
+				}
+				return '"' . h($var) . '"';
+			break;
+			case 'object':
+				return get_class($var) . "\n" . $_this->__object($var);
+			case 'array':
+				$out = "array(";
+				$vars = array();
+				foreach ($var as $key => $val) {
+					if ($recursion >= 0) {
+						if (is_numeric($key)) {
+							$vars[] = "\n\t" . $_this->exportVar($val, $recursion - 1);
+						} else {
+							$vars[] = "\n\t" .$_this->exportVar($key, $recursion - 1)
+										. ' => ' . $_this->exportVar($val, $recursion - 1);
+						}
+					}
+				}
+				$n = null;
+				if (count($vars) > 0) {
+					$n = "\n";
+				}
+				return $out . join(",", $vars) . "{$n})";
+			break;
+			case 'resource':
+				return strtolower(gettype($var));
+			break;
+			case 'null':
+				return 'null';
+			break;
+		}
+	}
+/**
+ * Handles object to string conversion.
+ *
+ * @param string $var Object to convert
+ * @return string
+ * @access private
+ * @see Debugger:exportVar()
+ */
+	function __object($var) {
+		$out = array();
+
+		if (is_object($var)) {
+			$className = get_class($var);
+			$objectVars = get_object_vars($var);
+
+			foreach ($objectVars as $key => $value) {
+				if (is_object($value)) {
+					$value = get_class($value) . ' object';
+				} elseif (is_array($value)) {
+					$value = 'array';
+				} elseif ($value === null) {
+					$value = 'NULL';
+				} elseif (in_array(gettype($value), array('boolean', 'integer', 'double', 'string', 'array', 'resource'))) {
+					$value = Debugger::exportVar($value);
+				}
+				$out[] = "$className::$$key = " . $value;
+			}
+		}
+		return join("\n", $out);
+	}
+/**
+ * Handles object conversion to debug string.
+ *
+ * @param string $var Object to convert
+ * @access protected
+ */
+	function output($format = 'js') {
+		$_this = Debugger::getInstance();
+		$data = null;
+
+		if ($format === true && !empty($_this->__data)) {
+			$data = $_this->__data;
+			$_this->__data = array();
+			$format = false;
+		}
+		$_this->_outputFormat = $format;
+
+		return $data;
+	}
+/**
+ * Handles object conversion to debug string.
+ *
+ * @param string $var Object to convert
+ * @access private
+ */
+	function _output($level, $error, $code, $helpCode, $description, $file, $line, $kontext) {
+		$files = $this->trace(array('start' => 2, 'format' => 'points'));
+		$listing = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
+		$trace = $this->trace(array('start' => 2, 'depth' => '20'));
+		$context = array();
+
+		foreach ((array)$kontext as $var => $value) {
+			$context[] = "\${$var}\t=\t" . $this->exportVar($value, 1);
+		}
+
+		switch ($this->_outputFormat) {
+			default:
+			case 'js':
+				$link = "document.getElementById(\"CakeStackTrace" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeStackTrace" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
+				$out = "<a href='javascript:void(0);' onclick='{$link}'><b>{$error}</b> ({$code})</a>: {$description} [<b>{$file}</b>, line <b>{$line}</b>]";
+				if (Configure::read() > 0) {
+					debug($out, false, false);
+					echo '<div id="CakeStackTrace' . count($this->errors) . '" class="cake-stack-trace" style="display: none;">';
+						$link = "document.getElementById(\"CakeErrorCode" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeErrorCode" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
+						echo "<a href='javascript:void(0);' onclick='{$link}'>Code</a>";
+
+						if (!empty($context)) {
+							$link = "document.getElementById(\"CakeErrorContext" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeErrorContext" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
+							echo " | <a href='javascript:void(0);' onclick='{$link}'>Context</a>";
+
+							if (!empty($helpCode)) {
+								echo " | <a href='{$this->helpPath}{$helpCode}' target='_blank'>Help</a>";
+							}
+
+							echo "<pre id=\"CakeErrorContext" . count($this->errors) . "\" class=\"cake-context\" style=\"display: none;\">";
+							echo implode("\n", $context);
+							echo "</pre>";
+						}
+
+						if (!empty($listing)) {
+							echo "<div id=\"CakeErrorCode" . count($this->errors) . "\" class=\"cake-code-dump\" style=\"display: none;\">";
+								pr(implode("\n", $listing) . "\n", false);
+							echo '</div>';
+						}
+						pr($trace, false);
+					echo '</div>';
+				}
+			break;
+			case 'html':
+				echo "<pre class=\"cake-debug\"><b>{$error}</b> ({$code}) : {$description} [<b>{$file}</b>, line <b>{$line}]</b></pre>";
+				if (!empty($context)) {
+					echo "Context:\n" .implode("\n", $context) . "\n";
+				}
+				echo "<pre class=\"cake-debug context\"><b>Context</b> <p>" . implode("\n", $context) . "</p></pre>";
+				echo "<pre class=\"cake-debug trace\"><b>Trace</b> <p>" . $trace. "</p></pre>";
+			break;
+			case 'text':
+			case 'txt':
+				echo "{$error}: {$code} :: {$description} on line {$line} of {$file}\n";
+				if (!empty($context)) {
+					echo "Context:\n" .implode("\n", $context) . "\n";
+				}
+				echo "Trace:\n" . $trace;
+			break;
+			case 'log':
+				$this->log(compact('error', 'code', 'description', 'line', 'file', 'context', 'trace'));
+			break;
+			case false:
+				$this->__data[] = compact('error', 'code', 'description', 'line', 'file', 'context', 'trace');
+			break;
+		}
+	}
+/**
+ * Verifies that the application's salt value has been changed from the default value.
+ *
+ * @access public
+ * @static
+ */
+	function checkSessionKey() {
+		if (Configure::read('Security.salt') == 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') {
+			trigger_error(__('Please change the value of \'Security.salt\' in app/config/core.php to a salt value specific to your application', true), E_USER_NOTICE);
+		}
+	}
+/**
+ * Invokes the given debugger object as the current error handler, taking over control from the previous handler
+ * in a stack-like hierarchy.
+ *
+ * @param object $debugger A reference to the Debugger object
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class
+ */
+	function invoke(&$debugger) {
+		set_error_handler(array(&$debugger, 'handleError'));
+	}
+}
+
+if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
+	Debugger::invoke(Debugger::getInstance());
+}
+?>
\ No newline at end of file
Index: cake/libs/inflector.php
===================================================================
--- cake/libs/inflector.php	(revision 0)
+++ cake/libs/inflector.php	(revision 0)
@@ -0,0 +1,527 @@
+<?php
+/* SVN FILE: $Id: inflector.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Pluralize and singularize English words.
+ *
+ * Used by Cake's naming conventions throughout the framework.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	uses('object');
+}
+if (!class_exists('Set')) {
+	require LIBS . 'set.php';
+}
+/**
+ * Pluralize and singularize English words.
+ *
+ * Inflector pluralizes and singularizes English nouns.
+ * Used by Cake's naming conventions throughout the framework.
+ * Test with $i = new Inflector(); $i->test();
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @link          http://book.cakephp.org/view/491/Inflector
+ */
+class Inflector extends Object {
+/**
+ * Pluralized words.
+ *
+ * @var array
+ * @access private
+ **/
+	var $pluralized = array();
+/**
+ * List of pluralization rules in the form of pattern => replacement.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/47/Custom-Inflections
+ **/
+	var $pluralRules = array();
+/**
+ * Singularized words.
+ *
+ * @var array
+ * @access private
+ **/
+	var $singularized = array();
+/**
+ * List of singularization rules in the form of pattern => replacement.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/47/Custom-Inflections
+ **/
+	var $singularRules = array();
+/**
+ * Plural rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__pluralRules = array();
+/**
+ * Un-inflected plural rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__uninflectedPlural = array();
+/**
+ * Irregular plural rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__irregularPlural = array();
+/**
+ * Singular rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__singularRules = array();
+/**
+ * Un-inflectd singular rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__uninflectedSingular = array();
+/**
+ * Irregular singular rules from inflections.php
+ *
+ * @var array
+ * @access private
+ **/
+	var $__irregularSingular = array();
+/**
+ * Gets a reference to the Inflector object instance
+ *
+ * @return object
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Inflector();
+			if (file_exists(CONFIGS.'inflections.php')) {
+				include(CONFIGS.'inflections.php');
+				$instance[0]->__pluralRules = $pluralRules;
+				$instance[0]->__uninflectedPlural = $uninflectedPlural;
+				$instance[0]->__irregularPlural = $irregularPlural;
+				$instance[0]->__singularRules = $singularRules;
+				$instance[0]->__uninflectedSingular = $uninflectedPlural;
+				$instance[0]->__irregularSingular = array_flip($irregularPlural);
+			}
+		}
+		return $instance[0];
+	}
+/**
+ * Initializes plural inflection rules.
+ *
+ * @return void
+ * @access private
+ */
+	function __initPluralRules() {
+		$corePluralRules = array(
+			'/(s)tatus$/i' => '\1\2tatuses',
+			'/(quiz)$/i' => '\1zes',
+			'/^(ox)$/i' => '\1\2en',
+			'/([m|l])ouse$/i' => '\1ice',
+			'/(matr|vert|ind)(ix|ex)$/i'  => '\1ices',
+			'/(x|ch|ss|sh)$/i' => '\1es',
+			'/([^aeiouy]|qu)y$/i' => '\1ies',
+			'/(hive)$/i' => '\1s',
+			'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+			'/sis$/i' => 'ses',
+			'/([ti])um$/i' => '\1a',
+			'/(p)erson$/i' => '\1eople',
+			'/(m)an$/i' => '\1en',
+			'/(c)hild$/i' => '\1hildren',
+			'/(buffal|tomat)o$/i' => '\1\2oes',
+			'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
+			'/us$/' => 'uses',
+			'/(alias)$/i' => '\1es',
+			'/(ax|cri|test)is$/i' => '\1es',
+			'/s$/' => 's',
+			'/^$/' => '',
+			'/$/' => 's');
+
+		$coreUninflectedPlural = array(
+			'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'Amoyese',
+			'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers',
+			'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk',
+			'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
+			'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese',
+			'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news',
+			'nexus', 'Niasese', 'Pekingese', 'People', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings',
+			'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears',
+			'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese',
+			'whiting', 'wildebeest', 'Yengeese');
+
+		$coreIrregularPlural = array(
+			'atlas' => 'atlases',
+			'beef' => 'beefs',
+			'brother' => 'brothers',
+			'child' => 'children',
+			'corpus' => 'corpuses',
+			'cow' => 'cows',
+			'ganglion' => 'ganglions',
+			'genie' => 'genies',
+			'genus' => 'genera',
+			'graffito' => 'graffiti',
+			'hoof' => 'hoofs',
+			'loaf' => 'loaves',
+			'man' => 'men',
+			'money' => 'monies',
+			'mongoose' => 'mongooses',
+			'move' => 'moves',
+			'mythos' => 'mythoi',
+			'numen' => 'numina',
+			'occiput' => 'occiputs',
+			'octopus' => 'octopuses',
+			'opus' => 'opuses',
+			'ox' => 'oxen',
+			'penis' => 'penises',
+			'person' => 'people',
+			'sex' => 'sexes',
+			'soliloquy' => 'soliloquies',
+			'testis' => 'testes',
+			'trilby' => 'trilbys',
+			'turf' => 'turfs');
+
+		$pluralRules = Set::pushDiff($this->__pluralRules, $corePluralRules);
+		$uninflected = Set::pushDiff($this->__uninflectedPlural, $coreUninflectedPlural);
+		$irregular = Set::pushDiff($this->__irregularPlural, $coreIrregularPlural);
+
+		$this->pluralRules = array('pluralRules' => $pluralRules, 'uninflected' => $uninflected, 'irregular' => $irregular);
+		$this->pluralized = array();
+	}
+/**
+ * Return $word in plural form.
+ *
+ * @param string $word Word in singular
+ * @return string Word in plural
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function pluralize($word) {
+		$_this =& Inflector::getInstance();
+		if (!isset($_this->pluralRules) || empty($_this->pluralRules)) {
+			$_this->__initPluralRules();
+		}
+
+		if (isset($_this->pluralized[$word])) {
+			return $_this->pluralized[$word];
+		}
+		extract($_this->pluralRules);
+
+		if (!isset($regexUninflected) || !isset($regexIrregular)) {
+			$regexUninflected = __enclose(join( '|', $uninflected));
+			$regexIrregular = __enclose(join( '|', array_keys($irregular)));
+			$_this->pluralRules['regexUninflected'] = $regexUninflected;
+			$_this->pluralRules['regexIrregular'] = $regexIrregular;
+		}
+
+		if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) {
+			$_this->pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1);
+			return $_this->pluralized[$word];
+		}
+
+		if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) {
+			$_this->pluralized[$word] = $word;
+			return $word;
+		}
+
+		foreach ($pluralRules as $rule => $replacement) {
+			if (preg_match($rule, $word)) {
+				$_this->pluralized[$word] = preg_replace($rule, $replacement, $word);
+				return $_this->pluralized[$word];
+			}
+		}
+	}
+/**
+ * Initializes singular inflection rules.
+ *
+ * @return void
+ * @access protected
+ */
+	function __initSingularRules() {
+		$coreSingularRules = array(
+			'/(s)tatuses$/i' => '\1\2tatus',
+			'/^(.*)(menu)s$/i' => '\1\2',
+			'/(quiz)zes$/i' => '\\1',
+			'/(matr)ices$/i' => '\1ix',
+			'/(vert|ind)ices$/i' => '\1ex',
+			'/^(ox)en/i' => '\1',
+			'/(alias)(es)*$/i' => '\1',
+			'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
+			'/(cris|ax|test)es$/i' => '\1is',
+			'/(shoe)s$/i' => '\1',
+			'/(o)es$/i' => '\1',
+			'/ouses$/' => 'ouse',
+			'/uses$/' => 'us',
+			'/([m|l])ice$/i' => '\1ouse',
+			'/(x|ch|ss|sh)es$/i' => '\1',
+			'/(m)ovies$/i' => '\1\2ovie',
+			'/(s)eries$/i' => '\1\2eries',
+			'/([^aeiouy]|qu)ies$/i' => '\1y',
+			'/([lr])ves$/i' => '\1f',
+			'/(tive)s$/i' => '\1',
+			'/(hive)s$/i' => '\1',
+			'/(drive)s$/i' => '\1',
+			'/([^fo])ves$/i' => '\1fe',
+			'/(^analy)ses$/i' => '\1sis',
+			'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+			'/([ti])a$/i' => '\1um',
+			'/(p)eople$/i' => '\1\2erson',
+			'/(m)en$/i' => '\1an',
+			'/(c)hildren$/i' => '\1\2hild',
+			'/(n)ews$/i' => '\1\2ews',
+			'/^(.*us)$/' => '\\1',
+			'/s$/i' => '');
+
+		$coreUninflectedSingular = array(
+			'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', 'Amoyese',
+			'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers',
+			'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk',
+			'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
+			'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese',
+			'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news',
+			'nexus', 'Niasese', 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings',
+			'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears',
+			'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese',
+			'whiting', 'wildebeest', 'Yengeese');
+
+		$coreIrregularSingular = array(
+			'atlases' => 'atlas',
+			'beefs' => 'beef',
+			'brothers' => 'brother',
+			'children' => 'child',
+			'corpuses' => 'corpus',
+			'cows' => 'cow',
+			'ganglions' => 'ganglion',
+			'genies' => 'genie',
+			'genera' => 'genus',
+			'graffiti' => 'graffito',
+			'hoofs' => 'hoof',
+			'loaves' => 'loaf',
+			'men' => 'man',
+			'monies' => 'money',
+			'mongooses' => 'mongoose',
+			'moves' => 'move',
+			'mythoi' => 'mythos',
+			'numina' => 'numen',
+			'occiputs' => 'occiput',
+			'octopuses' => 'octopus',
+			'opuses' => 'opus',
+			'oxen' => 'ox',
+			'penises' => 'penis',
+			'people' => 'person',
+			'sexes' => 'sex',
+			'soliloquies' => 'soliloquy',
+			'testes' => 'testis',
+			'trilbys' => 'trilby',
+			'turfs' => 'turf');
+
+		$singularRules = Set::pushDiff($this->__singularRules, $coreSingularRules);
+		$uninflected = Set::pushDiff($this->__uninflectedSingular, $coreUninflectedSingular);
+		$irregular = Set::pushDiff($this->__irregularSingular, $coreIrregularSingular);
+
+		$this->singularRules = array('singularRules' => $singularRules, 'uninflected' => $uninflected, 'irregular' => $irregular);
+		$this->singularized = array();
+	}
+/**
+ * Return $word in singular form.
+ *
+ * @param string $word Word in plural
+ * @return string Word in singular
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function singularize($word) {
+		$_this =& Inflector::getInstance();
+		if (!isset($_this->singularRules) || empty($_this->singularRules)) {
+			$_this->__initSingularRules();
+		}
+
+		if (isset($_this->singularized[$word])) {
+			return $_this->singularized[$word];
+		}
+		extract($_this->singularRules);
+
+		if (!isset($regexUninflected) || !isset($regexIrregular)) {
+			$regexUninflected = __enclose(join( '|', $uninflected));
+			$regexIrregular = __enclose(join( '|', array_keys($irregular)));
+			$_this->singularRules['regexUninflected'] = $regexUninflected;
+			$_this->singularRules['regexIrregular'] = $regexIrregular;
+		}
+
+		if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) {
+			$_this->singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1);
+			return $_this->singularized[$word];
+		}
+
+		if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) {
+			$_this->singularized[$word] = $word;
+			return $word;
+		}
+
+		foreach ($singularRules as $rule => $replacement) {
+			if (preg_match($rule, $word)) {
+				$_this->singularized[$word] = preg_replace($rule, $replacement, $word);
+				return $_this->singularized[$word];
+			}
+		}
+		$_this->singularized[$word] = $word;
+		return $word;
+	}
+/**
+ * Returns the given lower_case_and_underscored_word as a CamelCased word.
+ *
+ * @param string $lower_case_and_underscored_word Word to camelize
+ * @return string Camelized word. LikeThis.
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function camelize($lowerCaseAndUnderscoredWord) {
+		return str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)));
+	}
+/**
+ * Returns the given camelCasedWord as an underscored_word.
+ *
+ * @param string $camelCasedWord Camel-cased word to be "underscorized"
+ * @return string Underscore-syntaxed version of the $camelCasedWord
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function underscore($camelCasedWord) {
+		return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
+	}
+/**
+ * Returns the given underscored_word_group as a Human Readable Word Group.
+ * (Underscores are replaced by spaces and capitalized following words.)
+ *
+ * @param string $lower_case_and_underscored_word String to be made more readable
+ * @return string Human-readable string
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function humanize($lowerCaseAndUnderscoredWord) {
+		return ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord));
+	}
+/**
+ * Returns corresponding table name for given model $className. ("people" for the model class "Person").
+ *
+ * @param string $className Name of class to get database table name for
+ * @return string Name of the database table for given class
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function tableize($className) {
+		return Inflector::pluralize(Inflector::underscore($className));
+	}
+/**
+ * Returns Cake model class name ("Person" for the database table "people".) for given database table.
+ *
+ * @param string $tableName Name of database table to get class name for
+ * @return string Class name
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function classify($tableName) {
+		return Inflector::camelize(Inflector::singularize($tableName));
+	}
+/**
+ * Returns camelBacked version of an underscored string.
+ *
+ * @param string $string
+ * @return string in variable form
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function variable($string) {
+		$string = Inflector::camelize(Inflector::underscore($string));
+		$replace = strtolower(substr($string, 0, 1));
+		return preg_replace('/\\w/', $replace, $string, 1);
+	}
+/**
+ * Returns a string with all spaces converted to underscores (by default), accented
+ * characters converted to non-accented characters, and non word characters removed.
+ *
+ * @param string $string
+ * @param string $replacement
+ * @return string
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/572/Class-methods
+ */
+	function slug($string, $replacement = '_') {
+		if (!class_exists('String')) {
+			require LIBS . 'string.php';
+		}
+		$map = array(
+			'/à|á|å|â/' => 'a',
+			'/è|é|ê|ẽ|ë/' => 'e',
+			'/ì|í|î/' => 'i',
+			'/ò|ó|ô|ø/' => 'o',
+			'/ù|ú|ů|û/' => 'u',
+			'/ç/' => 'c',
+			'/ñ/' => 'n',
+			'/ä|æ/' => 'ae',
+			'/ö/' => 'oe',
+			'/ü/' => 'ue',
+			'/Ä/' => 'Ae',
+			'/Ü/' => 'Ue',
+			'/Ö/' => 'Oe',
+			'/ß/' => 'ss',
+			'/[^\w\s]/' => ' ',
+			'/\\s+/' => $replacement,
+			String::insert('/^[:replacement]+|[:replacement]+$/', array('replacement' => preg_quote($replacement, '/'))) => '',
+		);
+		return preg_replace(array_keys($map), array_values($map), $string);
+	}
+}
+/**
+ * Enclose a string for preg matching.
+ *
+ * @param string $string String to enclose
+ * @return string Enclosed string
+ */
+	function __enclose($string) {
+		return '(?:' . $string . ')';
+	}
+?>
\ No newline at end of file
Index: cake/libs/magic_db.php
===================================================================
--- cake/libs/magic_db.php	(revision 0)
+++ cake/libs/magic_db.php	(revision 0)
@@ -0,0 +1,297 @@
+<?php
+/* SVN FILE: $Id: magic_db.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * MagicDb parser and file analyzer
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+if (!class_exists('File')) {
+	uses('object', 'file');
+}
+/**
+ * A class to parse and use the MagicDb for file type analysis
+ *
+ * @package       cake.tests
+ * @subpackage    cake.tests.cases.libs
+ */
+class MagicDb extends Object {
+/**
+ * Holds the parsed MagicDb for this class instance
+ *
+ * @var array
+ **/
+	var $db = array();
+
+/**
+ * Reads a MagicDb from various formats
+ *
+ * @var $magicDb mixed Can be an array containing the db, a magic db as a string, or a filename pointing to a magic db in .db or magic.db.php format
+ * @return boolean Returns false if reading / validation failed or true on success.
+ * @author        Felix
+ **/
+	function read($magicDb = null) {
+		if (!is_string($magicDb) && !is_array($magicDb)) {
+			return false;
+		}
+		if (is_array($magicDb) || strpos($magicDb, '# FILE_ID DB') === 0) {
+			$data = $magicDb;
+		} else {
+			$File =& new File($magicDb);
+			if (!$File->exists()) {
+				return false;
+			}
+			if ($File->ext() == 'php') {
+				include($File->pwd());
+				$data = $magicDb;
+			} else {
+				// @TODO: Needs test coverage
+				$data = $File->read();
+			}
+		}
+
+		$magicDb = $this->toArray($data);
+		if (!$this->validates($magicDb)) {
+			return false;
+		}
+		return !!($this->db = $magicDb);
+	}
+
+/**
+ * Parses a MagicDb $data string into an array or returns the current MagicDb instance as an array
+ *
+ * @param string $data A MagicDb string to turn into an array
+ * @return array A parsed MagicDb array or an empty array if the $data param was invalid. Returns the db property if $data is not set.
+ * @access public
+ */
+	function toArray($data = null) {
+		if (is_array($data)) {
+			return $data;
+		}
+		if ($data === null) {
+			return $this->db;
+		}
+
+		if (strpos($data, '# FILE_ID DB') !== 0) {
+			return array();
+		}
+
+		$lines = explode("\r\n", $data);
+		$db = array();
+
+		$validHeader = count($lines > 3)
+					&& preg_match('/^# Date:([0-9]{4}-[0-9]{2}-[0-9]{2})$/', $lines[1], $date)
+					&& preg_match('/^# Source:(.+)$/', $lines[2], $source)
+					&& strlen($lines[3]) == 0;
+		if (!$validHeader) {
+			return $db;
+		}
+
+		$db = array('header' => array('Date' => $date[1], 'Source' => $source[1]), 'database' => array());
+		$lines = array_splice($lines, 3);
+
+		$format = array();
+		while (!empty($lines)) {
+			$line = array_shift($lines);
+			if (isset($line[0]) && $line[0] == '#' || empty($line)) {
+				continue;
+			}
+
+			$columns = explode("\t", $line);
+			if (in_array($columns[0]{0}, array('>', '&'))) {
+				$format[] = $columns;
+			} elseif (!empty($format)) {
+				$db['database'][] = $format;
+				$format = array($columns);
+			} else {
+				$format = array($columns);
+			}
+		}
+
+		return $db;
+	}
+
+/**
+ * Returns true if the MagicDb instance or the passed $magicDb is valid
+ *
+ * @param mixed $magicDb A $magicDb string / array to validate (optional)
+ * @return boolean True if the $magicDb / instance db validates, false if not
+ * @access public
+ */
+	function validates($magicDb = null) {
+		if (is_null($magicDb)) {
+			$magicDb = $this->db;
+		} elseif (!is_array($magicDb)) {
+			$magicDb = $this->toArray($magicDb);
+		}
+
+		return isset($magicDb['header'], $magicDb['database']) && is_array($magicDb['header']) && is_array($magicDb['database']);
+	}
+
+/**
+ * Analyzes a given $file using the currently loaded MagicDb information based on the desired $options
+ *
+ * @param string $file Absolute path to the file to analyze
+ * @param array $options TBT
+ * @return mixed
+ * @access public
+ */
+	function analyze($file, $options = array()) {
+		if (!is_string($file)) {
+			return false;
+		}
+
+		$matches = array();
+		$MagicFileResource =& new MagicFileResource($file);
+		foreach ($this->db['database'] as $format) {
+			$magic = $format[0];
+			$match = $MagicFileResource->test($magic);
+			if ($match === false) {
+				continue;
+			}
+			$matches[] = $magic;
+		}
+
+		return $matches;
+	}
+}
+
+/**
+ * undocumented class
+ *
+ * @package       cake.tests
+ * @subpackage    cake.tests.cases.libs
+ */
+class MagicFileResource extends Object{
+/**
+ * undocumented variable
+ *
+ * @var unknown
+ * @access public
+ */
+	var $resource = null;
+/**
+ * undocumented variable
+ *
+ * @var unknown
+ * @access public
+ */
+	var $offset = 0;
+/**
+ * undocumented function
+ *
+ * @param unknown $file
+ * @return void
+ * @access public
+ */
+	function __construct($file) {
+		if (file_exists($file)) {
+			$this->resource =& new File($file);
+		} else {
+			$this->resource = $file;
+		}
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $magic
+ * @return void
+ * @access public
+ */
+	function test($magic) {
+		$offset = null;
+		$type = null;
+		$expected = null;
+		$comment = null;
+		if (isset($magic[0])) {
+			$offset = $magic[0];
+		}
+		if (isset($magic[1])) {
+			$type = $magic[1];
+		}
+		if (isset($magic[2])) {
+			$expected = $magic[2];
+		}
+		if (isset($magic[3])) {
+			$comment = $magic[3];
+		}
+		$val = $this->extract($offset, $type, $expected);
+		return $val == $expected;
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $type
+ * @param unknown $length
+ * @return void
+ * @access public
+ */
+	function read($length = null) {
+		if (!is_object($this->resource)) {
+			return substr($this->resource, $this->offset, $length);
+		}
+		return $this->resource->read($length);
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $type
+ * @param unknown $expected
+ * @return void
+ * @access public
+ */
+	function extract($offset, $type, $expected) {
+		switch ($type) {
+			case 'string':
+				$this->offset($offset);
+				$val = $this->read(strlen($expected));
+				if ($val === $expected) {
+					return true;
+				}
+				break;
+		}
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $offset
+ * @param unknown $whence
+ * @return void
+ * @access public
+ */
+	function offset($offset = null) {
+		if (is_null($offset)) {
+			if (!is_object($this->resource)) {
+				return $this->offset;
+			}
+			return $this->offset;
+		}
+
+		if (!ctype_digit($offset)) {
+			return false;
+		}
+		if (is_object($this->resource)) {
+			$this->resource->offset($offset);
+		} else {
+			$this->offset = $offset;
+		}
+	}
+}
+
+?>
\ No newline at end of file
Index: cake/libs/sanitize.php
===================================================================
--- cake/libs/sanitize.php	(revision 0)
+++ cake/libs/sanitize.php	(revision 0)
@@ -0,0 +1,308 @@
+<?php
+/* SVN FILE: $Id: sanitize.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Washes strings from unwanted noise.
+ *
+ * Helpful methods to make unsafe strings usable.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Data Sanitization.
+ *
+ * Removal of alpahnumeric characters, SQL-safe slash-added strings, HTML-friendly strings,
+ * and all of the above on arrays.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Sanitize {
+/**
+ * Removes any non-alphanumeric characters.
+ *
+ * @param string $string String to sanitize
+ * @return string Sanitized string
+ * @access public
+ * @static
+ */
+	function paranoid($string, $allowed = array()) {
+		$allow = null;
+		if (!empty($allowed)) {
+			foreach ($allowed as $value) {
+				$allow .= "\\$value";
+			}
+		}
+
+		if (is_array($string)) {
+			$cleaned = array();
+			foreach ($string as $key => $clean) {
+				$cleaned[$key] = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $clean);
+			}
+		} else {
+			$cleaned = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $string);
+		}
+		return $cleaned;
+	}
+/**
+ * Makes a string SQL-safe.
+ *
+ * @param string $string String to sanitize
+ * @param string $connection Database connection being used
+ * @return string SQL safe string
+ * @access public
+ * @static
+ */
+	function escape($string, $connection = 'default') {
+		$db =& ConnectionManager::getDataSource($connection);
+		if (is_numeric($string) || $string === null || is_bool($string)) {
+			return $string;
+		}
+		$string = substr($db->value($string), 1);
+		$string = substr($string, 0, -1);
+		return $string;
+	}
+/**
+ * Returns given string safe for display as HTML. Renders entities.
+ *
+ * @param string $string String from where to strip tags
+ * @param boolean $remove If true, the string is stripped of all HTML tags
+ * @return string Sanitized string
+ * @access public
+ * @static
+ */
+	function html($string, $remove = false) {
+		if ($remove) {
+			$string = strip_tags($string);
+		} else {
+			$patterns = array("/\&/", "/%/", "/</", "/>/", '/"/', "/'/", "/\(/", "/\)/", "/\+/", "/-/");
+			$replacements = array("&amp;", "&#37;", "&lt;", "&gt;", "&quot;", "&#39;", "&#40;", "&#41;", "&#43;", "&#45;");
+			$string = preg_replace($patterns, $replacements, $string);
+		}
+		return $string;
+	}
+/**
+ * Strips extra whitespace from output
+ *
+ * @param string $str String to sanitize
+ * @return string whitespace sanitized string
+ * @access public
+ * @static
+ */
+	function stripWhitespace($str) {
+		$r = preg_replace('/[\n\r\t]+/', '', $str);
+		return preg_replace('/\s{2,}/', ' ', $r);
+	}
+/**
+ * Strips image tags from output
+ *
+ * @param string $str String to sanitize
+ * @return string Sting with images stripped.
+ * @access public
+ * @static
+ */
+	function stripImages($str) {
+		$str = preg_replace('/(<a[^>]*>)(<img[^>]+alt=")([^"]*)("[^>]*>)(<\/a>)/i', '$1$3$5<br />', $str);
+		$str = preg_replace('/(<img[^>]+alt=")([^"]*)("[^>]*>)/i', '$2<br />', $str);
+		$str = preg_replace('/<img[^>]*>/i', '', $str);
+		return $str;
+	}
+/**
+ * Strips scripts and stylesheets from output
+ *
+ * @param string $str String to sanitize
+ * @return string String with <script>, <style>, <link> elements removed.
+ * @access public
+ * @static
+ */
+	function stripScripts($str) {
+		return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/i', '', $str);
+	}
+/**
+ * Strips extra whitespace, images, scripts and stylesheets from output
+ *
+ * @param string $str String to sanitize
+ * @return string sanitized string
+ * @access public
+ */
+	function stripAll($str) {
+		$str = Sanitize::stripWhitespace($str);
+		$str = Sanitize::stripImages($str);
+		$str = Sanitize::stripScripts($str);
+		return $str;
+	}
+/**
+ * Strips the specified tags from output. First parameter is string from
+ * where to remove tags. All subsequent parameters are tags.
+ *
+ * @param string $str String to sanitize
+ * @param string $tag Tag to remove (add more parameters as needed)
+ * @return string sanitized String
+ * @access public
+ * @static
+ */
+	function stripTags() {
+		$params = params(func_get_args());
+		$str = $params[0];
+
+		for ($i = 1; $i < count($params); $i++) {
+			$str = preg_replace('/<' . $params[$i] . '\b[^>]*>/i', '', $str);
+			$str = preg_replace('/<\/' . $params[$i] . '[^>]*>/i', '', $str);
+		}
+		return $str;
+	}
+/**
+ * Sanitizes given array or value for safe input. Use the options to specify
+ * the connection to use, and what filters should be applied (with a boolean
+ * value). Valid filters: odd_spaces, encode, dollar, carriage, unicode,
+ * escape, backslash.
+ *
+ * @param mixed $data Data to sanitize
+ * @param mixed $options If string, DB connection being used, otherwise set of options
+ * @return mixed Sanitized data
+ * @access public
+ * @static
+ */
+	function clean($data, $options = array()) {
+		if (empty($data)) {
+			return $data;
+		}
+
+		if (is_string($options)) {
+			$options = array('connection' => $options);
+		} else if (!is_array($options)) {
+			$options = array();
+		}
+
+		$options = array_merge(array(
+			'connection' => 'default',
+			'odd_spaces' => true,
+			'encode' => true,
+			'dollar' => true,
+			'carriage' => true,
+			'unicode' => true,
+			'escape' => true,
+			'backslash' => true
+		), $options);
+
+		if (is_array($data)) {
+			foreach ($data as $key => $val) {
+				$data[$key] = Sanitize::clean($val, $options);
+			}
+			return $data;
+		} else {
+			if ($options['odd_spaces']) {
+				$data = str_replace(chr(0xCA), '', str_replace(' ', ' ', $data));
+			}
+			if ($options['encode']) {
+				$data = Sanitize::html($data);
+			}
+			if ($options['dollar']) {
+				$data = str_replace("\\\$", "$", $data);
+			}
+			if ($options['carriage']) {
+				$data = str_replace("\r", "", $data);
+			}
+
+			$data = str_replace("'", "'", str_replace("!", "!", $data));
+
+			if ($options['unicode']) {
+				$data = preg_replace("/&amp;#([0-9]+);/s", "&#\\1;", $data);
+			}
+			if ($options['escape']) {
+				$data = Sanitize::escape($data, $options['connection']);
+			}
+			if ($options['backslash']) {
+				$data = preg_replace("/\\\(?!&amp;#|\?#)/", "\\", $data);
+			}
+			return $data;
+		}
+	}
+/**
+ * Formats column data from definition in DBO's $columns array
+ *
+ * @param Model $model The model containing the data to be formatted
+ * @access public
+ * @static
+ */
+	function formatColumns(&$model) {
+		foreach ($model->data as $name => $values) {
+			if ($name == $model->alias) {
+				$curModel =& $model;
+			} elseif (isset($model->{$name}) && is_object($model->{$name}) && is_subclass_of($model->{$name}, 'Model')) {
+				$curModel =& $model->{$name};
+			} else {
+				$curModel = null;
+			}
+
+			if ($curModel != null) {
+				foreach ($values as $column => $data) {
+					$colType = $curModel->getColumnType($column);
+
+					if ($colType != null) {
+						$db =& ConnectionManager::getDataSource($curModel->useDbConfig);
+						$colData = $db->columns[$colType];
+
+						if (isset($colData['limit']) && strlen(strval($data)) > $colData['limit']) {
+							$data = substr(strval($data), 0, $colData['limit']);
+						}
+
+						if (isset($colData['formatter']) || isset($colData['format'])) {
+
+							switch (strtolower($colData['formatter'])) {
+								case 'date':
+									$data = date($colData['format'], strtotime($data));
+								break;
+								case 'sprintf':
+									$data = sprintf($colData['format'], $data);
+								break;
+								case 'intval':
+									$data = intval($data);
+								break;
+								case 'floatval':
+									$data = floatval($data);
+								break;
+							}
+						}
+						$model->data[$name][$column]=$data;
+						/*
+						switch ($colType) {
+							case 'integer':
+							case 'int':
+								return  $data;
+							break;
+							case 'string':
+							case 'text':
+							case 'binary':
+							case 'date':
+							case 'time':
+							case 'datetime':
+							case 'timestamp':
+							case 'date':
+								return "'" . $data . "'";
+							break;
+						}
+						*/
+					}
+				}
+			}
+		}
+	}
+}
+?>
Index: cake/libs/http_socket.php
===================================================================
--- cake/libs/http_socket.php	(revision 0)
+++ cake/libs/http_socket.php	(revision 0)
@@ -0,0 +1,978 @@
+<?php
+/* SVN FILE: $Id: http_socket.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * HTTP Socket connection class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+App::import('Core', array('Socket', 'Set', 'Router'));
+/**
+ * Cake network socket connection class.
+ *
+ * Core base class for HTTP network communication.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class HttpSocket extends CakeSocket {
+/**
+ * Object description
+ *
+ * @var string
+ * @access public
+ */
+	var $description = 'HTTP-based DataSource Interface';
+/**
+ * When one activates the $quirksMode by setting it to true, all checks meant to enforce RFC 2616 (HTTP/1.1 specs)
+ * will be disabled and additional measures to deal with non-standard responses will be enabled.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $quirksMode = false;
+/**
+ * The default values to use for a request
+ *
+ * @var array
+ * @access public
+ */
+	var $request = array(
+		'method' => 'GET',
+		'uri' => array(
+			'scheme' => 'http',
+			'host' => null,
+			'port' => 80,
+			'user' => null,
+			'pass' => null,
+			'path' => null,
+			'query' => null,
+			'fragment' => null
+		),
+		'auth' => array(
+			'method' => 'basic',
+			'user' => null,
+			'pass' => null
+		),
+		'version' => '1.1',
+		'body' => '',
+		'line' => null,
+		'header' => array(
+			'Connection' => 'close',
+			'User-Agent' => 'CakePHP'
+		),
+		'raw' => null,
+		'cookies' => array()
+	);
+/**
+* The default structure for storing the response
+*
+* @var array
+* @access public
+*/
+	var $response = array(
+		'raw' => array(
+			'status-line' => null,
+			'header' => null,
+			'body' => null,
+			'response' => null
+		),
+		'status' => array(
+			'http-version' => null,
+			'code' => null,
+			'reason-phrase' => null
+		),
+		'header' => array(),
+		'body' => '',
+		'cookies' => array()
+	);
+/**
+ * Default configuration settings for the HttpSocket
+ *
+ * @var array
+ * @access public
+ */
+	var $config = array(
+		'persistent' => false,
+		'host' 		 => 'localhost',
+		'protocol'   => 'tcp',
+		'port' 		 => 80,
+		'timeout' 	 =>	30,
+		'request' => array(
+			'uri' => array(
+				'scheme' => 'http',
+				'host' => 'localhost',
+				'port' => 80
+			),
+			'auth' => array(
+				'method' => 'basic',
+				'user' => null,
+				'pass' => null
+			),
+			'cookies' => array()
+		)
+	);
+/**
+ * String that represents a line break.
+ *
+ * @var string
+ * @access public
+ */
+	var $lineBreak = "\r\n";
+
+/**
+ * Build an HTTP Socket using the specified configuration.
+ *
+ * @param array $config	Configuration
+ */
+	function __construct($config = array()) {
+		if (is_string($config)) {
+			$this->configUri($config);
+		} elseif (is_array($config)) {
+			if (isset($config['request']['uri']) && is_string($config['request']['uri'])) {
+				$this->configUri($config['request']['uri']);
+				unset($config['request']['uri']);
+			}
+			$this->config = Set::merge($this->config, $config);
+		}
+		parent::__construct($this->config);
+	}
+/**
+ * Issue the specified request.
+ *
+ * @param mixed $request Either an URI string, or an array defining host/uri
+ * @return mixed false on error, request body on success
+ * @access public
+ */
+	function request($request = array()) {
+		$this->reset(false);
+
+		if (is_string($request)) {
+			$request = array('uri' => $request);
+		} elseif (!is_array($request)) {
+			return false;
+		}
+
+		if (!isset($request['uri'])) {
+			$request['uri'] = null;
+		}
+		$uri = $this->parseUri($request['uri']);
+
+		if (!isset($uri['host'])) {
+			$host = $this->config['host'];
+		}
+		if (isset($request['host'])) {
+			$host = $request['host'];
+			unset($request['host']);
+		}
+
+		$request['uri'] = $this->url($request['uri']);
+		$request['uri'] = $this->parseUri($request['uri'], true);
+		$this->request = Set::merge($this->request, $this->config['request'], $request);
+
+		$this->configUri($this->request['uri']);
+
+		if (isset($host)) {
+			$this->config['host'] = $host;
+		}
+		$cookies = null;
+
+		if (is_array($this->request['header'])) {
+			$this->request['header'] = $this->parseHeader($this->request['header']);
+			if (!empty($this->request['cookies'])) {
+				$cookies = $this->buildCookies($this->request['cookies']);
+			}
+			$this->request['header'] = array_merge(array('Host' => $this->request['uri']['host']), $this->request['header']);
+		}
+
+		if (isset($this->request['auth']['user']) && isset($this->request['auth']['pass'])) {
+			$this->request['header']['Authorization'] = $this->request['auth']['method'] ." ". base64_encode($this->request['auth']['user'] .":".$this->request['auth']['pass']);
+		}
+		if (isset($this->request['uri']['user']) && isset($this->request['uri']['pass'])) {
+			$this->request['header']['Authorization'] = $this->request['auth']['method'] ." ". base64_encode($this->request['uri']['user'] .":".$this->request['uri']['pass']);
+		}
+
+		if (is_array($this->request['body'])) {
+			$this->request['body'] = $this->httpSerialize($this->request['body']);
+		}
+
+		if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) {
+			$this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded';
+		}
+
+		if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) {
+			$this->request['header']['Content-Length'] = strlen($this->request['body']);
+		}
+
+		$connectionType = @$this->request['header']['Connection'];
+		$this->request['header'] = $this->buildHeader($this->request['header']).$cookies;
+
+		if (empty($this->request['line'])) {
+			$this->request['line'] = $this->buildRequestLine($this->request);
+		}
+
+		if ($this->quirksMode === false && $this->request['line'] === false) {
+			return $this->response = false;
+		}
+
+		if ($this->request['line'] !== false) {
+			$this->request['raw'] = $this->request['line'];
+		}
+
+		if ($this->request['header'] !== false) {
+			$this->request['raw'] .= $this->request['header'];
+		}
+
+		$this->request['raw'] .= "\r\n";
+		$this->request['raw'] .= $this->request['body'];
+		$this->write($this->request['raw']);
+
+		$response = null;
+		while ($data = $this->read()) {
+			$response .= $data;
+		}
+
+		if ($connectionType == 'close') {
+			$this->disconnect();
+		}
+
+		$this->response = $this->parseResponse($response);
+		if (!empty($this->response['cookies'])) {
+			$this->config['request']['cookies'] = array_merge($this->config['request']['cookies'], $this->response['cookies']);
+		}
+
+		return $this->response['body'];
+	}
+/**
+ * Issues a GET request to the specified URI, query, and request.
+ *
+ * @param mixed $uri URI to request (see {@link parseUri()})
+ * @param array $query Query to append to URI
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request
+ * @access public
+ */
+	function get($uri = null, $query = array(), $request = array()) {
+		if (!empty($query)) {
+			$uri =$this->parseUri($uri);
+			if (isset($uri['query'])) {
+				$uri['query'] = array_merge($uri['query'], $query);
+			} else {
+				$uri['query'] = $query;
+			}
+			$uri = $this->buildUri($uri);
+		}
+
+		$request = Set::merge(array('method' => 'GET', 'uri' => $uri), $request);
+		return $this->request($request);
+	}
+
+/**
+ * Issues a POST request to the specified URI, query, and request.
+ *
+ * @param mixed $uri URI to request (see {@link parseUri()})
+ * @param array $query Query to append to URI
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request
+ * @access public
+ */
+	function post($uri = null, $data = array(), $request = array()) {
+		$request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request);
+		return $this->request($request);
+	}
+/**
+ * Issues a PUT request to the specified URI, query, and request.
+ *
+ * @param mixed $uri URI to request (see {@link parseUri()})
+ * @param array $query Query to append to URI
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request
+ * @access public
+ */
+	function put($uri = null, $data = array(), $request = array()) {
+		$request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request);
+		return $this->request($request);
+	}
+/**
+ * Issues a DELETE request to the specified URI, query, and request.
+ *
+ * @param mixed $uri URI to request (see {@link parseUri()})
+ * @param array $query Query to append to URI
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request
+ * @access public
+ */
+	function delete($uri = null, $data = array(), $request = array()) {
+		$request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request);
+		return $this->request($request);
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $url
+ * @param unknown $uriTemplate
+ * @return void
+ * @access public
+ */
+	function url($url = null, $uriTemplate = null) {
+		if (is_null($url)) {
+			$url = '/';
+		}
+		if (is_string($url)) {
+			if ($url{0} == '/') {
+				$url = $this->config['request']['uri']['host'].':'.$this->config['request']['uri']['port'] . $url;
+			}
+			if (!preg_match('/^.+:\/\/|\*|^\//', $url)) {
+				$url = $this->config['request']['uri']['scheme'].'://'.$url;
+			}
+		} elseif (!is_array($url) && !empty($url)) {
+			return false;
+		}
+
+		$base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443)));
+		$url = $this->parseUri($url, $base);
+
+		if (empty($url)) {
+			$url = $this->config['request']['uri'];
+		}
+
+		if (!empty($uriTemplate)) {
+			return $this->buildUri($url, $uriTemplate);
+		}
+		return $this->buildUri($url);
+	}
+/**
+ * Parses the given message and breaks it down in parts.
+ *
+ * @param string $message Message to parse
+ * @return array Parsed message (with indexed elements such as raw, status, header, body)
+ * @access protected
+ */
+	function parseResponse($message) {
+		if (is_array($message)) {
+			return $message;
+		} elseif (!is_string($message)) {
+			return false;
+		}
+
+		static $responseTemplate;
+
+		if (empty($responseTemplate)) {
+			$classVars = get_class_vars(__CLASS__);
+			$responseTemplate = $classVars['response'];
+		}
+
+		$response = $responseTemplate;
+
+		if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) {
+			return false;
+		}
+
+		list($null, $response['raw']['status-line'], $response['raw']['header']) = $match;
+		$response['raw']['response'] = $message;
+		$response['raw']['body'] = substr($message, strlen($match[0]));
+
+		if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $response['raw']['status-line'], $match)) {
+			$response['status']['http-version'] = $match[1];
+			$response['status']['code'] = (int)$match[2];
+			$response['status']['reason-phrase'] = $match[3];
+		}
+
+		$response['header'] = $this->parseHeader($response['raw']['header']);
+		$decoded = $this->decodeBody($response['raw']['body'], @$response['header']['Transfer-Encoding']);
+		$response['body'] = $decoded['body'];
+
+		if (!empty($decoded['header'])) {
+			$response['header'] = $this->parseHeader($this->buildHeader($response['header']).$this->buildHeader($decoded['header']));
+		}
+
+		if (!empty($response['header'])) {
+			$response['cookies'] = $this->parseCookies($response['header']);
+		}
+
+		foreach ($response['raw'] as $field => $val) {
+			if ($val === '') {
+				$response['raw'][$field] = null;
+			}
+		}
+
+		return $response;
+	}
+/**
+ * Generic function to decode a $body with a given $encoding. Returns either an array with the keys
+ * 'body' and 'header' or false on failure.
+ *
+ * @param string $body A string continaing the body to decode
+ * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding
+ * @return mixed Array or false
+ * @access protected
+ */
+	function decodeBody($body, $encoding = 'chunked') {
+		if (!is_string($body)) {
+			return false;
+		}
+		if (empty($encoding)) {
+			return array('body' => $body, 'header' => false);
+		}
+		$decodeMethod = 'decode'.Inflector::camelize(str_replace('-', '_', $encoding)).'Body';
+
+		if (!is_callable(array(&$this, $decodeMethod))) {
+			if (!$this->quirksMode) {
+				trigger_error(sprintf(__('HttpSocket::decodeBody - Unknown encoding: %s. Activate quirks mode to surpress error.', true), h($encoding)), E_USER_WARNING);
+			}
+			return array('body' => $body, 'header' => false);
+		}
+		return $this->{$decodeMethod}($body);
+	}
+/**
+ * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as
+ * a result.
+ *
+ * @param string $body A string continaing the chunked body to decode
+ * @return mixed Array or false
+ * @access protected
+ */
+	function decodeChunkedBody($body) {
+		if (!is_string($body)) {
+			return false;
+		}
+
+		$decodedBody = null;
+		$chunkLength = null;
+
+		while ($chunkLength !== 0) {
+			if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) {
+				if (!$this->quirksMode) {
+					trigger_error(__('HttpSocket::decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.', true), E_USER_WARNING);
+					return false;
+				}
+				break;
+			}
+
+			$chunkSize = 0;
+			$hexLength = 0;
+			$chunkExtensionName = '';
+			$chunkExtensionValue = '';
+			if (isset($match[0])) {
+				$chunkSize = $match[0];
+			}
+			if (isset($match[1])) {
+				$hexLength = $match[1];
+			}
+			if (isset($match[2])) {
+				$chunkExtensionName = $match[2];
+			}
+			if (isset($match[3])) {
+				$chunkExtensionValue = $match[3];
+			}
+
+			$body = substr($body, strlen($chunkSize));
+			$chunkLength = hexdec($hexLength);
+			$chunk = substr($body, 0, $chunkLength);
+			if (!empty($chunkExtensionName)) {
+				/**
+				 * @todo See if there are popular chunk extensions we should implement
+				 */
+			}
+			$decodedBody .= $chunk;
+			if ($chunkLength !== 0) {
+				$body = substr($body, $chunkLength+strlen("\r\n"));
+			}
+		}
+
+		$entityHeader = false;
+		if (!empty($body)) {
+			$entityHeader = $this->parseHeader($body);
+		}
+		return array('body' => $decodedBody, 'header' => $entityHeader);
+	}
+/**
+ * Parses and sets the specified URI into current request configuration.
+ *
+ * @param mixed $uri URI (see {@link parseUri()})
+ * @return array Current configuration settings
+ * @access protected
+ */
+	function configUri($uri = null) {
+		if (empty($uri)) {
+			return false;
+		}
+
+		if (is_array($uri)) {
+			$uri = $this->parseUri($uri);
+		} else {
+			$uri = $this->parseUri($uri, true);
+		}
+
+		if (!isset($uri['host'])) {
+			return false;
+		}
+
+		$config = array(
+			'request' => array(
+				'uri' => array_intersect_key($uri, $this->config['request']['uri']),
+				'auth' => array_intersect_key($uri, $this->config['request']['auth'])
+			)
+		);
+		$this->config = Set::merge($this->config, $config);
+		$this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config));
+		return $this->config;
+	}
+/**
+ * Takes a $uri array and turns it into a fully qualified URL string
+ *
+ * @param array $uri A $uri array, or uses $this->config if left empty
+ * @param string $uriTemplate The Uri template/format to use
+ * @return string A fully qualified URL formated according to $uriTemplate
+ * @access protected
+ */
+	function buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') {
+		if (is_string($uri)) {
+			$uri = array('host' => $uri);
+		}
+		$uri = $this->parseUri($uri, true);
+
+		if (!is_array($uri) || empty($uri)) {
+			return false;
+		}
+
+		$uri['path'] = preg_replace('/^\//', null, $uri['path']);
+		$uri['query'] = $this->httpSerialize($uri['query']);
+		$stripIfEmpty = array(
+			'query' => '?%query',
+			'fragment' => '#%fragment',
+			'user' => '%user:%pass@'
+		);
+
+		foreach ($stripIfEmpty as $key => $strip) {
+			if (empty($uri[$key])) {
+				$uriTemplate = str_replace($strip, null, $uriTemplate);
+			}
+		}
+
+		$defaultPorts = array('http' => 80, 'https' => 443);
+		if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) {
+			$uriTemplate = str_replace(':%port', null, $uriTemplate);
+		}
+
+		foreach ($uri as $property => $value) {
+			$uriTemplate = str_replace('%'.$property, $value, $uriTemplate);
+		}
+
+		if ($uriTemplate === '/*') {
+			$uriTemplate = '*';
+		}
+		return $uriTemplate;
+	}
+/**
+ * Parses the given URI and breaks it down into pieces as an indexed array with elements
+ * such as 'scheme', 'port', 'query'.
+ *
+ * @param string $uri URI to parse
+ * @param mixed $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc.
+ * @return array Parsed URI
+ * @access protected
+ */
+	function parseUri($uri = null, $base = array()) {
+		$uriBase = array(
+			'scheme' => array('http', 'https'),
+			'host' => null,
+			'port' => array(80, 443),
+			'user' => null,
+			'pass' => null,
+			'path' => '/',
+			'query' => null,
+			'fragment' => null
+		);
+
+		if (is_string($uri)) {
+			$uri = parse_url($uri);
+		}
+		if (!is_array($uri) || empty($uri)) {
+			return false;
+		}
+		if ($base === true) {
+			$base = $uriBase;
+		}
+
+		if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) {
+			if (isset($uri['scheme']) && !isset($uri['port'])) {
+				$base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])];
+			} elseif (isset($uri['port']) && !isset($uri['scheme'])) {
+				$base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])];
+			}
+		}
+
+		if (is_array($base) && !empty($base)) {
+			$uri = array_merge($base, $uri);
+		}
+
+		if (isset($uri['scheme']) && is_array($uri['scheme'])) {
+			$uri['scheme'] = array_shift($uri['scheme']);
+		}
+		if (isset($uri['port']) && is_array($uri['port'])) {
+			$uri['port'] = array_shift($uri['port']);
+		}
+
+		if (array_key_exists('query', $uri)) {
+			$uri['query'] = $this->parseQuery($uri['query']);
+		}
+
+		if (!array_intersect_key($uriBase, $uri)) {
+			return false;
+		}
+		return $uri;
+	}
+/**
+ * This function can be thought of as a reverse to PHP5's http_build_query(). It takes a given query string and turns it into an array and
+ * supports nesting by using the php bracket syntax. So this menas you can parse queries like:
+ *
+ * - ?key[subKey]=value
+ * - ?key[]=value1&key[]=value2
+ *
+ * A leading '?' mark in $query is optional and does not effect the outcome of this function. For the complete capabilities of this implementation
+ * take a look at HttpSocketTest::testParseQuery()
+ *
+ * @param mixed $query A query string to parse into an array or an array to return directly "as is"
+ * @return array The $query parsed into a possibly multi-level array. If an empty $query is given, an empty array is returned.
+ * @access protected
+ */
+	function parseQuery($query) {
+		if (is_array($query)) {
+			return $query;
+		}
+		$parsedQuery = array();
+
+		if (is_string($query) && !empty($query)) {
+			$query = preg_replace('/^\?/', '', $query);
+			$items = explode('&', $query);
+
+			foreach ($items as $item) {
+				if (strpos($item, '=') !== false) {
+					list($key, $value) = explode('=', $item);
+				} else {
+					$key = $item;
+					$value = null;
+				}
+
+				$key = urldecode($key);
+				$value = urldecode($value);
+
+				if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) {
+					$subKeys = $matches[1];
+					$rootKey = substr($key, 0, strpos($key, '['));
+					if (!empty($rootKey)) {
+						array_unshift($subKeys, $rootKey);
+					}
+					$queryNode =& $parsedQuery;
+
+					foreach ($subKeys as $subKey) {
+						if (!is_array($queryNode)) {
+							$queryNode = array();
+						}
+
+						if ($subKey === '') {
+							$queryNode[] = array();
+							end($queryNode);
+							$subKey = key($queryNode);
+						}
+						$queryNode =& $queryNode[$subKey];
+					}
+					$queryNode = $value;
+				} else {
+					$parsedQuery[$key] = $value;
+				}
+			}
+		}
+		return $parsedQuery;
+	}
+/**
+ * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs.
+ *
+ * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
+ * @param string $versionToken The version token to use, defaults to HTTP/1.1
+ * @return string Request line
+ * @access protected
+ */
+	function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') {
+		$asteriskMethods = array('OPTIONS');
+
+		if (is_string($request)) {
+			$isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match);
+			if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) {
+				trigger_error(__('HttpSocket::buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.', true), E_USER_WARNING);
+				return false;
+			}
+			return $request;
+		} elseif (!is_array($request)) {
+			return false;
+		} elseif (!array_key_exists('uri', $request)) {
+			return false;
+		}
+
+		$request['uri']	= $this->parseUri($request['uri']);
+		$request = array_merge(array('method' => 'GET'), $request);
+		$request['uri'] = $this->buildUri($request['uri'], '/%path?%query');
+
+		if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) {
+			trigger_error(sprintf(__('HttpSocket::buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', true), join(',', $asteriskMethods)), E_USER_WARNING);
+			return false;
+		}
+		return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak;
+	}
+/**
+ * Serializes an array for transport.
+ *
+ * @param array $data Data to serialize
+ * @return string Serialized variable
+ * @access protected
+ */
+	function httpSerialize($data = array()) {
+		if (is_string($data)) {
+			return $data;
+		}
+		if (empty($data) || !is_array($data)) {
+			return false;
+		}
+		return substr(Router::queryString($data), 1);
+	}
+/**
+ * Builds the header.
+ *
+ * @param array $header Header to build
+ * @return string Header built from array
+ * @access protected
+ */
+	function buildHeader($header, $mode = 'standard') {
+		if (is_string($header)) {
+			return $header;
+		} elseif (!is_array($header)) {
+			return false;
+		}
+
+		$returnHeader = '';
+		foreach ($header as $field => $contents) {
+			if (is_array($contents) && $mode == 'standard') {
+				$contents = join(',', $contents);
+			}
+			foreach ((array)$contents as $content) {
+				$contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content);
+				$field = $this->escapeToken($field);
+
+				$returnHeader .= $field.': '.$contents.$this->lineBreak;
+			}
+		}
+		return $returnHeader;
+	}
+
+/**
+ * Parses an array based header.
+ *
+ * @param array $header Header as an indexed array (field => value)
+ * @return array Parsed header
+ * @access protected
+ */
+	function parseHeader($header) {
+		if (is_array($header)) {
+			foreach ($header as $field => $value) {
+				unset($header[$field]);
+				$field = strtolower($field);
+				preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
+
+				foreach ($offsets[0] as $offset) {
+					$field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
+				}
+				$header[$field] = $value;
+			}
+			return $header;
+		} elseif (!is_string($header)) {
+			return false;
+		}
+
+		preg_match_all("/(.+):(.+)(?:(?<![\t ])".$this->lineBreak."|\$)/Uis", $header, $matches, PREG_SET_ORDER);
+
+		$header = array();
+		foreach ($matches as $match) {
+			list(, $field, $value) = $match;
+
+			$value = trim($value);
+			$value = preg_replace("/[\t ]\r\n/", "\r\n", $value);
+
+			$field = $this->unescapeToken($field);
+
+			$field = strtolower($field);
+			preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
+			foreach ($offsets[0] as $offset) {
+				$field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
+			}
+
+			if (!isset($header[$field])) {
+				$header[$field] = $value;
+			} else {
+				$header[$field] = array_merge((array)$header[$field], (array)$value);
+			}
+		}
+		return $header;
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $header
+ * @return void
+ * @access public
+ * @todo Make this 100% RFC 2965 confirm
+ */
+	function parseCookies($header) {
+		if (!isset($header['Set-Cookie'])) {
+			return false;
+		}
+
+		$cookies = array();
+		foreach ((array)$header['Set-Cookie'] as $cookie) {
+			$parts = preg_split('/(?<![^;]");[ \t]*/', $cookie);
+			list($name, $value) = explode('=', array_shift($parts));
+			$cookies[$name] = compact('value');
+			foreach ($parts as $part) {
+				if (strpos($part, '=') !== false) {
+					list($key, $value) = explode('=', $part);
+				} else {
+					$key = $part;
+					$value = true;
+				}
+
+				$key = strtolower($key);
+				if (!isset($cookies[$name][$key])) {
+					$cookies[$name][$key] = $value;
+				}
+			}
+		}
+		return $cookies;
+	}
+/**
+ * undocumented function
+ *
+ * @param unknown $cookies
+ * @return void
+ * @access public
+ * @todo Refactor token escape mechanism to be configurable
+ */
+	function buildCookies($cookies) {
+		$header = array();
+		foreach ($cookies as $name => $cookie) {
+			$header[] = $name.'='.$this->escapeToken($cookie['value'], array(';'));
+		}
+		$header = $this->buildHeader(array('Cookie' => $header), 'pragmatic');
+		return $header;
+	}
+/**
+ * undocumented function
+ *
+ * @return void
+ * @access public
+ */
+	function saveCookies() {
+
+	}
+/**
+ * undocumented function
+ *
+ * @return void
+ * @access public
+ */
+	function loadCookies() {
+
+	}
+/**
+ * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs)
+ *
+ * @param string $token Token to unescape
+ * @return string Unescaped token
+ * @access protected
+ * @todo Test $chars parameter
+ */
+	function unescapeToken($token, $chars = null) {
+		$regex = '/"(['.join('', $this->__tokenEscapeChars(true, $chars)).'])"/';
+		$token = preg_replace($regex, '\\1', $token);
+		return $token;
+	}
+/**
+ * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs)
+ *
+ * @param string $token Token to escape
+ * @return string Escaped token
+ * @access protected
+ * @todo Test $chars parameter
+ */
+	function escapeToken($token, $chars = null) {
+		$regex = '/(['.join('', $this->__tokenEscapeChars(true, $chars)).'])/';
+		$token = preg_replace($regex, '"\\1"', $token);
+		return $token;
+	}
+/**
+ * Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
+ *
+ * @param boolean $hex true to get them as HEX values, false otherwise
+ * @return array Escape chars
+ * @access private
+ * @todo Test $chars parameter
+ */
+	function __tokenEscapeChars($hex = true, $chars = null) {
+		if (!empty($chars)) {
+			$escape = $chars;
+		} else {
+			$escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
+			for ($i = 0; $i <= 31; $i++) {
+				$escape[] = chr($i);
+			}
+			$escape[] = chr(127);
+		}
+
+		if ($hex == false) {
+			return $escape;
+		}
+		$regexChars = '';
+		foreach ($escape as $key => $char) {
+			$escape[$key] = '\\x'.str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
+		}
+		return $escape;
+	}
+/**
+ * Resets the state of this HttpSocket instance to it's initial state (before Object::__construct got executed) or does
+ * the same thing partially for the request and the response property only.
+ *
+ * @param boolean $full If set to false only HttpSocket::response and HttpSocket::request are reseted
+ * @return boolean True on success
+ * @access public
+ */
+	function reset($full = true) {
+		static $initalState = array();
+		if (empty($initalState)) {
+			$initalState = get_class_vars(__CLASS__);
+		}
+
+		if ($full == false) {
+			$this->request = $initalState['request'];
+			$this->response = $initalState['response'];
+			return true;
+		}
+		parent::reset($initalState);
+		return true;
+	}
+}
+?>
\ No newline at end of file
Index: cake/basics.php
===================================================================
--- cake/basics.php	(revision 0)
+++ cake/basics.php	(revision 0)
@@ -0,0 +1,945 @@
+<?php
+/* SVN FILE: $Id: basics.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Basic Cake functionality.
+ *
+ * Core functions for including other source files, loading models and so forth.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Basic defines for timing functions.
+ */
+	define('SECOND', 1);
+	define('MINUTE', 60 * SECOND);
+	define('HOUR', 60 * MINUTE);
+	define('DAY', 24 * HOUR);
+	define('WEEK', 7 * DAY);
+	define('MONTH', 30 * DAY);
+	define('YEAR', 365 * DAY);
+/**
+ * Patch for PHP < 5.0
+ */
+if (!function_exists('clone')) {
+	if (version_compare(PHP_VERSION, '5.0') < 0) {
+		eval ('
+		function clone($object)
+		{
+			return $object;
+		}');
+	}
+}
+/**
+ * Loads configuration files. Receives a set of configuration files
+ * to load.
+ * Example:
+ * <code>
+ * config('config1', 'config2');
+ * </code>
+ *
+ * @return boolean Success
+ */
+	function config() {
+		$args = func_get_args();
+		foreach ($args as $arg) {
+			if ($arg === 'database' && file_exists(CONFIGS . 'database.php')) {
+				include_once(CONFIGS . $arg . '.php');
+			} elseif (file_exists(CONFIGS . $arg . '.php')) {
+				include_once(CONFIGS . $arg . '.php');
+
+				if (count($args) == 1) {
+					return true;
+				}
+			} else {
+				if (count($args) == 1) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+/**
+ * Loads component/components from LIBS. Takes optional number of parameters.
+ *
+ * Example:
+ * <code>
+ * uses('flay', 'time');
+ * </code>
+ *
+ * @param string $name Filename without the .php part
+ */
+	function uses() {
+		$args = func_get_args();
+		foreach ($args as $file) {
+			require_once(LIBS . strtolower($file) . '.php');
+		}
+	}
+/**
+ * Prints out debug information about given variable.
+ *
+ * Only runs if debug level is greater than zero.
+ *
+ * @param boolean $var Variable to show debug information for.
+ * @param boolean $showHtml If set to true, the method prints the debug data in a screen-friendly way.
+ * @param boolean $showFrom If set to true, the method prints from where the function was called.
+ * @link http://book.cakephp.org/view/458/Basic-Debugging
+ */
+	function debug($var = false, $showHtml = false, $showFrom = true) {
+		if (Configure::read() > 0) {
+			if ($showFrom) {
+				$calledFrom = debug_backtrace();
+				echo '<strong>' . substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1) . '</strong>';
+				echo ' (line <strong>' . $calledFrom[0]['line'] . '</strong>)';
+			}
+			echo "\n<pre class=\"cake-debug\">\n";
+
+			$var = print_r($var, true);
+			if ($showHtml) {
+				$var = str_replace('<', '&lt;', str_replace('>', '&gt;', $var));
+			}
+			echo $var . "\n</pre>\n";
+		}
+	}
+if (!function_exists('getMicrotime')) {
+/**
+ * Returns microtime for execution time checking
+ *
+ * @return float Microtime
+ */
+	function getMicrotime() {
+		list($usec, $sec) = explode(' ', microtime());
+		return ((float)$usec + (float)$sec);
+	}
+}
+if (!function_exists('sortByKey')) {
+/**
+ * Sorts given $array by key $sortby.
+ *
+ * @param array $array Array to sort
+ * @param string $sortby Sort by this key
+ * @param string $order  Sort order asc/desc (ascending or descending).
+ * @param integer $type Type of sorting to perform
+ * @return mixed Sorted array
+ */
+	function sortByKey(&$array, $sortby, $order = 'asc', $type = SORT_NUMERIC) {
+		if (!is_array($array)) {
+			return null;
+		}
+
+		foreach ($array as $key => $val) {
+			$sa[$key] = $val[$sortby];
+		}
+
+		if ($order == 'asc') {
+			asort($sa, $type);
+		} else {
+			arsort($sa, $type);
+		}
+
+		foreach ($sa as $key => $val) {
+			$out[] = $array[$key];
+		}
+		return $out;
+	}
+}
+if (!function_exists('array_combine')) {
+/**
+ * Combines given identical arrays by using the first array's values as keys,
+ * and the second one's values as values. (Implemented for backwards compatibility with PHP4)
+ *
+ * @param array $a1 Array to use for keys
+ * @param array $a2 Array to use for values
+ * @return mixed Outputs either combined array or false.
+ */
+	function array_combine($a1, $a2) {
+		$a1 = array_values($a1);
+		$a2 = array_values($a2);
+		$c1 = count($a1);
+		$c2 = count($a2);
+
+		if ($c1 != $c2) {
+			return false;
+		}
+		if ($c1 <= 0) {
+			return false;
+		}
+		$output = array();
+
+		for ($i = 0; $i < $c1; $i++) {
+			$output[$a1[$i]] = $a2[$i];
+		}
+		return $output;
+	}
+}
+/**
+ * Convenience method for htmlspecialchars.
+ *
+ * @param string $text Text to wrap through htmlspecialchars
+ * @param string $charset Character set to use when escaping.  Defaults to config value in 'App.encoding' or 'UTF-8'
+ * @return string Wrapped text
+ * @link http://book.cakephp.org/view/703/h
+ */
+	function h($text, $charset = null) {
+		if (is_array($text)) {
+			return array_map('h', $text);
+		}
+		if (empty($charset)) {
+			$charset = Configure::read('App.encoding');
+		}
+		if (empty($charset)) {
+			$charset = 'UTF-8';
+		}
+		return htmlspecialchars($text, ENT_QUOTES, $charset);
+	}
+/**
+ * Returns an array of all the given parameters.
+ *
+ * Example:
+ * <code>
+ * a('a', 'b')
+ * </code>
+ *
+ * Would return:
+ * <code>
+ * array('a', 'b')
+ * </code>
+ *
+ * @return array Array of given parameters
+ * @link http://book.cakephp.org/view/694/a
+ */
+	function a() {
+		$args = func_get_args();
+		return $args;
+	}
+/**
+ * Constructs associative array from pairs of arguments.
+ *
+ * Example:
+ * <code>
+ * aa('a','b')
+ * </code>
+ *
+ * Would return:
+ * <code>
+ * array('a'=>'b')
+ * </code>
+ *
+ * @return array Associative array
+ * @link http://book.cakephp.org/view/695/aa
+ */
+	function aa() {
+		$args = func_get_args();
+		$argc = count($args);
+		for ($i = 0; $i < $argc; $i++) {
+			if ($i + 1 < $argc) {
+				$a[$args[$i]] = $args[$i + 1];
+			} else {
+				$a[$args[$i]] = null;
+			}
+			$i++;
+		}
+		return $a;
+	}
+/**
+ * Convenience method for echo().
+ *
+ * @param string $text String to echo
+ * @link http://book.cakephp.org/view/700/e
+ */
+	function e($text) {
+		echo $text;
+	}
+/**
+ * Convenience method for strtolower().
+ *
+ * @param string $str String to lowercase
+ * @return string Lowercased string
+ * @link http://book.cakephp.org/view/705/low
+ */
+	function low($str) {
+		return strtolower($str);
+	}
+/**
+ * Convenience method for strtoupper().
+ *
+ * @param string $str String to uppercase
+ * @return string Uppercased string
+ * @link http://book.cakephp.org/view/710/up
+ */
+	function up($str) {
+		return strtoupper($str);
+	}
+/**
+ * Convenience method for str_replace().
+ *
+ * @param string $search String to be replaced
+ * @param string $replace String to insert
+ * @param string $subject String to search
+ * @return string Replaced string
+ * @link http://book.cakephp.org/view/708/r
+ */
+	function r($search, $replace, $subject) {
+		return str_replace($search, $replace, $subject);
+	}
+/**
+ * Print_r convenience function, which prints out <PRE> tags around
+ * the output of given array. Similar to debug().
+ *
+ * @see	debug()
+ * @param array $var Variable to print out
+ * @param boolean $showFrom If set to true, the method prints from where the function was called
+ * @link http://book.cakephp.org/view/707/pr
+ */
+	function pr($var) {
+		if (Configure::read() > 0) {
+			echo '<pre>';
+			print_r($var);
+			echo '</pre>';
+		}
+	}
+/**
+ * Display parameters.
+ *
+ * @param mixed $p Parameter as string or array
+ * @return string
+ */
+	function params($p) {
+		if (!is_array($p) || count($p) == 0) {
+			return null;
+		}
+		if (is_array($p[0]) && count($p) == 1) {
+			return $p[0];
+		}
+		return $p;
+	}
+/**
+ * Merge a group of arrays
+ *
+ * @param array First array
+ * @param array Second array
+ * @param array Third array
+ * @param array Etc...
+ * @return array All array parameters merged into one
+ * @link http://book.cakephp.org/view/696/am
+ */
+	function am() {
+		$r = array();
+		$args = func_get_args();
+		foreach ($args as $a) {
+			if (!is_array($a)) {
+				$a = array($a);
+			}
+			$r = array_merge($r, $a);
+		}
+		return $r;
+	}
+/**
+ * Gets an environment variable from available sources, and provides emulation
+ * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
+ * IIS, or SCRIPT_NAME in CGI mode).  Also exposes some additional custom
+ * environment information.
+ *
+ * @param  string $key Environment variable name.
+ * @return string Environment variable setting.
+ * @link http://book.cakephp.org/view/701/env
+ */
+	function env($key) {
+		if ($key == 'HTTPS') {
+			if (isset($_SERVER) && !empty($_SERVER)) {
+				return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on');
+			}
+			return (strpos(env('SCRIPT_URI'), 'https://') === 0);
+		}
+
+		if ($key == 'SCRIPT_NAME') {
+			if (env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
+				$key = 'SCRIPT_URL';
+			}
+		}
+
+		$val = null;
+		if (isset($_SERVER[$key])) {
+			$val = $_SERVER[$key];
+		} elseif (isset($_ENV[$key])) {
+			$val = $_ENV[$key];
+		} elseif (getenv($key) !== false) {
+			$val = getenv($key);
+		}
+
+		if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) {
+			$addr = env('HTTP_PC_REMOTE_ADDR');
+			if ($addr !== null) {
+				$val = $addr;
+			}
+		}
+
+		if ($val !== null) {
+			return $val;
+		}
+
+		switch ($key) {
+			case 'SCRIPT_FILENAME':
+				if (defined('SERVER_IIS') && SERVER_IIS === true) {
+					return str_replace('\\\\', '\\', env('PATH_TRANSLATED'));
+				}
+			break;
+			case 'DOCUMENT_ROOT':
+				$name = env('SCRIPT_NAME');
+				$filename = env('SCRIPT_FILENAME');
+				$offset = 0;
+				if (!strpos($name, '.php')) {
+					$offset = 4;
+				}
+				return substr($filename, 0, strlen($filename) - (strlen($name) + $offset));
+			break;
+			case 'PHP_SELF':
+				return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME'));
+			break;
+			case 'CGI_MODE':
+				return (PHP_SAPI === 'cgi');
+			break;
+			case 'HTTP_BASE':
+				$host = env('HTTP_HOST');
+				if (substr_count($host, '.') !== 1) {
+					return preg_replace('/^([^.])*/i', null, env('HTTP_HOST'));
+				}
+			return '.' . $host;
+			break;
+		}
+		return null;
+	}
+if (!function_exists('file_put_contents')) {
+/**
+ * Writes data into file.
+ *
+ * If file exists, it will be overwritten. If data is an array, it will be join()ed with an empty string.
+ *
+ * @param string $fileName File name.
+ * @param mixed  $data String or array.
+ * @return boolean Success
+ */
+	function file_put_contents($fileName, $data) {
+		if (is_array($data)) {
+			$data = join('', $data);
+		}
+		$res = @fopen($fileName, 'w+b');
+
+		if ($res) {
+			$write = @fwrite($res, $data);
+			if ($write === false) {
+				return false;
+			} else {
+				@fclose($res);
+				return $write;
+			}
+		}
+		return false;
+	}
+}
+/**
+ * Reads/writes temporary data to cache files or session.
+ *
+ * @param  string $path	File path within /tmp to save the file.
+ * @param  mixed  $data	The data to save to the temporary file.
+ * @param  mixed  $expires A valid strtotime string when the data expires.
+ * @param  string $target  The target of the cached data; either 'cache' or 'public'.
+ * @return mixed  The contents of the temporary file.
+ * @deprecated Please use Cache::write() instead
+ */
+	function cache($path, $data = null, $expires = '+1 day', $target = 'cache') {
+		if (Configure::read('Cache.disable')) {
+			return null;
+		}
+		$now = time();
+
+		if (!is_numeric($expires)) {
+			$expires = strtotime($expires, $now);
+		}
+
+		switch (low($target)) {
+			case 'cache':
+				$filename = CACHE . $path;
+			break;
+			case 'public':
+				$filename = WWW_ROOT . $path;
+			break;
+			case 'tmp':
+				$filename = TMP . $path;
+			break;
+		}
+		$timediff = $expires - $now;
+		$filetime = false;
+
+		if (file_exists($filename)) {
+			$filetime = @filemtime($filename);
+		}
+
+		if ($data === null) {
+			if (file_exists($filename) && $filetime !== false) {
+				if ($filetime + $timediff < $now) {
+					@unlink($filename);
+				} else {
+					$data = @file_get_contents($filename);
+				}
+			}
+		} elseif (is_writable(dirname($filename))) {
+			@file_put_contents($filename, $data);
+		}
+		return $data;
+	}
+/**
+ * Used to delete files in the cache directories, or clear contents of cache directories
+ *
+ * @param mixed $params As String name to be searched for deletion, if name is a directory all files in directory will be deleted.
+ *              If array, names to be searched for deletion.
+ *              If clearCache() without params, all files in app/tmp/cache/views will be deleted
+ *
+ * @param string $type Directory in tmp/cache defaults to view directory
+ * @param string $ext The file extension you are deleting
+ * @return true if files found and deleted false otherwise
+ */
+	function clearCache($params = null, $type = 'views', $ext = '.php') {
+		if (is_string($params) || $params === null) {
+			$params = preg_replace('/\/\//', '/', $params);
+			$cache = CACHE . $type . DS . $params;
+
+			if (is_file($cache . $ext)) {
+				@unlink($cache . $ext);
+				return true;
+			} elseif (is_dir($cache)) {
+				$files = glob($cache . '*');
+
+				if ($files === false) {
+					return false;
+				}
+
+				foreach ($files as $file) {
+					if (is_file($file)) {
+						@unlink($file);
+					}
+				}
+				return true;
+			} else {
+				$cache = array(
+					CACHE . $type . DS . '*' . $params . $ext,
+					CACHE . $type . DS . '*' . $params . '_*' . $ext
+				);
+				$files = array();
+				while ($search = array_shift($cache)) {
+					$results = glob($search);
+					if ($results !== false) {
+						$files = array_merge($files, $results);
+					}
+				}
+				if (empty($files)) {
+					return false;
+				}
+				foreach ($files as $file) {
+					if (is_file($file)) {
+						@unlink($file);
+					}
+				}
+				return true;
+			}
+		} elseif (is_array($params)) {
+			foreach ($params as $file) {
+				clearCache($file, $type, $ext);
+			}
+			return true;
+		}
+		return false;
+	}
+/**
+ * Recursively strips slashes from all values in an array
+ *
+ * @param array $values Array of values to strip slashes
+ * @return mixed What is returned from calling stripslashes
+ * @link http://book.cakephp.org/view/709/stripslashes_deep
+ */
+	function stripslashes_deep($values) {
+		if (is_array($values)) {
+			foreach ($values as $key => $value) {
+				$values[$key] = stripslashes_deep($value);
+			}
+		} else {
+			$values = stripslashes($values);
+		}
+		return $values;
+	}
+/**
+ * Returns a translated string if one is found; Otherwise, the submitted message.
+ *
+ * @param string $singular Text to translate
+ * @param boolean $return Set to true to return translated string, or false to echo
+ * @return mixed translated string if $return is false string will be echoed
+ * @link http://book.cakephp.org/view/693/__
+ */
+	function __($singular, $return = false) {
+		if (!$singular) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($singular);
+		} else {
+			return I18n::translate($singular);
+		}
+	}
+/**
+ * Returns correct plural form of message identified by $singular and $plural for count $count.
+ * Some languages have more than one form for plural messages dependent on the count.
+ *
+ * @param string $singular Singular text to translate
+ * @param string $plural Plural text
+ * @param integer $count Count
+ * @param boolean $return true to return, false to echo
+ * @return mixed plural form of translated string if $return is false string will be echoed
+ */
+	function __n($singular, $plural, $count, $return = false) {
+		if (!$singular) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($singular, $plural, null, 5, $count);
+		} else {
+			return I18n::translate($singular, $plural, null, 5, $count);
+		}
+	}
+/**
+ * Allows you to override the current domain for a single message lookup.
+ *
+ * @param string $domain Domain
+ * @param string $msg String to translate
+ * @param string $return true to return, false to echo
+ * @return translated string if $return is false string will be echoed
+ */
+	function __d($domain, $msg, $return = false) {
+		if (!$msg) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($msg, null, $domain);
+		} else {
+			return I18n::translate($msg, null, $domain);
+		}
+	}
+/**
+ * Allows you to override the current domain for a single plural message lookup.
+ * Returns correct plural form of message identified by $singular and $plural for count $count
+ * from domain $domain.
+ *
+ * @param string $domain Domain
+ * @param string $singular Singular string to translate
+ * @param string $plural Plural
+ * @param integer $count Count
+ * @param boolean $return true to return, false to echo
+ * @return plural form of translated string if $return is false string will be echoed
+ */
+	function __dn($domain, $singular, $plural, $count, $return = false) {
+		if (!$singular) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($singular, $plural, $domain, 5, $count);
+		} else {
+			return I18n::translate($singular, $plural, $domain, 5, $count);
+		}
+	}
+/**
+ * Allows you to override the current domain for a single message lookup.
+ * It also allows you to specify a category.
+ *
+ * The category argument allows a specific category of the locale settings to be used for fetching a message.
+ * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
+ *
+ * Note that the category must be specified with a numeric value, instead of the constant name.  The values are:
+ * LC_CTYPE     0
+ * LC_NUMERIC   1
+ * LC_TIME      2
+ * LC_COLLATE   3
+ * LC_MONETARY  4
+ * LC_MESSAGES  5
+ * LC_ALL       6
+ *
+ * @param string $domain Domain
+ * @param string $msg Message to translate
+ * @param integer $category Category
+ * @param boolean $return true to return, false to echo
+ * @return translated string if $return is false string will be echoed
+ */
+	function __dc($domain, $msg, $category, $return = false) {
+		if (!$msg) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($msg, null, $domain, $category);
+		} else {
+			return I18n::translate($msg, null, $domain, $category);
+		}
+	}
+/**
+ * Allows you to override the current domain for a single plural message lookup.
+ * It also allows you to specify a category.
+ * Returns correct plural form of message identified by $singular and $plural for count $count
+ * from domain $domain.
+ *
+ * The category argument allows a specific category of the locale settings to be used for fetching a message.
+ * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
+ *
+ * Note that the category must be specified with a numeric value, instead of the constant name.  The values are:
+ * LC_CTYPE     0
+ * LC_NUMERIC   1
+ * LC_TIME      2
+ * LC_COLLATE   3
+ * LC_MONETARY  4
+ * LC_MESSAGES  5
+ * LC_ALL       6
+ *
+ * @param string $domain Domain
+ * @param string $singular Singular string to translate
+ * @param string $plural Plural
+ * @param integer $count Count
+ * @param integer $category Category
+ * @param boolean $return true to return, false to echo
+ * @return plural form of translated string if $return is false string will be echoed
+ */
+	function __dcn($domain, $singular, $plural, $count, $category, $return = false) {
+		if (!$singular) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($singular, $plural, $domain, $category, $count);
+		} else {
+			return I18n::translate($singular, $plural, $domain, $category, $count);
+		}
+	}
+/**
+ * The category argument allows a specific category of the locale settings to be used for fetching a message.
+ * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL.
+ *
+ * Note that the category must be specified with a numeric value, instead of the constant name.  The values are:
+ * LC_CTYPE     0
+ * LC_NUMERIC   1
+ * LC_TIME      2
+ * LC_COLLATE   3
+ * LC_MONETARY  4
+ * LC_MESSAGES  5
+ * LC_ALL       6
+ *
+ * @param string $msg String to translate
+ * @param integer $category Category
+ * @param string $return true to return, false to echo
+ * @return translated string if $return is false string will be echoed
+ */
+	function __c($msg, $category, $return = false) {
+		if (!$msg) {
+			return;
+		}
+		if (!class_exists('I18n')) {
+			App::import('Core', 'i18n');
+		}
+
+		if ($return === false) {
+			echo I18n::translate($msg, null, null, $category);
+		} else {
+			return I18n::translate($msg, null, null, $category);
+		}
+	}
+/**
+ * Computes the difference of arrays using keys for comparison.
+ *
+ * @param array First array
+ * @param array Second array
+ * @return array Array with different keys
+ */
+	if (!function_exists('array_diff_key')) {
+		function array_diff_key() {
+			$valuesDiff = array();
+
+			$argc = func_num_args();
+			if ($argc < 2) {
+				return false;
+			}
+
+			$args = func_get_args();
+			foreach ($args as $param) {
+				if (!is_array($param)) {
+					return false;
+				}
+			}
+
+			foreach ($args[0] as $valueKey => $valueData) {
+				for ($i = 1; $i < $argc; $i++) {
+					if (isset($args[$i][$valueKey])) {
+						continue 2;
+					}
+				}
+				$valuesDiff[$valueKey] = $valueData;
+			}
+			return $valuesDiff;
+		}
+	}
+/**
+ * Computes the intersection of arrays using keys for comparison
+ *
+ * @param array First array
+ * @param array Second array
+ * @return array Array with interesected keys
+ */
+	if (!function_exists('array_intersect_key')) {
+		function array_intersect_key($arr1, $arr2) {
+			$res = array();
+			foreach ($arr1 as $key => $value) {
+				if (isset($arr2[$key])) {
+					$res[$key] = $arr1[$key];
+				}
+			}
+			return $res;
+		}
+	}
+/**
+ * Shortcut to Log::write.
+ *
+ * @param string $message Message to write to log
+ */
+	function LogError($message) {
+		if (!class_exists('CakeLog')) {
+			App::import('Core', 'CakeLog');
+		}
+		$bad = array("\n", "\r", "\t");
+		$good = ' ';
+		CakeLog::write('error', str_replace($bad, $good, $message));
+	}
+/**
+ * Searches include path for files.
+ *
+ * @param string $file File to look for
+ * @return Full path to file if exists, otherwise false
+ * @link http://book.cakephp.org/view/702/fileExistsInPath
+ */
+	function fileExistsInPath($file) {
+		$paths = explode(PATH_SEPARATOR, ini_get('include_path'));
+		foreach ($paths as $path) {
+			$fullPath = $path . DS . $file;
+
+			if (file_exists($fullPath)) {
+				return $fullPath;
+			} elseif (file_exists($file)) {
+				return $file;
+			}
+		}
+		return false;
+	}
+/**
+ * Convert forward slashes to underscores and removes first and last underscores in a string
+ *
+ * @param string String to convert
+ * @return string with underscore remove from start and end of string
+ * @link http://book.cakephp.org/view/697/convertSlash
+ */
+	function convertSlash($string) {
+		$string = trim($string, '/');
+		$string = preg_replace('/\/\//', '/', $string);
+		$string = str_replace('/', '_', $string);
+		return $string;
+	}
+/**
+ * Implements http_build_query for PHP4.
+ *
+ * @param string $data Data to set in query string
+ * @param string $prefix If numeric indices, prepend this to index for elements in base array.
+ * @param string $argSep String used to separate arguments
+ * @param string $baseKey Base key
+ * @return string URL encoded query string
+ * @see http://php.net/http_build_query
+ */
+	if (!function_exists('http_build_query')) {
+		function http_build_query($data, $prefix = null, $argSep = null, $baseKey = null) {
+			if (empty($argSep)) {
+				$argSep = ini_get('arg_separator.output');
+			}
+			if (is_object($data)) {
+				$data = get_object_vars($data);
+			}
+			$out = array();
+
+			foreach ((array)$data as $key => $v) {
+				if (is_numeric($key) && !empty($prefix)) {
+					$key = $prefix . $key;
+				}
+				$key = urlencode($key);
+
+				if (!empty($baseKey)) {
+					$key = $baseKey . '[' . $key . ']';
+				}
+
+				if (is_array($v) || is_object($v)) {
+					$out[] = http_build_query($v, $prefix, $argSep, $key);
+				} else {
+					$out[] = $key . '=' . urlencode($v);
+				}
+			}
+			return implode($argSep, $out);
+		}
+	}
+/**
+ * Wraps ternary operations. If $condition is a non-empty value, $val1 is returned, otherwise $val2.
+ * Don't use for isset() conditions, or wrap your variable with @ operator:
+ * Example:
+ * <code>
+ * ife(isset($variable), @$variable, 'default');
+ * </code>
+ *
+ * @param mixed $condition Conditional expression
+ * @param mixed $val1 Value to return in case condition matches
+ * @param mixed $val2 Value to return if condition doesn't match
+ * @return mixed $val1 or $val2, depending on whether $condition evaluates to a non-empty expression.
+ * @link http://book.cakephp.org/view/704/ife
+ */
+	function ife($condition, $val1 = null, $val2 = null) {
+		if (!empty($condition)) {
+			return $val1;
+		}
+		return $val2;
+	}
+?>
\ No newline at end of file
Index: cake/dispatcher.php
===================================================================
--- cake/dispatcher.php	(revision 0)
+++ cake/dispatcher.php	(revision 0)
@@ -0,0 +1,710 @@
+<?php
+/* SVN FILE: $Id: dispatcher.php 7961 2008-12-25 23:21:36Z gwoo $ */
+/**
+ * Dispatcher takes the URL information, parses it for paramters and
+ * tells the involved controllers what to do.
+ *
+ * This is the heart of Cake's operation.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7961 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-25 15:21:36 -0800 (Thu, 25 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * List of helpers to include
+ */
+App::import('Core', array('Router', 'Controller'));
+/**
+ * Dispatcher translates URLs to controller-action-paramter triads.
+ *
+ * Dispatches the request, creating appropriate models and controllers.
+ *
+ * @package       cake
+ * @subpackage    cake.cake
+ */
+class Dispatcher extends Object {
+/**
+ * Base URL
+ *
+ * @var string
+ * @access public
+ */
+	var $base = false;
+/**
+ * webroot path
+ *
+ * @var string
+ * @access public
+ */
+	var $webroot = '/';
+/**
+ * Current URL
+ *
+ * @var string
+ * @access public
+ */
+	var $here = false;
+/**
+ * Admin route (if on it)
+ *
+ * @var string
+ * @access public
+ */
+	var $admin = false;
+/**
+ * Plugin being served (if any)
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+/**
+ * the params for this request
+ *
+ * @var string
+ * @access public
+ */
+	var $params = null;
+/**
+ * Constructor.
+ */
+	function __construct($url = null, $base = false) {
+		if ($base !== false) {
+			Configure::write('App.base', $base);
+		}
+
+		if ($url !== null) {
+			return $this->dispatch($url);
+		}
+	}
+/**
+ * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the results (if autoRender is set).
+ *
+ * If no controller of given name can be found, invoke() shows error messages in
+ * the form of Missing Controllers information. It does the same with Actions (methods of Controllers are called
+ * Actions).
+ *
+ * @param string $url URL information to work on
+ * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
+ * @return boolean Success
+ * @access public
+ */
+	function dispatch($url = null, $additionalParams = array()) {
+		$parse = true;
+
+		if (is_array($url)) {
+			$url = $this->__extractParams($url, $additionalParams);
+			$parse = false;
+		}
+
+		if ($this->base === false) {
+			$this->base = $this->baseUrl();
+		}
+
+		if ($url !== null) {
+			$_GET['url'] = $url;
+		}
+
+		if ($parse) {
+			$url = $this->getUrl();
+		}
+		$this->here = $this->base . '/' . $url;
+
+		if ($this->cached($url)) {
+			$this->_stop();
+		}
+
+		if ($parse) {
+			$this->params = array_merge($this->parseParams($url), $additionalParams);
+		}
+		$controller =& $this->__getController();
+
+		if (!is_object($controller)) {
+			Router::setRequestInfo(array($this->params, array('base' => $this->base, 'webroot' => $this->webroot)));
+			return $this->cakeError('missingController', array(
+				array(
+					'className' => Inflector::camelize($this->params['controller']) . 'Controller',
+					'webroot' => $this->webroot,
+					'url' => $url,
+					'base' => $this->base
+				)
+			));
+		}
+
+		$privateAction = (bool)(strpos($this->params['action'], '_', 0) === 0);
+		$prefixes = Router::prefixes();
+
+		if (!empty($prefixes)) {
+			if (isset($this->params['prefix'])) {
+				$this->params['action'] = $this->params['prefix'] . '_' . $this->params['action'];
+			} elseif (strpos($this->params['action'], '_') !== false && !$privateAction) {
+				list($prefix, $action) = explode('_', $this->params['action']);
+				$privateAction = in_array($prefix, $prefixes);
+			}
+		}
+
+		Router::setRequestInfo(array(
+			$this->params, array('base' => $this->base, 'here' => $this->here, 'webroot' => $this->webroot)
+		));
+
+		if ($privateAction) {
+			return $this->cakeError('privateAction', array(
+				array(
+					'className' => Inflector::camelize($this->params['controller'] . "Controller"),
+					'action' => $this->params['action'],
+					'webroot' => $this->webroot,
+					'url' => $url,
+					'base' => $this->base
+				)
+			));
+		}
+
+		$controller->base = $this->base;
+		$controller->here = $this->here;
+		$controller->webroot = $this->webroot;
+		$controller->plugin = $this->plugin;
+		$controller->params =& $this->params;
+		$controller->action =& $this->params['action'];
+		$controller->passedArgs = array_merge($this->params['pass'], $this->params['named']);
+
+		if (!empty($this->params['data'])) {
+			$controller->data =& $this->params['data'];
+		} else {
+			$controller->data = null;
+		}
+
+		if (array_key_exists('return', $this->params) && $this->params['return'] == 1) {
+			$controller->autoRender = false;
+		}
+
+		if (!empty($this->params['bare'])) {
+			$controller->autoLayout = false;
+		}
+
+		if (array_key_exists('layout', $this->params)) {
+			if (empty($this->params['layout'])) {
+				$controller->autoLayout = false;
+			} else {
+				$controller->layout = $this->params['layout'];
+			}
+		}
+
+		if (isset($this->params['viewPath'])) {
+			$controller->viewPath = $this->params['viewPath'];
+		}
+
+		return $this->_invoke($controller, $this->params);
+	}
+/**
+ * Invokes given controller's render action if autoRender option is set. Otherwise the
+ * contents of the operation are returned as a string.
+ *
+ * @param object $controller Controller to invoke
+ * @param array $params Parameters with at least the 'action' to invoke
+ * @param boolean $missingAction Set to true if missing action should be rendered, false otherwise
+ * @return string Output as sent by controller
+ * @access protected
+ */
+	function _invoke(&$controller, $params) {
+		$controller->constructClasses();
+		$controller->Component->initialize($controller);
+		$controller->beforeFilter();
+		$controller->Component->startup($controller);
+
+		$methods = array_flip($controller->methods);
+
+		if (!isset($methods[strtolower($params['action'])])) {
+			if ($controller->scaffold !== false) {
+				App::import('Core', 'Scaffold');
+				return new Scaffold($controller, $params);
+			}
+			return $this->cakeError('missingAction', array(
+				array(
+					'className' => Inflector::camelize($params['controller']."Controller"),
+					'action' => $params['action'],
+					'webroot' => $this->webroot,
+					'url' => $this->here,
+					'base' => $this->base)));
+
+		}
+		$output = $controller->dispatchMethod($params['action'], $params['pass']);
+
+		if ($controller->autoRender) {
+			$controller->output = $controller->render();
+		} elseif (empty($controller->output)) {
+			$controller->output = $output;
+		}
+		$controller->Component->shutdown($controller);
+		$controller->afterFilter();
+
+		if (isset($params['return'])) {
+			return $controller->output;
+		}
+		echo($controller->output);
+	}
+/**
+ * Sets the params when $url is passed as an array to Object::requestAction();
+ *
+ * @param array $url
+ * @param array $additionalParams
+ * @return null
+ * @access private
+ * @todo commented Router::url(). this improved performance,
+ *		 will work on this more later.
+ */
+	function __extractParams($url, $additionalParams = array()) {
+		$defaults = array('pass' => array(), 'named' => array(), 'form' => array());
+		$this->params = array_merge($defaults, $url, $additionalParams);
+		//$url = Router::url($url);
+		//return $url;
+	}
+/**
+ * Returns array of GET and POST parameters. GET parameters are taken from given URL.
+ *
+ * @param string $fromUrl URL to mine for parameter information.
+ * @return array Parameters found in POST and GET.
+ * @access public
+ */
+	function parseParams($fromUrl) {
+		$params = array();
+
+		if (isset($_POST)) {
+			$params['form'] = $_POST;
+			if (ini_get('magic_quotes_gpc') === '1') {
+				$params['form'] = stripslashes_deep($params['form']);
+			}
+			if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
+				$params['form']['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE');
+			}
+			if (isset($params['form']['_method'])) {
+				if (isset($_SERVER) && !empty($_SERVER)) {
+					$_SERVER['REQUEST_METHOD'] = $params['form']['_method'];
+				} else {
+					$_ENV['REQUEST_METHOD'] = $params['form']['_method'];
+				}
+				unset($params['form']['_method']);
+			}
+		}
+		extract(Router::getNamedExpressions());
+		include CONFIGS . 'routes.php';
+		$params = array_merge(Router::parse($fromUrl), $params);
+
+		if (empty($params['action'])) {
+			$params['action'] = 'index';
+		}
+
+		if (isset($params['form']['data'])) {
+			$params['data'] = Router::stripEscape($params['form']['data']);
+			unset($params['form']['data']);
+		}
+
+		if (isset($_GET)) {
+			if (ini_get('magic_quotes_gpc') === '1') {
+				$url = stripslashes_deep($_GET);
+			} else {
+				$url = $_GET;
+			}
+			if (isset($params['url'])) {
+				$params['url'] = array_merge($params['url'], $url);
+			} else {
+				$params['url'] = $url;
+			}
+		}
+
+		foreach ($_FILES as $name => $data) {
+			if ($name != 'data') {
+				$params['form'][$name] = $data;
+			}
+		}
+
+		if (isset($_FILES['data'])) {
+			foreach ($_FILES['data'] as $key => $data) {
+				foreach ($data as $model => $fields) {
+					foreach ($fields as $field => $value) {
+						if (is_array($value)) {
+							foreach ($value as $k => $v) {
+								$params['data'][$model][$field][$k][$key] = $v;
+							}
+						} else {
+							$params['data'][$model][$field][$key] = $value;
+						}
+					}
+				}
+			}
+		}
+		return $params;
+	}
+/**
+ * Returns a base URL and sets the proper webroot
+ *
+ * @return string Base URL
+ * @access public
+ */
+	function baseUrl() {
+		$dir = $webroot = null;
+		$config = Configure::read('App');
+		extract($config);
+
+		if (!$base) {
+			$base = $this->base;
+		}
+
+		if ($base !== false) {
+			$this->webroot = $base . '/';
+			return $this->base = $base;
+		}
+
+		if (!$baseUrl) {
+			$base = dirname(env('PHP_SELF'));
+
+			if ($webroot === 'webroot' && $webroot === basename($base)) {
+				$base = dirname($base);
+			}
+			if ($dir === 'app' && $dir === basename($base)) {
+				$base = dirname($base);
+			}
+
+			if ($base === DS || $base === '.') {
+				$base = '';
+			}
+
+			$this->webroot = $base .'/';
+			return $base;
+		}
+		$file = null;
+
+		if ($baseUrl) {
+			$file = '/' . basename($baseUrl);
+			$base = dirname($baseUrl);
+
+			if ($base === DS || $base === '.') {
+				$base = '';
+			}
+			$this->webroot = $base .'/';
+
+			if (strpos($this->webroot, $dir) === false) {
+				$this->webroot .= $dir . '/' ;
+			}
+			if (strpos($this->webroot, $webroot) === false) {
+				$this->webroot .= $webroot . '/';
+			}
+			return $base . $file;
+		}
+		return false;
+	}
+/**
+ * Restructure params in case we're serving a plugin.
+ *
+ * @param array $params Array on where to re-set 'controller', 'action', and 'pass' indexes
+ * @param boolean $reverse
+ * @return array Restructured array
+ * @access protected
+ */
+	function _restructureParams($params, $reverse = false) {
+		if ($reverse === true) {
+			extract(Router::getArgs($params['action']));
+			$params = array_merge($params, array('controller'=> $params['plugin'],
+						'action'=> $params['controller'],
+						'pass' => array_merge($pass, $params['pass']),
+						'named' => array_merge($named, $params['named'])));
+			$this->plugin = $params['plugin'];
+		} else {
+			$params['plugin'] = $params['controller'];
+			$params['controller'] = $params['action'];
+			if (isset($params['pass'][0])) {
+				$params['action'] = $params['pass'][0];
+				array_shift($params['pass']);
+			} else {
+				$params['action'] = null;
+			}
+		}
+		return $params;
+	}
+/**
+ * Get controller to use, either plugin controller or application controller
+ *
+ * @param array $params Array of parameters
+ * @return mixed name of controller if not loaded, or object if loaded
+ * @access private
+ */
+	function &__getController($params = null) {
+		if (!is_array($params)) {
+			$params = $this->params;
+		}
+		$controller = false;
+
+		if (!$ctrlClass = $this->__loadController($params)) {
+			if (!isset($params['plugin'])) {
+				$params = $this->_restructureParams($params);
+			} else {
+				$params = $this->_restructureParams($params, true);
+			}
+
+			if (!$ctrlClass = $this->__loadController($params)) {
+				return $controller;
+			}
+		}
+		$name = $ctrlClass;
+		$ctrlClass = $ctrlClass . 'Controller';
+
+		if (class_exists($ctrlClass)) {
+			if (strtolower(get_parent_class($ctrlClass)) === strtolower($name . 'AppController') && empty($params['plugin'])) {
+				$params = $this->_restructureParams($params);
+				$params = $this->_restructureParams($params, true);
+			}
+			$this->params = $params;
+			$controller =& new $ctrlClass();
+		}
+		return $controller;
+	}
+/**
+ * Load controller and return controller class
+ *
+ * @param array $params Array of parameters
+ * @return string|bool Name of controller class name
+ * @access private
+ */
+	function __loadController($params) {
+		$pluginName = $pluginPath = $controller = null;
+
+		if (!empty($params['plugin'])) {
+			$this->plugin = $params['plugin'];
+			$pluginName = Inflector::camelize($params['plugin']);
+			$pluginPath = $pluginName . '.';
+			$this->params['controller'] = $this->plugin;
+			$controller = $pluginName;
+		}
+
+		if (!empty($params['controller'])) {
+			$controller = Inflector::camelize($params['controller']);
+		}
+
+		if ($pluginPath . $controller) {
+			if (App::import('Controller', $pluginPath . $controller)) {
+				return $controller;
+			}
+		}
+		return false;
+	}
+/**
+ * Returns the REQUEST_URI from the server environment, or, failing that,
+ * constructs a new one, using the PHP_SELF constant and other variables.
+ *
+ * @return string URI
+ * @access public
+ */
+	function uri() {
+		foreach (array('HTTP_X_REWRITE_URL', 'REQUEST_URI', 'argv') as $var) {
+			if ($uri = env($var)) {
+				if ($var == 'argv') {
+					$uri = $uri[0];
+				}
+				break;
+			}
+		}
+		$base = preg_replace('/^\//', '', '' . Configure::read('App.baseUrl'));
+
+		if ($base) {
+			$uri = preg_replace('/^(?:\/)?(?:' . preg_quote($base, '/') . ')?(?:url=)?/', '', $uri);
+		}
+
+		if (PHP_SAPI == 'isapi') {
+			$uri = preg_replace('/^(?:\/)?(?:\/)?(?:\?)?(?:url=)?/', '', $uri);
+		}
+
+		if (!empty($uri)) {
+			if (key($_GET) && strpos(key($_GET), '?') !== false) {
+				unset($_GET[key($_GET)]);
+			}
+			$uri = preg_split('/\?/', $uri, 2);
+
+			if (isset($uri[1])) {
+				parse_str($uri[1], $_GET);
+			}
+			$uri = $uri[0];
+		} elseif (empty($uri) && is_string(env('QUERY_STRING'))) {
+			$uri = env('QUERY_STRING');
+		}
+
+		if (strpos($uri, 'index.php') !== false) {
+			list(, $uri) = explode('index.php', $uri, 2);
+		}
+
+		if (empty($uri) || $uri == '/' || $uri == '//') {
+			return '';
+		}
+		return str_replace('//', '/', '/' . $uri);
+	}
+/**
+ * Returns and sets the $_GET[url] derived from the REQUEST_URI
+ *
+ * @param string $uri Request URI
+ * @param string $base Base path
+ * @return string URL
+ * @access public
+ */
+	function getUrl($uri = null, $base = null) {
+		if (empty($_GET['url'])) {
+			if ($uri == null) {
+				$uri = $this->uri();
+			}
+
+			if ($base == null) {
+				$base = $this->base;
+			}
+			$url = null;
+			$tmpUri = preg_replace('/^(?:\?)?(?:\/)?/', '', $uri);
+			$baseDir = preg_replace('/^\//', '', dirname($base)) . '/';
+
+			if ($tmpUri === '/' || $tmpUri == $baseDir || $tmpUri == $base) {
+				$url = $_GET['url'] = '/';
+			} else {
+				if ($base && strpos($uri, $base) !== false) {
+					$elements = explode($base, $uri);
+				} elseif (preg_match('/^[\/\?\/|\/\?|\?\/]/', $uri)) {
+					$elements = array(1 => preg_replace('/^[\/\?\/|\/\?|\?\/]/', '', $uri));
+				} else {
+					$elements = array();
+				}
+
+				if (!empty($elements[1])) {
+					$_GET['url'] = $elements[1];
+					$url = $elements[1];
+				} else {
+					$url = $_GET['url'] = '/';
+				}
+
+				if (strpos($url, '/') === 0 && $url != '/') {
+					$url = $_GET['url'] = substr($url, 1);
+				}
+			}
+		} else {
+			$url = $_GET['url'];
+		}
+
+		if ($url{0} == '/') {
+			$url = substr($url, 1);
+		}
+		return $url;
+	}
+/**
+ * Outputs cached dispatch for js, css, img, view cache
+ *
+ * @param string $url Requested URL
+ * @access public
+ */
+	function cached($url) {
+		if (strpos($url, 'css/') !== false || strpos($url, 'js/') !== false || strpos($url, 'img/') !== false) {
+			if (strpos($url, 'ccss/') === 0) {
+				include WWW_ROOT . DS . Configure::read('Asset.filter.css');
+				$this->_stop();
+			} elseif (strpos($url, 'cjs/') === 0) {
+				include WWW_ROOT . DS . Configure::read('Asset.filter.js');
+				$this->_stop();
+			}
+			$isAsset = false;
+			$assets = array('js' => 'text/javascript', 'css' => 'text/css', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'png' => 'image/png');
+			$ext = array_pop(explode('.', $url));
+
+			foreach ($assets as $type => $contentType) {
+				if ($type === $ext) {
+					if ($type === 'css' || $type === 'js') {
+						$pos = strpos($url, $type . '/');
+					} else {
+						$pos = strpos($url, 'img/');
+					}
+					$isAsset = true;
+					break;
+				}
+			}
+
+			if ($isAsset === true) {
+				$ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
+
+				if ($ob && Configure::read('Asset.compress')) {
+					ob_start();
+					ob_start('ob_gzhandler');
+				}
+				$assetFile = null;
+				$paths = array();
+
+				if ($pos > 0) {
+					$plugin = substr($url, 0, $pos - 1);
+					$url = str_replace($plugin . '/', '', $url);
+					$pluginPaths = Configure::read('pluginPaths');
+					$count = count($pluginPaths);
+					for ($i = 0; $i < $count; $i++) {
+						$paths[] = $pluginPaths[$i] . $plugin . DS . 'vendors' . DS;
+					}
+				}
+				$paths = array_merge($paths, Configure::read('vendorPaths'));
+
+				foreach ($paths as $path) {
+					if (is_file($path . $url) && file_exists($path . $url)) {
+						$assetFile = $path . $url;
+						break;
+					}
+				}
+
+				if ($assetFile !== null) {
+					$fileModified = filemtime($assetFile);
+					header("Date: " . date("D, j M Y G:i:s ", $fileModified) . 'GMT');
+					header('Content-type: ' . $assets[$type]);
+					header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
+					header("Cache-Control: cache");
+					header("Pragma: cache");
+					if ($type === 'css' || $type === 'js') {
+						include($assetFile);
+					} else {
+						readfile($assetFile);
+					}
+
+					if (Configure::read('Asset.compress')) {
+						ob_end_flush();
+					}
+					return true;
+				}
+			}
+		}
+
+		if (Configure::read('Cache.check') === true) {
+			$path = $this->here;
+			if ($this->here == '/') {
+				$path = 'home';
+			}
+			$path = strtolower(Inflector::slug($path));
+
+			$filename = CACHE . 'views' . DS . $path . '.php';
+
+			if (!file_exists($filename)) {
+				$filename = CACHE . 'views' . DS . $path . '_index.php';
+			}
+
+			if (file_exists($filename)) {
+				if (!class_exists('View')) {
+					App::import('Core', 'View');
+				}
+				$controller = null;
+				$view =& new View($controller, false);
+				return $view->renderCache($filename, getMicrotime());
+			}
+		}
+		return false;
+	}
+}
+?>
\ No newline at end of file
Index: README
===================================================================
--- README	(revision 0)
+++ README	(revision 0)
@@ -0,0 +1,34 @@
+CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC. Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility.
+
+The Cake Software Foundation - promoting development related to CakePHP
+http://www.cakefoundation.org/
+
+CakePHP - the rapid development PHP framework
+http://www.cakephp.org
+
+Cookbook - user documentation for learning about CakePHP
+http://book.cakephp.org
+
+API - quick reference to CakePHP
+http://api.cakephp.org
+
+The Bakery - everything CakePHP
+http://bakery.cakephp.org
+
+The Show - live and archived podcasts about CakePHP and more
+http://live.cakephp.org
+
+CakePHP Google Group - community mailing list and forum
+http://groups.google.com/group/cake-php
+
+#cakephp on irc.freenode.net - chat with CakePHP developers
+irc://irc.freenode.net/cakephp
+
+CakeForge - open development for CakePHP
+http://cakeforge.org
+
+CakePHP gear
+http://www.cafepress.com/cakefoundation
+
+Recommended Reading
+http://astore.amazon.com/cakesoftwaref-20/
\ No newline at end of file
Index: index.php
===================================================================
--- index.php	(revision 0)
+++ index.php	(revision 0)
@@ -0,0 +1,62 @@
+<?php
+/* SVN FILE: $Id: index.php 7945 2008-12-19 02:16:01Z gwoo $ */
+/**
+ * Requests collector.
+ *
+ *  This file collects requests if:
+ *	- no mod_rewrite is avilable or .htaccess files are not supported
+ *	-/public is not set as a web root.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @version       $Revision: 7945 $
+ * @modifiedby    $LastChangedBy: gwoo $
+ * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ *  Get Cake's root directory
+ */
+	define('APP_DIR', 'app');
+	define('DS', DIRECTORY_SEPARATOR);
+	define('ROOT', dirname(__FILE__));
+	define('WEBROOT_DIR', 'webroot');
+	define('WWW_ROOT', ROOT . DS . APP_DIR . DS . WEBROOT_DIR . DS);
+/**
+ * This only needs to be changed if the cake installed libs are located
+ * outside of the distributed directory structure.
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		//define ('CAKE_CORE_INCLUDE_PATH', FULL PATH TO DIRECTORY WHERE CAKE CORE IS INSTALLED DO NOT ADD A TRAILING DIRECTORY SEPARATOR';
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+	if (function_exists('ini_set')) {
+		ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS);
+		define('APP_PATH', null);
+		define('CORE_PATH', null);
+	} else {
+		define('APP_PATH', ROOT . DS . APP_DIR . DS);
+		define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+	}
+	require CORE_PATH . 'cake' . DS . 'basics.php';
+	$TIME_START = getMicrotime();
+	require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php';
+	require LIBS . 'object.php';
+	require LIBS . 'inflector.php';
+	require LIBS . 'configure.php';
+
+	$bootstrap = true;
+	$url = null;
+	require APP_DIR . DS . WEBROOT_DIR . DS . 'index.php';
+?>
\ No newline at end of file
